// // Programmer: Craig Stuart Sapp // Creation Date: Tue Aug 19 13:13:21 PDT 2014 // Last Modified: Tue Sep 23 14:20:21 PDT 2014 (reverse part order, add quartls) // Filename: ...sig/examples/all/beat.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/beat.cpp // Syntax: C++; museinfo // // Description: Calculate the average pitch for a single staff or all staves. // with a selectable analysis window size. // #include "humdrum.h" #include #include #include class Data { public: int startindex; int endindex; vector minpitch; vector maxpitch; vector average; vector q25; vector q75; vector sd; vector attacks; void clear(int voicecount); }; void Data::clear(int voicecount) { startindex = -1; endindex = -1; minpitch.resize(voicecount); std::fill(minpitch.begin(), minpitch.end(), 1000); maxpitch.resize(voicecount); std::fill(maxpitch.begin(), maxpitch.end(), -1000); average.resize(voicecount); std::fill(average.begin(), average.end(), 0.0); q25.resize(voicecount); std::fill(q25.begin(), q25.end(), 0.0); q75.resize(voicecount); std::fill(q75.begin(), q75.end(), 0.0); sd.resize(voicecount); std::fill(sd.begin(), sd.end(), 0.0); attacks.resize(voicecount); std::fill(attacks.begin(), attacks.end(), 0.0); } // function declarations void checkOptions (Options& opts, int argc, char* argv[]); void example (void); void usage (const char* command); void processFile (HumdrumFile& infile); void generateAnalysis (HumdrumFile& infile, vector >& data, int windowsize, int windowindex, vector > >& histograms, vector >& attacks, double hop); void printJsonData (HumdrumFile& infile, vector >& data, vector window); void generatePitchHistogram(HumdrumFile& infile, vector > >& hophistogram, vector >& attacks, double hopsize); void addToHistogram (vector > >& hophistogram, int timebin, double amount, int track, int base7); void addToHistogram2 (vector >& notehist, vector& attacksum, int index, vector > >& histograms, vector >& attacks); void subtractFromHistogram2(vector >& notehist, vector& attacksum, int index, vector > >& histograms, vector >& attacks); void doAnalysis (Data& data, vector >& notehist, vector& attacksum); double getWeightedAverage (vector& notes); double getStandardDeviation(double weightedaverage, vector& notes); void printDataContentJson(vector >& data); void printPartNames (HumdrumFile& infile); void printVoiceData (vector >& data, int index, int vcount); void printPitchMean (vector >& data, int index, int voice); void printPitchMax (vector >& data, int index, int voice); void printPitchMin (vector >& data, int index, int voice); void printPitchSD (vector >& data, int index, int voice); void printPitchAttacks (vector >& data, int index, int voice); void printStartFrame (vector >& data, int index); void printEndFrame (vector >& data, int index); void printPitch25q (vector >& data, int index, int voice); void printPitch75q (vector >& data, int index, int voice); double get25thQuartile (vector& notes); double get75thQuartile (vector& notes); double getMinPitch (vector& notes); double getMaxPitch (vector& notes); void printVoicePartData (vector >& data, int index, int vcount, int v, int comma); // global variables Options options; // database for command-line arguments int jsonQ = 0; // output analysis JSON data vector Window; // used with -w option double Hopsize = 2.0; // used with -h option vector Minpitch; vector Maxpitch; string optionfilename = ""; /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { checkOptions(options, argc, argv); HumdrumFileSet infiles; infiles.read(options); for (int i=0; i 0) { infiles[i].setFilename(optionfilename.c_str()); } processFile(infiles[i]); } return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // processFile -- create analysis for one data file. // void processFile(HumdrumFile& infile) { infile.analyzeRhythm("4"); vector ktracks; infile.getTracksByExInterp(ktracks, "**kern"); vector > > hophistogram; vector > attacks; // dimension 1: time in score // dimension 2: voice in score (0th index is for all parts) // dimension 3: diatonic pitch class generatePitchHistogram(infile, hophistogram, attacks, Hopsize); vector > data; // dimension 1: time in score // dimension 2: window size data.resize(attacks.size()); int i, j; for (i=0; i<(int)data.size(); i++) { data[i].resize(Window.size()); for (j=0; j<(int)data[i].size(); j++) { data[i][j].clear(ktracks.size()+1); } } for (int i=0; i<(int)Window.size(); i++) { generateAnalysis(infile, data, Window[i], i, hophistogram, attacks, Hopsize); } if (jsonQ) { printJsonData(infile, data, Window); } } ////////////////////////////// // // generatePitchHistogram -- // // dimension 1: time in score // dimension 2: voice in score (0th index is for all parts) // dimension 3: diatonic pitch class // void generatePitchHistogram(HumdrumFile& infile, vector > >& hophistogram, vector >& attacks, double hopsize) { double beatcount = infile[infile.getNumLines()-1].getAbsBeat(); int bincount = beatcount / hopsize + 1; hophistogram.resize(bincount); attacks.resize(bincount); int trackcount = infile.getMaxTracks() + 1; int i, j; for (i=0; i Maxpitch[track]) { Maxpitch[track] = base7; } if (base7 < Minpitch[0]) { Minpitch[0] = base7; } if (base7 > Maxpitch[0]) { Maxpitch[0] = base7; } // split note into hop histograms: tdur = dur; sdiff = (1.0 - (startbin - int(startbin))) * hopsize; if (tdur < sdiff) { sdiff = tdur; } tdur -= sdiff; curbin = (int)startbin; addToHistogram(hophistogram, curbin++, sdiff, track, base7); while (tdur > 0.0) { sdiff = 1.0 * hopsize; if (tdur < sdiff) { sdiff = tdur; } addToHistogram(hophistogram, curbin++, sdiff, track, base7); tdur -= sdiff; } } } } } ////////////////////////////// // // addToHistogram -- // void addToHistogram(vector > >& hophistogram, int timebin, double amount, int track, int base7) { hophistogram[timebin][track][base7] += amount; hophistogram[timebin][0][base7] += amount; } ////////////////////////////// // // generateAnalysis -- // void generateAnalysis(HumdrumFile& infile, vector >& data, int windowsize, int windowindex, vector > >& histograms, vector >& attacks, double hop) { int trackcount = infile.getMaxTracks() + 1; vector kerntracks; infile.getTracksByExInterp(kerntracks, "**kern"); int ktracks = (int)kerntracks.size() + 1; // +1 for full score. vector rtrack(trackcount); rtrack.setAll(-1); int i, j; for (i=0; i<(int)kerntracks.size(); i++) { rtrack[kerntracks[i]] = i+1; } vector > notehist; notehist.resize(ktracks); for (i=0; i<(int)notehist.size(); i++) { notehist[i].resize(70); std::fill(notehist[i].begin(), notehist[i].end(), 0.0); } vector attacksum; attacksum.resize(ktracks); attacksum.setAll(0); int windowstart = 0; int windowend = windowsize-1; int newstart = 0; int newend = 0; for (i=windowstart; i<=windowend; i++) { addToHistogram2(notehist, attacksum, i, histograms, attacks); } //int shiftval; for (i=0; i<(int)data.size(); i++) { newstart = i - windowsize/2; if (newstart < 0) { // shiftval = -newstart; newstart = 0; } newend = newstart + windowsize - 1; if (newend >= (int)data.size()) { newstart = windowstart; newend = windowend; } if (newstart > windowstart) { for (j=windowstart; j windowend) { for (j=windowend+1; j<=newend; j++) { addToHistogram2(notehist, attacksum, j, histograms, attacks); } windowend = newend; } data[i][windowindex].startindex = windowstart; data[i][windowindex].endindex = windowend; doAnalysis(data[i][windowindex], notehist, attacksum); } } ////////////////////////////// // // doAnalysis -- // void doAnalysis(Data& data, vector >& notehist, vector& attacksum) { data.average.resize(attacksum.size()); data.q25.resize(attacksum.size()); data.q75.resize(attacksum.size()); data.sd.resize(attacksum.size()); data.attacks.resize(attacksum.size()); for (int v=0; v<(int)attacksum.size(); v++) { data.attacks[v] = attacksum[v]; data.average[v] = getWeightedAverage(notehist[v]); data.average[v] = getWeightedAverage(notehist[v]); data.minpitch[v] = getMinPitch(notehist[v]); data.maxpitch[v] = getMaxPitch(notehist[v]); data.q25[v] = get25thQuartile(notehist[v]); data.q75[v] = get75thQuartile(notehist[v]); data.sd[v] = getStandardDeviation(data.average[v], notehist[v]); } } ////////////////////////////// // // getStandardDeviation -- // double getStandardDeviation(double weightedaverage, vector& notes) { double topsum = 0; double botsum = 0; double temp; int i; for (i=0; i<(int)notes.size(); i++) { if (notes[i] == 0.0) { continue; } temp = i - weightedaverage; topsum += notes[i] * temp * temp; botsum += notes[i]; } if (botsum == 0.0) { return 0; } return sqrt(topsum/botsum); } ////////////////////////////// // // getWeightedAverage -- // double getWeightedAverage(vector& notes) { double topsum = 0.0; double botsum = 0.0; for (int i=0; i<(int)notes.size(); i++) { if (notes[i] <= 0.0) { continue; } topsum += notes[i] * i; botsum += notes[i]; } if (botsum <= 0.0) { return 0; } return topsum / botsum; } ////////////////////////////// // // getMinPitch -- // double getMinPitch(vector& notes) { for (int i=0; i<(int)notes.size(); i++) { if (fabs(notes[i]) > 100.0) { continue; } if (notes[i] > 0.0) { return i; } } return 0; } ////////////////////////////// // // getMaxPitch -- // double getMaxPitch(vector& notes) { for (int i=(int)notes.size()-1; i>=0; i--) { if (fabs(notes[i]) > 100.0) { continue; } if (notes[i] > 0.0) { return i; } } return 0; } ////////////////////////////// // // get25thQuartile -- // double get25thQuartile(vector& notes) { double sum = 0.0; int i; for (i=0; i<(int)notes.size(); i++) { sum += notes[i]; } double p; double tsum = 0.0; for (i=0; i<(int)notes.size(); i++) { if (notes[i] == 0.0) { continue; } tsum += notes[i]; p = tsum / sum; if (p >= 0.245) { return (double)i; } } return 0.0; } ////////////////////////////// // // get75thQuartile -- // double get75thQuartile(vector& notes) { double sum = 0.0; int i; for (i=0; i<(int)notes.size(); i++) { sum += notes[i]; } double p; double tsum = 0.0; for (i=(int)notes.size()-1; i>=0; i--) { if (notes[i] == 0.0) { continue; } tsum += notes[i]; p = tsum / sum; if (p >= 0.245) { return (double)i; } } return 0.0; } ////////////////////////////// // // addToHistogram2 -- // void addToHistogram2(vector >& notehist, vector& attacksum, int index, vector > >& histograms, vector >& attacks) { int j, k; for (j=0; j<(int)histograms[index].size(); j++) { attacksum[j] += attacks[index][j]; for (k=0; k<(int)histograms[index][j].size(); k++) { notehist[j][k] += histograms[index][j][k]; } } } ////////////////////////////// // // subtractFromHistogram2 -- // void subtractFromHistogram2(vector >& notehist, vector& attacksum, int index, vector > >& histograms, vector >& attacks) { int j, k; for (j=0; j<(int)histograms[index].size(); j++) { attacksum[j] -= attacks[index][j]; for (k=0; k<(int)histograms[index][j].size(); k++) { notehist[j][k] -= histograms[index][j][k]; } } } ////////////////////////////// // // printJsonData -- // void printJsonData(HumdrumFile& infile, vector >& data, vector window) { cout << "{" << endl; cout << "\t\"analysistype\"\t:\t\"avgpitch\",\n"; cout << "\t\"version\"\t:\t2.0,\n"; cout << "\t\"analysisdate\"\t:\t\""; struct tm *current; time_t now; time(&now); current = localtime(&now); int year = current->tm_year + 1900; int month = current->tm_mon + 1; int day = current->tm_mday; cout << year; if (month < 10) { cout << "0"; } cout << month; if (day < 10) { cout << "0"; } cout << day; cout << "\",\n"; cout << "\t\"filename\"\t:\t\"" << infile.getFilename() << "\",\n"; cout << "\t\"scorelength\"\t:\t" << infile[infile.getNumLines()-1].getAbsBeat() << ",\n"; cout << "\t\"hopsize\"\t:\t" << Hopsize << ",\n"; cout << "\t\"framecount\"\t:\t" << data.size() << ",\n"; cout << "\t\"windowsizes\"\t:\t["; for (int i=0; i<(int)window.size(); i++) { cout << window[i]; if (i < (int)window.size()-1) { cout << ", "; } } cout << "],\n"; // cout << "\t\"parts\"\t\t:\t" << (int)data[0][0].attacks.size() << ",\n"; cout << "\t\"partnames\"\t:\t["; printPartNames(infile); cout << "\"full score\""; cout << "],\n"; cout << "\t\"rangemin\"\t:\t["; for (int i=(int)Minpitch.size()-1; i>0; i--) { cout << Minpitch[i] << ", "; } cout << Minpitch[0]; cout << "],\n"; cout << "\t\"rangemax\"\t:\t["; for (int i=(int)Maxpitch.size()-1; i>0; i--) { cout << Maxpitch[i] << ", "; } cout << Maxpitch[0]; cout << "],\n"; cout << "\t\"framedata\"\t:\n"; cout << "\t\t[\n"; printDataContentJson(data); cout << "\t\t]\n"; cout << "}\n"; // end of data } ////////////////////////////// // // printPartNames -- // void printPartNames(HumdrumFile& infile) { int i, j; vector names; vector ktracks; infile.getTracksByExInterp(ktracks, "**kern"); names.resize(ktracks.size()); char buffer[1024] = {0}; for (i=0; i<(int)names.size(); i++) { sprintf(buffer, "part %ld", (int)names.size() - i); names[i] = buffer; } vector rkern; rkern.resize(infile.getMaxTracks()+1); rkern.setAll(-1); for (i=0; i<(int)ktracks.size(); i++) { rkern[ktracks[i]] = i; } int track; for (i=0; i=0; i--) { cout << "\"" << names[i] << "\", "; } } ////////////////////////////// // // printDataContentJson -- // void printDataContentJson(vector >& data) { int i; int frameindex = 0; for (i=0; i<(int)data.size(); i++) { cout << "\t\t{\n"; cout << "\t\t\t\"frameindex\"\t:\t" << frameindex++ << ",\n"; cout << "\t\t\t\"partdata\"\t:\n"; cout << "\t\t\t\t[\n"; printVoiceData(data, i, (int)data[0][0].attacks.size()); cout << "\t\t\t\t]\n"; cout << "\t\t}"; if (i < (int)data.size() - 1) { cout << ","; } cout << "\n"; } } ////////////////////////////// // // printVoiceData -- For a particular frame, print the voices from // highest part to lowest, then for the full score. // void printVoiceData(vector >& data, int index, int vcount) { int v; for (v=vcount-1; v>0; v--) { printVoicePartData(data, index, vcount, v, 1); } printVoicePartData(data, index, vcount, 0, 0); } ////////////////////////////// // // printVoicePartData -- For a particular frame, print the voices from // highest part to lowest, then for the full score. // void printVoicePartData(vector >& data, int index, int vcount, int v, int comma) { cout << "\t\t\t\t{\n"; cout << "\t\t\t\t\t\"partindex\"\t:\t" << v << ",\n"; cout << "\t\t\t\t\t\"startframe\"\t:\t["; printStartFrame(data, index); cout << "],\n"; cout << "\t\t\t\t\t\"endframe\"\t:\t["; printEndFrame(data, index); cout << "],\n"; cout << "\t\t\t\t\t\"pitchmin\"\t:\t["; printPitchMin(data, index, v); cout << "],\n"; cout << "\t\t\t\t\t\"pitchmax\"\t:\t["; printPitchMax(data, index, v); cout << "],\n"; cout << "\t\t\t\t\t\"pitch25q\"\t:\t["; printPitch25q(data, index, v); cout << "],\n"; cout << "\t\t\t\t\t\"pitch75q\"\t:\t["; printPitch75q(data, index, v); cout << "],\n"; cout << "\t\t\t\t\t\"pitchmean\"\t:\t["; printPitchMean(data, index, v); cout << "],\n"; cout << "\t\t\t\t\t\"pitchsd\"\t:\t["; printPitchSD(data, index, v); cout << "],\n"; cout << "\t\t\t\t\t\"attacks\"\t:\t["; printPitchAttacks(data, index, v); cout << "]\n"; cout << "\t\t\t\t}"; if (comma) { cout << ","; } cout << "\n"; } ////////////////////////////// // // printStartFrame -- // void printStartFrame(vector >& data, int index) { int wcount = (int)data[index].size(); for (int w=0; w >& data, int index) { int wcount = (int)data[index].size(); for (int w=0; w >& data, int index, int voice) { int wcount = (int)data[index].size(); int w; for (w=0; w >& data, int index, int voice) { int wcount = (int)data[index].size(); int w; for (w=0; w >& data, int index, int voice) { int wcount = (int)data[index].size(); int w; for (w=0; w >& data, int index, int voice) { int wcount = (int)data[index].size(); int w; for (w=0; w >& data, int index, int voice) { int wcount = (int)data[index].size(); int w; for (w=0; w >& data, int index, int voice) { int wcount = (int)data[index].size(); int w; double value; for (w=0; w 0.0)) { value = 0.0; } cout << int(value * 100.0 + 0.05) / 100.0; if (w < wcount - 1) { cout << ", "; } } } ////////////////////////////// // // printPitchAttacks -- // void printPitchAttacks(vector >& data, int index, int voice) { int wcount = (int)data[index].size(); int w; for (w=0; w numbers; pre.getTokens(numbers, "[^\\d.]+", opts.getString("window").c_str()); Window.resize(numbers.size()); for (int i=0; i<(int)numbers.size(); i++) { Window[i] = int(stod(numbers[i].getBase()) + 0.5); } jsonQ = opts.getBoolean("json"); optionfilename = opts.getString("filename"); } ////////////////////////////// // // example -- example usage of the quality program // void example(void) { cout << " \n" << endl; } ////////////////////////////// // // usage -- gives the usage statement for the meter program // void usage(const char* command) { cout << " \n" << endl; } // md5sum: e7a5037bc385e67c765fb76ee852cf7e avgpitch.cpp [20160305]