// // 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]