// // Programmer: Craig Stuart Sapp // Creation Date: Sun Dec 26 03:28:25 PST 2010 // Last Modified: Mon Feb 7 06:22:42 PST 2011 (added beam directions) // Filename: ...sig/examples/all/autostem.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/autostem.cpp // Syntax: C++; museinfo // // Description: Converts Humdrum files into MuseData files. // #include #ifndef OLDCPP #include #include #define SSTREAM stringstream #define CSTRING str().c_str() using namespace std; #else #include #ifdef VISUAL #include #else #include #endif #define SSTREAM strstream #define CSTRING str() #endif #include "string.h" #include "humdrum.h" #include "PerlRegularExpression.h" #include "MuseData.h" ////////////////////////////////////////////////////////////////////////// class Coord { public: Coord(void) { clear(); } void clear(void) { i = j = -1; } int i; int j; }; ////////////////////////////////////////////////////////////////////////// // function declarations: void checkOptions (Options& opts, int argc, char** argv); void example (void); void usage (const char* command); void autostem (HumdrumFile& infile); void getClefInfo (Array >& baseline, HumdrumFile& infile); void addStem (char* output, const char* input, const char* piece); void processKernTokenStemsSimpleModel(HumdrumFile& infile, Array >& baseline, int row, int col); void removeStems (ostream& out, HumdrumFile& infile); void removeStem2 (HumdrumFile& infile, int row, int col); int getVoice (HumdrumFile& infile, int row, int col); void getNotePositions (Array > >& notepos, Array >& baseline, HumdrumFile& infile); void printNotePositions (HumdrumFile& infile, Array > >& notepos); void getVoiceInfo (Array >& voice, HumdrumFile& infile); void printVoiceInfo (HumdrumFile& infile, Array >& voice); void processKernTokenStems(HumdrumFile& infile, Array >& baseline, int row, int col); void getMaxLayers (Array& maxlayer, Array >& voice, HumdrumFile& infile); void assignStemDirections (Array >& stemdir, Array > & voice, Array > >& notepos, HumdrumFile& infile); void assignBasicStemDirections(Array >& stemdir, Array >& voice, Array > >& notepos, HumdrumFile& infile); int determineChordStem (Array >& voice, Array > >& notepos, HumdrumFile& infile, int row, int col); void insertStems (HumdrumFile& infile, Array >& stemdir); void setStemDirection (HumdrumFile& infile, int row, int col, int direction); void getBeamState (Array > >& beams, HumdrumFile& infile); void countBeamStuff (const char* token, int& start, int& stop, int& flagr, int& flagl); void getBeamSegments (Array >& beamednotes, Array > >& beamstates, HumdrumFile& infile, Array maxlayer); int getBeamDirection (Array& coords, Array >& voice, Array > >& notepos); void setBeamDirection (Array >& stemdir, Array& bnote, int direction); // User interface variables: Options options; int debugQ = 0; // used with --debug option int removeQ = 0; // used with -r option int noteposQ = 0; // used with -p option int voiceQ = 0; // used with --voice option int removeallQ = 0; // used with -R option int overwriteQ = 0; // used with -o option int overwriteallQ = 0; // used with -O option int Middle = 4; // used with -u option ////////////////////////////////////////////////////////////////////////// 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)); } if (removeQ || overwriteQ) { SSTREAM tempstream; removeStems(tempstream, infile); infile.clear(); infile.read(tempstream); if (removeQ) { cout << infile; exit(0); } } autostem(infile); cout << infile; return 0; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // removeStems -- // void removeStems(ostream& out, HumdrumFile& infile) { int i, j; PerlRegularExpression pre; Array buffer; buffer.setSize(1024); for (i=0; i > baseline; getClefInfo(baseline, infile); Array > > notepos; // staff line position of all notes getNotePositions(notepos, baseline, infile); if (noteposQ) { printNotePositions(infile, notepos); exit(0); } Array > voice; // voice/layer number in track getVoiceInfo(voice, infile); if (voiceQ) { printVoiceInfo(infile, voice); exit(0); } Array > stemdir; // stem directions; stemdir.setSize(infile.getNumLines()); for (int i=0; i >& stemdir) { int i, j; for (i=0; i 0) { addStem(buffer2, buffer, "/"); } else if (direction < 0) { addStem(buffer2, buffer, "\\"); } else { strcpy(buffer2, buffer); } } else { strcpy(buffer2, buffer); } strcat(output, buffer2); if (k < tokencount-1) { strcat(output, " "); } } infile[i].setToken(j, output); } ////////////////////////////// // // assignStemDirections -- // void assignStemDirections(Array >& stemdir, Array > & voice, Array > >& notepos, HumdrumFile& infile) { Array maxlayer; getMaxLayers(maxlayer, voice, infile); assignBasicStemDirections(stemdir, voice, notepos, infile); Array > > beamstates; getBeamState(beamstates, infile); Array > beamednotes; getBeamSegments(beamednotes, beamstates, infile, maxlayer); // print notes which are beamed together for debugging: int i, j; if (debugQ) { for (i=0; i >& stemdir, Array& bnote, int direction) { int x; int i, j; for (x=0; x& coords, Array >& voice, Array > >& notepos) { // voice values are presumbed to be 0 at the moment. int minn = 1000; int maxx = -1000; int x; int i, j, k; for (x=0; x notepos[i][j][k]) { minn = notepos[i][j][k]; } if (maxx < notepos[i][j][k]) { maxx = notepos[i][j][k]; } } } if (maxx < 0) { // both minn and maxx are less than zero, so place stems up return +1; } if (minn > 0) { // both minn and maxx are greater than zero, so place stems down return -1; } if (abs(maxx) > abs(minn)) { // highest note is higher than lower note is lower, so place // stems down return -1; } if (abs(maxx) > abs(minn)) { // highest note is lower than lower note is lower, so place // stems up return +1; } // its a draw, so place stem up. return +1; } ////////////////////////////// // // getBeamSegments -- arrange the beamed notes into a long list with // each entry being a list of notes containing one beam. Each // beamed note set should have their beams all pointing in the same // direction. // void getBeamSegments(Array >& beamednotes, Array > >& beamstates, HumdrumFile& infile, Array maxlayer) { beamednotes.setSize(infile.getNumLines() * infile.getMaxTracks() / 2); beamednotes.setGrowth(1000000); beamednotes.setSize(0); Array > > beambuffer; beambuffer.setSize(infile.getMaxTracks() + 1); int i, j; for (i=0; i& maxlayer, Array >& voice, HumdrumFile& infile) { int track; maxlayer.setSize(infile.getMaxTracks() + 1); maxlayer.setAll(0); int i, j; for (i=0; i maxlayer[track]) { maxlayer[track] = voice[i][j] + 1; } } } } ////////////////////////////// // // getVoiceInfo -- 0 = only voice in track, 1 = layer 1, 2 = layer 2, etc. // // 0 voices will be stemmed up or down based on vertical positions of notes // 1 voices will be stemmed up always // 2 voices will be stemmed down always. // 3 and higher are still to be determined. // // Future enhancement of this algorithm: if one voice contains an invisible // rest, then it will be ignored in the voice calculation. // void getVoiceInfo(Array >& voice, HumdrumFile& infile) { voice.setSize(infile.getNumLines()); int i, j, v; for (i=0; i >& voice) { PerlRegularExpression pre; Array data; SSTREAM *pstream; int i, j; for (i=0; iCSTRING) + 1); strcpy(data.getBase(), pstream->CSTRING); pre.sar(data, "\\s+$", "", ""); if (strlen(data.getBase()) == 0) { pre.sar(data, "^\\s*$", "."); } cout << data.getBase(); cout << "\n"; delete pstream; pstream = NULL; } } ////////////////////////////// // // printNotePositions -- prints the vertical position of notes on the // staves. Mostly for debugging purposes. A spine at the end of the // data will be added containing all positions for notes on the line // in the sequence in which the notes occur from left to right. // // The middle line of a 5-line staff is the zero position, and // position values are diatonic steps above or below that level: // // ===== +4 // +3 // ===== +2 // +1 // ===== 0 // -1 // ===== -2 // -3 // ===== -4 // void printNotePositions(HumdrumFile& infile, Array > >& notepos) { PerlRegularExpression pre; Array data; SSTREAM *pstream; int i, j, k; for (i=0; iCSTRING) + 1); strcpy(data.getBase(), pstream->CSTRING); pre.sar(data, "\\s+$", "", ""); if (strlen(data.getBase()) == 0) { pre.sar(data, "^\\s*$", "."); } cout << data.getBase(); cout << "\n"; delete pstream; pstream = NULL; } } ////////////////////////////// // // getNotePositions -- Extract the vertical position of the notes // on the staves, with the centerline of the staff being the 0 position // and each diatonic step equal to 1, so that lines of 5-lined staff are // at positions from bottom to top: -4, -2, 0, +2, +4. // void getNotePositions(Array > >& notepos, Array >& baseline, HumdrumFile& infile) { notepos.setSize(infile.getNumLines()); int location; char buffer[128] = {0}; int i, j, k; int tokencount; for (i=0; i >& baseline, int row, int col) { exit(1); } ////////////////////////////// // // assignBasicStemDirections -- don't take beams into consideration. // void assignBasicStemDirections(Array >& stemdir, Array >& voice, Array > >& notepos, HumdrumFile& infile) { int i, j; for (i=0; i >& voice, Array > >& notepos, HumdrumFile& infile, int row, int col) { if (notepos[row][col].getSize() == 0) { return 0; } if (voice[row][col] == 1) { return +1; } if (voice[row][col] == 2) { return -1; } if (voice[row][col] == 3) { return +1; } // voice == 0 means determine by vertical position if (notepos[row][col].getSize() == 1) { int location = notepos[row][col][0]; if (location >= 0) { return -1; } else { return +1; } } // chord with more than one note so choose the extreme note as the // one which decides the direction int i; int minn = notepos[row][col][0]; int maxx = notepos[row][col][0]; for (i=1; i notepos[row][col][i]) { minn = notepos[row][col][i]; } if (maxx < notepos[row][col][i]) { maxx = notepos[row][col][i]; } } if (maxx < 0) { // all stems want to point upwards: return +1; } if (minn > 0) { // all stems want to point downwards: return -1; } if (abs(maxx) > abs(minn)) { return -1; } if (abs(minn) > abs(maxx)) { return +1; } return +1; } ////////////////////////////// // // processKernTokenStemsSimpleModel -- // void processKernTokenStemsSimpleModel(HumdrumFile& infile, Array >& baseline, int row, int col) { int k; int& i = row; int& j = col; int tokencount = infile[i].getTokenCount(j); RationalNumber duration; if (tokencount == 1) { duration = Convert::kernToDurationR(infile[i][j]); if (duration >= 4) { // whole note or larger for note/chord, to not append a stem return; } if ((strchr(infile[i][j], '/') != NULL) || (strchr(infile[i][j], '/') != NULL)) { if (removeallQ || overwriteallQ) { if (strstr(infile[i][j], "/x") != NULL) { if (strstr(infile[i][j], "/xx") == NULL) { return; } } else if (strstr(infile[i][j], "\\x") != NULL) { if (strstr(infile[i][j], "\\xx") == NULL) { return; } } } else if (removeallQ || overwriteallQ) { removeStem2(infile, i, j); } else { // nothing to do return; } } if (strchr(infile[i][j], 'r') != NULL) { // rest which does not have a stem return; } } if (removeQ) { removeStem2(infile, i, j); } int voice = getVoice(infile, row, col); int len = strlen(infile[i][j]); char buffer[128] = {0}; char buffer2[128] = {0}; int location; char output[len*2+tokencount]; output[0] = '\0'; for (k=0; k= 4) { // // whole note or larger for note/chord, do not append a stem // return; // } } if (!((strchr(infile[i][j], '/') != NULL) || (strchr(infile[i][j], '\\') != NULL))) { location = Convert::kernToDiatonicPitch(buffer) - baseline[row][col] - Middle; if (voice == 1) { addStem(buffer2, buffer, "/"); } else if (voice == 2) { addStem(buffer2, buffer, "\\"); } else { if (location > 0) { addStem(buffer2, buffer, "\\"); } else { addStem(buffer2, buffer, "/"); } } strcat(output, buffer2); if (k < tokencount-1) { strcat(output, " "); } } else { strcat(output, buffer); if (k < tokencount-1) { strcat(output, " "); } } } infile[i].setToken(j, output); } ////////////////////////////// // // getVoice -- return 0 if the only spine in primary track, otherwise, return // the nth column offset from 1 in the primary track. // int getVoice(HumdrumFile& infile, int row, int col) { int output = 0; int tcount = 0; int track = infile[row].getPrimaryTrack(col); int j; int testtrack; for (j=0; j strin; int len = strlen(infile[row][col]); strin.setSize(len+1); strcpy(strin.getBase(), infile[row][col]); PerlRegularExpression pre; pre.sar(strin, "[\\\\/]x(?!x)", "", "g"); pre.sar(strin, "[\\\\/](?!x)", "", "g"); infile[row].setToken(col, strin.getBase()); } ////////////////////////////// // // addStem -- // void addStem(char* output, const char* input, const char* piece) { PerlRegularExpression pre; if (pre.search(input, "(.*[ABCDEFG][n#-]*)(.*)$", "i")) { strcpy(output, pre.getSubmatch(1)); strcat(output, piece); strcat(output, pre.getSubmatch(2)); } else { strcpy(output, input); strcat(output, piece); } } /////////////////////////////// // // getClefInfo -- Identify the clef of each note in the score. // Does not consider the case where a primary track contains // more than one clef at a time. // void getClefInfo(Array >& baseline, HumdrumFile& infile) { Array states; states.setSize(infile.getMaxTracks()+1); // +1 to allow for unused 0 index states.setAll(Convert::kernClefToBaseline("*clefG2")); int i, j; baseline.setSize(infile.getNumLines()); for (i=0; i > >& beams, HumdrumFile& infile) { int i, j, t; int ii; int len; int contin; int start; int stop; int flagr; int flagl; int track; RationalNumber rn; Array > beamstate; // state of beams in tracks/layers Array > gracestate; // independents state for grace notes Array gbinfo; gbinfo.setSize(100); gbinfo.allowGrowth(0); beamstate.setSize(infile.getMaxTracks() + 1); gracestate.setSize(infile.getMaxTracks() + 1); beamstate.allowGrowth(0); gracestate.allowGrowth(0); for (i=0; i curlayer; curlayer.setSize(infile.getMaxTracks() + 1); Array laycounter; for (i=0; i= 1) { beamstate[track][curlayer[track]] = 0; continue; } if (rn == 0) { // grace notes; countBeamStuff(infile[i][j], start, stop, flagr, flagl); if ((start != 0) && (stop != 0)) { cerr << "Funny error in grace note beam calculation" << endl; exit(1); } if (start > 7) { cerr << "Too many beam starts" << endl; } if (stop > 7) { cerr << "Too many beam ends" << endl; } if (flagr > 7) { cerr << "Too many beam flagright" << endl; } if (flagl > 7) { cerr << "Too many beam flagleft" << endl; } contin = gracestate[track][curlayer[track]]; contin -= stop; gbinfo.setAll(0); for (ii=0; ii 0) { for (ii=0; ii 0) { for (ii=0; ii 6) { cerr << "Error too many grace note beams" << endl; exit(1); } beams[i][j].setSize(len+1); strcpy(beams[i][j].getBase(), gbinfo.getBase()); gracestate[track][curlayer[track]] = contin; gracestate[track][curlayer[track]] += start; } else { // regular notes which are shorter than a quarter note // (including tuplet quarter notes which should be removed): countBeamStuff(infile[i][j], start, stop, flagr, flagl); if ((start != 0) && (stop != 0)) { cerr << "Funny error in note beam calculation" << endl; exit(1); } if (start > 7) { cerr << "Too many beam starts" << endl; } if (stop > 7) { cerr << "Too many beam ends" << endl; } if (flagr > 7) { cerr << "Too many beam flagright" << endl; } if (flagl > 7) { cerr << "Too many beam flagleft" << endl; } contin = beamstate[track][curlayer[track]]; contin -= stop; gbinfo.setAll(0); for (ii=0; ii 0) { for (ii=0; ii 0) { for (ii=0; ii 6) { cerr << "Error too many grace note beams" << endl; exit(1); } beams[i][j].setSize(len+1); strcpy(beams[i][j].getBase(), gbinfo.getBase()); beamstate[track][curlayer[track]] = contin; beamstate[track][curlayer[track]] += start; } } } } ////////////////////////////// // // countBeamStuff -- // void countBeamStuff(const char* token, int& start, int& stop, int& flagr, int& flagl) { int i = 0; start = stop = flagr = flagl = 0; while (token[i] != '\0') { switch (token[i]) { case 'L': start++; break; case 'J': stop++; break; case 'K': flagr++; break; case 'k': flagl++; break; } i++; } } // md5sum: b9893a73d1185750447603c86fcc34d1 autostem.cpp [20110215]