// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Creation Date: Fri Jul 3 22:16:30 PDT 1998 // Last Modified: Sat Jul 4 11:59:48 PDT 1998 // Last Modified: Wed Oct 18 16:13:43 PDT 2000 (upgraded to musedata 1.1) // Filename: ...sig/examples/all/composite.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/composite.cpp // Syntax: C++; museinfo // // Description: Generates composite rhythm data spine from **kern input // #include "humdrum.h" #include <stdlib.h> #include <ctype.h> #include <string.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[]); double determineDuration(const HumdrumRecord& aRecord); int determineRest(const HumdrumRecord& aRecord); int determineTie(HumdrumRecord& aRecord); void example(void); void generateMupOutput(HumdrumFile& aFile); void modifyRecordTie(HumdrumRecord& aRecord, int state); void numberToString(double number, char* string); void processRecords(HumdrumFile& infile, HumdrumFile& outfile); void usage(const char* command); // global variables Options options; // database for command-line arguments double meterposition = 1; // position of current event in meter double scalevalue = 1; // for changing duration size (not used yet) int durinit; // for initializing duration function int restinit; // for initializing rests function int tieinit; // for initializing composite tie detector int lastTie = 0; // for determining tie start/stop char* title = ""; // for MUP title /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { HumdrumFile infile, outfile; // 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++) { durinit = restinit = tieinit = 1; lastTie = 0; infile.clear(); outfile.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)); } // analyze the input file according to command-line options processRecords(infile, outfile); // check to see if only the analysis spine is required if (!options.getBoolean("assemble")) { outfile = outfile.extract(-1); } if (!options.getBoolean("mup")) { outfile.write(cout); } else { generateMupOutput(outfile); } } return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // checkOptions -- validate and process command-line options. // void checkOptions(Options& opts, int argc, char* argv[]) { opts.define("a|assemble=b"); // assemble analysis with input opts.define("r|rest|rests|no-rests=b");// don't indicate rests opts.define("m|mup=b"); // determine if to output mup opts.define("t|title=s:"); // sets the MUP title 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: 3 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); } } ////////////////////////////// // // determineDuration -- determines the duration of the **kern // entries before a new **kern entry. // double determineDuration(const HumdrumRecord& aRecord) { static SigCollection<double> lastdurations; // for keeping track of durations static SigCollection<double> runningstatus; // for keeping track current dur int i; // initialization: if (durinit) { durinit = 0; lastdurations.setSize(aRecord.getFieldCount()); runningstatus.setSize(aRecord.getFieldCount()); for (i=0; i<aRecord.getFieldCount(); i++) { lastdurations[i] = 0; runningstatus[i] = 0; } // remove non-kern spines from note list for (i=0; i<aRecord.getFieldCount(); i++) { if (aRecord.getExInterpNum(i) != E_KERN_EXINT) { lastdurations.setSize(lastdurations.getSize() - 1); runningstatus.setSize(runningstatus.getSize() - 1); } } } // Step (1): if lastdurations == runningstatus, then zero running // status. for (i=0; i<runningstatus.getSize(); i++) { if (runningstatus[i] == lastdurations[i]) { runningstatus[i] = 0; } } // Step (2): input new durations into the lastdurations array int count = 0; for (i=0; i<aRecord.getFieldCount(); i++) { if (aRecord.getExInterpNum(i) == E_KERN_EXINT) { if (strcmp(aRecord[i], ".") == 0) { ; // don't do anything } else { lastdurations[i] = Convert::kernToDuration(aRecord[i]); } count++; } } if (count != runningstatus.getSize()) { cerr << "Error: spine count has changed" << endl; } // Step (3): find minimum duration by subtracting last from running double min = 99999999; double test; for (i=0; i<lastdurations.getSize(); i++) { test = lastdurations[i] - runningstatus[i]; if (test < min) { min = test; } } // Step (4): add the duration to the running values and to meter position for (i=0; i<runningstatus.getSize(); i++) { runningstatus[i] += min; } meterposition += min; return min; } ////////////////////////////// // // determineRest -- extracts notes from humdrum data record // and determines if all notes are rests or not. Notes from previous // records are remembered, and replace any null records in the // data. Rests are represented by some negative value. // int determineRest(const HumdrumRecord& aRecord) { static SigCollection<int> lastnotes; // for keeping track of null record notes int output = 1; int i; if (restinit) { restinit = 0; lastnotes.setSize(aRecord.getFieldCount()); for (i=0; i<aRecord.getFieldCount(); i++) { lastnotes[i] = E_root_rest; } // remove non-kern spines from note list for (i=0; i<aRecord.getFieldCount(); i++) { if (aRecord.getExInterpNum(i) != E_KERN_EXINT) { lastnotes.setSize(lastnotes.getSize() - 1); } } } // determine chord notes and send to chord identifier SigCollection<int> currentNotes(lastnotes.getSize()); int count = 0; for (i=0; i<aRecord.getFieldCount(); i++) { if (aRecord.getExInterpNum(i) == E_KERN_EXINT) { if (strcmp(aRecord[i], ".") == 0) { currentNotes[count] = lastnotes[count]; } else { currentNotes[count] = Convert::kernToBase40(aRecord[i]); lastnotes[count] = currentNotes[count]; } if (currentNotes[count] != E_root_rest) { output = 0; } count++; } } return output; } ////////////////////////////// // // determineTie -- determines if notes in record are all tied. // int determineTie(HumdrumRecord& aRecord) { static SigCollection<int> ties; // for keeping track of notes states static SigCollection<int> sustains; // for keeping track of note sustains static SigCollection<int> rests; // for keeping track of notes states int i; if (tieinit) { tieinit = 0; ties.setSize(aRecord.getFieldCount()); sustains.setSize(aRecord.getFieldCount()); rests.setSize(aRecord.getFieldCount()); for (i=0; i<aRecord.getFieldCount(); i++) { ties[i] = 0; sustains[i] = 0; rests[i] = 0; } // remove non-kern spines from note list for (i=0; i<aRecord.getFieldCount(); i++) { if (aRecord.getExInterpNum(i) != E_KERN_EXINT) { ties.setSize(ties.getSize() - 1); sustains.setSize(sustains.getSize() - 1); rests.setSize(rests.getSize() - 1); } } } // determine note states SigCollection<int> currentTies(ties.getSize()); SigCollection<int> currentSustains(sustains.getSize()); SigCollection<int> currentRests(rests.getSize()); int count = 0; for (i=0; i<aRecord.getFieldCount(); i++) { if (aRecord.getExInterpNum(i) == E_KERN_EXINT) { // sustain: check if a continuing note if (strcmp(aRecord[i], ".") == 0) { currentSustains[count] = 1; } else { currentSustains[count] = 0; } // tie: check for a tie marking if (strchr(aRecord[i], '[') != NULL) { currentTies[count] = 1; } else if (strchr(aRecord[i], ']') != NULL) { currentTies[count] = 0; } else { currentTies[count] = -1; } // rest: check for a rest if (strchr(aRecord[i], 'r') != NULL) { currentRests[count] = 1; } else { currentRests[count] = -1; } count++; } } // process the findings for (i=0; i<ties.getSize(); i++) { // resolve rests: if (currentRests[i] == -1 && currentSustains[i] == 1) { currentRests[i] = rests[i]; } else if (currentRests[i] == -1) { currentRests[i] = 0; } // resolve ties: if (currentTies[i] == -1 && ties[i] == 1) { currentTies[i] = 1; } else if (currentTies[i] == -1) { currentTies[i] = 0; } } // determine output SigCollection<int> results(ties.getSize()); int tiechange; for (i=0; i<ties.getSize(); i++) { if (ties[i] == 1 && currentTies[i] == 1) { tiechange = 1; } else if (ties[i] == 1 && currentTies[i] == 0) { tiechange = 1; } else { tiechange = 0; } if (tiechange || currentSustains[i] || currentRests[i]) { results[i] = 1; } else { results[i] = 0; } } // copy new values onto old values for (i=0; i<ties.getSize(); i++) { ties[i] = currentTies[i]; rests[i] = currentRests[i]; sustains[i] = currentSustains[i]; } int output = 1; for (i=0; i<results.getSize(); i++) { if (results[i] == 0) { output = 0; break; } } int testrests = 1; for (i=0; i<results.getSize(); i++) { if (currentRests[i] == 0) { testrests = 0; break; } } if (testrests) { output = 0; } return output; } ////////////////////////////// // // numberToString -- // void numberToString(double number, char* string) { SSTREAM temp; temp << number << ends; strncpy(string, temp.CSTRING, 64); } ////////////////////////////// // // example -- example usage of the quality program // void example(void) { cout << " \n" "# example usage of the composite program. \n" "# analyze a Bach chorale for composite rhythm: \n" " composite chor217.krn \n" " \n" "# display the composite spine with original data: \n" " composite -a chor217.krn \n" " \n" << endl; } //////////////////// // // generateMupOutput // void generateMupOutput(HumdrumFile& aFile) { int tietrace = 0; char aString[128] = {0}; int starttest = -1; int measurestart = 1; cout << "// beat output\n\n"; cout << "header\n title (28) \" \"\n title (14) \"\\f(TI) " << "composite rhythm\"\n"; cout << " title (18) \"\\f(TB)"; if (strcmp(options.getString("title"), "") != 0) { cout << options.getString("title"); } else if (strcmp(title, "") != 0) { cout << title; } cout << "\"\n\n"; cout << "score\n stafflines=1\n scale=0.75\n measnum=y\n\n"; HumdrumFile analysis = aFile.extract(-1); HumdrumRecord currRecord; for (int i=0; i<analysis.getNumLines(); i++) { currRecord = analysis[i]; switch (currRecord.getType()) { case E_humrec_none: case E_humrec_empty: case E_humrec_global_comment: case E_humrec_bibliography: case E_humrec_data_comment: break; case E_humrec_data_interpretation: if (currRecord[0][1] == 'M' && isdigit(currRecord[0][2])) { cout << "score\n time="; strcpy(aString, &currRecord[0][2]); cout << aString << '\n'; if (strcmp(aString, "1/4") == 0) { cout << " beamstyle=4\n"; } else if (strcmp(aString, "2/4") == 0) { cout << " beamstyle=4,4\n"; } else if (strcmp(aString, "3/4") == 0) { cout << " beamstyle=4,4,4\n"; } else if (strcmp(aString, "4/4") == 0) { cout << " beamstyle=4,4,4,4\n"; } cout << "\nmusic\n\n"; } break; case E_humrec_data_measure: measurestart = 1; cout << '\n'; if (starttest != -1) { if (currRecord[0][strlen(currRecord[0])-1] == '-') { cout << "\ninvisbar\n\n"; } else if (isdigit(currRecord[0][1])) { cout << "\nbar\n\n"; } else if (currRecord[0][1] == '=') { cout << "\nendbar\n\n"; } else if (strcmp(currRecord[0], "=||") == 0) { cout << "\ndblbar\n\n"; } else if (strcmp(currRecord[0], "=|!") == 0) { cout << "\nendbar\n\n"; } else if (strcmp(currRecord[0], "=!|:") == 0) { cout << "\nrepeatstart\n\n"; } else if (strcmp(currRecord[0], "=:|!") == 0) { cout << "\nrepeatend\n\n"; } else if (strcmp(currRecord[0], "=:!!:") == 0) { cout << "\nrepeatboth\n\n"; } else { cout << "\nbar\n\n"; } cout << "music\n\n"; break; case E_humrec_data: starttest = 1; // ignore null fields if (strcmp(currRecord[0], ".") == 0) { break; } if (measurestart) { measurestart = 0; cout << "\n1: "; } if (strchr(currRecord[0], '[') != NULL) { tietrace = 1; } else if (strchr(currRecord[0], ']') != NULL) { tietrace = 0; } if (strchr(currRecord[0], 'r') != NULL) { cout << currRecord[0]; } else if (strchr(currRecord[0], '[') != NULL) { for (unsigned int j=0; j<strlen(currRecord[0]); j++) { if (currRecord[0][j] != '[') { cout << currRecord[0][j]; } } cout << 'e'; } else if (strchr(currRecord[0], ']') != NULL) { for (unsigned int j=0; j<strlen(currRecord[0]); j++) { if (currRecord[0][j] != ']') { cout << currRecord[0][j]; } } cout << 'e'; } else { cout << currRecord[0] << 'e'; } if (tietrace) { cout << '~'; } cout << "; "; break; default: cerr << "Error on line " << (i+1) << " of output" << endl; exit(1); } } } } //////////////////// // // modifyRecordTie -- adjust the tie for the last data in list // state = 1 --> turn on a tie // state = 0 --> turn off a tie // void modifyRecordTie(HumdrumRecord& aRecord, int state) { char temp[128]; if (state) { // starting a tie strcpy(temp, "["); strcat(temp, aRecord[aRecord.getFieldCount() - 1]); } else { strcpy(temp, aRecord[aRecord.getFieldCount() - 1]); strcat(temp, "]"); } aRecord.changeField(aRecord.getFieldCount()-1, temp); } ////////////////////////////// // // processRecords -- looks at humdrum records and determines chord // quality // void processRecords(HumdrumFile& infile, HumdrumFile& outfile) { char aString[256] = {0}; double duration; int rest; int tie; int lastDataRecord = 0; HumdrumRecord currRecord; 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: if (strncmp(currRecord[0], "!!!OTL: ", 8) == 0) { title = new char[strlen(currRecord[0])]; strcpy(title, &currRecord[0][8]); } else if (strncmp(currRecord[0], "!!!OTL:", 7) == 0) { title = new char[strlen(currRecord[0])]; strcpy(title, &currRecord[0][7]); } case E_humrec_global_comment: case E_humrec_bibliography: outfile.appendLine(currRecord); break; case E_humrec_data_comment: if (currRecord.equalFieldsQ("**kern")) { currRecord.appendField(currRecord[0]); } else { currRecord.appendField("!"); } outfile.appendLine(currRecord); break; case E_humrec_data_interpretation: if (currRecord.hasExclusiveQ()) { currRecord.appendField("**composite"); outfile.appendLine(currRecord); } else { if (currRecord.equalFieldsQ("**kern")) { currRecord.appendField(currRecord[0]); } else { currRecord.appendField("*"); } outfile.appendLine(currRecord); } break; case E_humrec_data_kern_measure: if (currRecord.equalFieldsQ("**kern")) { currRecord.appendField(currRecord[0]); } else { currRecord.appendField("="); } outfile.appendLine(currRecord); meterposition = 1; break; case E_humrec_data: // handle null fields if (currRecord.equalFieldsQ("**kern", ".")) { currRecord.appendField("."); outfile.appendLine(currRecord); break; } duration = determineDuration(infile[i]); Convert::durationToKernRhythm(aString, duration); if (!options.getBoolean("no-rests")) { rest = determineRest(infile[i]); if (rest) { strcat(aString, "r"); } } tie = determineTie(infile[i]); if (tie == 1 && lastTie == 0) { modifyRecordTie(outfile[lastDataRecord], 1); } else if (tie == 0 && lastTie == 1) { modifyRecordTie(outfile[lastDataRecord], 0); } lastTie = tie; currRecord.appendField(aString); outfile.appendLine(currRecord); lastDataRecord = i; break; default: cerr << "Error on line " << (i+1) << " of input" << endl; exit(1); } } } ////////////////////////////// // // usage -- gives the usage statement for the meter program // void usage(const char* command) { cout << " \n" "Analyzes **kern data and generates a composite rhythm spine from the \n" "input **kern data records. Currently, input spines cannot split or join.\n" " \n" "Usage: " << command << " [-a][-c][input1 [input2 ...]] \n" " \n" "Options: \n" " -a = assemble the composite analysis spine with the input data. \n" " -r = don't indicate rests in composite rhythm analysis. \n" " -m = generate mup output instead of humdrum output. \n" " --options = list of all options, aliases and default values \n" " \n" << endl; } // md5sum: 8449e17e46a32f7af6c7e012af55d3fd compositeold.cpp [20090626]