//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Thu Mar 29 17:22:53 PST 2001
// Last Modified: Sun Feb 16 07:58:18 PST 2003 (convert to RootSpectrum class)
// Last Modified: Thu Jun 3 13:39:25 PDT 2004 (adjusted statistics output)
// Filename: ...sig/examples/all/rootcomp.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/rootcomp.cpp
// Syntax: C++; museinfo
//
// Description: determine the root of chords in music according
// to the timespan of a preexisting root analysis spine.
//
#include "humdrum.h"
#include <string.h>
#include <math.h>
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void generateAnalysis(HumdrumFile& hfile);
void analyzeDataLine(HumdrumFile& hfile, int line);
// global variables
Options options; // database for command-line arguments
int debugQ = 0; // used with the --debug option
int rawQ = 0; // used with the --raw option
int appendQ = 0; // used with the -a option
double rval = 1.0; // used with the -r option
double rlevel = 1.0; // used with the -r option
int octave = -1; // used with the -o option
int weightsQ = 0; // used with the -w option
int algorithm = 0; // used with the --algorithm option
int countQ = 0; // used with the -c option
int errorQ = 0; // used with the -e option
int errorcount = 0; // used with the -c option
double dweight = 0.25;// duration weight from -d option
double lweight = 0.25;// metric level weight from -l option
int durationQ = 1; // used with -D option
int levelQ = 1; // used with -M option
int melodyQ = 1; // used with -H option
int linearQ = 1; // used with -L option
double scaleFactor= 1.0; // used with -s option
int chordsum = 0;
int errorsum = 0;
int summaryQ = 0; // used with -S option
int rateQ = 0; // used with --rate option
int fullQ = 0; // display full input
int chordcountQ = 0; // used with --totalchords
IntervalWeight iweights;
RootSpectrum spectrum;
///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
HumdrumFile hfile;
// process the command-line options
checkOptions(options, argc, argv);
// figure out the number of input files to process
int numinputs = options.getArgCount();
Array<int> rootanalysis; // roots analysed at various times
Array<double> rootbeat; // absolute start beat of root
Array<double> rootscores; // score of best root
for (int i=0; i<numinputs || i==0; i++) {
hfile.clear();
// if no command-line arguments read data file from standard input
if (numinputs < 1) {
hfile.read(cin);
} else {
hfile.read(options.getArg(i+1));
}
generateAnalysis(hfile);
}
if (rateQ) {
cout << 100.0*errorsum/chordsum << endl;
} else if (chordcountQ) {
cout << chordsum << endl;
} else if (summaryQ) {
cout << "!! Total Number of Chords: " << chordsum << endl;
cout << "!! Total Errors: " << errorsum << endl;
cout << "!! Error Rate: " << 100.0*errorsum/chordsum << "%" << endl;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// generateAnalysis -- analyze the roots of the chords
//
void generateAnalysis(HumdrumFile& hfile) {
hfile.analyzeRhythm();
int i;
for (i=0; i<hfile.getNumLines(); i++) {
switch (hfile[i].getType()) {
case E_humrec_none:
case E_humrec_empty:
case E_humrec_global_comment:
case E_humrec_bibliography:
if (fullQ) {
cout << hfile[i].getLine() << endl;
}
break;
case E_humrec_data_comment:
if (fullQ) {
cout << hfile[i].getLine() << "\t!" << endl;
}
break;
case E_humrec_data_kern_measure:
if (fullQ) {
cout << hfile[i].getLine() << "\t" << hfile[i][0] << endl;
}
break;
case E_humrec_interpretation:
if (fullQ) {
cout << hfile[i].getLine() << "\t*" << endl;
}
break;
case E_humrec_data:
analyzeDataLine(hfile, i);
break;
default:
break;
}
}
if (countQ && !rateQ) {
cout << "!! Total root errors: " << errorcount << endl;
}
}
//////////////////////////////
//
// analyzeDataLine --
//
void analyzeDataLine(HumdrumFile& hfile, int line) {
Array<double> values;
values.setSize(0);
int startindex;
int stopindex;
int nextroot;
int lastroot = -1;
int most = 0;
int i;
int rooti = -1;
for (i=0; i<hfile[line].getFieldCount(); i++) {
if (strcmp(hfile[line].getExInterp(i), "**root") == 0) {
rooti = i;
break;
}
}
if (rooti == -1) {
cout << "Error: no **root spine" << endl;
exit(1);
}
if (strcmp(hfile[line][rooti], ".") == 0) {
if (fullQ) {
cout << hfile[line].getLine() << "\t." << endl;
}
return;
}
if (strchr(hfile[line][rooti], 'r') != NULL) {
// ignore rests
if (fullQ) {
cout << hfile[line].getLine() << "\t." << endl;
}
return;
}
double dur = Convert::kernToDuration(hfile[line][rooti]);
char durstring[32] = {0};
char pitchbuffer[32] = {0};
Convert::durationToKernRhythm(durstring, dur);
int pitch = Convert::kernToBase40(hfile[line][rooti]);
startindex = line;
stopindex = hfile.getStopIndex(hfile[line].getAbsBeat() + dur - 0.01);
// where all of the work gets done:
most = spectrum.calculate(iweights, hfile, startindex, stopindex, debugQ);
chordsum++;
if (most >= 0) {
if (octave < 0) {
nextroot = (most + 2) % 40 + 80;
if (abs(nextroot + 40 - lastroot) < 13) {
nextroot += 40;
} else if (abs(nextroot - 40 - lastroot) < 13) {
nextroot -= 40;
}
} else {
nextroot = (most + 2) % 40 + 40 * octave;
}
Convert::base40ToKern(pitchbuffer, nextroot);
lastroot = nextroot;
} else {
nextroot = 0;
pitchbuffer[0] = 'r';
pitchbuffer[1] = '\0';
}
if ((pitch % 40) != (nextroot % 40)) {
errorcount++;
errorsum++;
}
if (fullQ) {
if (strchr(durstring, '-') != 0) {
cout << hfile[line] << "\t" << pitchbuffer << endl;
} else {
cout << hfile[line] << "\t" << durstring << pitchbuffer << endl;
}
} else {
if (errorQ && ((pitch % 40) != (nextroot % 40))) {
Convert::base40ToKern(pitchbuffer, pitch);
cout << "Wanted: " << pitchbuffer;
Convert::base40ToKern(pitchbuffer, nextroot);
cout << "\tbut got: " << pitchbuffer;
cout << "\ton line: " << line+1 << endl;
}
}
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("n|norm=b", "normalize root scores so that best is 1.0");
opts.define("t|theta=d:55.0", "theta1 for interval weight calculations");
opts.define("F|no-functions=b", "do not display MMA functions for plotting");
opts.define("wf|weightfile=s", "interval weight file");
opts.define("W|display-weights=b", "display interval weights and exit");
opts.define("H|no-non-harmonic=b", "don't do non-harmonic note id");
opts.define("I|no-invert=b", "don't invert root scores");
opts.define("L|rhythm-log=b", "calculate rhythm in log scale");
opts.define("d|duration-weight=d:0.25", "duration weighting factor");
opts.define("D|duration-weighting-off=b", "turn off duration weighting");
opts.define("m|metric-weight=d:0.25", "metric level weighting factor");
opts.define("M|metric-weighting-off=b", "turn off metric weighting");
opts.define("R|rhythm-weighting-off=b", "turn off duration and metric weighting");
opts.define("rate=b", "display only the root error rate in percentage");
opts.define("range|lines=s:-1:-1", "range of lines offset from 1 to analyze in file");
opts.define("data=b", "trace input parsing");
opts.define("a|append=b", "append analysis to data in output");
opts.define("summary=b", "summarize error rate of all files");
opts.define("raw=b", "raw display of output");
opts.define("c|count=b", "count the numbers of errors only");
opts.define("e|error=b", "display pitch errors");
opts.define("r|rhythm=i:4", "rhythm to measure root at");
opts.define("s|non-harmonic-scale-factor=d:1.0", "scale factor for non-harmonic tones");
opts.define("S|non-resolution-scaling=d:1.0", "scale factor for non-harmonic notes which do not resolve");
opts.define("o|octave=i:-1", "octave number of bass line");
opts.define("w|weights=b", "display parallel spine of scores next to roots");
opts.define("algorithm=i:0", "analysis algorithm to use");
opts.define("totalcount=b", "display a count of all chords");
opts.define("debug=b", "trace input parsing");
opts.define("author=b", "author of the program");
opts.define("version=b", "compilation information");
opts.define("example=b", "example usage");
opts.define("h|help=b", "short description");
opts.process(argc, argv);
// handle basic options:
if (opts.getBoolean("author")) {
cout << "Written by Craig Stuart Sapp, "
<< "craig@ccrma.stanford.edu, Dec 2000" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 16 Feb 2003" << endl;
cout << "compiled: " << __DATE__ << endl;
cout << MUSEINFO_VERSION << endl;
exit(0);
} else if (opts.getBoolean("help")) {
usage(opts.getCommand());
exit(0);
} else if (opts.getBoolean("example")) {
example();
exit(0);
}
lweight = opts.getDouble("metric-weight");
dweight = opts.getDouble("duration-weight");
durationQ = !opts.getBoolean("duration-weighting-off");
levelQ = !opts.getBoolean("metric-weighting-off");
linearQ = !opts.getBoolean("rhythm-log");
melodyQ = !opts.getBoolean("no-non-harmonic");
scaleFactor= opts.getDouble("non-harmonic-scale-factor");
if (opts.getBoolean("rhythm-weighting-off")) {
durationQ = 0;
levelQ = 0;
}
// set the spectrum analysis values
spectrum.setResolutionFactor(opts.getDouble("non-resolution-scaling"));
spectrum.setDuration(durationQ);
spectrum.setMetricLevel(levelQ);
spectrum.setMelodyScaleFactor(scaleFactor);
if (linearQ) {
spectrum.rhythmLinear();
spectrum.setDurationWeight(dweight);
spectrum.setMetricLevelWeight(lweight);
} else {
spectrum.rhythmLog();
spectrum.setDurationBias(dweight);
spectrum.setMeterBias(lweight);
}
if (melodyQ) {
spectrum.melodyOn(); // do non-harmonic tone estimation
} else {
spectrum.melodyOff(); // do not do non-harmonic tone estimation
}
if (opts.getBoolean("weightfile")) {
iweights.setFromFile(opts.getString("weightfile"));
} else {
iweights.setChromatic1(opts.getDouble("theta"));
}
if (opts.getBoolean("display-weights")) {
cout << iweights;
exit(0);
}
debugQ = opts.getBoolean("debug");
appendQ = opts.getBoolean("append");
rawQ = opts.getBoolean("raw");
countQ = opts.getBoolean("count");
chordcountQ = opts.getBoolean("totalcount");
errorQ = opts.getBoolean("error");
if (errorQ) {
countQ = 1;
}
rateQ = opts.getBoolean("rate");
octave = opts.getInteger("octave");
weightsQ = opts.getBoolean("weights");
summaryQ = opts.getBoolean("summary");
if (rateQ) {
fullQ = 0;
}
if (countQ) {
fullQ = 0;
}
if (chordcountQ) {
fullQ = 0;
}
}
//////////////////////////////
//
// example -- example usage of the maxent program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the quality program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
// md5sum: ff3f93a54ba66f1647a9921317a30828 rootcomp.cpp [20050403]