// // Programmer: Craig Stuart Sapp // Creation Date: Sun Oct 22 15:33:41 PDT 2000 // Last Modified: Sun May 26 19:39:01 PDT 2002 Mostly finished // Last Modified: Tue Mar 16 05:53:19 PST 2010 Added *M meter description // Last Modified: Wed Apr 21 14:31:44 PDT 2010 Added search feature // Last Modified: Wed May 19 15:30:49 PDT 2010 Added tick & rational values // Last Modified: Sat Apr 28 09:03:39 PDT 2018 Converted to HumdrumStream input // Last Modified: Sun Apr 29 15:07:07 PDT 2018 Added -m and --meter-bottom options // Filename: ...sig/examples/all/beat.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/beat.cpp // Syntax: C++; museinfo // vim: ts=3 // // Description: Generates metrical location data for **kern or **recip // entries. Test functions for the built-in rhythmic analysis // in the HumdrumFile class. Should give the same // output as the beat program. // // There are two cases when an incomplete measure needs to // be counted backwards. These cases will be handled by // the beat program: // (1) an initial pickup beat // (2) a repeat sign breaks a true measure // // There is a bug that needs fixing: // The *M2/2 interpretations are erased (at least with the -s option) // // five types of outputs can be given: // -s = sum the number of beats in a measure // = display the beat (default if no other output type given) // -d = duration // -c = cumulative running total beat/duration // #include "humdrum.h" #include #include #include #include #include #include using namespace std; // function declarations void checkOptions (Options& opts, int argc, char* argv[]); void example (void); void usage (const char* command); void printOutput (HumdrumFile& file, vector& Bfeatures, vector& Blines, vector& Dfeatures, vector& Dlines, vector& tickanalysis); RationalNumber getPickupDuration (HumdrumFile& file); void fillSearchString (vector& searcher, const string& astring); void printSearchResults (HumdrumFile& infile, vector& Bfeatures, vector& Blines, vector& Dfeatures, vector& Dlines); void printSearchResultsFinal(vector& linematch, HumdrumFile& infile, vector& Bfeatures, vector& Blines, vector& Dfeatures, vector& Dloines); void doBeatSearch (vector& results, HumdrumFile& infile, vector search, vector& Bfeatures, vector& Blines); void doDurSearch (vector& results, HumdrumFile& infile, vector search, vector& Dfeatures, vector& Dlines); void doDurSearch (vector& results, HumdrumFile& infile, vector search, vector& Dfeatures, vector& Dlines); void mergeResults (vector& output, vector& input1, vector& input2); void printSequence (vector& pattern); void printSequence (vector& pattern); void fillMeasureInfo (HumdrumFile& infile, vector& measures); void doComparison (vector& results, vector& line, vector& search, vector& data, HumdrumFile& infile); int checkForWildcard (vector& sequence); void extractBeatFeatures(HumdrumFile& infile, vector& line, vector& data); void extractDurFeatures (HumdrumFile& infile, vector& line, vector& data); void printSequence (vector& features, vector& lines, vector& search, int startline); void printSequence (vector& features, vector& lines, vector& search, int startline); void printSequence (vector& features, vector& lines, vector& search, int startline); void printMatchesWithData(vector& linematch, HumdrumFile& infile); void fillAttackArray (HumdrumFile& infile, vector& attacks); int getCountForLine (HumdrumFile& infile, int line); int doTickAnalysis (vector& tickanalysis, HumdrumFile& infile); RationalNumber getDurationOfFirstMeasure(HumdrumFile& file); void analyzeFile (HumdrumFile& infile); void prepareMeterData (HumdrumFile& infile); // global variables Options options; // database for command-line arguments int appendQ = 0; // used with -a option int prependQ = 0; // used with -p option int durQ = 0; // used with -d option int absQ = 0; // used with -t option int beatQ = 0; // used with -b option int sumQ = 0; // used with -s option int zeroQ = 0; // zero offset instead of 1 for first beat int nullQ = 0; // used with -n option vector Bsearch; // used with -B option vector Dsearch; // used with -D option double Rvalue = -1.0; // used with -R option double Tolerance = 0.001; // used for rounding int Attack = 1; // used with -A option vector Attacks; // used with -A option int tickQ = 0; // used with -t option int metertopQ= 0; // used with -m option int meterbotQ= 0; // used with --meter-bottom option int meterdurQ= 0; // used with --meter-duration option int rationalQ= 0; // used with -r option int tpwQ = 0; // used with --tpw option int tpqQ = 0; // used with --tpq option int fillQ = 0; // used with --fill option string beatbase = "4"; // used with --beatsize option int uQ = 0; // used for -f and -u interactions int debugQ = 0; // used with --debug option vector Metertop; // used with -m option vector Meterbot; // used with -meter-bottom option vector Meterdur; // used with --meter-duration option /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { checkOptions(options, argc, argv); HumdrumStream streamer(options); HumdrumFile infile; while (streamer.read(infile)) { analyzeFile(infile); } return 0; } ////////////////////////////// // // analyzeFile -- // void analyzeFile(HumdrumFile& infile) { vector Bfeatures; // used to extract beat data from input vector Dfeatures; // used to extract duration data from input vector Blines; // used to extract beat data from input vector Dlines; // used to extract duration data from input infile.analyzeRhythm(beatbase.c_str()); if (metertopQ || meterbotQ || meterdurQ) { prepareMeterData(infile); } vector tickanalysis; tickanalysis.resize(infile.getNumLines()); std::fill(tickanalysis.begin(), tickanalysis.end(), 0); int tickfactor = 1; if (tickQ) { tickfactor = doTickAnalysis(tickanalysis, infile); } if (tpwQ) { cout << infile.getMinTimeBase() * tickfactor << endl; exit(0); } else if (tpqQ) { cout << infile.getMinTimeBase() * tickfactor /4.0 << endl; exit(0); } fillAttackArray(infile, Attacks); extractBeatFeatures(infile, Blines, Bfeatures); if (debugQ) { cout << "BEAT FEATURES ====================" << endl; for (int ii=0; ii<(int)Bfeatures.size(); ii++) { cout << Bfeatures[ii].getFloat() << endl; } cout << "==================================" << endl; } extractDurFeatures(infile, Dlines, Dfeatures); if (Bsearch.size() > 0 || Dsearch.size() > 0) { printSearchResults(infile, Bfeatures, Blines, Dfeatures, Dlines); } else { printOutput(infile, Bfeatures, Blines, Dfeatures, Dlines, tickanalysis); } } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // prepareMeterData -- // void prepareMeterData(HumdrumFile& infile) { Metertop.resize(infile.getNumLines()); Meterbot.resize(infile.getNumLines()); Meterdur.resize(infile.getNumLines()); fill(Meterdur.begin(), Meterdur.end(), 0); std::fill(Metertop.begin(), Metertop.end(), "."); std::fill(Meterbot.begin(), Meterbot.end(), "."); for (int i=0; i& tickanalysis, HumdrumFile& infile) { int i; tickanalysis.resize(infile.getNumLines()); vector pretick(tickanalysis.size()); int minrhy = infile.getMinTimeBase(); if (minrhy <= 0.0) { return 1; } RationalNumber value; int monitor = 0; for (i=0; i& attacks) { int i; attacks.clear(); attacks.reserve(infile.getNumLines()); if (Attack <= 0) { // don't need to waste time analyzing the attack structure of the data... return; } int count; for (i=0; i& Bfeatures, vector& Blines, vector& Dfeatures, vector& Dlines) { vector Bresults; vector Dresults; Bresults.clear(); Bresults.reserve(100000); Dresults.clear(); Dresults.reserve(100000); if ((Bsearch.size() > 0) && (Dsearch.size() > 0)) { doBeatSearch(Bresults, infile, Bsearch, Bfeatures, Blines); doDurSearch(Dresults, infile, Dsearch, Dfeatures, Dlines); vector finalresults; mergeResults(finalresults, Bresults, Dresults); cout << "!!parallel beat search: "; printSequence(Bsearch); cout << endl; cout << "!!parallel duration search: "; printSequence(Dsearch); cout << endl; printSearchResultsFinal(finalresults, infile, Bfeatures, Blines, Dfeatures, Dlines); } else if (Bsearch.size() > 0) { doBeatSearch(Bresults, infile, Bsearch, Bfeatures, Blines); cout << "!!beat search: "; printSequence(Bsearch); cout << endl; printSearchResultsFinal(Bresults, infile, Bfeatures, Blines, Dfeatures, Dlines); } else if (Dsearch.size() > 0) { doDurSearch(Dresults, infile, Dsearch, Dfeatures, Dlines); cout << "!!duration search: "; printSequence(Dsearch); cout << endl; printSearchResultsFinal(Dresults, infile, Bfeatures, Blines, Dfeatures, Dlines); } else { cout << "ERROR in search" << endl; } } ////////////////////////////// // // printSequence -- // void printSequence(vector& pattern) { int i; for (i=0; i<(int)pattern.size(); i++) { if (pattern[i] < 0) { cout << "*"; } else { cout << pattern[i]; } if (i < (int)pattern.size()-1) { cout << ' '; } } } void printSequence(vector& pattern) { int i; for (i=0; i<(int)pattern.size(); i++) { if (pattern[i] < 0) { cout << "*"; } else { cout << pattern[i]; } if (i < (int)pattern.size()-1) { cout << ' '; } } } ////////////////////////////// // // checkForWildcard -- returns true if any of the values are negative. // int checkForWildcard(vector& sequence) { int i; for (i=0; i<(int)sequence.size(); i++) { if (sequence[i] < 0.0) { return 1; } } return 0; } ////////////////////////////// // // printMatchesWithData -- // void printMatchesWithData(vector& linematch, HumdrumFile& infile) { int i; int counter = 1; vector lookup; lookup.resize(infile.getNumLines()); std::fill(lookup.begin(), lookup.end(), -1); for (i=0; i<(int)linematch.size(); i++) { lookup[linematch[i]] = counter++; } for (int i=0; i", 2) == 0) { cout << infile[i][0]; } else { cout << "*"; } if (prependQ) { cout << "\t" << infile[i]; } cout << "\n"; break; case E_humrec_data: if (appendQ) { cout << infile[i] << "\t"; } if (lookup[i] < 0) { cout << "."; } else { cout << lookup[i]; } if (prependQ) { cout << "\t" << infile[i]; } cout << "\n"; break; case E_humrec_local_comment: if (appendQ) { cout << infile[i] << "\t"; } cout << "!"; if (prependQ) { cout << "\t" << infile[i]; } cout << "\n"; break; case E_humrec_none: case E_humrec_empty: case E_humrec_global_comment: case E_humrec_bibliography: default: cout << infile[i] << "\n"; break; } } } ////////////////////////////// // // printSearchResultsFinal -- // void printSearchResultsFinal(vector& linematch, HumdrumFile& infile, vector& Bfeatures, vector& Blines, vector& Dfeatures, vector& Dlines) { cout << "!!matches: " << (int)linematch.size() << "\n"; if (appendQ || prependQ) { printMatchesWithData(linematch, infile); return; } vector measures; fillMeasureInfo(infile, measures); int hasBWildcard = checkForWildcard(Bsearch); int hasDWildcard = checkForWildcard(Dsearch); int i; cout << "**line\t**bar\t**beat\t**absb"; if (hasBWildcard) { cout << "\t**bseq"; } if (hasDWildcard) { cout << "\t**dseq"; } cout << "\n"; for (i=0; i<(int)linematch.size(); i++) { cout << linematch[i]-1; cout << "\t" << measures[linematch[i]]; if (zeroQ) { cout << "\t" << infile[linematch[i]].getBeat()-1; } else { cout << "\t" << infile[linematch[i]].getBeat(); } cout << "\t" << infile[linematch[i]].getAbsBeat(); if (hasBWildcard) { cout << "\t"; printSequence(Bfeatures, Blines, Bsearch, linematch[i]); } if (hasDWildcard) { cout << "\t"; printSequence(Dfeatures, Dlines, Dsearch, linematch[i]); } cout << endl; } cout << "*-\t*-\t*-\t*-"; if (hasBWildcard) { cout << "\t*-"; } if (hasDWildcard) { cout << "\t*-"; } cout << "\n"; } /////////////////////////////// // // printSequence -- // void printSequence(vector& features, vector& lines, vector& search, int startline) { int index = -1; int i; for (i=0; i<(int)lines.size(); i++) { if (lines[i] == startline) { index = i; break; } } if (index < 0) { cout << "."; return; } int stopindex = index + (int)search.size() - 1; for (i=index; (i<(int)features.size()) && (i<=stopindex); i++) { cout << features[i]; if (i < stopindex) { cout << " "; } } } void printSequence(vector& features, vector& lines, vector& search, int startline) { int index = -1; int i; for (i=0; i<(int)lines.size(); i++) { if (lines[i] == startline) { index = i; break; } } if (index < 0) { cout << "."; return; } int stopindex = index + (int)search.size() - 1; for (i=index; (i<(int)features.size()) && (i<=stopindex); i++) { cout << features[i]; if (i < stopindex) { cout << " "; } } } void printSequence(vector& features, vector& lines, vector& search, int startline) { int index = -1; int i; for (i=0; i<(int)lines.size(); i++) { if (lines[i] == startline) { index = i; break; } } if (index < 0) { cout << "."; return; } int stopindex = index + (int)search.size() - 1; for (i=index; (i<(int)features.size()) && (i<=stopindex); i++) { cout << features[i]; if (i < stopindex) { cout << " "; } } } ////////////////////////////// // // fillMeasureInfo -- // void fillMeasureInfo(HumdrumFile& infile, vector& measures) { int i; measures.resize(infile.getNumLines(), 0.0); double current = 0.0; for (i=0; i& output, vector& input1, vector& input2) { int i, j; int maxsize = (int)input1.size(); if ((int)input2.size() < maxsize) { maxsize = (int)input2.size(); } output.clear(); output.reserve(maxsize); if (maxsize == 0) { return; } int similar; j=0; for (i=0; i<(int)input1.size(); i++) { while ((j<(int)input2.size()) && (input2[j] < input1[i])) { j++; } if (j >= (int)input2.size()) { break; } if (input2[j] == input1[i]) { similar = input2[j]; output.push_back(similar); } } } ////////////////////////////// // // doBeatSearch -- search for specific beat pattern in data. // void doBeatSearch(vector& results, HumdrumFile& infile, vector search, vector& Bfeatures, vector& Blines) { // extractBeatFeatures(infile, Blines, Bfeatures); vector doubleBfeatures(Bfeatures.size()); int i; for (i=0; i<(int)doubleBfeatures.size(); i++) { doubleBfeatures[i] = Bfeatures[i].getFloat(); } doComparison(results, Blines, search, doubleBfeatures, infile); } ////////////////////////////// // // extractBeatFeatures -- // void extractBeatFeatures(HumdrumFile& infile, vector& line, vector& data) { line.clear(); line.reserve(infile.getNumLines()); data.clear(); data.reserve(infile.getNumLines()); int lval; RationalNumber bval; int i; for (i=0; i& line, vector& data) { line.clear(); line.resize(infile.getNumLines()); data.clear(); data.resize(infile.getNumLines()); int lval; RationalNumber bval; int i; for (i=0; i 0) { data[(int)data.size()-1] += bval; } continue; } line.push_back(lval); data.push_back(bval); } } ////////////////////////////// // // doComparison -- // void doComparison(vector& results, vector& line, vector& search, vector& data, HumdrumFile& infile) { results.clear(); results.reserve((int)data.size() - (int)search.size() + 1); double startdur; double stopdur; int match; int i, j; for (i=0; i<(int)data.size() - (int)search.size() + 1; i++) { match = 1; for (j=0; j<(int)search.size(); j++) { if (search[j] < 0) { // for wildcard match (*) continue; } if (fabs(search[j] - data[i+j]) > Tolerance) { match = 0; break; } } if (match) { if (Rvalue > 0) { startdur = infile[line[i]].getAbsBeat(); stopdur = infile[line[i+(int)search.size()-1]].getAbsBeat(); if (fabs(Rvalue - (stopdur-startdur)) < Tolerance) { results.push_back(line[i]); } } else { results.push_back(line[i]); } } } } ////////////////////////////// // // doDurSearch -- search for specific beat pattern in data. // void doDurSearch(vector& results, HumdrumFile& infile, vector search, vector& Dfeatures, vector& Dlines) { // extractDurFeatures(infile, Dlines, Dfeatures); doComparison(results, Dlines, search, Dfeatures, infile); } void doDurSearch(vector& results, HumdrumFile& infile, vector search, vector& Dfeatures, vector& Dlines) { int i; vector doubleDfeatures((int)Dfeatures.size()); for (i=0; i<(int)doubleDfeatures.size(); i++) { doubleDfeatures[i] = Dfeatures[i].getFloat(); } doComparison(results, Dlines, search, doubleDfeatures, infile); } ////////////////////////////// // // getPickupDuration -- // RationalNumber getPickupDuration(HumdrumFile& file) { int i; for (i=0; i& Bfeatures, vector& Blines, vector& Dfeatures, vector& Dlines, vector& tickanalysis) { int lastmeasureline = -1; int pickupstate = 0; int suppressreturn = 0; int i; string lastSum = "."; vector abstick; if (tickQ) { unsigned long csum = 0; abstick.resize((int)tickanalysis.size()); std::fill(abstick.begin(), abstick.end(), 0); for (i=0; i<(int)tickanalysis.size(); i++) { abstick[i] = csum; csum += tickanalysis[i]; } } RationalNumber minrhy(file.getMinTimeBase(), 4); RationalNumber rat; vector Binfo(file.getNumLines(), -1); vector Dinfo(file.getNumLines(), -1); int measurecount = 0; for (i=0; i<(int)Blines.size(); i++) { Binfo[Blines[i]] = Bfeatures[i]; if (Binfo[Blines[i]] == file[Blines[i]].getAbsBeatR()) { Binfo[Blines[i]]++; Binfo[Blines[i]] -= file.getPickupDurationR(); } if (zeroQ) { Binfo[Blines[i]]--; } } for (i=0; i<(int)Dlines.size(); i++) { Dinfo[Dlines[i]] = Dfeatures[i]; } for (i=0; i", 2) == 0) { cout << file[i][0]; } else { if ((strncmp(file[i][0], "*M", 2) == 0) && (strchr(file[i][0], '/') != NULL)) { cout << file[i][0]; } else if (strncmp(file[i][0], "*MM", 3) == 0) { cout << file[i][0]; } else if (appendQ || prependQ) { cout << "*"; } else { cout << "*"; } } if (prependQ) { cout << "\t" << file[i]; } cout << "\n"; break; case E_humrec_data: if (appendQ) { cout << file[i] << "\t"; } if (file[i][0][0] == '=') { pickupstate++; } if (durQ) { // cout << file[i].getDuration(); if (Dinfo[i] >= 0) { if (tickQ && !rationalQ) { cout << tickanalysis[i]; } else if (tickQ && rationalQ) { rat.setValue(tickanalysis[i], file.getMinTimeBase()); if (uQ) { rat *= 4; } cout << rat; } else { cout << Dinfo[i].getFloat(); } } else { if (nullQ || appendQ || prependQ) { cout << "."; } else { suppressreturn = 1; } } } else if (metertopQ) { cout << Metertop[i]; } else if (meterbotQ) { cout << Meterbot[i]; } else if (meterdurQ) { if (Meterdur[i] == 0) { cout << "."; } else { cout << Meterdur[i]; } } else if (absQ) { if (tickQ && !rationalQ) { cout << abstick[i]; } else if (tickQ && rationalQ) { RationalNumber anumber(abstick[i], file.getMinTimeBase()); if (uQ) { anumber *= 4; } anumber.printTwoPart(cout); } else { cout << file[i].getAbsBeat(); } } else if (sumQ) { if (lastmeasureline > 0) { stringstream ss; ss << fabs(file[lastmeasureline].getBeat()); cout << ss.str(); lastSum = ss.str(); ss.str(""); pickupstate++; lastmeasureline = -1; } else if (pickupstate < 1) { if (!file.getPickupDurationR().isNegative()) { if (measurecount == 0) { stringstream ss; ss << getDurationOfFirstMeasure(file).getFloat(); cout << ss.str(); lastSum = ss.str(); ss.str(""); } else { stringstream ss; ss << file.getPickupDuration(); cout << ss.str(); lastSum = ss.str(); ss.str(""); } } else if (file.getPickupDurationR().isZero()) { stringstream ss; ss << file.getTotalDurationR().getFloat(); cout << ss.str(); lastSum = ss.str(); ss.str(""); } else { stringstream ss; ss << file.getTotalDurationR().getFloat(); cout << ss.str(); lastSum = ss.str(); ss.str(""); } pickupstate++; lastmeasureline = -1; } else { if (appendQ || prependQ) { if (fillQ) { cout << lastSum; } else { cout << "."; } } else { if (nullQ) { if (fillQ) { cout << lastSum; } else { cout << "."; } } else { suppressreturn = 1; } } } } else if (beatQ) { if (Binfo[i] >= 0) { if (!tickQ && !rationalQ) { cout << Binfo[i].getFloat(); } else if (tickQ && !rationalQ) { cout << (Binfo[i] * minrhy); } else { Binfo[i].printTwoPart(cout); } } else { if (nullQ || appendQ || prependQ) { cout << "."; } else { suppressreturn = 1; } } } if (prependQ) { cout << "\t" << file[i]; } if (suppressreturn) { suppressreturn = 0; } else { cout << "\n"; } break; case E_humrec_local_comment: if (appendQ) { cout << file[i] << "\t"; } cout << "!"; if (prependQ) { cout << "\t" << file[i]; } cout << "\n"; break; case E_humrec_none: case E_humrec_empty: case E_humrec_global_comment: case E_humrec_bibliography: default: cout << file[i] << "\n"; break; } } } ////////////////////////////// // // getDurationOfFirstMeasure -- // RationalNumber getDurationOfFirstMeasure(HumdrumFile& file) { int i; RationalNumber output(0,1); for (i=0; i& searcher, const string& astring) { int len = astring.size(); char* tempstr; tempstr = new char[len+1]; strcpy(tempstr, astring.c_str()); char* ptr; ptr = strtok(tempstr, " \t\n:;,"); double value; searcher.reserve(1000); while(ptr != NULL) { if (strcmp(ptr, "*") == 0) { value = -1; } else { value = atof(ptr); } searcher.push_back(value); ptr = strtok(NULL, " \t\n:;,"); } delete [] tempstr; } ////////////////////////////// // // example -- example usage of the quality program // void example(void) { cout << " \n" "# example usage of the meter program. \n" "# analyze a Bach chorale for meter position: \n" " meter chor217.krn \n" " \n" "# display the metrical location spine with original data: \n" " meter -a chor217.krn \n" " \n" << endl; } ////////////////////////////// // // usage -- gives the usage statement for the meter program // void usage(const char* command) { cout << " \n" "Analyzes **kern data and generates a rhythmic analysis which gives \n" "the beat location of **kern data records in the measure. Currently, \n" "input spines cannot split or join. \n" " \n" "Usage: " << command << " [-a][-b base-rhythm][-s|-d][input1 [input2 ...]]\n" " \n" "Options: \n" " -a = assemble the analysis spine with the input data. \n" " -b = set the base rhythm for analysis to specified kern rhythm value. \n" " -d = gives the duration of each kern record in beat measurements. \n" " -s = sum the beat count in each measure. \n" " --options = list of all options, aliases and default values \n" " \n" << endl; } // md5sum: 3f05fc40ec75224a766159a6a217ce7b beat.cpp [20170605]