// // Programmer: Craig Stuart Sapp // Creation Date: Tue Mar 19 15:51:09 PDT 2013 // Last Modified: Tue Apr 23 17:32:43 PDT 2013 Added colored notes // Filename: ...sig/examples/all/hum2enp.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/hum2enp.cpp // Syntax: C++; museinfo // // Description: Converts Humdrum files into ENP (Expressive Notation Package) // files. // #include #include #include #include using namespace std; #include "humdrum.h" #include "PerlRegularExpression.h" #define CLEF_UNKNOWN 0 #define CLEF_TREBLE 1 #define CLEF_BASS 2 class Coordinate { public: int i, j; }; ////////////////////////////////////////////////////////////////////////// // function declarations: void checkOptions (Options& opts, int argc, char** argv); void example (void); void usage (const char* command); void convertHumdrumToEnp (ostream& out, HumdrumFile& infile); void getKernTracks (Array& tracks, HumdrumFile& infile); void getPartNames (HumdrumFile& infile, Array >& PartNames); void pline (ostream& out, int level, const char* string); void plineStart (ostream& out, int level, const char* string); void indent (ostream& out, int level); void printPart (ostream& out, HumdrumFile& infile, int spine, int subspine, Array& barlines, int keysig, int clef); void getBarlines (Array& barlines, HumdrumFile& infile); void printMeasure (ostream& out, HumdrumFile& infile, int spine, int voice, Array& barlines, int index, int defaulkeysig, int defaultclef, int& activeclef); void printInitialStaff (ostream& out, HumdrumFile& infile, int spine, int& defaultClef); int printKeySignature (ostream& out, HumdrumFile& infile, int spine, int line); void printTimeSignature (ostream& out, HumdrumFile& infile, int spine, int line); void extractVoiceItems (Array& items, HumdrumFile& infile, int spine, int voice, int startbar, int endbar); int printSubBeatLevel (ostream& out, HumdrumFile& infile, Array& items, Array& notes, int noteindex, int keysig, int defaultclef, int currentclef); void printMeasureContent (ostream& out, HumdrumFile& infile, Array& items, RationalNumber& starttime, RationalNumber& endtime, int defaultkeysig, int defaultclef, int& activeclef); void printMidiNotes (ostream& out, HumdrumFile& infile, int line, int field, int keysig); int getBeatGroupCount (HumdrumFile& infile, Array& items, Array& notes, int noteindex); RationalNumber getSmallestRhythm(HumdrumFile& infile, Array& items, Array& notes, int noteindex, int groupcount); void printChordArticulations (ostream& out, HumdrumFile& infile, int line, int field); void printHeaderComments (ostream& out, HumdrumFile& infile); void printTrailerComments (ostream& out, HumdrumFile& infile); void printDataComments (ostream& out, HumdrumFile& infile, Array& items, int index); void printTieDot (ostream& out, HumdrumFile& infile, int line, int field); void checkMarks (HumdrumFile& infile, Array& marks, Array >& markcolors); void getNoteAttributes (stringstream& attributes, HumdrumFile& infile, int line, int field, int subfield, const char* kernnote, int keysig); void getNoteExpressions (stringstream& expressions, HumdrumFile& infile, int line, int field, int subfield, const char* kernnote); void getSubspines (Array& subtracks, HumdrumFile& infile, Array& kerntracks); void printChord (ostream& out, HumdrumFile& infile, int line, int field, RationalNumber& dur, int keysig, int defaultclef, int currentclef); void printRest (ostream& out, HumdrumFile& infile, int line, int field, RationalNumber& dur); void printStem (ostream& out, HumdrumFile& infile, int line, int field); void getEnharmonic (ostream& out, const char* note, int keysig); ostream& printClefAttribute (ostream& out, int activeclef); ostream& printScoreInformation(ostream& out, HumdrumFile& infile); // User interface variables: Options options; int debugQ = 0; // used with --debug option int originalQ = 0; // used with --original option int labelQ = 0; // used with -L option int commentQ = 0; // used with -C option int humdrumQ = 0; // not hooked up to an option yet const char* INDENT = "\t"; // indentation for each level int LEVEL = 0; // used to indent the score Array marks; // used to color notes Array > markcolors; // used to color notes Array markline; // used to search for circles // The instance ID works similar to XML::id int InstanceIdCounter = 0; ////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { HumdrumFile infile; // initial processing of the command-line options checkOptions(options, argc, argv); if (options.getArgCount() < 1) { infile.read(cin); } else { infile.read(options.getArg(1)); } convertHumdrumToEnp(cout, infile); return 0; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // getSubspineCount -- Return a count of each track's maximum sub-spine // count. // void getSubspines(Array& subtracks, HumdrumFile& infile, Array& kerntracks) { int i; int j; int track; int maxtracks = infile.getMaxTracks(); Array maxvals(maxtracks+1); maxvals.setAll(0); Array linevals(maxtracks+1); for (i=0; i maxvals[j]) { maxvals[j] = linevals[j]; } } } subtracks.setSize(kerntracks.getSize()); subtracks.setAll(0); for (i=0; i kerntracks; getKernTracks(kerntracks, infile); Array subtracks; getSubspines(subtracks, infile, kerntracks); Array > partnames; getPartNames(infile, partnames); Array barlines; getBarlines(barlines, infile); checkMarks(infile, marks, markcolors); if (commentQ) { printHeaderComments(out, infile); } LEVEL = 0; // Print the outer score parentheses plineStart(out, LEVEL++, "("); if (labelQ) { out << ":begin :score"; } out << endl; printScoreInformation(out, infile); int i, j; int partnum = 0; for (i=kerntracks.getSize()-1; i>=0; i--) { partnum++; indent(out, LEVEL++); // print part-level parenthesis out << "("; if (labelQ) { out << ":begin :part" << partnum; } out << endl; int initialclef = CLEF_UNKNOWN; printInitialStaff(out, infile, kerntracks[i], initialclef); int initialkeysig = printKeySignature(out, infile, kerntracks[i], 0); printTimeSignature(out, infile, kerntracks[i], 0); printPart(out, infile, kerntracks[i], 0, barlines, initialkeysig, initialclef); // print voices/layers after the first one: for (j=1; j value1; Array value2; if (opr >= 0) { if (otl >= 0) { infile[opr].getBibValue(value1); infile[otl].getBibValue(value2); pre.sar(value1, "\"", "\'", "g"); pre.sar(value2, "\"", "\'", "g"); indent(out, LEVEL); out << "(:title \""; out << value1; out << "\")" << endl; indent(out, LEVEL); out << "(:subtitle \""; out << value2; out << "\")" << endl; } else { infile[opr].getBibValue(value1); pre.sar(value1, "\"", "\'", "g"); indent(out, LEVEL); out << "(:title \""; out << value1; out << "\")" << endl; } } else if (otl >= 0) { infile[otl].getBibValue(value1); pre.sar(value1, "\"", "\'", "g"); indent(out, LEVEL); out << "(:title \""; out << value1; out << "\")" << endl; } if (com >= 0) { infile[com].getBibValue(value1); pre.sar(value1, "\"", "\'", "g"); indent(out, LEVEL); out << "(:composer \""; out << value1; out << "\")" << endl; } if (omd >= 0) { infile[omd].getBibValue(value1); pre.sar(value1, "\"", "\'", "g"); indent(out, LEVEL); out << "(:tempo-heading \""; out << value1; out << "\")" << endl; } if (yec >= 0) { infile[yec].getBibValue(value1); pre.sar(value1, "\"", "\'", "g"); indent(out, LEVEL); out << "(:copyright \""; out << value1; out << "\")" << endl; } indent(out, --LEVEL); out << ")" << endl; return out; } ////////////////////////////// // // printHeaderComments -- // void printHeaderComments(ostream& out, HumdrumFile& infile) { int i; for (i=0; i=0; i--) { if (infile[i].isInterpretation()) { break; } endline = i; } if (endline < 0) { return; } for (i=endline; i0; i--) { if (infile[i].isData()) { // quit loop when a data line is found. break; } for (j=0; j 0) { out << " :kind :pickup"; } } out << ")" << endl; } ////////////////////////////// // // printKeySignature -- Print a key signature for the given // voice. Search anywhere within enclosing data lines for // the *k[] (key signature) and *C: (key) markers. // // Returns the key signatures in terms of sharp/flat count. // int printKeySignature(ostream& out, HumdrumFile& infile, int spine, int line) { Coordinate keysig; Coordinate key; keysig.i = -1; keysig.j = -1; key.i = -1; key.j = -1; int i, j; int track; PerlRegularExpression pre; // first search backwards from current location: for (i=line; i>0; i--) { if (infile[i].isData()) { // quit loop when a data line is found. break; } for (j=0; j 0) { if (std::islower(infile[key.i][key.j][1])) { mode = 1; } } int fifthskey = 0; if (keysig.i > 0) { fifthskey = Convert::kernKeyToNumber(infile[keysig.i][keysig.j]); } indent(out, LEVEL); out << ":key-signature "; if (mode == 0) { // major mode switch (fifthskey) { case -7: out << ":c-flat-major"; break; case -6: out << ":g-flat-major"; break; case -5: out << ":d-flat-major"; break; case -4: out << ":a-flat-major"; break; case -3: out << ":e-flat-major"; break; case -2: out << ":b-flat-major"; break; case -1: out << ":f-major"; break; case 0: out << ":c-major"; break; case +1: out << ":g-major"; break; case +2: out << ":d-major"; break; case +3: out << ":a-major"; break; case +4: out << ":e-major"; break; case +5: out << ":b-major"; break; case +6: out << ":f-sharp-major"; break; case +7: out << ":c-sharp-major"; break; } } else if (mode == 1) { // minor modes switch (fifthskey) { case -7: out << ":a-flat-minor"; break; case -6: out << ":e-flat-minor"; break; case -5: out << ":b-flat-minor"; break; case -4: out << ":f-minor"; break; case -3: out << ":c-minor"; break; case -2: out << ":g-minor"; break; case -1: out << ":d-minor"; break; case 0: out << ":a-minor"; break; case +1: out << ":e-minor"; break; case +2: out << ":b-minor"; break; case +3: out << ":f-sharp-minor"; break; case +4: out << ":c-sharp-minor"; break; case +5: out << ":g-sharp-minor"; break; case +6: out << ":d-sharp-minor"; break; case +7: out << ":a-sharp-minor"; break; } } out << endl; return fifthskey; } ////////////////////////////// // // printInitialStaff -- print the starting staff for a part (if any). // void printInitialStaff(ostream& out, HumdrumFile& infile, int spine, int& defaultClef) { int i, j; int track; for (i=0; i& barlines, int defaultkeysig, int defaultclef) { int i; int voice = subspine; plineStart(out, LEVEL++, "("); if (labelQ) { out << ":begin :voice" << voice+1; } out << endl; int activeclef = defaultclef; for (i=0; i& barlines, int index, int defaultkeysig, int defaultclef, int& activeclef) { int startbar = barlines[index]; int endbar = barlines[index+1]; int i; Array items; extractVoiceItems(items, infile, spine, voice, startbar, endbar); indent(out, LEVEL++); out << "("; if (labelQ) { int barnum = -1; if (items.getSize() == 0) { if (sscanf(infile[startbar][0], "=%d", &barnum)) { out << ":begin :measure" << barnum; if (humdrumQ && infile[items[0].i].isMeasure()) { out << "\t; " << infile[items[0].i][0]; } } } else { if (sscanf(infile[items[0].i][items[0].j], "=%d", &barnum)) { out << ":begin :measure" << barnum; if (humdrumQ && infile[items[0].i].isMeasure()) { out << "\t; " << infile[items[0].i][0]; } } } } out << endl; if (humdrumQ) { for (i=0; i& items, RationalNumber& starttime, RationalNumber& endtime, int keysig, int defaultclef, int& activeclef) { RationalNumber mdur; mdur = endtime - starttime; if (items.getSize() == 0) { // print invisible full-measure rest indent(out, LEVEL); out << "(" << mdur << " ((-1 :visible-p nil)))" << endl; return; } int i; Array notes; notes.setSize(items.getSize()); notes.setSize(0); Array clefs; clefs.setSize(items.getSize()); clefs.setSize(0); for (i=0; i& items, Array& notes, int noteindex, int keysig, int defaultclef, int currentclef) { // groupcount is the number of notes in an integer number // of beats within the measure. int groupcount = getBeatGroupCount(infile, items, notes, noteindex); RationalNumber groupduration; RationalNumber startpos; RationalNumber endpos; RationalNumber enddur; int ii, jj; ii = items[notes[noteindex]].i; jj = items[notes[noteindex]].j; startpos = infile[ii].getAbsBeatR(); ii = items[notes[noteindex+groupcount-1]].i; jj = items[notes[noteindex+groupcount-1]].j; endpos = infile[ii].getAbsBeatR(); enddur = Convert::kernToDurationR(infile[ii][jj]); groupduration = (endpos - startpos) + enddur; if (!groupduration.isInteger()) { cerr << "Funny error: group duration is not an integer" << endl; exit(1); } RationalNumber minrhy; minrhy.setValue(1,2); // minrhy is smallest duration in terms of quarter notes. minrhy = getSmallestRhythm(infile, items, notes, noteindex, groupcount); indent(out, LEVEL++); out << "(" << groupduration << " ("<< endl; RationalNumber quarternote(1,4); // print group notes int i; RationalNumber notediv; for (i=noteindex; i :stem-direction :up & \ -> :stem-direction :down // Can't have a chord with stems in both directions (will print with stem up). // void printStem(ostream& out, HumdrumFile& infile, int line, int field) { if (strchr(infile[line][field], '/') != NULL) { out << " :stem-direction :up"; } else if (strchr(infile[line][field], '\\') != NULL) { out << " :stem-direction :down"; } } ////////////////////////////// // // printRest -- // void printRest(ostream& out, HumdrumFile& infile, int line, int field, RationalNumber& dur) { int& ii = line; int& jj = field; int tdur = dur.getNumerator(); if (tdur == 0) { // grace notes are stored with duration of 1 (and :class :grace-beat) tdur = 1; } // this rest has no attributes so not adding an extra paren set // otherwise it would be "((-". out << "(" << tdur << " (-" << 1; printTieDot(out, infile, ii, jj); out << ")"; // paren for inner units if (dur == 0) { out << " :class :grace-beat"; } out << ")"; // paren for outer unit } ////////////////////////////// // // printTieDot -- print a dot after a rhythm to indicate that it is // tied to a previous note. Do not put a marker on the first note // in a tied group. Ties are created by converting a rhythmic // value into a floating-point number by adding ".0" to the end // of the rhythm. Note that adding "." by itself does not work. // void printTieDot(ostream& out, HumdrumFile& infile, int line, int field) { int tieQ = 0; if (strchr(infile[line][field], ']') != NULL) { tieQ = 1; } else if (strchr(infile[line][field], '_') != NULL) { tieQ = 1; } if (tieQ) { out << ".0"; } } ///////////////////////////// // // printChordArticulations -- // void printChordArticulations(ostream& out, HumdrumFile& infile, int line, int field) { int fermataQ = 0; int staccatoQ = 0; int accentQ = 0; // visually: > in Humdrum: ' int marcatoQ = 0; // visually: ^ in Humdrum: ^ if (strchr(infile[line][field], ';') != NULL) { fermataQ = 1; } if (strchr(infile[line][field], '\'') != NULL) { staccatoQ = 1; } if (strchr(infile[line][field], '^') != NULL) { accentQ = 1; } if (strchr(infile[line][field], '`') != NULL) { marcatoQ = 1; } int expressionQ = 0; expressionQ |= fermataQ; expressionQ |= staccatoQ; expressionQ |= marcatoQ; expressionQ |= accentQ; int counter = 0; if (expressionQ) { out << " :expressions ("; if (fermataQ) { if (counter++ != 0) { out << " "; } out << ":fermata"; } if (staccatoQ && accentQ) { if (counter++ != 0) { out << " "; } out << ":accent+staccato"; } if (staccatoQ && marcatoQ) { if (counter++ != 0) { out << " "; } out << ":marcato+staccato"; } if (staccatoQ && !accentQ && !marcatoQ) { if (counter++ != 0) { out << " "; } out << ":staccato"; } if (!staccatoQ && accentQ) { if (counter++ != 0) { out << " "; } out << ":accent"; } if (!marcatoQ && accentQ) { if (counter++ != 0) { out << " "; } out << ":marcato"; } out << ")"; } } ////////////////////////////// // // printDataComments -- Print any comments before the current // line of data for the voice within the part's voice. Stop // if a previous note/rest is found. // void printDataComments(ostream& out, HumdrumFile& infile, Array& items, int index) { int start = -1; int ii; int i; for (i=index-1; i>=0; i--) { ii = items[i].i; if (infile[ii].isComment()) { start = i; continue; } break; } if (start < 0) { // nothing to do return; } for (i=start; i 0) { out << " "; } stringstream slots; getNoteAttributes(slots, infile, line, field, k, buffer, keysig); slots << ends; if (strlen(slots.str().c_str()) > 0) { out << "("; out << base12; out << slots.str().c_str(); out << ")"; } else { out << base12; } } } ////////////////////////////// // // getNoteAttributes -- returns a list of attributes for a note (if any) // void getNoteAttributes(stringstream& attributes, HumdrumFile& infile, int line, int field, int subfield, const char* kernnote, int keysig) { // if the note is supposed to be shows as a flatted note, then // add an attribute which says to display it as a flat (otherwise // ENP will always show accidentals as sharped notes). /* * :enharmonic :flat will be in context of the key so 61 may be default * sharp if in A major, but if (strchr(buffer, '-') != NULL) { // indicate the the MIDI pitch should be displayed as a diatonic flat attributes << " :enharmonic :flat"; } */ /* Temporarily get rid of enharmonics: stringstream enharmonic; getEnharmonic(enharmonic, kernnote, keysig); enharmonic << ends; if (strlen(enharmonic.str().c_str()) > 0) { attributes << " :enharmonic " << enharmonic.str().c_str(); } */ // check for cautionary accidentals. These are marked with "X" immediately after the // accidental. if ((strstr(kernnote, "nX") != NULL) || (strstr(kernnote, "#X") != NULL) || (strstr(kernnote, "-X") != NULL)) { attributes << " :draw-alteration-p :force"; } // check for colored notes based on !!!RDF: entries in the file. int i; for (i=0; i 0) { attributes << " :expressions ("; attributes << expressions.str().c_str(); attributes << ")"; } } ////////////////////////////// // // getEnharmonic -- // void getEnharmonic(ostream& out, const char* note, int keysig) { Array diatonic(7); diatonic.setAll(0); if (keysig > 0) { switch (keysig) { case 7: diatonic[6] = 1; // B case 6: diatonic[2] = 1; // E case 5: diatonic[5] = 1; // A case 4: diatonic[1] = 1; // D case 3: diatonic[4] = 1; // G case 2: diatonic[0] = 1; // C case 1: diatonic[3] = 1; // F } } else if (keysig < 0) { switch (keysig) { case -7: diatonic[3] = -1; // F case -6: diatonic[0] = -1; // C case -5: diatonic[4] = -1; // G case -4: diatonic[1] = -1; // D case -3: diatonic[5] = -1; // A case -2: diatonic[2] = -1; // E case -1: diatonic[6] = -1; // B } } int notedia = Convert::kernToDiatonicPitch(note) % 7; int base40 = Convert::kernToBase40(note); int accid = Convert::base40ToAccidental(base40); //int midi = Convert::kernToMidiNoteNumber(note); int difference = accid - diatonic[notedia]; if (difference == 0) { // MIDI note is in the scale, so no need to alter it. return; } else if (difference == 1) { out << ":sharp"; return; } else if (difference == -1) { out << ":flat"; return; } // don't know what to do: some exotic accidental } ////////////////////////////// // // getNoteExpressions -- // void getNoteExpressions(stringstream& expressions, HumdrumFile& infile, int line, int field, int subfield, const char* kernnote) { PerlRegularExpression pre; int i; for (i=0; i& items, Array& notes, int noteindex, int groupcount) { int i; RationalNumber minrhy; RationalNumber testrhy; minrhy.setValue(1,1); int ii, jj; for (i=noteindex; i testrhy) { minrhy = testrhy; } } return minrhy; } ////////////////////////////// // // getBeatGroupCount -- return the number of notes in an integer number // of beats within the measure. // int getBeatGroupCount(HumdrumFile& infile, Array& items, Array& notes, int noteindex) { int output = 0; int i; RationalNumber dursum; dursum.setValue(0,1); int ii, jj; for (i=noteindex; i& items, HumdrumFile& infile, int spine, int voice, int startbar, int endbar) { items.setSize(endbar-startbar+1); items.setSize(0); int i, j; int track; int voicenum; Coordinate loc; for (i=startbar; i<=endbar; i++ ) { voicenum = 0; for (j=0; j& barlines, HumdrumFile& infile) { int i; int zero = 0; int foundstartdata = 0; int founddata = 0; barlines.setSize(infile.getNumLines()); barlines.setSize(0); for (i=0; i >& PartNames) { int i, j; PartNames.setSize(infile.getMaxTracks()+1); // 0 = unused for (i=0; i ignore; ignore.setSize(infile.getMaxTracks()+1); ignore.setAll(0); PerlRegularExpression pre; int track; for (i=0; i& tracks, HumdrumFile& infile) { tracks.setSize(infile.getMaxTracks()); tracks.setSize(0); int i; for (i=1; i<=infile.getMaxTracks(); i++) { if (infile.getTrackExInterp(i) == "**kern") { tracks.append(i); } } if (debugQ) { cerr << "\t**kern tracks:\n"; for (i=0; i& marks, Array >& markcolors) { int markQ = 1; if (!markQ) { marks.setSize(0); markline.setSize(0); markcolors.setSize(0); return; } marks.setSize(0); markline.setSize(0); markcolors.setSize(0); int i; char target; PerlRegularExpression pre; for (i=0; i