// // Programmer: Craig Stuart Sapp // Creation Date: Sat Jun 9 14:48:41 PDT 2001 // Last Modified: Mon Apr 18 08:58:17 PDT 2011 // Last Modified: Tue Jul 17 12:42:32 PDT 2012 marked all tied notes // Filename: ...museinfo/examples/all/parallel.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/parallel.cpp // Syntax: C++; museinfo // // Description: calculates the minimum timebase which is the least common // multiple of all rhythms in the file. // // Todo: some things to clean up in rhythmic representation due to conversions // from ints to RationalNumber class for rhythms/durations... #include "humdrum.h" #include "SigString.h" #include #include "PerlRegularExpression.h" class NoteNode { public: int b40; // base-40 pitch number or 0 if a rest, negative if tied0. int b40next; // not used yet int dia; // base-40 pitch number or 0 if a rest, negative if tied0. int line; // line number in original score of note int spine; // spine number in original score of note int lastint; // interval to last note (or 0 if no last or last is rest) int nextint; // interval to next note (or 0 if no next or last is rest) int lastdint; // interval to last note (or 0 if no last or last is rest) int nextdint; // interval to next note (or 0 if no next or last is rest) int serial; // notes serial number int measure; // measure number of note NoteNode(void) { clear(); } void clear(void) { serial = b40next = b40 = dia = 0; line = spine = -1; lastdint = nextdint = lastint = nextint = 0; } }; #define REST 0 #define RESTINT -1000000 /////////////////////////////////////////////////////////////////////////// // function declarations void checkOptions (Options& opts, int argc, char* argv[]); void example (void); void usage (const char* command); void processFile (HumdrumFile& infile, const char* filename); void getKernTracks (Array& ktracks, HumdrumFile& infile); void calculateMelodicIntervals(Array >& notes); void markParallels (Array >& notes, HumdrumFile& infile, const char* filename, Array >& names, Array reverselookup); void addMark (HumdrumFile& infile, int line, int spine, char marker); void markParallel (HumdrumFile& infile, Array >& notes, int hint, int i, int j, int k); int validateInterval (Array >& notes, int i, int j, int k); void markForward (HumdrumFile& infile, int line, int spine, char marker); void markBackward (HumdrumFile& infile, int line, int spine, char marker); int getNoteStart (HumdrumFile& infile, int line, int spine); // global variables Options options; // database for command-line arguments int debugQ = 0; // used with --debug int rawQ = 0; // used with -r option int locQ = 0; // used with -l option int raw2Q = 0; // used with --r2 option int fileQ = 0; // used with -f option int typeQ = 0; // used with -t option int dataQ = 0; // used with -i option int notiedQ = 0; // used with -T option (to be interfaced) int diatonicQ= 0; // used with -d option string Filename; // used if filename is "" int OCTAVE = 0; int FIFTH = 23; /////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { checkOptions(options, argc, argv); HumdrumFile infile; int i; // figure out the number of input files to process int numinputs = options.getArgCount(); string filename = ""; PerlRegularExpression pre; if (locQ) { // print start of data if (fileQ) { cout << "**file\t"; } cout << "**bar\t"; cout << "**description\t"; cout << "**voice1\t"; cout << "**voice2"; if (typeQ) { cout << "\t**type"; } if (dataQ) { cout << "\t**data"; } cout << "\n"; } for (i=0; i >& names, Array& reverselookup, HumdrumFile& infile) { names.setSize(reverselookup.getSize()-1); names.allowGrowth(0); char buffer[1024] = {0}; int value; PerlRegularExpression pre; int i; int j; int track; for (i=0; i > notes; Array > names; Array ktracks; Array reverselookup; int ii; int jj; getKernTracks(ktracks, infile); notes.setSize(ktracks.getSize()); reverselookup.setSize(infile.getMaxTracks()+1); reverselookup.setAll(-1); for (i=0; i current; current.setSize(ktracks.getSize()); int sign; int track; int index; int snum = 0; int measurenumber = 0; for (i=0; i 0) { current[index].serial = ++snum; } } if (debugQ) { cout << "NOTES:"; } for (j=0; j= 0; j--) { notes[i][j].b40next = nextone; if (notes[i][j].b40 >= 0) { nextone = notes[i][j].b40; } } } if (rawQ) { for (i=0; i >& notes, HumdrumFile& infile, const char* filename, Array >& names, Array reverselookup) { int i; int j; int k; int hint; int ii; int jj; int track; int start1; int start2; int interval; int dint; int hasoctavemarks = 0; int has5thmarks = 0; char topstate = '?'; char bottomstate = '?'; int onote1; int onote2; int otherhint = 0; int otherdint = 0; int nextlower = 0; int nextupper = 0; int nextdlower = 0; int nextdupper = 0; for (j=0; j abs(notes[i][j].b40)) { hint = (abs(notes[k][j].b40) - abs(notes[i][j].b40) + 400) % 40; dint = (abs(notes[k][j].dia) - abs(notes[i][j].dia) + 700) % 7; onote1 = notes[k][j].b40next; onote2 = notes[i][j].b40next; if (debugQ) { cout << "!! >>> " << Convert::base40ToKern(buf, abs(notes[k][j].b40)); cout << " is higher than " << Convert::base40ToKern(buf, abs(notes[i][j].b40)); cout << " INTERVAL " << hint << endl; } nextlower = notes[k][j].nextint; nextupper = notes[i][j].nextint; nextdupper = Convert::base40IntervalToDiatonic(nextupper); if (nextdupper < 0) { nextdupper--; } else { nextdupper++; } nextdlower = Convert::base40IntervalToDiatonic(nextlower); if (nextdlower < 0) { nextdlower--; } else { nextdlower++; } otherhint = hint + (nextlower - nextupper); if (debugQ) { cout << "!! " << Convert::base40ToKern(buf, onote1); cout << " and " << Convert::base40ToKern(buf, onote2); cout << " == OTHER HINT = " << otherhint << endl; cout << "!! OR1 : " << hint + (nextupper - nextlower) << endl; } otherdint = Convert::base40IntervalToDiatonic(otherhint); if (notes[k][j].b40 > 0) { topstate = 'x'; } else { topstate = 's'; } if (notes[i][j].b40 > 0) { bottomstate = 'x'; } else { bottomstate = 's'; } } else { hint = (abs(notes[i][j].b40) - abs(notes[k][j].b40) + 400) % 40; dint = (abs(notes[i][j].dia) - abs(notes[k][j].dia) + 700) % 7; onote1 = notes[i][j].b40next; onote2 = notes[k][j].b40next; if (debugQ) { cout << "!! >>X " << Convert::base40ToKern(buf, abs(notes[k][j].b40)); cout << " is lower than " << Convert::base40ToKern(buf, abs(notes[i][j].b40)); cout << " INTERVAL " << hint << endl; } nextupper = notes[i][j].nextint; nextlower = notes[k][j].nextint; nextdupper = Convert::base40IntervalToDiatonic(nextupper); if (nextdupper < 0) { nextdupper--; } else { nextdupper++; } nextdlower = Convert::base40IntervalToDiatonic(nextlower); if (nextdlower < 0) { nextdlower--; } else { nextdlower++; } otherhint = hint + (nextupper - nextlower); if (debugQ) { cout << "!! " << Convert::base40ToKern(buf, onote1); cout << " and " << Convert::base40ToKern(buf, onote2); cout << " == OTHER HINT = " << otherhint << endl; cout << "!! OR2 : " << hint + (nextlower - nextupper) << endl; } otherdint = Convert::base40IntervalToDiatonic(otherhint); if (notes[i][j].b40 > 0) { topstate = 'x'; } else { topstate = 's'; } if (notes[k][j].b40 > 0) { bottomstate = 'x'; } else { bottomstate = 's'; } } if (debugQ) { cout << "comparing " << notes[i][j].b40 << " to " << notes[k][j].b40 << "\tHINT = " << hint << "\tDINT = " << dint << endl; } if (diatonicQ) { interval = dint; } else { interval = hint; } if (!((interval == OCTAVE) || (interval == FIFTH))) { // not a fifth or an octave continue; } if (diatonicQ) { if (notes[k][j].nextdint != notes[i][j].nextdint) { // note are not creating a parallel motion continue; } if ((abs(notes[k][j].nextint) < 3) || (abs(notes[i][j].nextint) < 3)) { // non-perfect unison/octave, ignore continue; } } else { if (notes[k][j].nextint != notes[i][j].nextint) { // note are not creating a parallel motion continue; } } //cout << "Parallel " << hint << endl; if (validateInterval(notes, i, j, k)) { if (locQ) { if (interval == OCTAVE) { if (fileQ) { if (Filename.size() > 0) { cout << Filename; } else { cout << filename; } cout << "\t"; } cout << notes[i][j].measure; cout << "\t" << "parallel octave/unison"; cout << "\t"; ii = notes[i][j].line; jj = notes[i][j].spine; track = infile[ii].getPrimaryTrack(jj); cout << names[reverselookup[track]]; cout << "\t"; ii = notes[k][j].line; jj = notes[k][j].spine; track = infile[ii].getPrimaryTrack(jj); cout << names[reverselookup[track]]; if (typeQ) { start1 = getNoteStart(infile, notes[i][j].line, notes[i][j].spine); start2 = getNoteStart(infile, notes[k][j].line, notes[k][j].spine); if (start1 == start2) { cout << "\t" << "hard"; } else { cout << "\t" << "soft"; } } if (dataQ) { cout << "\t" << topstate << bottomstate << "," << hint << "(" << dint+1 << ")" << ";xx," << otherhint << "(" << otherdint+1<< ")" << ";" << nextupper << "(" << nextdupper << ")" << ";" << nextlower << "(" << nextdlower << ")"; } cout << endl; } else if (interval == FIFTH) { if (fileQ) { if (Filename.size() > 0) { cout << Filename; } else { cout << filename; } cout << "\t"; } cout << notes[i][j].measure; cout << "\t" << "parallel fifth"; cout << "\t"; ii = notes[i][j].line; jj = notes[i][j].spine; track = infile[ii].getPrimaryTrack(jj); cout << names[reverselookup[track]]; cout << "\t"; ii = notes[k][j].line; jj = notes[k][j].spine; track = infile[ii].getPrimaryTrack(jj); cout << names[reverselookup[track]]; if (typeQ) { start1 = getNoteStart(infile, notes[i][j].line, notes[i][j].spine); start2 = getNoteStart(infile, notes[k][j].line, notes[k][j].spine); if (start1 == start2) { cout << "\t" << "hard"; } else { cout << "\t" << "soft"; } } if (dataQ) { cout << "\t" << topstate << bottomstate << "," << hint << "(" << dint+1 << ")" << ";xx," << otherhint << "(" << otherdint+1 << ")" << ";" << nextupper << "(" << nextdupper << ")" << ";" << nextlower << "(" << nextdlower << ")"; } cout << endl; } } markParallel(infile, notes, interval, i, j, k); if (interval == OCTAVE) { hasoctavemarks = 1; } if (interval == FIFTH) { has5thmarks = 1; } } } } } /* // mark the ending notes in the parallel motion for (i=0; i abs(notes[i][j].b40)) { hint = (abs(notes[k][j].b40) - abs(notes[i][j].b40) + 400) % 40; } else { hint = (abs(notes[i][j].b40) - abs(notes[k][j].b40) + 400) % 40; } if (!((hint == 0) || (hint == 23))) { // not a fifth or an octave continue; } if (notes[k][j].lastint != notes[i][j].lastint) { // notes are not creating a parallel motion continue; } // cout << "Parallel2 " << hint << endl; //if (validateInterval(notes, i, j, k)) { markParallel(infile, notes, hint, i, j, k); if (hint == 0) { hasoctavemarks = 1; } if (hint == 23) { has5thmarks = 1; } //} } } } */ if (!locQ) { cout << infile; if (hasoctavemarks) { cout << "!!!RDF**kern: < = marked note, color=\"#00ff00\", "; cout << "parallel octave" << endl; } if (has5thmarks) { cout << "!!!RDF**kern: > = marked note, color=\"#ff0000\", "; cout << "parallel fifth" << endl; } } } ////////////////////////////// // // getNoteStart -- // int getNoteStart(HumdrumFile& infile, int line, int spine) { int isdot = 0; if ((strchr(infile[line][spine], ']') != NULL) || (strchr(infile[line][spine], '_') != NULL) ) { // do nothing } else if (strcmp(infile[line][spine], ".") == 0) { isdot = 1; } else { return line; } int track, ptrack; int i, j; // search for starting note of tied note group track = infile[line].getPrimaryTrack(spine); for (i=line-1; i>0; i--) { if (!infile[i].isData()) { continue; } for (j=0; j >& notes, int i, int j, int k) { int nij = -1; int nkj = -1; int m; for (m=j+1; m= 0) { nij = m; break; } } for (m=j+1; m= 0) { nkj = m; break; } } if (nij == nkj) { return 1; } if (nij < 0) { // strange case if it occurs, but just mark as good return 1; } if (nkj < 0) { // strage case if it occurs, but just mark as good return 1; } return 0; } ////////////////////////////// // // markParallel -- // void markParallel(HumdrumFile& infile, Array >& notes, int interval, int i, int j, int k) { char marker = '<'; // parallel octaves/unisons if (interval == FIFTH) { marker = '>'; // parallel fifths } int ii; int jj; // mark first note in parallel motion ii = notes[i][j].line; jj = notes[i][j].spine; if (strcmp(infile[ii][jj], ".") == 0) { int ti; int tj; ti = infile[ii].getDotLine(jj); tj = infile[ii].getDotSpine(jj); ii = ti; jj = tj; } if (strcmp(infile[ii][jj], ".") != 0) { // if (strchr(infile[ii][jj], marker) == NULL) { addMark(infile, ii, jj, marker); // } } // mark second note in parallel motion ii = notes[k][j].line; jj = notes[k][j].spine; if (strcmp(infile[ii][jj], ".") == 0) { int ti; int tj; ti = infile[ii].getDotLine(jj); tj = infile[ii].getDotSpine(jj); ii = ti; jj = tj; } if (strcmp(infile[ii][jj], ".") != 0) { // if (strchr(infile[ii][jj], marker) == NULL) { addMark(infile, ii, jj, marker); // } } int m; // mark third note in parallel motion for (m=j+1; m0; i--) { if (!infile[i].isData()) { continue; } for (j=0; j >& notes) { int i, j; int lastint = RESTINT; int lastdint = RESTINT; int lastnote = 0; int lastdnote = 0; // caluculate the intervals to previous notes: for (i=0; i=0; j--) { notes[i][j].nextint = nextint; notes[i][j].nextdint = nextdint; if (notes[i][j].b40 >= 0) { nextint = notes[i][j].lastint; nextdint = notes[i][j].lastdint; } } } } ////////////////////////////// // // getKernTracks -- return a list of track number for **kern spines. // void getKernTracks(Array& ktracks, HumdrumFile& infile) { int i, j; ktracks.setSize(infile.getMaxTracks()); ktracks.setSize(0); int track; for (i=0; i