// // Programmer: Craig Stuart Sapp // Creation Date: Mon Sep 16 13:53:47 PDT 2013 // Last Modified: Thu Sep 19 16:10:27 PDT 2013 // Filename: ...museinfo/examples/all/cint.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/cint.cpp // Syntax: C++; museinfo // // Description: Calculates counterpoint interval modules in polyphonic // music. // // Crossing code not yet finished. // #include #include #include #include #include #include #include "humdrum.h" #include "PerlRegularExpression.h" using namespace std; #define EMPTY_ID "" #define REST 0 #define RESTINT -1000000 #define RESTSTRING "R" #define INTERVAL_HARMONIC 1 #define INTERVAL_MELODIC 2 #define MARKNOTES 1 class NoteNode { public: int b40; // base-40 pitch number or 0 if a rest, negative if tied int line; // line number in original score of note int spine; // spine number in original score of note int measure; // measure number of note int serial; // serial number int mark; // for marking search matches string notemarker; // for pass-through of marks double beatsize; // time signature bottom value which or // 3 times the bottom if compound meter RationalNumber duration; // duration void clear (void); NoteNode (void) { clear(); } NoteNode (const NoteNode& anode); NoteNode (NoteNode& anode); NoteNode& operator= (NoteNode& anode); ~NoteNode (void); int isRest (void) { return b40 == 0 ? 1 : 0; } int isSustain (void) { return b40 < 0 ? 1 : 0; } int isAttack (void) { return b40 > 0 ? 1 : 0; } int getB40 (void) { return abs(b40); } void setId (const string& anid); string getIdString (void); string getId (void); protected: string protected_id; // id number provided by data }; NoteNode::NoteNode(NoteNode& anode) { b40 = anode.b40; line = anode.line; spine = anode.spine; measure = anode.measure; serial = anode.serial; mark = anode.mark; notemarker = anode.notemarker; beatsize = anode.beatsize; duration = 0; protected_id = anode.protected_id; } NoteNode::NoteNode(const NoteNode& anode) { b40 = anode.b40; line = anode.line; spine = anode.spine; measure = anode.measure; serial = anode.serial; mark = anode.mark; notemarker = anode.notemarker; beatsize = anode.beatsize; duration = 0; protected_id = anode.protected_id; } NoteNode& NoteNode::operator=(NoteNode& anode) { if (this == &anode) { return *this; } b40 = anode.b40; line = anode.line; spine = anode.spine; measure = anode.measure; serial = anode.serial; mark = anode.mark; notemarker = anode.notemarker; beatsize = anode.beatsize; duration = anode.duration; protected_id = anode.protected_id; return *this; } void NoteNode::setId(const string& anid) { protected_id = anid; } NoteNode::~NoteNode(void) { // do nothing } void NoteNode::clear(void) { mark = measure = beatsize = serial = b40 = 0; notemarker = ""; line = spine = -1; protected_id.clear(); } string NoteNode::getIdString(void) { return protected_id; } string NoteNode::getId(void) { return protected_id; } /////////////////////////////////////////////////////////////////////////// // function declarations void checkOptions (Options& opts, int argc, char* argv[]); void example (void); void usage (const string& command); int processFile (HumdrumFile& infile, const string& filename, Options& opts); void getKernTracks (vector& ktracks, HumdrumFile& infile); int validateInterval (vector >& notes, int i, int j, int k); void printIntervalInfo (HumdrumFile& infile, int line, int spine, vector >& notes, int noteline, int noteindex, vector& abbr); void getAbbreviations (vector& abbreviations, vector& names); void getAbbreviation (string& abbr, string& name); void extractNoteArray (vector >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup); int onlyRests (vector& data); int hasAttack (vector& data); int allSustained (vector& data); void printPitchGrid (vector >& notes, HumdrumFile& infile); void getNames (vector& names, vector& reverselookup, HumdrumFile& infile); void printLattice (vector >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup, int n); void printSpacer (ostream& out); int printInterval (ostream& out, NoteNode& note1, NoteNode& note2, int type, int octaveadjust = 0); int printLatticeItem (vector >& notes, int n, int currentindex, int fileline); int printLatticeItemRows (vector >& notes, int n, int currentindex, int fileline); int printLatticeModule (ostream& out, vector >& notes, int n, int startline, int part1, int part2); void printInterleaved (HumdrumFile& infile, int line, vector& ktracks, vector& reverselookup, const string& interstring); void printLatticeInterleaved(vector >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup, int n); int printInterleavedLattice(HumdrumFile& infile, int line, vector& ktracks, vector& reverselookup, int n, int currentindex, vector >& notes); int printCombinations (vector >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup, int n, vector >& retrospective); void printAsCombination (HumdrumFile& infile, int line, vector& ktracks, vector& reverselookup, const string& interstring); int printModuleCombinations(HumdrumFile& infile, int line, vector& ktracks, vector& reverselookup, int n, int currentindex, vector >& notes, int& matchcount, vector >& retrospective); int printCombinationsSuspensions(vector >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup, int n, vector >& retrospective); int printCombinationModule(ostream& out, const string& filename, vector >& notes, int n, int startline, int part1, int part2, vector >& retrospective, string& notemarker, int markstate = 0); int printCombinationModulePrepare(ostream& out, const string& filename, vector >& notes, int n, int startline, int part1, int part2, vector >& retrospective, HumdrumFile& infile); int getOctaveAdjustForCombinationModule(vector >& notes, int n, int startline, int part1, int part2); void addMarksToInputData (HumdrumFile& infile, vector >& notes, vector& ktracks, vector& reverselookup); void markNote (HumdrumFile& infile, int line, int col); void initializeRetrospective(vector >& retrospective, HumdrumFile& infile, vector& ktracks); int getTriangleIndex (int number, int num1, int num2); void adjustKTracks (vector& ktracks, const string& koption); int getMeasure (HumdrumFile& infile, int line); // global variables Options options; // database for command-line arguments int debugQ = 0; // used with --debug option int base40Q = 0; // used with --40 option int base12Q = 0; // used with --12 option int base7Q = 0; // used with -7 option int pitchesQ = 0; // used with --pitches option int rhythmQ = 0; // used with -r option and others int durationQ = 0; // used with --dur option int latticeQ = 0; // used with -l option int interleavedQ = 0; // used with -L option int Chaincount = 1; // used with -n option int chromaticQ = 0; // used with --chromatic option int sustainQ = 0; // used with -s option int zeroQ = 0; // used with -z option int topQ = 0; // used with -t option int toponlyQ = 0; // used with -T option int hparenQ = 0; // used with -h option int mparenQ = 0; // used with -y option int locationQ = 0; // used with --location option int koptionQ = 0; // used with -k option int parenQ = 0; // used with -p option int rowsQ = 0; // used with --rows option int hmarkerQ = 0; // used with -h option int mmarkerQ = 0; // used with -m option int attackQ = 0; // used with --attacks option int rawQ = 0; // used with --raw option int raw2Q = 0; // used with --raw2 option int xoptionQ = 0; // used with -x option int octaveallQ = 0; // used with -O option int octaveQ = 0; // used with -o option int noharmonicQ = 0; // used with -H option int nomelodicQ = 0; // used with -M option int norestsQ = 0; // used with -R option int nounisonsQ = 0; // used with -U option int filenameQ = 0; // used with -f option int searchQ = 0; // used with --search option int markQ = 0; // used with --mark option int countQ = 0; // used with --count option int suspensionsQ = 0; // used with --suspensions option int uncrossQ = 0; // used with -c option int retroQ = 0; // used with --retro option int idQ = 0; // used with --id option vector Ids; // used with --id option string NoteMarker; // used with -N option PerlRegularExpression SearchString; string Spacer; /////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { checkOptions(options, argc, argv); HumdrumStream streamer(options); HumdrumFile infile; int count = 0; int totalcount = 0; while (streamer.read(infile)) { count = processFile(infile, infile.getFileName(), options); totalcount += count; if (countQ) { if (filenameQ && (count > 0)) { cout << infile.getFileName() << "\t"; cout << count << endl; } } } if (countQ) { if (filenameQ) { cout << "TOTAL:\t"; } cout << totalcount << endl; } } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // processFile -- Do requested analysis on a given file. // int processFile(HumdrumFile& infile, const string& filename, Options& options) { vector > notes; vector names; vector ktracks; vector reverselookup; infile.getTracksByExInterp(ktracks, "**kern"); if (koptionQ) { adjustKTracks(ktracks, options.getString("koption").c_str()); } notes.resize(ktracks.size()); reverselookup.resize(infile.getMaxTracks()+1); std::fill(reverselookup.begin(), reverselookup.end(), -1); vector > retrospective; if (retroQ) { initializeRetrospective(retrospective, infile, ktracks); } if (locationQ || rhythmQ || durationQ) { infile.analyzeRhythm("4"); } int i; for (i=0; i<(int)ktracks.size(); i++) { reverselookup[ktracks[i]] = i; notes[i].reserve(infile.getNumLines()); notes[i].resize(0); } getNames(names, reverselookup, infile); PerlRegularExpression pre; extractNoteArray(notes, infile, ktracks, reverselookup); if (pitchesQ) { printPitchGrid(notes, infile); exit(0); } int count = 0; if (latticeQ) { printLattice(notes, infile, ktracks, reverselookup, Chaincount); } else if (interleavedQ) { printLatticeInterleaved(notes, infile, ktracks, reverselookup, Chaincount); } else if (suspensionsQ) { count = printCombinationsSuspensions(notes, infile, ktracks, reverselookup, Chaincount, retrospective); } else { count = printCombinations(notes, infile, ktracks, reverselookup, Chaincount, retrospective); } // handle search results here if (markQ) { if (count > 0) { addMarksToInputData(infile, notes, ktracks, reverselookup); } cout << infile; cout << "!!!RDF**kern: @ = matched note, color=\"#ff0000\"\n"; } if (debugQ) { int j; for (i=0; i<(int)retrospective[0].size(); i++) { for (j=0; j<(int)retrospective.size(); j++) { cout << retrospective[j][i]; if (j < (int)retrospective.size() - 1) { cout << "\t"; } } cout << "\n"; } } return count; } ////////////////////////////// // // adjustKTracks -- Select only two spines to do analysis on. // void adjustKTracks(vector& ktracks, const string& koption) { PerlRegularExpression pre; if (!pre.search(koption, "(\\$|\\$?\\d*)[^\\$\\d]+(\\$|\\$?\\d*)")) { return; } int number1 = 0; int number2 = 0; PerlRegularExpression pre2; if (pre2.search(pre.getSubmatch(1), "\\d+")) { number1 = atoi(pre.getSubmatch(1)); if (strchr(pre.getSubmatch(), '$') != NULL) { number1 = (int)ktracks.size() - number1; } } else { number1 = (int)ktracks.size(); } if (pre2.search(pre.getSubmatch(2), "\\d+")) { number2 = atoi(pre.getSubmatch(2)); if (strchr(pre.getSubmatch(), '$') != NULL) { number2 = (int)ktracks.size() - number2; } } else { number2 = (int)ktracks.size(); } number1--; number2--; int track1 = ktracks[number1]; int track2 = ktracks[number2]; ktracks.resize(2); ktracks[0] = track1; ktracks[1] = track2; } ////////////////////////////// // // initializeRetrospective -- // void initializeRetrospective(vector >& retrospective, HumdrumFile& infile, vector& ktracks) { int columns = (int)ktracks.size(); columns = columns * (columns + 1) / 2; // triangle number of analysis cols. retrospective.resize(columns); for (int i=0; i<(int)retrospective.size(); i++) { retrospective[i].resize(infile.getNumLines()); } string token; for (int i=0; i >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup, int n, vector >& retrospective) { char sbuffer[24096] = {0}; int oldcountQ = countQ; countQ = 1; // mostly used to suppress intermediate output int countsum = 0; searchQ = 1; // turn on searching // Suspensions with length-2 modules n = 2; // -n 2 xoptionQ = 1; // -x strcpy(sbuffer, ""); strcat(sbuffer, "^7xs 1 6sx -2 8xx$"); strcat(sbuffer, "|"); strcat(sbuffer, "^2sx -2 3xs 2 1xx$"); strcat(sbuffer, "|"); strcat(sbuffer, "^7xs 1 6sx 2 6xx$"); strcat(sbuffer, "|"); strcat(sbuffer, "^11xs 1 10sx -5 15xx$"); strcat(sbuffer, "|"); strcat(sbuffer, "^4xs 1 3sx -5 8xx$"); strcat(sbuffer, "|"); strcat(sbuffer, "^2sx -2 3xs 2 3xx$"); strcat(sbuffer, "|"); // "9xs 1 8sx -2 10xx" archetype: Jos1405 m10 A&B strcat(sbuffer, "^9xs 1 8sx -2 10xx$"); strcat(sbuffer, "|"); // "4xs 1 3sx 5xx" archetype: Jos1713 m87-88 A&B strcat(sbuffer, "^4xs 1 3sx -2 5xx$"); strcat(sbuffer, "|"); // "11xs 1 10sx 4 8xx" archetype: Jos1402 m23-24 S&B strcat(sbuffer, "^11xs 1 10sx 4 8xx$"); SearchString.initializeSearchAndStudy(sbuffer); countsum += printCombinations(notes, infile, ktracks, reverselookup, n, retrospective); // Suspensions with length-3 modules ///////////////////////////////// n = 3; // -n 2 xoptionQ = 1; // -x strcpy(sbuffer, ""); // "7xs 1 6sx 1 5sx 1 6sx" archetype: Jos2721 m27-78 S&T strcat(sbuffer, "^7xs 1 6sx 1 5sx 1 6sx$"); strcat(sbuffer, "|"); // "7xs 1 6sx 1 6sx -2 8xx" archetype: Rue2018 m38-88 S&T strcat(sbuffer, "^7xs 1 6sx 1 6sx -2 8xx$"); strcat(sbuffer, "|"); // "11xs 1 10sx 1 10sx -5 15xx" archetype: Rue2018 m38-88 S&B strcat(sbuffer, "^11xs 1 10sx 1 10sx -5 15xx$"); SearchString.initializeSearchAndStudy(sbuffer); countsum += printCombinations(notes, infile, ktracks, reverselookup, n, retrospective); // Suspensions with length-5 modules ///////////////////////////////// n = 5; // -n 2 xoptionQ = 1; // -x strcpy(sbuffer, ""); // "8xs 1 7sx 1 7sx 1 6sx 1 6sx 1 5sx -1 8xx" archetype: Duf3015a m94 S&T strcat(sbuffer, "^8xs 1 7sx 1 7sx 1 6sx 1 5sx -2 8xx$"); SearchString.initializeSearchAndStudy(sbuffer); countsum += printCombinations(notes, infile, ktracks, reverselookup, n, retrospective); // Suspensions with rests modules // done with multiple searches. Mark the notes in the score if required. countQ = oldcountQ; return countsum; } ////////////////////////////// // // printCombinations -- // int printCombinations(vector >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup, int n, vector >& retrospective) { int i; int currentindex = 0; int matchcount = 0; for (i=0; i", 2) == 0) { pattern = infile[i][0]; } printAsCombination(infile, i, ktracks, reverselookup, pattern); } else if (infile[i].isLocalComment()) { printAsCombination(infile, i, ktracks, reverselookup, "!"); } else if (infile[i].isBarline()) { printAsCombination(infile, i, ktracks, reverselookup, infile[i][0]); } else { // print combination data currentindex = printModuleCombinations(infile, i, ktracks, reverselookup, n, currentindex, notes, matchcount, retrospective); } if (!(raw2Q || rawQ || markQ || retroQ || countQ)) { cout << "\n"; } } return matchcount; } ////////////////////////////// // // printModuleCombinations -- // int printModuleCombinations(HumdrumFile& infile, int line, vector& ktracks, vector& reverselookup, int n, int currentindex, vector >& notes, int& matchcount, vector >& retrospective) { int fileline = line; string filename = infile.getFilename(); while ((currentindex < (int)notes[0].size()) && (fileline > notes[0][currentindex].line)) { currentindex++; } if (currentindex >= (int)notes[0].size()) { if (!(raw2Q || rawQ || markQ || retroQ || countQ)) { cout << "."; printAsCombination(infile, line, ktracks, reverselookup, "."); } return currentindex; } if (notes[0][currentindex].line != fileline) { // This section occurs when two voices are both sustaining // at the start of the module. Print a "." to indicate that // the counterpoint module is continuing from a previous line. printAsCombination(infile, line, ktracks, reverselookup, "."); return currentindex; } // found the index into notes which matches to the current fileline. if (currentindex + n >= (int)notes[0].size()) { // asking for chain longer than rest of available data. printAsCombination(infile, line, ktracks, reverselookup, "."); return currentindex; } // printAsCombination(infile, line, ktracks, reverselookup, "."); // return currentindex; int tracknext; int track; int j, jj; int count = 0; for (j=0; j= 0)) { count = (int)ktracks.size() - reverselookup[track] - 1; for (jj = 0; jj >& notes, int n, int startline, int part1, int part2, vector >& retrospective, HumdrumFile& infile) { int count = 0; stringstream tempstream; int match; string notemarker; int status = printCombinationModule(tempstream, filename, notes, n, startline, part1, part2, retrospective, notemarker); if (status) { if (raw2Q || rawQ) { tempstream << "\n"; } tempstream << ends; if ((!NoteMarker.empty()) && (notemarker == NoteMarker)) { out << NoteMarker; } if (searchQ) { // Check to see if the extracted module matches to the // search query. string tstring = tempstream.str(); string newstring; for (int i=0; i<(int)tstring.size(); i++) { if (tstring[i] != '\0') { newstring += tstring[i]; } } match = SearchString.search(newstring); if (match) { count++; if (locationQ) { int line = notes[0][startline].line; double loc = infile[line].getAbsBeat() / infile[infile.getNumLines()-1].getAbsBeat(); loc = int(100.0 * loc + 0.5)/100.0; cout << "!!LOCATION:" << "\t" << loc << "\tm" << getMeasure(infile, line) << "\tv" << ((int)notes.size() - part2) << ":v" << ((int)notes.size() - part1) << "\t" << infile.getFilename() << endl; } if (raw2Q || rawQ) { string tempstring = tempstream.str(); for (int i=0; i<(int)tempstring.size(); i++) { if (tempstring[i] != '\0') { out << tempstring[i]; } } // newline already added somewhere previously. // cout << "\n"; } else { // mark notes of the matched module(s) in the note array // for later marking in input score. status = printCombinationModule(tempstream, filename, notes, n, startline, part1, part2, retrospective, notemarker, MARKNOTES); if (status && (raw2Q || rawQ)) { tempstream << "\n"; } } } } else { if (retroQ) { int column = getTriangleIndex((int)notes.size(), part1, part2); string tempstring = tempstream.str(); string newstring; for (int i=0; i<(int)tempstring.size(); i++) { if (tempstring[i] != '\0') { newstring += tempstring[i]; } } retrospective[column][status] = newstring; } else { string tempstring = tempstream.str(); for (int i=0; i<(int)tempstring.size(); i++) { if (tempstring[i] != '\0') { out << tempstring[i]; } } } } } else { if (!(raw2Q || rawQ || markQ || retroQ || countQ || searchQ)) { out << "."; } } return count; } ////////////////////////////// // // getMeasure -- return the last measure number of the given line index. // int getMeasure(HumdrumFile& infile, int line) { int i; int measure = 0; for (i=line; i>=0; i--) { if (!infile[i].isBarline()) { continue; } if (std::isdigit(infile[i][0][1])) { int flag = sscanf(infile[i][0], "=%d", &measure); if (flag > 0) { return measure; } } } return 0; } ////////////////////////////// // // getTriangleIndex -- // int getTriangleIndex(int number, int num1, int num2) { // int triangle = number * (number + 1) / 2; // intermediate code, not active yet return 0; } ////////////////////////////// // // addMarksToInputData -- mark notes in the score which matched // to the search query. // void addMarksToInputData(HumdrumFile& infile, vector >& notes, vector& ktracks, vector& reverselookup) { // first carry all marks from sustained portions of notes onto their // note attacks. int mark = 0; int track = 0; int markpitch = -1; for (int i=0; i<(int)notes.size(); i++) { mark = 0; for (int j=(int)notes[i].size()-1; j>=0; j--) { if (mark && (-markpitch == notes[i][j].b40)) { // In the sustain region between a note // attack and the marked sustain. Mark the // sustained region as well (don't know // if this behavior might change in the // future. notes[i][j].mark = mark; continue; } if (mark && (markpitch == notes[i][j].b40)) { // At the start of a notes which was marked. // Mark the attack since only note attacks // will be marked in the score notes[i][j].mark = mark; mark = 0; continue; } if (mark && (markpitch != notes[i][j].b40)) { // something strange happened. Probably // an open tie which was not started // properly, so just clear mark. mark = 0; } if (notes[i][j].mark) { mark = 1; markpitch = abs(notes[i][j].b40); } else { mark = 0; } } } // a forward loop here into notes array to continue // marks to end of sutained region of marked notes for (int i=0; i<(int)notes.size(); i++) { for (int j=0; j<(int)notes[i].size(); j++) { if (notes[i][j].mark) { markpitch = -abs(notes[i][j].b40); continue; } else if (notes[i][j].b40 == markpitch) { notes[i][j].mark = 1; continue; } else { markpitch = -1; } } } // print mark information: // for (j=0; j<(int)notes[0].size(); j++) { // for (i=0; i<(int)notes.size(); i++) { // cout << notes[i][j].b40; // if (notes[i][j].mark) { // cout << "m"; // } // cout << " "; // } // cout << "\n"; // } // now go through the input score placing user-markers onto notes // which were marked in the note array. int currentindex = 0; for (int i=0; i notes[0][currentindex].line)) { currentindex++; } if (currentindex >= (int)notes[0].size()) { continue; } if (notes[0][currentindex].line != i) { continue; } for (int j=0; j >& notes, int n, int startline, int part1, int part2) { // if the current two notes are both sustains, then skip if ((notes[part1][startline].b40 <= 0) && (notes[part2][startline].b40 <= 0)) { return 0; } if (norestsQ) { if (notes[part1][startline].b40 == 0) { return 0; } if (notes[part2][startline].b40 == 0) { return 0; } } int i; int count = 0; int attackcount = 0; int hint; vector hintlist; hintlist.reserve(1000); for (i=startline; i<(int)notes[0].size(); i++) { if ((notes[part1][i].b40 <= 0) && (notes[part2][i].b40 <= 0)) { // skip notes if both are sustained continue; } if (attackQ && ((notes[part1][i].b40 <= 0) || (notes[part2][i].b40 <= 0))) { if (attackcount == 0) { // not at the start of a pair of attacks. return 0; } } // consider harmonic interval if ((notes[part2][i].b40 != 0) && (notes[part1][i].b40 != 0)) { hint = abs(notes[part2][i].b40) - abs(notes[part1][i].b40); if (uncrossQ && (hint < 0)) { hint = -hint; } hintlist.push_back(hint); } // if count matches n, then exit loop if ((count == n) && !attackQ) { break; } count++; if ((notes[part1][i].b40 > 0) && (notes[part2][i].b40 > 0)) { // keep track of double attacks if (attackcount >= n) { break; } else { attackcount++; } } } int minimum = 100000; for (i=0; i<(int)hintlist.size(); i++) { if (hintlist[i] < minimum) { minimum = hintlist[i]; } } if (minimum > 1000) { // no intervals found to consider return 0; } if ((minimum >= 0) && (minimum <= 40)) { // nothing to do return 0; } if (minimum > 40) { return -(minimum/40); } else if (minimum < 0) { // don't go positive, this will invert the interval. return (-minimum)/40; } //int octaveadjust = -(minimum / 40); //if (attackQ && (attackcount == n)) { // return octaveadjust; //} else if (count == n) { // return octaveadjust; //} else { // // did not find the required number of modules. // return 0; //} return 0; } ////////////////////////////// // // printCombinationModule -- Similar to printLatticeModule, but harmonic // intervals will not be triggered by a pair of sustained notes. // Print a counterpoint module or module chain given the start notes // and pair of parts to calculate the module (chains) from. Will not // print anything if the chain length is longer than the note array. // The n parameter will be ignored if --attacks option is used // (--attacks will gnereate a variable length module chain). // int printCombinationModule(ostream& out, const string& filename, vector >& notes, int n, int startline, int part1, int part2, vector >& retrospective, string& notemarker, int markstate) { notemarker = ""; if (norestsQ) { if (notes[part1][startline].b40 == 0) { return 0; } if (notes[part2][startline].b40 == 0) { return 0; } } stringstream idstream; // int crossing = 0; //int oldcrossing = 0; int octaveadjust = 0; // used for -o option if (octaveQ) { octaveadjust = getOctaveAdjustForCombinationModule(notes, n, startline, part1, part2); } ostream *outp = &out; // if (rawQ && !searchQ) { // outp = &cout; // } if (n + startline >= (int)notes[0].size()) { // [20150202] // definitely nothing to do return 0; } if ((int)notes.size() == 0) { // nothing to do return 0; } // if the current two notes are both sustains, then skip if ((notes[part1][startline].b40 <= 0) && (notes[part2][startline].b40 <= 0)) { return 0; } if (raw2Q) { // print pitch of first bottom note if (filenameQ) { (*outp) << "file_" << filename; (*outp) << " "; } (*outp) << "v_" << part1 << " v_" << part2 << " "; if (base12Q) { (*outp) << "base12_"; (*outp) << Convert::base40ToMidiNoteNumber(fabs(notes[part1][startline].b40)); } else if (base40Q) { (*outp) << "base40_"; (*outp) << fabs(notes[part1][startline].b40); } else { (*outp) << "base7_"; (*outp) << Convert::base40ToDiatonic(fabs(notes[part1][startline].b40)); } (*outp) << " "; } if (parenQ) { (*outp) << "("; } int i; int count = 0; int countm = 0; int attackcount = 0; int idstart = 0; int lastindex = -1; int retroline = 0; for (i=startline; i<(int)notes[0].size(); i++) { if ((notes[part1][i].b40 <= 0) && (notes[part2][i].b40 <= 0)) { // skip notes if both are sustained continue; } if (norestsQ) { if (notes[part1][i].b40 == 0) { return 0; } if (notes[part2][i].b40 == 0) { return 0; } } if (attackQ && ((notes[part1][i].b40 <= 0) || (notes[part2][i].b40 <= 0))) { if (attackcount == 0) { // not at the start of a pair of attacks. return 0; } } // print the melodic intervals (if not the first item in chain) if ((count > 0) && !nomelodicQ) { if (mparenQ) { (*outp) << "{"; } if (nounisonsQ) { // suppress modules which contain melodic perfect unisons: if ((notes[part1][i].b40 != 0) && (abs(notes[part1][i].b40) == abs(notes[part1][lastindex].b40))) { return 0; } if ((notes[part2][i].b40 != 0) && (abs(notes[part2][i].b40) == abs(notes[part2][lastindex].b40))) { return 0; } } // bottom melodic interval: if (!toponlyQ) { printInterval((*outp), notes[part1][lastindex], notes[part1][i], INTERVAL_MELODIC); if (mmarkerQ) { (*outp) << "m"; } } // print top melodic interval here if requested if (topQ || toponlyQ) { if (!toponlyQ) { printSpacer((*outp)); } // top melodic interval: printInterval((*outp), notes[part2][lastindex], notes[part2][i], INTERVAL_MELODIC); if (mmarkerQ) { (*outp) << "m"; } } if (mparenQ) { (*outp) << "}"; } printSpacer((*outp)); } countm++; // print harmonic interval if (!noharmonicQ) { if (hparenQ) { (*outp) << "["; } if (markstate) { notes[part1][i].mark = 1; notes[part2][i].mark = 1; } else { // oldcrossing = crossing; //crossing = printInterval((*outp), notes[part1][i], // notes[part2][i], INTERVAL_HARMONIC, octaveadjust); printInterval((*outp), notes[part1][i], notes[part2][i], INTERVAL_HARMONIC, octaveadjust); } if (durationQ) { if (notes[part1][i].isAttack()) { (*outp) << "D" << notes[part1][i].duration; } if (notes[part2][i].isAttack()) { (*outp) << "d" << notes[part1][i].duration; } } if (hmarkerQ) { (*outp) << "h"; } if (hparenQ) { (*outp) << "]"; } } // prepare the ids string if requested if (idQ) { // if (count == 0) { // insert both first two notes, even if sustain. if (idstart != 0) { idstream << ':'; } idstart++; idstream << notes[part1][i].getId() << ':' << notes[part2][i].getId(); // } else { // // only insert IDs if an attack // if (notes[part1][i].b40 > 0) { // if (idstart != 0) { idstream << ':'; } // idstart++; // idstream << notes[part1][i].getId(); // } // if (notes[part2][i].b40 > 0) { // if (idstart != 0) { idstream << ':'; } // idstart++; // idstream << notes[part2][i].getId(); // } // } } // keep track of notemarker state if (notes[part1][i].notemarker == NoteMarker) { notemarker = NoteMarker; } if (notes[part2][i].notemarker == NoteMarker) { notemarker = NoteMarker; } // if count matches n, then exit loop if ((count == n) && !attackQ) { retroline = i; break; } else { if (!noharmonicQ) { printSpacer((*outp)); } } lastindex = i; count++; if ((notes[part1][i].b40 > 0) && (notes[part2][i].b40 > 0)) { // keep track of double attacks if (attackcount >= n) { retroline = i; break; } else { attackcount++; } } } if (parenQ) { (*outp) << ")"; } if (idQ && idstart) { idstream << ends; (*outp) << " ID:" << idstream.str(); } if (attackQ && (attackcount == n)) { return retroline; } else if ((countm>1) && (count == n)) { return retroline; } else if (n == 0) { return retroline; } else { // did not print the required number of modules. return 0; } return 0; } ////////////////////////////// // // printAsCombination -- // void printAsCombination(HumdrumFile& infile, int line, vector& ktracks, vector& reverselookup, const string& interstring) { if (raw2Q || rawQ || markQ || retroQ || countQ) { return; } vector done(ktracks.size(), 0); int track; int tracknext; int count; for (int j=0; j= 0) { count = (int)ktracks.size() - reverselookup[track] - 1; for (int jj=0; jj >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup, int n) { int currentindex = 0; int i; for (i=0; i", 2) == 0) { pattern = infile[i][0]; } printInterleaved(infile, i, ktracks, reverselookup, pattern); } else if (infile[i].isLocalComment()) { printInterleaved(infile, i, ktracks, reverselookup, "!"); } else if (infile[i].isBarline()) { printInterleaved(infile, i, ktracks, reverselookup, infile[i][0]); } else { // print interleaved data currentindex = printInterleavedLattice(infile, i, ktracks, reverselookup, n, currentindex, notes); } if (!(rawQ || raw2Q)) { cout << "\n"; } } } ////////////////////////////// // // printInterleavedLattice -- // int printInterleavedLattice(HumdrumFile& infile, int line, vector& ktracks, vector& reverselookup, int n, int currentindex, vector >& notes) { int fileline = line; while ((currentindex < (int)notes[0].size()) && (fileline > notes[0][currentindex].line)) { currentindex++; } if (currentindex >= (int)notes[0].size()) { if (!(rawQ || raw2Q)) { cout << "."; printInterleaved(infile, line, ktracks, reverselookup, "."); } return currentindex; } if (notes[0][currentindex].line != fileline) { // should never get here. printInterleaved(infile, line, ktracks, reverselookup, "?"); return currentindex; } // found the index into notes which matches to the current fileline. if (currentindex + n >= (int)notes[0].size()) { // asking for chain longer than rest of available data. printInterleaved(infile, line, ktracks, reverselookup, "."); return currentindex; } int tracknext; int track; int j; for (j=0; j= 0)) { if (!(rawQ || raw2Q)) { cout << "\t"; } int part1 = reverselookup[track]; int part2 = part1+1; // cout << part1 << "," << part2; printLatticeModule(cout, notes, n, currentindex, part1, part2); } if (!(rawQ || raw2Q)) { if (j < infile[line].getFieldCount() - 1) { cout << "\t"; } } } return currentindex; } ////////////////////////////// // // printInterleaved -- // void printInterleaved(HumdrumFile& infile, int line, vector& ktracks, vector& reverselookup, const string& interstring) { vector done(ktracks.size(), 0); int track; int tracknext; int j; for (j=0; j= 0)) { cout << "\t" << interstring; } if (j < infile[line].getFieldCount() - 1) { cout << "\t"; } } } } ////////////////////////////// // // printLattice -- // void printLattice(vector >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup, int n) { int i; int ii = 0; for (i=0; i >& notes, int n, int startline, int part1, int part2) { if (n + startline >= (int)notes[0].size()) { return 0; } if (parenQ) { out << "("; } int i; for (i=0; i >& notes, int n, int currentindex, int fileline) { while ((currentindex < (int)notes[0].size()) && (fileline > notes[0][currentindex].line)) { currentindex++; } if (currentindex >= (int)notes[0].size()) { if (!(rawQ || raw2Q)) { cout << "."; } return currentindex; } if (notes[0][currentindex].line != fileline) { // should never get here. if (!(rawQ || raw2Q)) { cout << "?"; } return currentindex; } // found the index into notes which matches to the current fileline. if (currentindex + n >= (int)notes[0].size()) { // asking for chain longer than rest of available data. if (!(rawQ || raw2Q)) { cout << "."; } return currentindex; } stringstream tempstream; int j; int counter = 0; for (j=0; j<(int)notes.size()-1; j++) { // iterate through each part, printing the module // for adjacent parts. counter += printLatticeModule(tempstream, notes, n, currentindex, j, j+1); if (j < (int)notes.size()-2) { printSpacer(tempstream); } } if (!(rawQ || raw2Q)) { if (counter == 0) { cout << "."; } else { tempstream << ends; string tstring = tempstream.str(); for (int i=0; i<(int)tstring.size(); i++) { if (tstring[i] != '\0') { cout << tstring[i]; } } } } return currentindex; } ////////////////////////////// // // printLatticeItem -- // int printLatticeItem(vector >& notes, int n, int currentindex, int fileline) { while ((currentindex < (int)notes[0].size()) && (fileline > notes[0][currentindex].line)) { currentindex++; } if (currentindex >= (int)notes[0].size()) { if (!(rawQ || raw2Q)) { cout << "."; } return currentindex; } if (notes[0][currentindex].line != fileline) { // should never get here. if (!(rawQ || raw2Q)) { cout << "??"; } return currentindex; } // found the index into notes which matches to the current fileline. if (currentindex + n >= (int)notes[0].size()) { // asking for chain longer than rest of available data. if (!(rawQ || raw2Q)) { cout << "."; } return currentindex; } int count; int melcount; int j; if (parenQ) { cout << "("; } for (count = 0; count < n; count++) { // print harmonic intervals if (hparenQ) { cout << "["; } for (j=0; j<(int)notes.size()-1; j++) { printInterval(cout, notes[j][currentindex+count], notes[j+1][currentindex+count], INTERVAL_HARMONIC); if (j < (int)notes.size()-2) { printSpacer(cout); } } if (hparenQ) { cout << "]"; } printSpacer(cout); // print melodic intervals if (mparenQ) { cout << "{"; } melcount = (int)notes.size()-1; if (topQ) { melcount++; } for (j=0; j 40) { if (interval % 40 == 0) { interval = 40; } else { interval = interval % 40; } } else if (interval < 0) { interval = interval + 40; } } if (base12Q && !chromaticQ) { interval = Convert::base40ToMidiNoteNumber(interval + 40*4 + 2) - 12*5; if ((type == INTERVAL_HARMONIC) && (octaveallQ)) { if (interval <= -12) { interval = interval + 1200; } if (interval > 12) { if (interval % 12 == 0) { interval = 12; } else { interval = interval % 12; } } else if (interval < 0) { interval = interval + 12; } } interval = interval + octaveadjust * 12; } else if (base7Q && !chromaticQ) { interval = Convert::base40ToDiatonic(interval + 40*4 + 2) - 7*4; if ((type == INTERVAL_HARMONIC) && (octaveallQ)) { if (interval <= -7) { interval = interval + 700; } if (interval > 7) { if (interval % 7 == 0) { interval = 7; } else { interval = interval % 7; } } else if (interval < 0) { interval = interval + 7; } } interval = interval + octaveadjust * 7; } if (chromaticQ) { char buffer[1024] = {0}; out << Convert::base40ToIntervalAbbr(buffer, interval); } else { int negative = 1; if (interval < 0) { negative = -1; interval = -interval; } if (base7Q && !zeroQ) { out << negative * (interval+1); } else { out << negative * interval; } } if (sustainQ || ((type == INTERVAL_HARMONIC) && xoptionQ)) { // print sustain/attack information of intervals. if (note1.b40 < 0) { out << "s"; } else { out << "x"; } if (note2.b40 < 0) { out << "s"; } else { out << "x"; } } return cross; } ////////////////////////////// // // printSpacer -- space or comma... // void printSpacer(ostream& out) { out << Spacer; } ////////////////////////////// // // printPitchGrid -- print the pitch grid from which all counterpoint // modules are calculated. // void printPitchGrid(vector >& notes, HumdrumFile& infile) { int i = 0; int j = 0; int pitch; int abspitch; int newpitch; int partcount; int line; double beat; if (base40Q) { partcount = (int)notes.size(); if (rhythmQ) { cout << "**absq\t"; cout << "**bar\t"; cout << "**beat\t"; } for (i=0; i 0) && (i<(int)notes[j].size()-1) && (notes[j][i+1].b40 == -abspitch)) { // start of a note which continues into next // sonority. cout << "["; } cout << Convert::base40ToKern(buffer, abspitch); // print tie continue/termination as necessary. if (pitch < 0) { if ((i < (int)notes[j].size() - 1) && (notes[j][i+1].b40 == notes[j][i].b40)) { // note sustains further cout << "_"; } else { // note does not sustain any further. cout << "]"; } } } if (j < (int)notes.size()-1) { cout << "\t"; } } cout << endl; } if (rhythmQ) { cout << "*-\t"; cout << "*-\t"; cout << "*-\t"; } for (i=0; i >& notes, HumdrumFile& infile, vector& ktracks, vector& reverselookup) { PerlRegularExpression pre; Ids.resize(infile.getMaxTracks()+1); int i, j, ii, jj; for (i=0; i<(int)Ids.size(); i++) { Ids[i] = EMPTY_ID; } vector current(ktracks.size()); vector beatsizes(infile.getMaxTracks()+1, 1); int sign; int track = 0; int index; int snum = 0; int measurenumber = 0; int tempmeasurenum = 0; double beatsize = 1.0; int topnum, botnum; for (i=0; i= 0) { measurenumber = tempmeasurenum; } } for (j=0; j<(int)current.size(); j++) { current[j].clear(); current[j].measure = measurenumber; current[j].line = i; } if (infile[i].isMeasure() && (strstr(infile[i][0], "||") != NULL)) { // double barline (terminal for Josquin project), so add a row // of rests to prevent cint melodic interval identification between // adjacent notes in different sections. for (j=0; j<(int)notes.size(); j++) { notes[j].push_back(current[j]); } } else if (infile[i].isInterpretation()) { // search for time signatures from which to extract beat information. for (j=0; j 3) && (botnum > 1)) { // compound meter // fix later beatsize = botnum / 3; } beatsizes[track] = beatsize / 4.0; } else if (strcmp(infile[i][j], "*met(C|)") == 0) { // MenCutC, use 2 as the "beat" beatsizes[track] = 2.0 / 4.0; } } } else if (idQ && infile[i].isLocalComment()) { for (j=0; j 0) { current[index].serial = ++snum; if (durationQ) { current[index].duration = infile.getTiedDurationR(ii, jj); } } } if (onlyRests(current) && onlyRests(notes.back())) { // don't store more than one row of rests in the data array. continue; } if (allSustained(current)) { // don't store sonorities which are purely sutained // (may need to be updated with a --sustain option implementation) continue; } for (j=0; j<(int)notes.size(); j++) { notes[j].push_back(current[j]); } } // attach ID tag to all sustain sections of notes if (idQ) { for (j=0; j<(int)notes.size(); j++) { for (i=1; i<(int)notes[j].size(); i++) { if (notes[j][i].isAttack()) { continue; } if ((int)notes[j][i].getId().size() > 0) { // allow for Ids on sustained notes which probably means // that there is a written tied note in the music. continue; } if (notes[j][i].getB40() == notes[j][i-1].getB40()) { notes[j][i].getId() = notes[j][i-1].getId(); } } } } } ////////////////////////////// // // onlyRests -- returns true if all NoteNodes are for rests // int onlyRests(vector& data) { int i; for (i=0; i<(int)data.size(); i++) { if (!data[i].isRest()) { return 0; } } return 1; } ////////////////////////////// // // hasAttack -- returns true if all NoteNodes are for rests // int hasAttack(vector& data) { int i; for (i=0; i<(int)data.size(); i++) { if (data[i].isAttack()) { return 1; } } return 0; } ////////////////////////////// // // allSustained -- returns true if all NoteNodes are sustains // or rests (but not all rests). // int allSustained(vector& data) { int i; int hasnote = 0; for (i=0; i<(int)data.size(); i++) { if (data[i].b40 != 0) { hasnote = 1; } if (data[i].isAttack()) { return 0; } } if (hasnote == 0) { return 0; } return 1; } ////////////////////////////// // // getAbbreviations -- // void getAbbreviations(vector& abbreviations, vector& names) { abbreviations.resize(names.size()); int i; for (i=0; i<(int)names.size(); i++) { getAbbreviation(abbreviations[i], names[i]); } } ////////////////////////////// // // getAbbreviation -- // void getAbbreviation(string& abbr, string& name) { PerlRegularExpression pre; abbr = name; pre.sar(abbr, "(?<=[a-zA-Z])[a-zA-Z]*", "", ""); pre.tr(abbr, "123456789", "abcdefghi"); } ////////////////////////////// // // getKernTracks -- return a list of track number for **kern spines. // void getKernTracks(vector& ktracks, HumdrumFile& infile) { ktracks.resize(infile.getMaxTracks()); std::fill(ktracks.begin(), ktracks.end(), 0); for (int i=0; i& names, vector& reverselookup, HumdrumFile& infile) { names.resize((int)reverselookup.size()-1); char buffer[1024] = {0}; int value; PerlRegularExpression pre; int i; int j; int track; for (i=0; i<(int)names.size(); i++) { value = (int)reverselookup.size() - i; sprintf(buffer, "%d", value); names[i] = buffer; } for (i=0; i