// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Creation Date: Sun Oct 31 04:21:10 PDT 2010 // Last Modified: Sun Oct 31 04:21:17 PDT 2010 // Filename: ...sig/examples/all/motive.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/motive.cpp // Syntax: C++; museinfo // // Description: Tool for motive identification in monophonic or single-voice // polyphonic scores. // #include "humdrum.h" #ifndef OLDCPP using namespace std; #endif #define REST -1000 #define INVALID -7000 class Coord { public: int row; int col; Coord(void) { row = col = -1; } }; // function declarations void checkOptions(Options& opts, int argc, char* argv[]); void example(void); void usage(const char* command); void doDiatonicIntervalAnalysis(HumdrumFile& infile); void getDiatonicIntervals(Array<Array<int> >& intervals, Array<Array<Coord> >& coords, HumdrumFile& infile); void printDiatonicIntervals(Array<Array<int> >& intervals); void printDiatonicData(HumdrumFile& infile, Array<Array<int> >& intervals, Array<Array<Coord> >& coords); void printDiatonicLine(HumdrumFile& infile, int i, Array<Array<int> >& intervals, const char* strang); // global variables Options options; // database for command-line arguments int debugQ = 0; // used with --debug option int zeroQ = 0; // used with -z option int restQ = 0; // used with -r option int outputonlyQ = 0; // used with -I option int printDiatonicIntervalsQ = 1; // used with -D option /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { HumdrumFile infile; // process the command-line options checkOptions(options, argc, argv); // figure out the number of input files to process int numinputs = options.getArgCount(); for (int i=0; i<numinputs || i==0; i++) { infile.clear(); // if no command-line arguments read data file from standard input if (numinputs < 1) { infile.read(cin); } else { infile.read(options.getArg(i+1)); } infile.analyzeRhythm(); doDiatonicIntervalAnalysis(infile); } return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // doDiatonicIntervalAnalysis -- // void doDiatonicIntervalAnalysis(HumdrumFile& infile) { Array<Array<int> > intervals; Array<Array<Coord> > coords; getDiatonicIntervals(intervals, coords, infile); // printDiatonicIntervals(intervals); if (printDiatonicIntervalsQ) { printDiatonicData(infile, intervals, coords); exit(0); } } ////////////////////////////// // // printDiatonicData -- // void printDiatonicData(HumdrumFile& infile, Array<Array<int> >& intervals, Array<Array<Coord> >& coords) { int i; for (i=0; i<infile.getNumLines(); i++) { switch (infile[i].getType()) { case E_humrec_data_comment: printDiatonicLine(infile, i, intervals, "!"); break; case E_humrec_data_kern_measure: printDiatonicLine(infile, i, intervals, infile[i][0]); break; case E_humrec_interpretation: if (strncmp(infile[i][0], "**", 2) == 0) { printDiatonicLine(infile, i, intervals, "**dint"); } else if (strcmp(infile[i][0], "*-") == 0) { printDiatonicLine(infile, i, intervals, "*-"); } break; case E_humrec_data: printDiatonicLine(infile, i, intervals, "."); break; case E_humrec_none: case E_humrec_empty: case E_humrec_global_comment: case E_humrec_bibliography: default: cout << infile[i] << endl; } } } ////////////////////////////// // // printDiatonicLine -- // void printDiatonicLine(HumdrumFile& infile, int i, Array<Array<int> >& intervals, const char* strang) { int j; int tcounter = 0; int track; int track2; int value; for (j=0; j<infile[i].getFieldCount(); j++) { if (!outputonlyQ) { cout << infile[i][j]; } if (strcmp(infile[i].getExInterp(j), "**kern") == 0) { track = infile[i].getPrimaryTrack(j); track2 = -10000; if (j < infile[i].getFieldCount()-1) { track2 = infile[i].getPrimaryTrack(j+1); } if (track != track2) { if (outputonlyQ && tcounter) { cout << "\t"; } else if (!outputonlyQ) { cout << "\t"; } tcounter++; if (intervals[i][j] >= REST) { value = intervals[i][j]; if (restQ && (value == REST)) { cout << "r"; } else { if (!zeroQ) { if (value > 0) { value++; } else if (value < 0) { value--; } } if (value > 0) { cout << "+"; } cout << value; } } else { if ((strcmp(strang, "*") == 0) && (strncmp(infile[i][j], "*staff", strlen("*staff")) == 0)) { cout << infile[i][j]; } else { cout << strang; } } } } if (!outputonlyQ) { if (j < infile[i].getFieldCount()-1) { cout << "\t"; } } } cout << endl; } ////////////////////////////// // // printDiatonicIntervals -- // void printDiatonicIntervals(Array>& intervals) { int i, j; for (i=0; i<intervals.getSize(); i++) { for (j=0; j<intervals[i].getSize(); j++) { cout << intervals[i][j] << "\n"; } if (i < intervals.getSize()-1) { cout << "\n"; } } } ////////////////////////////// // // getDiatonicIntervals -- ignoring rests for now. // void getDiatonicIntervals(Array<Array<int> >& intervals, Array<Array<Coord> >& coords, HumdrumFile& infile) { int maxtrack = infile.getMaxTracks(); int track; int pitch; Coord coord; intervals.setSize(infile.getNumLines()); coords.setSize(maxtrack); int i, j; // pre-allocate storage space for data: for (i=0; i<maxtrack; i++) { coords[i].setSize(infile.getNumLines()); coords[i].setSize(0); } for (i=0; i<infile.getNumLines(); i++) { intervals[i].setSize(infile[i].getFieldCount()); intervals[i].allowGrowth(0); intervals[i].setAll(INVALID); } for (i=0; i<infile.getNumLines(); i++) { if (debugQ) { cout << "Processing input line " << i + 1 << "..." << endl; } if (!infile[i].isData()) { continue; } for (j=0; j<infile[i].getFieldCount(); j++) { if (strcmp(infile[i].getExInterp(j), "**kern") != 0) { continue; } track = infile[i].getPrimaryTrack(j)-1; if (strchr(infile[i][j], 'r') != NULL) { if (restQ) { pitch = REST; intervals[i][j] = pitch; coord.row = i; coord.col = j; coords[track].append(coord); } continue; } if (strcmp(infile[i][j], ".") == 0) { continue; } if (strchr(infile[i][j], '_') != NULL) { // middle tie note continue; } if (strchr(infile[i][j], ']') != NULL) { // ending tied note continue; } track = infile[i].getPrimaryTrack(j) - 1; pitch = Convert::kernToBase40(infile[i][j]); pitch = pitch/40 * 7 + Convert::base40ToDiatonic(pitch) % 7; coord.row = i; coord.col = j; intervals[i][j] = pitch; coords[track].append(coord); } } if (debugQ) { cout << "FINISHED EXTRACTING DATA FROM FILE" << endl; } Array<Array<Coord> >& c = coords; int lasti, lastj; // convert absolute diatonic pitch into diatonic interval. if (restQ) { for (i=0; i<c.getSize(); i++) { for (j=0; j<c[i].getSize()-1; j++) { if (intervals[c[i][j].row][c[i][j].col] == REST) { continue; } if (intervals[c[i][j+1].row][c[i][j+1].col] == REST) { intervals[c[i][j].row][c[i][j].col] = INVALID; continue; } intervals[c[i][j].row][c[i][j].col] = intervals[c[i][j+1].row][c[i][j+1].col] - intervals[c[i][j].row][c[i][j].col]; } lasti = c[i][c[i].getSize()-1].row; lastj = c[i][c[i].getSize()-1].col; intervals[lasti][lastj] = INVALID; c[i].setSize(c[i].getSize()-1); } } else { for (i=0; i<c.getSize(); i++) { for (j=0; j<c[i].getSize()-1; j++) { intervals[c[i][j].row][c[i][j].col] = intervals[c[i][j+1].row][c[i][j+1].col] - intervals[c[i][j].row][c[i][j].col]; } lasti = c[i][c[i].getSize()-1].row; lastj = c[i][c[i].getSize()-1].col; intervals[lasti][lastj] = INVALID; c[i].setSize(c[i].getSize()-1); } } int value; int writepos = 0; if (restQ) { // remove invalid intervals from list for (i=0; i<c.getSize(); i++) { if (c[i].getSize() == 0) { continue; } writepos = 0; for (j=0; j<c[i].getSize(); j++) { value = intervals[c[i][j].row][c[i][j].col]; if (value == INVALID) { continue; } else { c[i][writepos].row = c[i][j].row; c[i][writepos].col = c[i][j].col; writepos++; } } c[i].setSize(writepos); } } if (debugQ) { cout << "FINISHED CREATING INTERVALS" << endl; } } ////////////////////////////// // // checkOptions -- validate and process command-line options. // void checkOptions(Options& opts, int argc, char* argv[]) { opts.define("I=b", "display output without echoing input data"); opts.define("z|zero=b", "display diatonic intervals zero-offset"); opts.define("r|rest=b", "do not cross rests"); opts.define("debug=b"); // determine bad input line num opts.define("author=b"); // author of program opts.define("version=b"); // compilation info opts.define("example=b"); // example usages 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 2010" << endl; exit(0); } else if (opts.getBoolean("version")) { cout << argv[0] << ", version: 31 Oct 2010" << 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); } debugQ = opts.getBoolean("debug"); zeroQ = opts.getBoolean("zero"); restQ = opts.getBoolean("rest"); outputonlyQ = opts.getBoolean("I"); } ////////////////////////////// // // example -- example usage of the quality program // void example(void) { cout << " \n" "# example usage of the meter program. \n" "# analyze a Bach chorale for meter position: \n" " meter chor217.krn \n" " \n" "# display the metrical location spine with original data: \n" " meter -a chor217.krn \n" " \n" << endl; } ////////////////////////////// // // usage -- gives the usage statement for the meter program // void usage(const char* command) { cout << " \n" " \n" << endl; } // md5sum: 915382eb3b12ff8e7cc66b07c64fd8c5 motive.cpp [20101226]