// // Copyright 1998-2010 by Craig Stuart Sapp, All Rights Reserved. // Programmer: Craig Stuart Sapp // Creation Date: Mon May 18 13:43:47 PDT 1998 // Last Modified: Thu Jul 1 16:19:35 PDT 1999 // Last Modified: Thu Apr 13 18:43:34 PDT 2000 Added generalized ex interps // Last Modified: Sat May 6 14:52:19 PDT 2000 Added appendCompositeDuration // Last Modified: Mon Dec 4 14:23:17 PST 2000 After many analysis funtions // Last Modified: Wed Dec 6 13:22:08 PST 2000 Added analyzeMetricLevel() // Last Modified: Sat Dec 16 13:37:19 PST 2000 Added analyzeDataIndex() // Last Modified: Sat Dec 16 14:41:14 PST 2000 Added analyzeCliche() // Last Modified: Wed Dec 27 20:19:43 PST 2000 Improved combine functions // Last Modified: Wed Jan 10 12:21:22 PST 2001 Added analyzeChordProbability() // Last Modified: Sun Feb 11 16:01:26 PST 2001 AnalyzeChordProbabilityDur() // Last Modified: Fri Apr 6 13:51:44 PDT 2001 AnalyzeChordLikelihood() // Last Modified: Sun May 13 12:42:43 PDT 2001 getNoteArray() // Last Modified: Sat Jun 9 15:10:26 PDT 2001 Added getMinTimeBase() functs // Last Modified: Mon Nov 5 17:55:54 PST 2001 Added getNextDatum/getLastDatum // Last Modified: Mon Nov 19 23:52:13 PST 2001 Made define ROUNDERR // Last Modified: Wed Jan 2 12:07:52 PST 2002 Added **koto to analyzeRhythm // Last Modified: Sun Mar 24 12:10:00 PST 2002 Small changes for visual c++ // Last Modified: Mon Apr 29 22:41:32 PDT 2002 Fixed getTiedDuration for // spine change (not perfect) // Last Modified: Wed Jan 1 22:27:59 PST 2003 Extracted Maxwell functions // Last Modified: Mon Feb 10 17:56:13 PST 2003 Added getNoteArray2 // Last Modified: Thu Mar 18 23:11:14 PST 2004 Removd blank lines frm assemble // Last Modified: Mon May 17 00:16:55 PDT 2004 Fix multiple part *v in combine // Last Modified: Sat Jun 5 01:43:45 PDT 2004 Adjusted metric analysis // Last Modified: Wed Jun 16 21:15:06 PDT 2004 Indep. tracks in analyzeKeyKS // Last Modified: Thu Jun 17 23:04:17 PDT 2004 Fixed combine termination // Last Modified: Sat Jun 26 00:43:48 PDT 2004 spaceEmptyLines adjusted // Last Modified: Sun Jun 27 01:28:56 PDT 2004 Fixed rhythm parsing interrupted // by spine manipulators // Last Modified: Mon Jun 5 06:59:27 PDT 2006 Add fixIrritatingPickupProblem // Last Modified: Tue Jan 29 09:05:26 PST 2008 Fixed array bounds bug in // fixIrritatingPickupProblem() // Last Modified: Tue Oct 14 16:56:54 PDT 2008 Added 'Q' groupetto parsing // Last Modified: Fri Jun 12 22:58:34 PDT 2009 Renamed SigCollection class // Last Modified: Fri Jun 19 23:24:03 PDT 2009 Fixed malformed meter parsing // Last Modified: Sat Sep 5 22:03:28 PDT 2009 ArrayInt to Array // Last Modified: Mon Oct 12 15:49:27 PDT 2009 Fixed "*clef *v *v" type cases // Last Modified: Sat May 22 10:52:36 PDT 2010 Added RationalNumber // Last Modified: Thu Oct 28 21:22:51 PDT 2010 Some fixing of combine() // Last Modified: Sat Dec 25 13:07:09 PST 2010 Minrhythm fix with dots // Last Modified: Wed Feb 2 17:51:57 PST 2011 Partial fix for breve beat // Last Modified: Tue Apr 16 23:18:16 PDT 2013 Added attackQ to gBase12PchLst // Last Modified: Mon Sep 16 20:26:17 PDT 2013 Added getMeasureNumber() // Filename: ...sig/src/sigInfo/HumdrumFile.cpp // Web Address: http://sig.sapp.org/src/sigInfo/HumdrumFile.cpp // Syntax: C++ // // Description: Higher-level functions for processing Humdrum files. // Inherits HumdrumFileBasic and adds rhythmic and other // types of analyses to the HumdrumFile class. // #include "HumdrumFile.h" #include "humdrumfileextras.h" #include "Convert.h" #include "PerlRegularExpression.h" #include #include #include #include #include #ifndef OLDCPP #include #include #include #define SSTREAM stringstream #define CSTRING str().c_str() using namespace std; #else #include #include #ifdef VISUAL #include #else #include #endif #define SSTREAM strstream #define CSTRING str() #endif // #define ROUNDERR 0.005 // Changed on Tue Mar 23 17:48:42 PST 2004 #define ROUNDERR 0.005 ////////////////////////////// // // HumdrumFile::HumdrumFile -- // HumdrumFile::HumdrumFile(void) : HumdrumFileBasic() { rhythmcheck = 0; minrhythm = 0; minrhythmR = 0; pickupdur = -1; localrhythms.setSize(0); } HumdrumFile::HumdrumFile(const HumdrumFile& aHumdrumFile) : HumdrumFileBasic(aHumdrumFile) { rhythmcheck = 0; minrhythm = 0; minrhythmR = 0; pickupdur = -1; localrhythms.setSize(0); } HumdrumFile::HumdrumFile(const HumdrumFileBasic& aHumdrumFile) : HumdrumFileBasic(aHumdrumFile) { rhythmcheck = 0; minrhythm = 0; minrhythmR = 0; pickupdur = -1; localrhythms.setSize(0); } HumdrumFile::HumdrumFile(const char* filename) : HumdrumFileBasic(filename) { rhythmcheck = 0; minrhythm = 0; minrhythmR = 0; pickupdur = -1; localrhythms.setSize(0); } ////////////////////////////// // // HumdrumFile::~HumdrumFile // HumdrumFile::~HumdrumFile() { clear(); } ////////////////////////////// // // HumdrumFile::analyzeRhythm -- // default values: base = "", debug = 0 // void HumdrumFile::analyzeRhythm(const char* base, int debug) { privateRhythmAnalysis(base, debug); rhythmcheck = 1; } ////////////////////////////// // // getMinTimeBase -- // int HumdrumFile::getMinTimeBase(void) { // return minrhythm; return minrhythmR.getNumerator() * minrhythmR.getDenominator(); } ////////////////////////////// // // getMinTimeBaseR -- RationalNumber version of getMinTimeBase(). // RationalNumber HumdrumFile::getMinTimeBaseR(void) { return minrhythmR; } ////////////////////////////// // // getPickupDuration -- returns the duration of any pickup // measure as analysed with rhythm function // double HumdrumFile::getPickupDuration(void) { return pickupdur.getFloat(); } double HumdrumFile::getPickupDur(void) { return pickupdur.getFloat(); } RationalNumber HumdrumFile::getPickupDurationR(void) { return pickupdur; } RationalNumber HumdrumFile::getPickupDurR(void) { return pickupdur; } ////////////////////////////// // // getStartIndex -- Given an absolute beat, return the first // line starting at that time or just after if there is no // items at the specified time. // int HumdrumFile::getStartIndex(double startbeat) { HumdrumFile& score = *this; int index = 1; while (index < score.getNumLines() - 1) { if (score[index+1].getAbsBeat() == score[index].getAbsBeat()) { index++; continue; } if (startbeat < score[index].getAbsBeat() + ROUNDERR && startbeat > score[index-1].getAbsBeat() - ROUNDERR) { return index; } if (startbeat > score[index].getAbsBeat() + ROUNDERR && startbeat < score[index+1].getAbsBeat() - ROUNDERR) { return index; } index++; } return score.getNumLines() - 1; } int HumdrumFile::getStartIndex(RationalNumber startbeat) { HumdrumFile& score = *this; int index = 1; while (index < score.getNumLines() - 1) { if (score[index+1].getAbsBeatR() == score[index].getAbsBeatR()) { index++; continue; } if ((startbeat < score[index].getAbsBeatR()) && (startbeat > score[index-1].getAbsBeatR() ) ) { return index; } if ((startbeat > score[index].getAbsBeatR()) && (startbeat < score[index+1].getAbsBeatR() ) ) { return index; } index++; } return score.getNumLines() - 1; } ////////////////////////////// // // getStopIndex -- Given an absolute beat, return the last item // occuring on that beat. If there is no items at that time, // then return the first item before the specified time. // int HumdrumFile::getStopIndex(double stopbeat) { HumdrumFile& score = *this; int index = 1; while (index < score.getNumLines()) { if (stopbeat <= score[index].getAbsBeat() + ROUNDERR && stopbeat > score[index-1].getAbsBeat() - ROUNDERR) { return index; } if (stopbeat > score[index].getAbsBeat() + ROUNDERR && stopbeat < score[index+1].getAbsBeat() - ROUNDERR) { return index; } index++; } return score.getNumLines() - 1; } int HumdrumFile::getStopIndex(RationalNumber stopbeat) { HumdrumFile& score = *this; int index = 1; while (index < score.getNumLines()) { if ((stopbeat <= score[index].getAbsBeatR() ) && (stopbeat > score[index-1].getAbsBeatR() ) ) { return index; } if ((stopbeat > score[index].getAbsBeatR() ) && (stopbeat < score[index+1].getAbsBeatR() ) ) { return index; } index++; } return score.getNumLines() - 1; } ////////////////////////////// // // HumdrumFile::appendLine -- adds a line to a humdrum file // void HumdrumFile::appendLine(const char* aLine) { HumdrumFileBasic::appendLine(aLine); rhythmcheck = 0; } void HumdrumFile::appendLine(HumdrumRecord& aRecord) { HumdrumFileBasic::appendLine(aRecord); rhythmcheck = 0; } void HumdrumFile::appendLine(HumdrumRecord* aRecord) { appendLine(*aRecord); } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // HumdrumFile::assemble -- // int HumdrumFile::assemble(HumdrumFile& output, int count, HumdrumFile** pieces) { if (count <= 0) { return 0; } int flag = 1; HumdrumFile f[2]; f[0].clear(); f[1].clear(); f[0] = *(pieces[0]); int state = 0; int i; for (i=1; i& rhys) { if (rhythmQ() == 0) { analyzeRhythm("4"); } int i; Array& rats = this->localrhythms; rhys.setSize(rats.getSize()); for (i=0; i ROUNDERR) { cout << "Error: two files are not of same durational length" << endl; cout << A.getTotalDuration() << " compared to " << B.getTotalDuration() << endl; cout << "The Two files are: " << endl; cout << A << endl; cout << "====================================================\n" << endl; cout << B << endl; cout << "====================================================\n" << endl; exit(1); return 0; } if (A.getTotalDuration() == 0) { cout << "Error: cannot process file with zero duration" << endl; exit(1); return 0; } flag = HumdrumFile::processLinesForCombine(output, A, B, debug); if (!debug) { output.analyzeSpines(); output.analyzeRhythm("4"); } return flag; } ////////////////////////////// // // HumdrumFile::processLinesForCombine -- // int HumdrumFile::processLinesForCombine(HumdrumFile& output, HumdrumFile& A, HumdrumFile& B, int debug) { int a = 0; int b = 0; SSTREAM sout; int i; int foundStart = 0; // boolean for finding start of data while (a < A.getNumLines() || b < B.getNumLines()) { if (debug) { cout << "aline = " << a << "\tbline = " << b << endl; } if (a >= A.getNumLines()) { if (debug) { sout << "!!CASE AAA" << "\n"; } sout << B[b].getLine() << "\n"; b++; continue; } if (b >= B.getNumLines()) { if (debug) { sout << "!!CASE BBB" << "\n"; } sout << A[a].getLine() << "\n"; a++; continue; } if (A[a].getType() == E_humrec_empty) { if (debug) { sout << "!!CASE BBBa" << "\n"; } a++; continue; } if (B[b].getType() == E_humrec_empty) { if (debug) { sout << "!!CASE BBBb" << "\n"; } b++; continue; } if (A[a].getType() == E_humrec_bibliography) { for (i=0; i 0) { if (debug) { sout << "!!CASE: GFD2\n"; } // A is after B, print B and wait for A if (B[b].isInterpretation()) { printConstantTokenFields(sout, A[a], "*"); } else if (B[b].isLocalComment()) { printConstantTokenFields(sout, A[a], "!"); } else { printConstantTokenFields(sout, A[a], "."); } sout << "\t"; sout << B[b]; sout << "\n"; b++; continue; } else { if (debug) { sout << "!!CASE: GFD3\n"; } // A and B are both local comments so print both if (B[b].getType() == E_humrec_data_comment) { sout << A[a] << "\t" << B[b] << "\n"; a++; b++; } else { if (debug) { sout << "!!CASE: GFD4\n"; } // B is not a local comment so print A wait for B sout << A[a] << "\t"; printConstantTokenFields(sout, B[b], "!"); sout << "\n"; a++; } continue; } } else if (B[b].getType() == E_humrec_data_comment) { if (adur - bdur < 0) { if (debug) { sout << "!!CASE: GFE1\n"; } // A is earlier than B so print A and wait for B if (B[b].isInterpretation()) { printConstantTokenFields(sout, A[a], "*"); } else if (B[b].isLocalComment()) { printConstantTokenFields(sout, A[a], "!"); } else { printConstantTokenFields(sout, A[a], "."); } sout << "\t"; sout << B[b]; sout << "\n"; b++; continue; } else if (adur - bdur > 0) { if (debug) { sout << "!!CASE: GFE2\n"; } // A is after B, print B and wait for A printConstantTokenFields(sout, A[a], "!"); sout << "\t"; sout << B[b]; sout << "\n"; a++; continue; } else { if (debug) { sout << "!!CASE: GFE3\n"; } // A and B are both local comments so print both if (A[a].getType() == E_humrec_data_comment) { sout << A[a] << "\t" << B[b] << "\n"; a++; b++; } else { if (debug) { sout << "!!CASE: GFE4\n"; } // A is not a local comment, print B; wait for A printConstantTokenFields(sout, A[a], "!"); sout << "\t"; sout << B[b] << "\n"; b++; } continue; } } if ((adur - bdur) < 0) { // data lines are supposed to occur at the same time if (A[a].isData() && B[b].isData()) { if (A[a].getAbsBeatR() < B[b].getAbsBeatR()) { // print A and null tokens for B if (debug) { sout << "!!CASE KKKa" << "\n"; } sout << A[a].getLine(); for (i=0; i B[b].getAbsBeatR()) { // print null tokens for A and then B if (debug) { sout << "!!CASE KKKb" << "\n"; } for (i=0; i 0.0)) { // A contains grace note but B does not if (debug) { sout << "!!CASE MMM" << "\n"; } sout << A[a].getLine() << "\t"; int kk; for (kk=0; kk 0.0)) { // B contains grace note but A does not int kk; if (debug) { sout << "!!CASE NNN Adur=" << A[a].getDuration() << " Bdur=" << B[b].getDuration() << "\n"; } for (kk=0; kk A[a].getAbsBeat()) { int kk; if (debug) { sout << "!!CASE AAAA" << "\n"; } sout<< A[a] << "\t"; for (kk=0; kk B[b].getAbsBeat()) { if (debug) { sout << "!!CASE BBBB" << "\n"; } int kk; for (kk=0; kk B[b].getAbsBeatR()) { // print null tokens for A and then B if (debug) { sout << "!!CASE GGGGb" << "\n"; } for (i=0; i= 0) { if (file[i].getType() != E_humrec_data) { i--; continue; } // find matching spine lastspine = -1; for (j=0; j= getTotalDurationR()) { nspine = -1; return -1; } nextline = getStartIndex(getAbsBeatR(index) + duration); while (nextline < getNumLines() && getType(nextline) != E_humrec_data ) { nextline++; } if (nextline >= file.getNumLines()) { nspine = -1; return -1; } int i; double currenttrack = file[index].getTrack(spine); for (i=0; i& notes, int line, int flag) { Array temp; getNoteList(temp, line, flag); notes.resize(temp.getSize()); for (int i=0; i<(int)notes.size(); i++) { notes[i] = temp[i]; } return (int)notes.size(); } int HumdrumFile::getNoteList(Array& notes, int line, int flag) { // unpack flags: int restQ = flag & (1 << 0); int expandQ = flag & (1 << 1); int midiQ = flag & (1 << 2); int pcQ = flag & (1 << 3); int sortQ = flag & (1 << 4); int uniqQ = flag & (1 << 5); int tieQ = !(flag & (1 << 6)); int i, j; int note; char tokenbuffer[128] = {0}; Array rawnotes; rawnotes.setSize(32); rawnotes.setSize(0); rawnotes.allowGrowth(); HumdrumFile& score = *this; notes.setSize(0); notes.allowGrowth(); if (score[line].getType() != E_humrec_data) { return 0; } // store notes found on current line: int tokencount = 0; for (i=0; i= 0) { note = note % 12; } } else { note = Convert::kernToBase40(tokenbuffer); if (pcQ && note != E_base40_rest) { note = note % 40; } } if (note == E_base40_rest) { if (restQ) { rawnotes.append(note); } } else { rawnotes.append(note); } } } else if (strcmp(score[line][i], ".") != 0) { tokencount = score[line].getTokenCount(i); for (j=0; j= 0) { note = note % 12; } } else { note = Convert::kernToBase40(tokenbuffer); if (pcQ && note != E_base40_rest) { note = note % 40; } } if (note == E_base40_rest) { if (restQ) { rawnotes.append(note); } } else { rawnotes.append(note); } } } } if (rawnotes.getSize() == 0) { return 0; } if (rawnotes.getSize() == 1) { notes.setSize(1); notes[0] = rawnotes[0]; return 1; } // sort notes if needed: if (sortQ) { qsort(rawnotes.getBase(), rawnotes.getSize(), sizeof(int), intcompare); } // uniq notes if needed: if (uniqQ && sortQ) { int oldnote = rawnotes[0]; notes.setSize(0); notes.append(oldnote); for (i=1; i& absbeat, Array& pitches, Array& durations, Array& levels, int startLine, int endLine, int tracknum) { HumdrumFile& score = *this; if (endLine <= 0) { endLine = score.getNumLines() - 1; } if (startLine > endLine) { int temp = endLine; endLine = startLine; startLine = temp; } if (endLine > score.getNumLines() - 1) { endLine = score.getNumLines() - 1; } if (startLine > score.getNumLines() - 1) { startLine = score.getNumLines() - 1; } // estimate the largest amount necessary: absbeat.setSize(score.getNumLines() * score.getMaxTracks() * 10); pitches.setSize(score.getNumLines() * score.getMaxTracks() * 10); durations.setSize(score.getNumLines() * score.getMaxTracks() * 10); levels.setSize(score.getNumLines() * score.getMaxTracks() * 10); absbeat.setGrowth(score.getNumLines()); pitches.setGrowth(score.getNumLines()); durations.setGrowth(score.getNumLines()); levels.setGrowth(score.getNumLines()); absbeat.setSize(0); pitches.setSize(0); durations.setSize(0); levels.setSize(0); absbeat.allowGrowth(1); pitches.allowGrowth(1); durations.allowGrowth(1); levels.allowGrowth(1); Array scorelevels; score.analyzeMetricLevel(scorelevels); int firsttime = 1; int i, j, k; int ii, jj; int ccount; static char buffer[1024] = {0}; int pitch; double beatvalue; double duration; double level; for (i=startLine; i<=endLine; i++) { if (score[i].getType() != E_humrec_data) { // ignore non-note data lines continue; } beatvalue = score.getAbsBeat(i); ii = i; for (j=0; j& absbeat, Array& pitches, Array& durations, Array& levels, Array >& lastpitches, Array >& nextpitches, int startLine, int endLine) { HumdrumFile& score = *this; if (endLine <= 0) { endLine = score.getNumLines() - 1; } if (startLine > endLine) { int temp = endLine; endLine = startLine; startLine = temp; } // estimate the largest amount necessary: absbeat.setSize(score.getNumLines() * score.getMaxTracks() * 10); pitches.setSize(score.getNumLines() * score.getMaxTracks() * 10); durations.setSize(score.getNumLines() * score.getMaxTracks() * 10); levels.setSize(score.getNumLines() * score.getMaxTracks() * 10); lastpitches.setSize(score.getNumLines() * score.getMaxTracks() * 10); nextpitches.setSize(score.getNumLines() * score.getMaxTracks() * 10); absbeat.setGrowth(score.getNumLines()); pitches.setGrowth(score.getNumLines()); durations.setGrowth(score.getNumLines()); levels.setGrowth(score.getNumLines()); lastpitches.setGrowth(score.getNumLines()); nextpitches.setGrowth(score.getNumLines()); absbeat.setSize(0); pitches.setSize(0); durations.setSize(0); levels.setSize(0); lastpitches.setSize(0); nextpitches.setSize(0); absbeat.allowGrowth(1); pitches.allowGrowth(1); durations.allowGrowth(1); levels.allowGrowth(1); lastpitches.allowGrowth(1); nextpitches.allowGrowth(1); Array templastpitches; Array tempnextpitches; templastpitches.setSize(100); tempnextpitches.setSize(100); templastpitches.setSize(0); tempnextpitches.setSize(0); Array scorelevels; score.analyzeMetricLevel(scorelevels); int firsttime = 1; int i, j, k; int ii, jj; int ccount; static char buffer[1024] = {0}; int pitch; double beatvalue; double duration; double level; for (i=startLine; i<=endLine; i++) { if (score[i].getType() != E_humrec_data) { // ignore non-note data lines continue; } beatvalue = score.getAbsBeat(i); ii = i; for (j=0; j= startLine) { lastptr = score.getLastDatum(ii, jj); } else { lastptr = ""; } testline = getNextDatumLine(dummyspine, ii, jj, 0); if (testline <= endLine) { nextptr = score.getNextDatum(ii, jj); } else { nextptr = ""; } convertKernStringToArray(templastpitches, lastptr); convertKernStringToArray(tempnextpitches, nextptr); durations.append(duration); levels.append(level); pitches.append(pitch); absbeat.append(beatvalue); lastpitches.append(templastpitches); nextpitches.append(tempnextpitches); } // end of a chord } } // end of a line firsttime = 0; } // end of the music selection absbeat.allowGrowth(0); pitches.allowGrowth(0); durations.allowGrowth(0); levels.allowGrowth(0); /* for (i=0; i& array, const char* string) { array.setSize(0); int note; char* buffer; int size = strlen(string); if (size == 0) { return; } buffer = new char[size+1]; strcpy(buffer, string); char* ptr; ptr = strtok(buffer, " \t\n"); while (ptr != NULL) { note = Convert::kernToBase40(ptr); array.append(note); ptr = strtok(NULL, " \t\n"); } delete [] buffer; buffer = NULL; } ////////////////////////////// // // HumdrumFile::getTiedDuration -- returns the total duration of // a tied note if the first note is the beginning of a tie. // Returns the duration of the note if not a tied note, or // zero if the specified field is not a note. // default value: token = 0; // double HumdrumFile::getTiedDuration(int linenum, int field, int token) { RationalNumber anum; anum = getTiedDurationR(linenum, field, token); return anum.getFloat(); } RationalNumber HumdrumFile::getTiedDurationR(int linenum, int field, int token) { HumdrumFile& file = *this; int length = file.getNumLines(); char buffer[128] = {0}; RationalNumber duration(0,1); // total duration of tied notes. int done = 0; // true when end of tied note is found int startpitch = 0; // starting pitch of the tie int matchpitch = 0; // current matching pitch of the tie file[linenum].getToken(buffer, field, token); if (strchr(buffer, '[')) { duration = Convert::kernToDurationR(buffer); // allow for enharmonic ties: startpitch = Convert::kernToMidiNoteNumber(buffer); } else { return Convert::kernToDurationR(buffer); } // not quite perfect: if two primary tracks with common ties, will have prob: int m; int ptrack = file[linenum].getPrimaryTrack(field); int currentLine = linenum + 1; while (!done && currentLine < length) { if (file[currentLine].getType() != E_humrec_data) { currentLine++; continue; } for (m=0; m= 0) { if (file[currentLine].getType() != E_humrec_data) { currentLine--; continue; } for (m=0; m rhythms; Array rhythmsR; rhythms.setSize(32); rhythms.setSize(0); rhythms.allowGrowth(1); rhythmsR.setSize(32); rhythmsR.setSize(0); rhythmsR.allowGrowth(1); HumdrumFile& infile = *this; RationalNumber summation(0,1); // for summing measure duration RationalNumber duration; HumdrumRecord tempRecord; // for *beat: interpretation // const char* slash; // for metronome marking RationalNumber measureBeats(0,1); // for fixing meter locations: SigCollection meterbeats; SigCollection timebaseC; meterbeats.setSize(getNumLines()); timebaseC.setSize(getNumLines()); meterbeats.allowGrowth(0); timebaseC.allowGrowth(0); Array ignore; // for avoiding free rhythm spines ignore.setSize(infile.getMaxTracks()); ignore.setAll(0); // for analyzing record durations: SigCollection lastdurations; SigCollection runningstatus; // int fixedTimebase = 0; RationalNumber timebase = 4; if (strcmp(base, "") != 0) { // fixedTimebase = 1; int tempval; sscanf(base, "%d", &tempval); timebase = tempval; // check for prolongation dot. Ignore any double dots if (strchr(base, '.') != NULL) { timebase = (timebase*2)/3; } } HumdrumRecord currRecord; int measurecount = 0; int ii, jj; int nonblank = 0; int foundstart = 0; int i; for (i=0; i=0; i--) { if (!infile[i].isMeasure()) { continue; } barcount++; currentabs = infile[i].getAbsBeatR(); difference = lastabs - currentabs; lastabs = currentabs; infile[i].setBeatR(difference); } if (barcount > 0) { fixIncompleteBarMeterR(meterbeats, timebaseC, base); } // Fix cases where the first barline is not given in the data // Currently, the measure will be labeled as 0, and the beats // will be offset from 0 rather than 1. int dataline = -1; int barlineindex = -1; for (i=0; i= 0) { barlineindex = i; } break; } } if (barlineindex >= 0) { if (dataline >= 0) { if (infile[dataline].getBeatR() == 0) { for (i=dataline; i 0) && ((*this)[index[i]].getDuration() == 0) && ((*this)[index[i]].getType() != E_humrec_data_measure)) { count = 1; j = i+1; while ((j=0; i--) { if ((*this)[i].getAbsBeatR() > lastpos) { (*this)[i].setAbsBeatR(lastpos); // adjust the metric position (*this)[i].setBeatR(lastbeat); } else { lastpos = (*this)[i].getAbsBeatR(); lastbeat = (*this)[i].getBeatR(); } // adjust the number of beats found in the measure if (i < getNumLines() - 1) { if ((*this)[i+1].getType() == E_humrec_data_measure) { (*this)[i+1].setBeatR(lastbeat + (*this)[i].getDurationR() - 1); } } } // adjust the duration markers for each measure lastbeat = (*this)[getNumLines()-1].getAbsBeatR(); RationalNumber curbeat(0,1); for (i=getNumLines()-1; i>=0; i--) { if ((*this)[i].getType() == E_humrec_data_measure) { curbeat = (*this)[i].getAbsBeatR(); (*this)[i].setBeatR(lastbeat - curbeat); lastbeat = curbeat; // mark pickup-beats with a negative duration if ((*this)[i].getAbsBeatR() < (*this)[i].getBeatR()) { if (!(*this)[i].getAbsBeatR().isZero()) { (*this)[i].setBeatR((*this)[i].getBeatR() * -1); } } } } } ////////////////////////////// // // findlcm -- find the least common multiple between rhythms // int HumdrumFile::findlcm(Array& rhythms) { if (rhythms.getSize() == 0) { return 0; } int output = rhythms[0]; for (int i=1; i& lastdurations, SigCollection& runningstatus, HumdrumRecord& currRecord) { lastdurations.allowGrowth(1); runningstatus.allowGrowth(1); lastdurations.setSize(0); runningstatus.setSize(0); RationalNumber zero(0,1); int i; for (i=0; i& meterbeats, SigCollection& timebase, const char* base) { HumdrumFile& file = *this; Array barlocs; barlocs.setSize(file.getNumLines()); barlocs.setSize(0); int i, j; for (i=0; i timedur; // timesig dur at each bar timedur.setSize(barlocs.getSize()); timedur.setSize(0); PerlRegularExpression pre; PerlRegularExpression pre2; int top, bot, bot2; RationalNumber rat; int barcounter = 0; for (i=0; i 0) { timedur.last() = rat; } } break; } } // debuging: show bar number, expected sum, actual sum // for (i=0; i timedur[i]) { // measures cannot be combined into the expected duration // based on the time signature. break; } else if (barsum == timedur[i]) { // the sequence of multiple measures adds up to the // expected time signature, so make the meter values increase // instead of reset after each internal barline int endline = file.getNumLines()-1; if (j < barlocs.getSize() - 1) { endline = barlocs[j+1]-1; } RationalNumber correction = file[barlocs[i]].getBeatR(); for (ii=barlocs[i+1]+1; ii<=endline; ii++) { if (file[ii].isMeasure()) { correction += file[ii].getBeatR(); continue; } temp = file[ii].getBeatR() + correction; file[ii].setBeat(temp); } i += j-i; break; } } } if (barlocs.getSize() == 0) { return; } // Only handle pickup measures for quarter note beats for now. // Pickups in other time bases are being messed up by the line // file[i].setBeat(timedur[0]-pickupdur+file[i].getBeatR()+1); // Probably because of a mixture of "4" and non "4" timebase in // calculation. if (strcmp(base, "4") == 0) { pickupdur = 0; if (file[barlocs[0]].getAbsBeatR() > 0) { if (file[barlocs[0]].getAbsBeatR() < timedur[0]) { pickupdur = file[barlocs[0]].getAbsBeatR(); } } } if (pickupdur > 0) { // int dataQ = 0; for (i=0; i& lastdurations, SigCollection& runningstatus, int& init, int& datastart, Array& ignore) { SigCollection newdurations; SigCollection newstatus; newdurations.setSize(lastdurations.getSize() + 4); newstatus.setSize(runningstatus.getSize() + 4); newdurations.setGrowth(newdurations.getSize()); newstatus.setGrowth(newstatus.getSize()); newdurations.setSize(0); newstatus.setSize(0); newdurations.allowGrowth(); newstatus.allowGrowth(); if (aRecord.getExInterpNum(spine) != E_KERN_EXINT && (strcmp(aRecord.getExInterp(spine), "**recip") != 0) && (strcmp(aRecord.getExInterp(spine), "**koto") != 0)) { return; } int i; int ii = 0; for (i=0; i& lastdurations, SigCollection& runningstatus, int& init, int& datastart, Array& ignore) { int spinecount = aRecord.getFieldCount(); int subcount; int inindex = 0; SigCollection newdurations; SigCollection newstatus; newdurations.allowGrowth(); newstatus.allowGrowth(); newstatus.setSize(runningstatus.getSize() + 4); newdurations.setSize(lastdurations.getSize() + 4); newdurations.setGrowth(newdurations.getSize()); newstatus.setGrowth(newstatus.getSize()); newdurations.setSize(0); newstatus.setSize(0); int i, j; for (i=0; i& rhythms to // Array& rhythms. // RationalNumber HumdrumFile::determineDurationR2(HumdrumRecord& aRecord, int& init, SigCollection& lastdurations, SigCollection& runningstatus, Array& rhythms, Array& ignore) { int i; // initialization: if (init) { init = 0; int size = aRecord.getFieldCount("**kern"); size += aRecord.getFieldCount("**recip"); size += aRecord.getFieldCount("**koto"); lastdurations.setSize(size); runningstatus.setSize(size); for (i=0; i0; z--) { // if (rbuff[z] == '.') { // rbase = 2 * rbase; // } // } int done = 0; RationalNumber value; for (z=0; z& lastdurations, SigCollection& runningstatus, Array& rhythms, Array& ignore) { int i; // initialization: if (init) { init = 0; int size = aRecord.getFieldCount("**kern"); size += aRecord.getFieldCount("**recip"); size += aRecord.getFieldCount("**koto"); lastdurations.setSize(size); runningstatus.setSize(size); for (i=0; i0; z--) { if (rbuff[z] == '.') { rbase = 2 * rbase; } } int done = 0; for (z=0; z& indices, int segment) { HumdrumFile& score = *this; indices.setSize(score.getNumLines()); indices.setSize(0); indices.allowGrowth(1); int count = score.getSegmentCount(); if (segment < -1 || segment >= count) { indices.allowGrowth(0); return; } int start = 0; int scount = 0; if (segment >= 0) { while (start < score.getNumLines()) { if (score[start].getType() == E_humrec_interpretation && strncmp(score[start][0], "**", 2) == 0) { scount++; if (scount == segment) { break; } } start++; } } int i; for (i=start; i& cliche, double duration, int minimumcount, double start, double stop) { HumdrumFile& score = *this; int nlflag = NL_SORT | NL_UNIQ; // later flags to add: NL_PC; NL_FILL; cliche.setSize(score.getNumLines()); cliche.zero(); Array di; // data index list for score data lines score.analyzeDataIndex(di); Array > pitchset; int starti = 0; // cout << "di get size = " << di.getSize() << endl; if (start > 0) { while (score[di[starti]].getAbsBeat() < start && starti < di.getSize()) { starti++; } } int endi = di.getSize() - 1; if (stop > 0) { while (score[di[endi]].getAbsBeat() < stop && endi > 1) { endi--; } } int i, j; Array > allnotes; allnotes.setSize(endi-starti+1); for (i=0; i >& allnotes, Array& di, int starti, int i, int j, double duration) { HumdrumFile& score = *this; double sumduration = 0.0; int delta = 0; int k; while (sumduration < duration) { if (score[di[i+delta]].getDuration() != score[di[j+delta]].getDuration()) { return 0; } else { sumduration += duration; } if (allnotes[i+delta].getSize() != allnotes[j+delta].getSize()) { return 0; } for (k=0; k& tempo, double tdefault) { Array newtempo; analyzeTempoMarkings(newtempo, tdefault); tempo.resize(newtempo.getSize()); for (int i=0; i<(int)tempo.size(); i++) { tempo[i] = newtempo[i]; } } void HumdrumFile::analyzeTempoMarkings(Array& tempo, double tdefault) { HumdrumFile& score = *this; tempo.setSize(score.getNumLines()); tempo.zero(); int i, j; double lasttempo = tdefault; for (i=0; i& top, Array& bottom, int flag) { int compoundQ = flag & (0x01<& beatdur, int flag) { Array top; analyzeMeter(top, beatdur, flag); } ////////////////////////////// // // HumdrumFile::analyzeAttackAccentuation -- determine the attack // accentuation // void HumdrumFile::analyzeAttackAccentuation(Array& atakcent) { HumdrumFile& score = *this; int allnotes; int attacknotes; int sustainnotes; int analysis; Array notes; atakcent.setSize(score.getNumLines()); atakcent.setSize(0); atakcent.allowGrowth(1); for (int i=0; i& metlev) { Array tempout; analyzeMetricLevel(tempout); metlev.resize(tempout.getSize()); for (int i=0; i& metlev) { HumdrumFile& score = *this; int i; metlev.setSize(score.getNumLines()); metlev.zero(); Array iscompound; Array msigtop; Array msigbottom; analyzeMeter(msigtop, msigbottom); iscompound.setSize(msigtop.getSize()); iscompound.zero(); int mval; int ltop = 0; int lbottom = 0; for (i=0; i 0) && (ltop == msigtop[i]) && (lbottom == msigbottom[i])) { iscompound[i] = iscompound[i-1]; } else { mval = (int)(msigtop[i] * msigbottom[i] + 0.45); if ((mval % 3) == 0) { iscompound[i] = 1; // Prevent 6/4 meters from being interpreted as compound here? } else { iscompound[i] = 0; } } } if (score.rhythmQ() == 0) { score.analyzeRhythm(); } int level = 0; double metloc; double fraction; double testtriplet; double testhigher; double highertriplet; for (i=0; i (1-ROUNDERR)) { // check for higher levels of metrical grouping if (metloc == 0.0) { if (iscompound[i]) { metlev[i] = (int)(-1.0*log10(msigtop[i]*msigbottom[i]*2.0/3.0)/log10(2.0)); } else { metlev[i] = (int)(-1.0 * log10(msigtop[i]*msigbottom[i])/log10(2.0)); } continue; } testhigher = metloc; while (level > -5) { testhigher = testhigher / 2.0; level--; fraction = testhigher - (int)testhigher; if (fraction < ROUNDERR || fraction > (1-ROUNDERR)) { metlev[i] = level; break; } if (iscompound[i]) { highertriplet = metloc * 2.0/3.0; // check for triplets on level fraction = highertriplet - (int)highertriplet; if (fraction < ROUNDERR || fraction > (1-ROUNDERR)) { metlev[i] = level; break; } } } continue; } // inside of subdivision of a beat if getting to this point level = 0; while (level < 10) { if (iscompound[i]) { metloc = metloc * 2.0; // just base 2 checking for now level++; testtriplet = metloc * 3.0/2.0; // check for triplets on level fraction = testtriplet - (int)testtriplet; if (fraction < ROUNDERR || fraction > (1-ROUNDERR)) { metlev[i] = level; break; } } else { metloc = metloc * 2.0; // just base 2 checking for now level++; fraction = metloc - (int)metloc; if (fraction < ROUNDERR || fraction > (1-ROUNDERR)) { metlev[i] = level; break; } else { testtriplet = metloc * 3.0/2.0; // check for triplets on level fraction = testtriplet - (int)testtriplet; if (fraction < ROUNDERR || fraction > (1-ROUNDERR)) { metlev[i] = level; break; } } } } if (level >= 10) { metlev[i] = level; } } } ////////////////////////////// // // HumdrumFile::analyzeSonorityQuality -- try to find the root and // inversion of the given note sets. // void HumdrumFile::analyzeSonorityQuality(Array& cq) { Array notes; HumdrumFile& score = *this; cq.setSize(score.getNumLines()); int line; for (line=0; line& cq) { vector notes; HumdrumFile& score = *this; cq.resize(score.getNumLines()); for (int line=0; line& roots, int flag) { int base12Q = flag & (0x01 << PITCH_BASE_BIT); Array notes; HumdrumFile& score = *this; roots.setSize(score.getNumLines()); ChordQuality cq; int line; for (line=0; line& scores, int startindex, int stopindex, int rhythmQ, int binaryQ, int tracknum) { Array absbeat; Array pitches; Array durations; Array levels; getNoteArray(absbeat, pitches, durations, levels, startindex, stopindex, tracknum); scores.setSize(24); Array distribution(12); int i; for (i=0; i& scores, int startindex, int stopindex, double* majorprofile, double* minorprofile, int rhythmQ, int binaryQ, int tracknum) { Array absbeat; Array pitches; Array durations; Array levels; getNoteArray(absbeat, pitches, durations, levels, startindex, stopindex, tracknum); scores.setSize(24); Array distribution(12); int i; for (i=0; i& norm, int line, int attackQ) { Array base12; this->getBase12PitchList(base12, line, attackQ); Convert::base12ToNormalForm(norm, base12); } void HumdrumFile::getNormalForm(vector& norm, int line, int attackQ) { vector base12; this->getBase12PitchList(base12, line, attackQ); Convert::base12ToNormalForm(norm, base12); } ////////////////////////////// // // getBase12PitchList -- Returns a list of the MIDI note numbers // for all pitches sounding in **kern spines on the given line. // Both notes which are started on the current line and ones which // are sustained from a previous line are included. If attackQ is // true, then only consider notes attacked on the current line; otherwise, // include all notes, both sustained from previous lines and notes // attacked starting on the given line. // // Default value: attackQ = 0 // void HumdrumFile::getBase12PitchList(vector& list, int line, int attackQ) { Array temp; getBase12PitchList(temp, line, attackQ); list.resize(temp.getSize()); for (int i=0; i<(int)list.size(); i++) { list[i] = temp[i]; } } void HumdrumFile::getBase12PitchList(Array& list, int line, int attackQ) { HumdrumRecord& arecord = (*this)[line]; list.setSize(arecord.getFieldCount()); list.setSize(0); int j, k; int ii, jj; int tcount; char buffer[128] = {0}; int value; for (j=0; j& iv, int line, int attackQ) { Array base12; this->getBase12PitchList(base12, line, attackQ); Convert::base12ToIntervalVector(iv, base12); } void HumdrumFile::getIntervalVector(vector& iv, int line, int attackQ) { vector base12; this->getBase12PitchList(base12, line, attackQ); Convert::base12ToIntervalVector(iv, base12); } ////////////////////////////// // // HumdrumFile::getTnSetName -- return the Tn Set Name (Forte number with // A/B variants if necessary). The attackQ boolean is used for // selecting only note attacks on the line, if set to 0 (the default), // all notes, both sustained from previous lines and note started on // the current line, will be considered. // // Default value attackQ = 0; // string HumdrumFile::getTnSetNameString(int line, int attackQ) { vector base12; this->getBase12PitchList(base12, line, attackQ); string output = Convert::base12ToTnSetName(base12); return output; } const char* HumdrumFile::getTnSetName(int line, int attackQ) { Array base12; this->getBase12PitchList(base12, line, attackQ); return Convert::base12ToTnSetName(base12); } ////////////////////////////// // // HumdrumFile::getTnSetNameAllSubsets -- // void HumdrumFile::getTnSetNameAllSubsets(vector& list, int line, int attackQ) { vector base12; this->getBase12PitchList(base12, line, attackQ); list.resize(0); Convert::base12ToTnSetNameAllSubsets(list, base12); } void HumdrumFile::getTnSetNameAllSubsets(Array& list, int line, int attackQ) { Array base12; this->getBase12PitchList(base12, line, attackQ); list.setSize(0); Convert::base12ToTnSetNameAllSubsets(list, base12); } ////////////////////////////// // // HumdrumFile::getTnNormalForm -- 0-transposed normal form. // void HumdrumFile::getTnNormalForm(vector& tnorm, int line, int attackQ) { vector base12; this->getBase12PitchList(base12, line, attackQ); Convert::base12ToTnNormalForm(tnorm, base12); } void HumdrumFile::getTnNormalForm(Array& tnorm, int line, int attackQ) { Array base12; this->getBase12PitchList(base12, line, attackQ); Convert::base12ToTnNormalForm(tnorm, base12); } ////////////////////////////// // // HumdrumFile::getForteSetName -- // string HumdrumFile::getForteSetNameString(int line) { string output = getForteSetName(line); return output; } const char* HumdrumFile::getForteSetName(int line) { Array iv; Array base12; HumdrumRecord& arecord = (*this)[line]; base12.setSize(arecord.getFieldCount()); base12.setSize(0); int j, k; int ii, jj; int tcount; char buffer[128] = {0}; int value; for (j=0; j *((int*)b)) { return 1; } else { return 0; } } ////////////////////////////// // // HumdrumFile::printConstantTokenFields -- // ostream& HumdrumFile::printConstantTokenFields(ostream& out, HumdrumRecord& aRecord, const char* token) { int i; for (i=0; i