// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Creation Date: Wed Oct 27 11:03:38 PDT 2004 // Last Modified: Wed Oct 27 12:33:26 PDT 2004 // Filename: ...sig/examples/all/modid.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/modid.cpp // Syntax: C++; museinfo // // Description: Automatic modulation identification using the // Krumhansl-Schmuckler key-finding algorithm. // // A modulation is defined with following algorithm: // (1) defined a window size (e.g. 16 beats). // (2) choose a test modulation position in the score. // (3) if the window of music starting at that window generate the same // key analysis as a 1/2 window at the same location, then there // is a stable key after the test modulation position. // (4) if the window of music before the starting position is stable // according to the same method, then continue. // (5) if a window centered at the modulation point gives a lower // score for the two keys from the window before and after the // test modulation point, then mark the position in the score // as a modulation. // #include "humdrum.h" // function declarations void checkOptions(Options& opts, int argc, char* argv[]); void example(void); void usage(const char* command); int getDurationLineStart(HumdrumFile& infile, double target); int getDurationLineEnd(HumdrumFile& infile, double target); void getModulation(int& mod, int& before, HumdrumFile& infile, int line, double window); void getModulationAnalysis(HumdrumFile& infile, Array<int>& modulations, Array<int>& beforemod); void printModulations(HumdrumFile& infile, Array<int>& modulations, Array<int>& beforemod); void printPitch(int value); // user interface variables Options options; // database for command-line arguments int appendQ = 0; // used with -a option int prependQ = 0; // used with -p option double window = 16.0; // used with -w option int debugQ = 0; // used with --debug option /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { // process the command-line options checkOptions(options, argc, argv); // if no command-line arguments read data file from standard input HumdrumFile infile; if (options.getArgCount() < 1) { infile.read(cin); } else { infile.read(options.getArg(1)); } infile.analyzeRhythm(); Array<int> modulations; Array<int> beforemod; getModulationAnalysis(infile, modulations, beforemod); printModulations(infile, modulations, beforemod); return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // printModulations -- // void printModulations(HumdrumFile& infile, Array<int>& modulations, Array<int>& beforemod) { int i; for (i=0; i<infile.getNumLines(); i++) { if (appendQ) { cout << infile[i]; } if (appendQ) { if (infile[i].getType() == E_humrec_data) { if (modulations[i] == -1) { cout << "\t."; } else if (modulations[i] == -2) { cout << "\t"; printPitch(beforemod[i]); } else if (modulations[i] == -3) { cout << "\t("; printPitch(beforemod[i]); cout << ")"; } else if (beforemod[i] == -1) { cout << "\t."; } else { cout << "\t"; printPitch(beforemod[i]); cout << ":"; printPitch(modulations[i]); } } else if (infile[i].getType() == E_humrec_data_measure) { cout << "\t" << infile[i][0]; } else if (infile[i].getType() == E_humrec_interpretation) { if (strncmp(infile[i][0], "**", 2) == 0) { cout << "\t**mod"; } else if (strcmp(infile[i][0], "*-") == 0) { cout << "\t*-"; } else { cout << "\t*"; } } } else if (prependQ) { if (infile[i].getType() == E_humrec_data) { if (modulations[i] == -1) { cout << ".\t"; } else if (modulations[i] == -2) { printPitch(beforemod[i]); cout << "\t"; } else if (modulations[i] == -3) { cout << "("; printPitch(beforemod[i]); cout << ")"; cout << "\t"; } else if (beforemod[i] == -1) { cout << ".\t"; } else { printPitch(beforemod[i]); cout << ":"; printPitch(modulations[i]); cout << "\t"; } } else if (infile[i].getType() == E_humrec_data_measure) { cout << infile[i][0]; cout << "\t"; } else if (infile[i].getType() == E_humrec_interpretation) { if (strncmp(infile[i][0], "**", 2) == 0) { cout << "**mod\t"; } else if (strcmp(infile[i][0], "*-") == 0) { cout << "*-\t"; } else { cout << "*\t"; } } cout << infile[i]; } else { if (infile[i].getType() == E_humrec_data) { if (modulations[i] == -1) { cout << "."; } else if (modulations[i] == -2) { printPitch(beforemod[i]); } else if (modulations[i] == -3) { cout << "("; printPitch(beforemod[i]); cout << ")"; } else if (beforemod[i] == -1) { cout << "."; } else { cout << ""; printPitch(beforemod[i]); cout << ":"; printPitch(modulations[i]); } } else if (infile[i].getType() == E_humrec_data_measure) { cout << "" << infile[i][0]; } else if (infile[i].getType() == E_humrec_interpretation) { if (strncmp(infile[i][0], "**", 2) == 0) { cout << "**mod"; } else if (strcmp(infile[i][0], "*-") == 0) { cout << "*-"; } else { cout << "*"; } } else { cout << infile[i]; } } cout << "\n"; } } ////////////////////////////// // // printPitch -- // void printPitch(int value) { int mode = 0; if (value >= 12) { value -= 12; mode = 1; // minor key } int base40 = Convert::base12ToBase40(value); base40 = (base40 + 40) % 40; base40 = base40 + 3 * 40; if (mode == 1) { base40 += 40; } char buffer[64] = {0}; cout << Convert::base40ToKern(buffer, base40); } ////////////////////////////// // // getModulationAnalysis -- // void getModulationAnalysis(HumdrumFile& infile, Array<int>& modulations, Array<int>& beforemod) { modulations.setSize(infile.getNumLines()); modulations.setAll(-1); beforemod.setSize(infile.getNumLines()); beforemod.setAll(-1); int i; int mod; int before; for (i=0; i<infile.getNumLines(); i++) { if (infile[i].getType() != E_humrec_data) { continue; } getModulation(mod, before, infile, i, window); modulations[i] = mod; beforemod[i] = before; } } ////////////////////////////// // // getModulation -- // void getModulation(int& mod, int& before, HumdrumFile& infile, int line, double window) { if (infile[line].getAbsBeat() < window) { // not enough music before the modulation point to analyze. mod = -1; before = -1; return; } if (infile[infile.getNumLines()-1].getAbsBeat() - infile[line].getAbsBeat() < window) { mod = -1; before = -1; return; } Array<double> scores(24); int wafter = -1; // ending window index after mod point int w2after = -1; // ending half-window index after mod point int wbefore = -1; // starting window index before mod point int w2before = -1; // starting half-window index before mod point int midval = -1; double rafter = -1.0; // rvalue for window after mod point double r2after = -1.0; // rvalue for half-window after mod point double rbefore = -1.0; // rvalue for window before mod point double r2before = -1.0; // rvalue for half-window before mod point double rmidbefore = -1.0; // rvalue 1 for mid-window double rmidafter = -1.0; // rvalue 1 for mid-window int wafterstop = getDurationLineEnd(infile, infile[line].getAbsBeat()+window); int w2afterstop = getDurationLineEnd(infile, infile[line].getAbsBeat()+window/2.0); int wbeforestart = getDurationLineStart(infile, infile[line].getAbsBeat()-window); int w2beforestart = getDurationLineStart(infile, infile[line].getAbsBeat()-window/2.0); wafter = infile.analyzeKeyKS(scores, line, wafterstop); rafter = scores[wafter]; w2after = infile.analyzeKeyKS(scores, line, w2afterstop); r2after = scores[wafter]; wbefore = infile.analyzeKeyKS(scores, wbeforestart, line); rbefore = scores[wafter]; w2before = infile.analyzeKeyKS(scores, w2beforestart, line); r2before = scores[wafter]; midval = infile.analyzeKeyKS(scores, w2beforestart, w2afterstop); rmidbefore = scores[wbefore]; rmidafter = scores[wafter]; if (debugQ) { cout << "wa=" << wafter; cout << "\tw2a=" << w2after; cout << "\twb=" << wbefore; cout << "\tw2b=" << w2before; cout << endl; } mod = -1; before = -1; if ((midval == w2before) && (midval == w2after)) { if ((w2before != wbefore) || (w2after != wafter)) { before = midval; mod = -3; return; } } if (wbefore != w2before) { return; } if (wafter != w2after) { return; } if (wbefore == wafter) { if (midval == wbefore) { // indicate a stable local region before = midval; mod = -2; } return; } mod = wafter; before = wbefore; } ////////////////////////////// // // getDurationLineEnd -- return the index of the last line // with the specified duration (or less). // int getDurationLineEnd(HumdrumFile& infile, double target) { int i; for (i=infile.getNumLines()-1; i>=0; i--) { if (infile[i].getAbsBeat() <= target) { return i; } } return 0; } ////////////////////////////// // // getDurationLineStart -- return the index of the first line // with the specified duration (or greater). // int getDurationLineStart(HumdrumFile& infile, double target) { int i; for (i=0; i<infile.getNumLines(); i++) { if (infile[i].getAbsBeat() >= target) { return i; } } return infile.getNumLines()-1; } ////////////////////////////// // // checkOptions -- validate and process command-line options. // void checkOptions(Options& opts, int argc, char* argv[]) { opts.define("a|append=b", "append analysis to original score"); opts.define("p|prepend=b", "prepend analysis to original score"); opts.define("w|window=d:16.0", "analysis window size"); 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, Oct 2004" << endl; exit(0); } else if (opts.getBoolean("version")) { cout << argv[0] << ", version: 27 Oct 2004" << 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); } appendQ = opts.getBoolean("append"); prependQ = opts.getBoolean("prepend"); window = opts.getDouble("window"); if (window <= 0) { window = 16.0; } debugQ = opts.getBoolean("debug"); } ////////////////////////////// // // 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: efada328c3889e333d3f590620aa5d30 modid.cpp [20050403]