// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Creation Date: Tue Jul 7 22:29:32 PDT 1998 // Last Modified: Tue Jul 7 22:29:36 PDT 1998 // Filename: ...sig/examples/all/ana.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/ana.cpp // Syntax: C++; museinfo // // Description: Generates and ESAC-like analysis summary of melodic lines. // #include "humdrum.h" #include <stdlib.h> #include <ctype.h> #include <string.h> #include <math.h> #ifndef OLDCPP #include <iostream> #include <sstream> #define SSTREAM stringstream #define CSTRING str().c_str() using namespace std; #else #include <iostream.h> #ifdef VISUAL #include <strstrea.h> /* for Windows 95 */ #else #include <strstream.h> #endif #define SSTREAM strstream #define CSTRING str() #endif // function declarations void checkOptions(Options& opts, int argc, char* argv[]); void example(void); void processRecords(HumdrumFile& infile); void usage(const char* command); // global variables Options options; // database for command-line arguments /////////////////////////////////////////////////////////////////////////// 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)); cout << "\n\nFilename: " << options.getArg(i+1) << endl; } // analyze the input file according to command-line options processRecords(infile); } return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // checkOptions -- validate and process command-line options. // void checkOptions(Options& opts, int argc, char* argv[]) { opts.define("s|spine|c|column=i:0"); // spine to analyze 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, July 1998" << endl; exit(0); } else if (opts.getBoolean("version")) { cout << argv[0] << ", version: 7 July 1998" << 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); } } ////////////////////////////// // // 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" " \n" << endl; } ////////////////////////////// // // processRecords -- looks at humdrum records and determines chord // quality // void processRecords(HumdrumFile& infile) { char aString[128] = {0}; int lastBase40Pitch = 0x7fffffff; int base40Pitch = 0x7fffffff; int tempNote = 0x7fffffff; int lastNote = 0x7fffffff; int phraseCount = 0; int phraseQ = 0; int phraseBoundary = 1; int barCount = -1; int spine = options.getInteger("spine"); // the melodic spine to analyze int key = 0x7fffffff; int mode = 0x7fffffff; HumdrumRecord currRecord; Array<int> pitches(300); pitches.allowGrowth(0); pitches.zero(); Array<int> intervals(300); Array<int> breakIntervals(300); intervals.allowGrowth(0); intervals.zero(); breakIntervals.allowGrowth(0); breakIntervals.zero(); Array<int> phraseEndings; phraseEndings.allowGrowth(); phraseEndings.setSize(0); for (int i=0; i<infile.getNumLines(); i++) { if (options.getBoolean("debug")) { cout << "processing line " << (i+1) << " of input ..." << endl; } currRecord = infile[i]; switch (currRecord.getType()) { case E_humrec_none: case E_humrec_empty: case E_humrec_bibliography: if (strncmp(currRecord[0], "!!!OTL:", 7) == 0) { cout << "Title:" << &currRecord[0][7] << endl; } case E_humrec_global_comment: break; case E_humrec_data_comment: break; case E_humrec_interpretation: if (strcmp(currRecord[spine], "**kern") != 0) { cerr << "Error: input spine must be in **kern format. " << "Format is: " << currRecord[spine] << endl; exit(1); } if (currRecord[spine][strlen(currRecord[spine])-1] == ':') { if (strchr("abcdefg", tolower(currRecord[spine][1])) != NULL) { key = Convert::kernToBase40Class(currRecord[spine]); if (islower(currRecord[spine][1])) { mode = 2; } else if (isupper(currRecord[spine][1])) { mode = 1; } else { mode = 0; } } } break; case E_humrec_data_kern_measure: barCount++; break; case E_humrec_data: // ignore null records if (strcmp(currRecord[spine], ".") == 0) { break; } // extract the pitch lastBase40Pitch = base40Pitch; lastNote = tempNote; tempNote = Convert::kernToBase40(currRecord[spine]); if (tempNote != E_base40_rest) { base40Pitch = tempNote; } if (tempNote == E_base40_rest && lastNote != E_base40_rest) { pitches[0]++; } else if (tempNote == base40Pitch) { pitches[base40Pitch]++; } // check for phrase markings if (strchr(currRecord[spine], '{') != NULL) { if (phraseQ == 0) { phraseQ = 1; phraseBoundary = 1; phraseCount++; } else { cerr << "Error: starting a phrase before ending a " << "phrase on line " << i << " of input" << endl; exit(1); } } else if (strchr(currRecord[spine], '}') != NULL) { phraseBoundary = 0; if (phraseQ == 1) { phraseQ = 0; phraseEndings.append(base40Pitch); } else { cerr << "Error: ending a phrase before starting a " << "phrase on line " << i << " of input" << endl; exit(1); } } else { phraseBoundary = 0; } // check for intervals if (lastNote != 0x7fffffff) { if (tempNote != E_base40_rest && lastNote != E_base40_rest) { if (phraseBoundary) { breakIntervals[tempNote - lastNote + 80]++; } else if (!phraseBoundary) { intervals[tempNote - lastNote + 80]++; } } else if (tempNote == E_base40_rest) { // do nothing } else if (lastNote == E_base40_rest) { if (phraseBoundary) { breakIntervals[tempNote - lastNote + 80]++; } else if (!phraseBoundary) { intervals[tempNote - lastNote + 80]++; } } else if (lastNote == tempNote && lastNote == E_base40_rest) { tempNote = base40Pitch; } } break; default: cerr << "Error on line " << (i+1) << " of input" << endl; exit(1); } } ///// analysis summary ////////////////////////////////////////////////// /// key information cout << "key signature: "; switch (mode) { case 0: cout << " unknown" << endl; break; case 1: cout << Convert::kernPitchClass.getName(key) << " major" << endl; break; case 2: cout << Convert::kernPitchClass.getName(key) << " minor" << endl; break; default: cout << "Error (" << mode << ")" << endl; } /// pitch information int lowindex = 1; int highindex = 295; while (pitches[lowindex] == 0) { lowindex++; } while (pitches[highindex] == 0) { highindex--; } cout << "Range: " << Convert::base40ToKern(aString, lowindex); cout << " to " << Convert::base40ToKern(aString, highindex) << endl; cout << "Pitch frequency: "; for (int j=lowindex; j<=highindex; j++) { if (pitches[j] != 0) { cout << Convert::base40ToKern(aString, j) << ":" << pitches[j]; if (j != highindex) { cout << ", "; } } } cout << endl; cout << "Rest count: " << pitches[0] << endl; /// interval information cout << "phrase intervals: " << intervals.sum() << endl; cout << " pi-down: " << intervals.sum(0, 79) << " "; for (int j=79; j>=0; j--) { if (intervals[j] != 0) { cout << Convert::intervalNames.getName(-(j-80)) << ":" << intervals[j] << " "; } } cout << "\n pi-unisons: " << intervals[80]; cout << "\n pi-up: " << intervals.sum(81, 200) << " "; for (int j=81; j<80+80; j++) { if (intervals[j] != 0) { cout << Convert::intervalNames.getName(j-80) << ":" << intervals[j] << " "; } } cout << endl; cout << "phrase boundary intervals : " << breakIntervals.sum() << endl; cout << " pbi-down: " << breakIntervals.sum(1, 79) << " "; for (int j=79; j>=0; j--) { if (breakIntervals[j] != 0) { cout << Convert::intervalNames.getName(-(j-80)) << ":" << breakIntervals[j] << " "; } } cout << "\n pbi-unisons: " << breakIntervals[80]; cout << "\n pbi-up: " << breakIntervals.sum(81, 200) << " "; for (int j=81; j<80+80; j++) { if (breakIntervals[j] != 0) { cout << Convert::intervalNames.getName(j-80) << ":" << breakIntervals[j] << " "; } } cout << endl; /// phrase information cout << "Bar count: " << barCount << endl; cout << "Phrase count: " << phraseCount << endl; cout << "Phrase endings: "; for (int j=0; j<phraseEndings.getSize(); j++) { cout << Convert::base40ToKern(aString, phraseEndings[j]); if (j != phraseEndings.getSize()-1) { cout << ", "; } } cout << endl; } ////////////////////////////// // // usage -- gives the usage statement for the meter program // void usage(const char* command) { cout << " \n" "Analyzes **kern data and generates a rhythmic analysis which gives \n" "the beat location of **kern data records in the measure. Currently, \n" "input spines cannot split or join. \n" " \n" "Usage: " << command << " [-a][-b base-rhythm][-s|-d][input1 [input2 ...]]\n" " \n" "Options: \n" " -a = assemble the analysis spine with the input data. \n" " -b = set the base rhythm for analysis to specified kern rhythm value. \n" " -d = gives the duration of each kern record in beat measurements. \n" " -s = sum the beat count in each measure. \n" " --options = list of all options, aliases and default values \n" " \n" << endl; } // md5sum: a6e3a2008ed3887355b2dc4fd7e4a2d7 ana.cpp [20080518]