// // Programmer: Craig Stuart Sapp // Creation Date: Wed Nov 19 15:25:16 PST 2003 // Last Modified: Sat Mar 12 20:17:33 PST 2016 // Filename: ...sig/examples/all/lo5.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/lofcog.cpp // Syntax: C++; museinfo // // Description: Line of 5ths median (center of gravity) calculations. // // Reference: David Temperley, "The Cognition of Basic Musical Structures" // MIT Press; 2001, Chapter 5: Pitch Spelling and the // Tonal-Pitch-Class, pp. 114-136. // #include "humdrum.h" #include "CircularBuffer.h" #include "PlotFigure.h" #include #include using namespace std; // function declarations void checkOptions (Options& opts, int argc, char* argv[]); void example (void); void usage (const char* command); void analyzeFile (HumdrumFile& infile, vector& fifthmean); void printAnalysis (HumdrumFile& infile, vector& fifthmeean); int lo5ToBase40 (double lineval); int base40ToLo5 (int base40); double getFifthMean (HumdrumFile& infile, int line, double beats, CircularBuffer& notes, CircularBuffer& absbeat, double& meansum, int measure); void printXfig (HumdrumFile& infile, vector& fifthmean); int chooseLineNumber (int base12, double average); void convertBase12ToBase40(int base12, int& x, int& y, int& z); void getDeviation (HumdrumFile& infile, vector& cog, vector >& deviation); double getAverage (HumdrumFile& infile, vector >& pastdist, double abeat, int index); // global variables Options options; // database for command-line arguments int debugQ = 0; // used with --debug option int errorQ = 0; // used with -e option int appendQ = 0; // used with -a option double beats = 8.0; // used with -b option int basemode = 0; // used with -m option int pitchQ = 0; // used with -p option int xfigQ = 0; // used with -x option int startbranch= 0; // used with -f and -s options int keysig = 0; // used with -k option int keysigQ = 0; // used with -k option int deviationQ = 0; // used with -d option int averageQ = 0; // used with -g option double avgbeat = 4.0; // used with -g option const char* CurrentFile = "."; /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { HumdrumFile infile; vector fifthmean; // process the command-line options checkOptions(options, argc, argv); // build up the chord table from the input file(s): int i; for (i=1; i<=options.getArgCount() || options.getArgCount()==0; i++) { infile.clear(); // if no command-line arguments read data file from standard input if (options.getArgCount() < 1) { infile.read(cin); } else { infile.read(options.getArg(i).data()); CurrentFile = options.getArg(i).data(); if (strrchr(options.getArg(i).data(), '/') != NULL) { CurrentFile = strrchr(options.getArg(i).data(), '/') + 1; } } infile.analyzeRhythm(); analyzeFile(infile, fifthmean); if (xfigQ) { printXfig(infile, fifthmean); } else { printAnalysis(infile, fifthmean); } } return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // printXfig -- print data in xfig format // void printXfig(HumdrumFile& infile, vector& fifthmean) { int i; double xmin = 10000; double xmax = 0; double ymin = 100; double ymax = -100; PlotData plotdata; PlotFigure plot; plot.allocatePoints(infile.getNumLines()); for (i=0; i ymax) { ymax = fifthmean[i]; } if (infile[i].getAbsBeat() < xmin) { xmin = infile[i].getAbsBeat(); } if (infile[i].getAbsBeat() > xmax) { xmax = infile[i].getAbsBeat(); } } } plot.setXRangeAutoOff(); plot.setYRangeAutoOff(); // plot.setXMin(xmin); // plot.setXMax(xmax); plot.setXMin(0.0); plot.setXMax(infile[infile.getNumLines()-1].getAbsBeat()); plot.setYMin(ymin-0.5); plot.setYMax(ymax+0.5); int mnum; int count; char buffer[1024] = {0}; for (i=0; i 0) && (mnum % 10 == 0)) { plot.addText(buffer, infile[i].getAbsBeat(), ymin-0.75, 14, 0, 0); } } } } plot.addPlot(plotdata); plot.yTicksOn(); plot.setYTicksDelta(1.0); plot.printXfig(cout); } ////////////////////////////// // // analyzeFile -- analyze the line of fifth mean for each line in // the file. // void analyzeFile(HumdrumFile& infile, vector& fifthmean) { int i; int measure = 0; int linecount = infile.getNumLines(); fifthmean.resize(linecount); fill(fifthmean.begin(), fifthmean.end(), 0); CircularBuffer notes(10000); CircularBuffer absbeat(10000); CircularBuffer meanline(10000); notes.reset(); absbeat.reset(); meanline.reset(); double meansum = 0.0; int length, ii, jj; for (i=1; i 3) { startbranch = 1; } else if (keysig < -3) { startbranch = -1; } else { startbranch = 0; } } } } fifthmean[i] = fifthmean[i-1]; break; case E_humrec_data_kern_measure: sscanf(infile[i][0], "=%d", &measure); fifthmean[i] = fifthmean[i-1]; break; case E_humrec_data: fifthmean[i] = getFifthMean(infile, i, beats, notes, absbeat, meansum, measure); break; } } } ////////////////////////////// // // getFifthMean -- process the notes in a line. // double getFifthMean(HumdrumFile& infile, int line, double beats, CircularBuffer& notes, CircularBuffer& absbeat, double& meansum, int measure) { char buffer[1024] = {0}; int tnote; int base12; double bias; // double tbeat; int base40; int linenum; int tlinenum; int i, j; for (i=0; i 0) { linenum = chooseLineNumber(base12, bias+meansum/notes.getCount()); } else { linenum = chooseLineNumber(base12, bias); } tlinenum = base40ToLo5(base40); if (errorQ && (tlinenum != linenum)) { cout << "!! naming error"; if (measure > 0) { cout << " in bar=" << measure << ", beat=" << infile[line].getBeat(); } else { cout << " on original line=" << line; } cout << ", spine=" << i+1; if (infile[line].getTokenCount(i) > 1) { cout << " token=" << j+1; } cout << " (" << Convert::base40ToKern(buffer, lo5ToBase40(tlinenum)+4*40) << " is changed to "; cout << Convert::base40ToKern(buffer, lo5ToBase40(linenum)+4*40) << ")"; cout << " [" << tlinenum << " to " << linenum << "]"; cout << endl; } } notes.insert(linenum); absbeat.insert(infile[line].getAbsBeat()); meansum += linenum; // remove any old notes which are outside of the analysis window while ((notes.getCount() > 0) && (infile[line].getAbsBeat() - absbeat[absbeat.getCount()-1] > beats)) { tnote = notes.extract(); absbeat.extract(); meansum -= tnote; } } } if (notes.getCount() > 0) { return meansum/notes.getCount(); } else { return -1000; } } ////////////////////////////// // // chooseLineNumber -- Choose the pitch spelling which is closest to // the average pitch value. // int chooseLineNumber(int base12, double average) { int x, y, z; convertBase12ToBase40(base12, x, y, z); x = base40ToLo5(x); y = base40ToLo5(y); z = base40ToLo5(z); double diffx = fabs(average-x); double diffy = fabs(average-y); double diffz = fabs(average-z); if ((diffx < diffy) && (diffx < diffz)) { return x; } if (diffy < diffz) { return y; } return z; } /////////////////////////////// // // convertBase12ToBase40 -- give a list of the void convertBase12ToBase40(int base12, int& x, int& y, int& z) { switch (base12) { case 0: x=38; y=2; z=6; break; // B#/C/D-- case 1: x=39; y=3; z=7; break; // B##/C#/D- case 2: x= 4; y=8; z=12; break; // C##/D/E-- case 3: x= 9; y=13; z=17; break; // D#/E-/F-- case 4: x=10; y=14; z=18; break; // D##/E/F- case 5: x=15; y=19; z=23; break; // E#/F/G-- case 6: x=16; y=20; z=24; break; // E##/F#/G- case 7: x=21; y=25; z=29; break; // F##/G/A-- case 8: x=26; y=30; z=-1000; break; // G#/A- case 9: x=27; y=31; z=35; break; // G##/A/B-- case 10: x=32; y=36; z=0; break; // A#/B-/C-- case 11: x=33; y=37; z=1; break; // A##/B/C- default: x=-1000; y=-1000; z=-1000; break; // unknown } } ////////////////////////////// // // getDeviation -- calculate the deviation of notes from the // fifth mean. // void getDeviation(HumdrumFile& infile, vector& cog, vector >& deviation) { vector > notes; deviation.resize(infile.getNumLines()); notes.resize(infile.getNumLines()); int i, j, k; for (i=0; i<(int)deviation.size(); i++) { deviation[i].reserve(32); deviation[i].resize(0); notes[i].reserve(32); notes[i].resize(0); } char buffer[1024] = {0}; int tokencount; int base40; for (i=0; i& fifthmean) { int i; int linecount = infile.getNumLines(); char buffer[1024] = {0}; int printbeat = 0; int ii, kk; vector > deviation; if (deviationQ) { getDeviation(infile, fifthmean, deviation); } double tempval; for (i=0; i 19) { // can't go higher than B## return -1000; } if (lv >= 0) { return ((lv * 23 + 2) + 4000) % 40; } else { return ((-(lv-1) * 17 + 2) + 4000) % 40; } } /////////////////////////////// // // base40ToLo5 -- convert base40 to line of fifths // int base40ToLo5(int base40) { base40 = (base40 + 4000) % 40; // remove any octave information; switch (base40) { case 0: return -14; // C-- case 1: return -7; // C- case 2: return 0; // C case 3: return 7; // C# case 4: return 14; // C## case 5: return -1000; // unknown case 6: return -12; // D-- case 7: return -5; // D- case 8: return 2; // D case 9: return 9; // D# case 10: return 16; // D## case 11: return -1000; // unknown case 12: return -10; // E-- case 13: return -3; // E- case 14: return 4; // E case 15: return 11; // E# case 16: return 18; // E## case 17: return -15; // F-- case 18: return -8; // F- case 19: return -1; // F case 20: return 6; // F# case 21: return 13; // F## case 22: return -1000; // unknown case 23: return -13; // G-- case 24: return -6; // G- case 25: return 1; // G case 26: return 8; // G# case 27: return 15; // G## case 28: return -1000; // unknown case 29: return -11; // A-- case 30: return -4; // A- case 31: return 3; // A case 32: return 10; // A# case 33: return 17; // A## case 34: return -1000; // unknown case 35: return -9; // B-- case 36: return -2; // B- case 37: return 5; // B case 38: return 12; // B# case 39: return 19; // B## } return -1000; // unknown or rest } ////////////////////////////// // // getAverage -- // double getAverage(HumdrumFile& infile, vector >& data, double abeat, int index) { int i, j; int count = 0; double sum = 0.0; double startdur = infile[index].getAbsBeat(); for (i=index; i>=0; i--) { if (startdur - infile[i].getAbsBeat() > abeat) { break; } for (j=0; j<(int)data[i].size(); j++) { if (data[i][j] == -1.0) { sum += infile[i].getAbsBeat(); } else { sum += data[i][j]; } count++; } } if (count > 0) { return sum/count; } return 0; } // md5sum: eb5a1888c40059aec1ab5168d18865bd lofcog.cpp [20160320]