// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // 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 <math.h> #ifndef OLDCPP #include <iostream> #include <sstream> #define SSTREAM stringstream #define CSTRING str().c_str() using namespace std; #else #include <iostream.h> #ifdef VISUAL #include <strstrea.h> #else #include <strstream.h> #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<Array<int> >& baseline, HumdrumFile& infile); void addStem(char* output, const char* input, const char* piece); void processKernTokenStemsSimpleModel(HumdrumFile& infile, Array<Array<int> >& 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<Array<Array<int> > >& notepos, Array<Array<int> >& baseline, HumdrumFile& infile); void printNotePositions(HumdrumFile& infile, Array<Array<Array<int> > >& notepos); void getVoiceInfo(Array<Array<int> >& voice, HumdrumFile& infile); void printVoiceInfo(HumdrumFile& infile, Array<Array<int> >& voice); void processKernTokenStems(HumdrumFile& infile, Array<Array<int> >& baseline, int row, int col); void getMaxLayers(Array<int>& maxlayer, Array<Array<int> >& voice, HumdrumFile& infile); void assignStemDirections(Array<Array<int> >& stemdir, Array<Array<int> > & voice, Array<Array<Array<int> > >& notepos, HumdrumFile& infile); void assignBasicStemDirections(Array<Array<int> >& stemdir, Array<Array<int> >& voice, Array<Array<Array<int> > >& notepos, HumdrumFile& infile); int determineChordStem(Array<Array<int> >& voice, Array<Array<Array<int> > >& notepos, HumdrumFile& infile, int row, int col); void insertStems(HumdrumFile& infile, Array<Array<int> >& stemdir); void setStemDirection(HumdrumFile& infile, int row, int col, int direction); void getBeamState(Array<Array<Array<char> > >& beams, HumdrumFile& infile); void countBeamStuff(const char* token, int& start, int& stop, int& flagr, int& flagl); void getBeamSegments(Array<Array<Coord> >& beamednotes, Array<Array<Array<char> > >& beamstates, HumdrumFile& infile, Array<int> maxlayer); int getBeamDirection(Array<Coord>& coords, Array<Array<int> >& voice, Array<Array<Array<int> > >& notepos); void setBeamDirection(Array<Array<int> >& stemdir, Array<Coord>& 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<char> buffer; buffer.setSize(1024); for (i=0; i<infile.getNumLines(); i++) { if (!infile[i].isData()) { out << infile[i] << "\n"; continue; } for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { out << infile[i][j]; if (j < infile[i].getFieldCount()-1) { out << "\t"; } continue; } int len = strlen(infile[i][j]); buffer.setSize(len+1); strcpy(buffer.getBase(), infile[i][j]); if (removeallQ || overwriteallQ) { pre.sar(buffer, "[\\\\/]x(?!x)", "", "g"); pre.sar(buffer, "[\\\\/](?!x)", "", "g"); } else { pre.sar(buffer, "[\\\\/](?!x)", "", "g"); } out << buffer; if (j < infile[i].getFieldCount()-1) { out << "\t"; } } out << "\n"; } } ////////////////////////////// // // autostem -- add an up/down stem on notes in **kern data which do not // already have stem information. // void autostem(HumdrumFile& infile) { Array<Array<int> > baseline; getClefInfo(baseline, infile); Array<Array<Array<int> > > notepos; // staff line position of all notes getNotePositions(notepos, baseline, infile); if (noteposQ) { printNotePositions(infile, notepos); exit(0); } Array<Array<int> > voice; // voice/layer number in track getVoiceInfo(voice, infile); if (voiceQ) { printVoiceInfo(infile, voice); exit(0); } Array<Array<int> > stemdir; // stem directions; stemdir.setSize(infile.getNumLines()); for (int i=0; i<stemdir.getSize(); i++) { stemdir[i].setSize(infile[i].getFieldCount()); stemdir[i].setAll(0); // 0 = undefined; } // later add stems presently in data. assignStemDirections(stemdir, voice, notepos, infile); insertStems(infile, stemdir); /* // simple model which only considers notes in isolation from any // beaming to other notes... int i, j; for (i=0; i<infile.getNumLines(); i++) { if (!infile[i].isData()) { continue; } for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } if (strcmp(infile[i][j], ".") == 0) { continue; } processKernTokenStems(infile, stemdir, i, j); } } */ } ////////////////////////////// // // insertStems -- put stem directions into the data. // void insertStems(HumdrumFile& infile, Array>& stemdir) { int i, j; for (i=0; i<infile.getNumLines(); i++) { if (!infile[i].isData()) { continue; } for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } setStemDirection(infile, i, j, stemdir[i][j]); } } } ////////////////////////////// // // setStemDirection -- don't change stem direction if there is already // a stem on the note. // void setStemDirection(HumdrumFile& infile, int row, int col, int direction) { int& i = row; int& j = col; int k; if (strcmp(infile[i][j], ".") == 0) { return; } if (strchr(infile[i][j], 'r') != NULL) { return; } int tokencount = infile[i].getTokenCount(j); char buffer[128] = {0}; char buffer2[128] = {0}; int len = strlen(infile[i][j]); char output[len*2+tokencount]; output[0] = '\0'; for (k=0; k<tokencount; k++) { infile[i].getToken(buffer, j, k, 30); if ((strchr(buffer, '/') == NULL) & (strchr(buffer, '\\') == NULL)) { if (direction > 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<Array<int> >& stemdir, Array<Array<int> > & voice, Array<Array<Array<int> > >& notepos, HumdrumFile& infile) { Array<int> maxlayer; getMaxLayers(maxlayer, voice, infile); assignBasicStemDirections(stemdir, voice, notepos, infile); Array<Array<Array<char> > > beamstates; getBeamState(beamstates, infile); Array<Array<Coord> > beamednotes; getBeamSegments(beamednotes, beamstates, infile, maxlayer); // print notes which are beamed together for debugging: int i, j; if (debugQ) { for (i=0; i<beamednotes.getSize(); i++) { cout << "!! "; for (j=0; j<beamednotes[i].getSize(); j++) { cout << infile[beamednotes[i][j].i][beamednotes[i][j].j] << "\t"; } cout << "\n"; } } int direction; for (i=0; i<beamednotes.getSize(); i++) { direction = getBeamDirection(beamednotes[i], voice, notepos); setBeamDirection(stemdir, beamednotes[i], direction); } } ////////////////////////////// // // setBeamDirection -- // void setBeamDirection(Array<Array<int> >& stemdir, Array<Coord>& bnote, int direction) { int x; int i, j; for (x=0; x<bnote.getSize(); x++) { i = bnote[x].i; j = bnote[x].j; stemdir[i][j] = direction; } } ////////////////////////////// // // getBeamDirection -- return a consensus stem direction for beamed notes. // int getBeamDirection(Array<Coord>& coords, Array<Array<int> >& voice, Array<Array<Array<int> > >& 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<coords.getSize(); x++) { i = coords[x].i; j = coords[x].j; if (voice[i][j] == 1) { return +1; } if (voice[i][j] == 2) { return -1; } for (k=0; k<notepos[i][j].getSize(); k++) { if (minn > 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<Array<Coord> >& beamednotes, Array<Array<Array<char> > >& beamstates, HumdrumFile& infile, Array<int> maxlayer) { beamednotes.setSize(infile.getNumLines() * infile.getMaxTracks() / 2); beamednotes.setGrowth(1000000); beamednotes.setSize(0); Array<Array<Array<Coord> > > beambuffer; beambuffer.setSize(infile.getMaxTracks() + 1); int i, j; for (i=0; i<beambuffer.getSize(); i++) { beambuffer[i].setSize(10); // layer max 10, but allow growth for (j=0; j<beambuffer[i].getSize(); j++) { beambuffer[i][j].setSize(100); // 100 notes in a beam expected as max beambuffer[i][j].setGrowth(1000); // but grow by increments of 1000 beambuffer[i][j].setSize(0); } } Coord tcoord; char beamchar; int track, oldtrack, layer; for (i=0; i<infile.getNumLines(); i++) { if (!infile[i].isData()) { continue; } oldtrack = 0; layer = 0; for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } track = infile[i].getPrimaryTrack(j); if (track == oldtrack) { layer++; } else { layer = 0; } oldtrack = track; if (strcmp(infile[i][j], ".") == 0) { continue; } if (strchr(infile[i][j], 'r') != NULL) { continue; } beamchar = beamstates[i][j][0]; if (beamchar == '\0') { beambuffer[track][layer].setSize(0); // possible unter. beam continue; } if ((beamchar == '[') || (beamchar == '=')) { // add a beam to the buffer and wait for more tcoord.i = i; tcoord.j = j; beambuffer[track][layer].append(tcoord); continue; } if (beamchar == ']') { // ending of a beam so store in permanent storage tcoord.i = i; tcoord.j = j; beambuffer[track][layer].append(tcoord); beamednotes.append(beambuffer[track][layer]); beambuffer[track][layer].setSize(0); } } } } ////////////////////////////// // // getMaxLayers -- // void getMaxLayers(Array<int>& maxlayer, Array<Array<int> >& voice, HumdrumFile& infile) { int track; maxlayer.setSize(infile.getMaxTracks() + 1); maxlayer.setAll(0); int i, j; for (i=0; i<infile.getNumLines(); i++) { if (!infile[i].isData()) { continue; } for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } if (strcmp(infile[i][j], ".") == 0) { continue; } if (strchr(infile[i][j], 'r') != NULL) { continue; } track = infile[i].getPrimaryTrack(j); if (voice[i][j] + 1 > 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<infile.getNumLines(); i++) { if (!infile[i].isData()) { continue; } voice[i].setSize(infile[i].getFieldCount()); voice[i].setAll(-1); for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } if (strcmp(infile[i][j], ".") == 0) { continue; } v = getVoice(infile, i, j); voice[i][j] = v; } } } ////////////////////////////// // // printvoiceInfo -- // void printVoiceInfo(HumdrumFile& infile, Array >& voice) { PerlRegularExpression pre; Array<char> data; SSTREAM *pstream; int i, j; for (i=0; i<infile.getNumLines(); i++) { if (!(infile[i].isData() || infile[i].isLocalComment() || infile[i].isMeasure() || infile[i].isInterpretation())) { cout << infile[i] << "\n"; continue; } cout << infile[i] << "\t"; if (infile[i].isMeasure()) { cout << infile[i][0] << "\n"; continue; } if (infile[i].isLocalComment()) { cout << "!\n"; continue; } if (infile[i].isInterpretation()) { if (strncmp(infile[i][0], "**", 2) == 0) { cout << "**voice"; } else if (strcmp(infile[i][0], "*-") == 0) { cout << "*-"; } else { cout << "*"; } cout << "\n"; continue; } pstream = new SSTREAM; for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } if (strcmp(infile[i][j], ".") == 0) { // ignore null tokens continue; } if (strchr(infile[i][j], 'r') != NULL) { // ignore rests continue; } (*pstream) << voice[i][j]; (*pstream) << " "; } (*pstream) << ends; data.setSize(strlen(pstream->CSTRING) + 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<Array<Array<int> > >& notepos) { PerlRegularExpression pre; Array<char> data; SSTREAM *pstream; int i, j, k; for (i=0; i<infile.getNumLines(); i++) { if (!(infile[i].isData() || infile[i].isLocalComment() || infile[i].isMeasure() || infile[i].isInterpretation())) { cout << infile[i] << "\n"; continue; } cout << infile[i] << "\t"; if (infile[i].isMeasure()) { cout << infile[i][0] << "\n"; continue; } if (infile[i].isLocalComment()) { cout << "!\n"; continue; } if (infile[i].isInterpretation()) { if (strncmp(infile[i][0], "**", 2) == 0) { cout << "**vpos"; } else if (strcmp(infile[i][0], "*-") == 0) { cout << "*-"; } else { cout << "*"; } cout << "\n"; continue; } pstream = new SSTREAM; for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } if (strcmp(infile[i][j], ".") == 0) { // ignore null tokens continue; } if (strchr(infile[i][j], 'r') != NULL) { // ignore rests continue; } for (k=0; k<notepos[i][j].getSize(); k++) { (*pstream) << notepos[i][j][k]; if (k < notepos[i][j].getSize()-1) { (*pstream) << " "; } } (*pstream) << " "; } (*pstream) << ends; data.setSize(strlen(pstream->CSTRING) + 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<Array<Array<int> > >& notepos, Array<Array<int> >& baseline, HumdrumFile& infile) { notepos.setSize(infile.getNumLines()); int location; char buffer[128] = {0}; int i, j, k; int tokencount; for (i=0; i<infile.getNumLines(); i++) { if (!infile[i].isData()) { continue; } notepos[i].setSize(infile[i].getFieldCount()); for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } if (strcmp(infile[i][j], ".") == 0) { // ignore null-tokens continue; } if (strchr(infile[i][j], 'r') != NULL) { // ignore rests continue; } tokencount = infile[i].getTokenCount(j); notepos[i][j].setSize(tokencount); for (k=0; k<tokencount; k++) { infile[i].getToken(buffer, j, k, 30); location = Convert::kernToDiatonicPitch(buffer) - baseline[i][j] - 4; notepos[i][j][k] = location; } } } } ////////////////////////////// // // processKernTokenStems -- // void processKernTokenStems(HumdrumFile& infile, Array<Array<int> >& baseline, int row, int col) { exit(1); } ////////////////////////////// // // assignBasicStemDirections -- don't take beams into consideration. // void assignBasicStemDirections(Array<Array<int> >& stemdir, Array<Array<int> >& voice, Array<Array<Array<int> > >& notepos, HumdrumFile& infile) { int i, j; for (i=0; i<infile.getNumLines(); i++) { if (!infile[i].isData()) { continue; } for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } if (strcmp(infile[i][j], ".") == 0) { continue; } if (strchr(infile[i][j], 'r') != NULL) { continue; } if (removeQ) { removeStem2(infile, i, j); } if (strchr(infile[i][j], '/') != NULL) { stemdir[i][j] = +1; } else if (strchr(infile[i][j], '\\') != NULL) { stemdir[i][j] = -1; } else { stemdir[i][j] = determineChordStem(voice, notepos, infile, i, j); } } } } ////////////////////////////// // // determineChordStem -- // int determineChordStem(Array<Array<int> >& voice, Array<Array<Array<int> > >& 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].getSize(); i++) { if (minn > 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<Array<int> >& 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<tokencount; k++) { infile[i].getToken(buffer, j, k, 30); if (i == 0) { duration = Convert::kernToDurationR(buffer); // if (duration >= 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<infile[row].getFieldCount(); j++) { testtrack = infile[row].getPrimaryTrack(j); if (testtrack == track) { tcount++; } if (col == j) { output = tcount; } } if (tcount == 1) { output = 0; } return output; } ////////////////////////////// // // removeStem2 -- remove stem and any single x after the stem. // void removeStem2(HumdrumFile& infile, int row, int col) { Array<char> 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<int> 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<baseline.getSize(); i++) { baseline[i].setSize(0); } int pt; for (i=0; i<infile.getNumLines(); i++) { if (infile[i].isInterpretation()) { for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } if (strncmp(infile[i][j], "*clef", 5) == 0) { pt = infile[i].getPrimaryTrack(j); states[pt] = Convert::kernClefToBaseline(infile[i][j]); } } } if (!infile[i].isData()) { continue; } baseline[i].setSize(infile[i].getFieldCount()); for (j=0; j<infile[i].getFieldCount(); j++) { if (!infile[i].isExInterp(j, "**kern")) { continue; } pt = infile[i].getPrimaryTrack(j); baseline[i][j] = states[pt]; } } } ////////////////////////////// // // checkOptions -- // void checkOptions(Options& opts, int argc, char** argv) { opts.define("d|debug=b", "Debugging information"); opts.define("r|remove=b", "Remove stems"); opts.define("R|removeall=b","Remove all stems including explicit beams"); opts.define("o|overwrite|replace=b","Overwrite non-explicit stems in input"); opts.define("O|overwriteall|replaceall=b", "Overwrite all stems in input"); opts.define("u|up=b", "Middle note on staff has stem up"); opts.define("p|pos=b", "Display only note vertical positions on staves"); opts.define("v|voice=b", "Display only voice/layer information"); opts.define("author=b", "Program author"); opts.define("version=b", "Program version"); opts.define("example=b", "Program examples"); 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, December 2010" << endl; exit(0); } else if (opts.getBoolean("version")) { cout << argv[0] << ", version: 26 December 2010" << 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); } debugQ = opts.getBoolean("debug"); removeQ = opts.getBoolean("remove"); removeallQ = opts.getBoolean("removeall"); noteposQ = opts.getBoolean("pos"); voiceQ = opts.getBoolean("voice"); overwriteQ = opts.getBoolean("overwrite"); overwriteallQ = opts.getBoolean("overwriteall"); if (opts.getBoolean("up")) { Middle = 4; } removeallQ = opts.getBoolean("removeall"); if (removeallQ) { removeQ = 1; } if (overwriteallQ) { overwriteQ = 1; } } ////////////////////////////// // // example -- example function calls to the program. // void example(void) { } ////////////////////////////// // // usage -- command-line usage description and brief summary // void usage(const char* command) { } ////////////////////////////// // // getBeamState -- Analyze structure of beams and store note layout // directives at the same time. // // Type Humdrum MuseData // start L [ // continue = // end J ] // forward hook K / // backward hook k \ x // void getBeamState(Array > >& 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<Array<int> > beamstate; // state of beams in tracks/layers Array<Array<int> > gracestate; // independents state for grace notes Array<char> 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<beamstate.getSize(); i++) { beamstate[i].setSize(100); // maximum of 100 layers in each track... gracestate[i].setSize(100); // maximum of 100 layers in each track... beamstate[i].allowGrowth(0); gracestate[i].allowGrowth(0); beamstate[i].setAll(0); gracestate[i].setAll(0); } beams.setSize(infile.getNumLines()); Array<int> curlayer; curlayer.setSize(infile.getMaxTracks() + 1); Array<int> laycounter; for (i=0; i<infile.getNumLines(); i++) { if (infile[i].isMeasure()) { // don't allow beams across barlines. Mostly for // preventing buggy beams from propagating... for (t=1; t<=infile.getMaxTracks(); t++) { beamstate[t].setAll(0); gracestate[t].setAll(0); } } if (!infile[i].isData() && !infile[i].isMeasure()) { continue; } if (!infile[i].isData()) { continue; } beams[i].setSize(infile[i].getFieldCount()); for (j=0; j<beams[i].getSize(); j++) { beams[i][j].setSize(1); beams[i][j][0] = '\0'; } curlayer.setAll(0); for (j=0; j<infile[i].getFieldCount(); j++) { track = infile[i].getPrimaryTrack(j); curlayer[track]++; if (strcmp(infile[i][j], ".") == 0) { // ignore null tokens continue; } if (strchr(infile[i][j], 'r') != NULL) { // ignore rests. Might be useful to not ignore // rests if beams extent over rests... continue; } rn = Convert::kernToDurationR(infile[i][j]); if (rn >= 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<contin; ii++) { gbinfo[ii] = '='; } if (start > 0) { for (ii=0; ii<start; ii++) { strcat(gbinfo.getBase(), "["); } } else if (stop > 0) { for (ii=0; ii<stop; ii++) { strcat(gbinfo.getBase(), "]"); } } for (ii=0; ii<flagr; ii++) { strcat(gbinfo.getBase(), "/"); } for (ii=0; ii<flagl; ii++) { strcat(gbinfo.getBase(), "\\"); } len = strlen(gbinfo.getBase()); if (len > 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<contin; ii++) { gbinfo[ii] = '='; } if (start > 0) { for (ii=0; ii<start; ii++) { strcat(gbinfo.getBase(), "["); } } else if (stop > 0) { for (ii=0; ii<stop; ii++) { strcat(gbinfo.getBase(), "]"); } } for (ii=0; ii<flagr; ii++) { strcat(gbinfo.getBase(), "/"); } for (ii=0; ii<flagl; ii++) { strcat(gbinfo.getBase(), "\\"); } len = strlen(gbinfo.getBase()); if (len > 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]