// // Programmer: Craig Stuart Sapp // Creation Date: Mon Mar 23 12:48:50 PST 2009 // Last Modified: Tue Mar 24 18:57:09 PST 2009 // Filename: ...sig/examples/all/noteheadtime.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/noteheadtime.cpp // Syntax: C++; museinfo // // Description: print performance data as noteheads outputting SCORE data. // #include #include "humdrum.h" #include "ScorePage.h" #ifndef OLDCPP #include #else #include #endif // function declarations: void checkOptions (Options& opts, int argc, char** argv); void example (void); void usage (const char* command); void processInput (ScorePage& scorepage, HumdrumFile& infile, SigCollection& marksfiles); void printPage (ScorePage& scorepage, int page, double maxtime, double mintime, Array& times, Array& dyns, Array& pitches, Array& pstaff, Array& measures, SigCollection& marks); void printSystem (ScorePage& scorepage, int basestaff, Array& times, Array& dyns, Array& pitches, Array& pstaff, Array& measures, double minsystime, double maxsystime, int finalBarlineQ); void normalizeDyns (Array& dyns, double maxdyn, double mindyn); void getPitchInfo (const char* string, int& p1, int& p2); void getBetterPitchInfo (const char* string, int& p1, int& p2, int auxdata); double printTicks (ScorePage& scorepage, int staff, int vpos, int direction, double starttime, double stoptime, double leftmar, double rightmar, double majdur, int secticks, int terticks, double majticklen, double secticklen, double terticklen); void drawtick (ScorePage& scorepage, int staff, double hpos, double vpos, int direction, double len); void printMarks (ScorePage& scorepage, int basestaff, Array& marktimes, Array& markmeter, double starttime, double stoptime, double leftmar, double rightmar, double markcolor, double markdashsize, double markspacesize, double markstyle, double markthick); void printPreMarks (ScorePage& scorepage, int basestaff, Array& marktimes, Array& markmeter, double starttime, double stoptime, double leftmar, double rightmar, double markcolor, double markdashsize, double markspacesize, double markstyle, double markthick); void getMarkInfo (HumdrumFile& marks, Array& marktimes, Array& markmeter); void printTitle (ScorePage& scorepage, int basestaff, HumdrumFile& infile); void printFooter (ScorePage& scorepage, int basestaff, HumdrumFile& infile, double footerscale); void printComment (ScorePage& scorepage, int basestaff, const char* string, double hpos, double scale); // User interface variables: Options options; int verboseQ = 0; double timewidth = 5.0; int systemsperpage = 6; int page = 1; int pagecountQ = 0; double staffscale = 0.75; double leftmargin = 10; double rightmargin = 195; double quietnote = 0.9; double loudnote = 0.0; int scorever = 6; // 6 = winscore and P26 used for color const char* ofile = ""; // timeline variables int tickDisplayQ = 1; double majortickdur = 1.0; int secondaryticks = 2; int tertiaryticks = 10; double majorticklen = 1.5; double secticklen = 1.0; double terticklen = 0.5; #define STYLE_LEFT 1 int ticklabelstyle = STYLE_LEFT; int printstartmeasureQ = 1; // additional information for correct pitch spelling int auxQ = 0; const char* auxfile = ""; int accidentalQ = 0; // used with --accidental option // marks options const char* marksfile = ""; double markdashsize = 0.0; double markspacesize = 0.0; //double markstyle = 7.0; // dashed line double markstyle = 0.0; // solid line int marksonlyQ = 0; double markcolor = 997777; // red by default double markthick = 7.0; // secondary marks options const char* marksfile2 = ""; double markdashsize2 = 0.0; double markspacesize2 = 0.0; //double markstyle2 = 7.0; // dashed line double markstyle2 = 0.0; // solid line double markcolor2 = 777799; // blue by default double markthick2 = 0.0; // tertiary marks options const char* marksfile3 = ""; double markdashsize3 = 0.0; double markspacesize3 = 0.0; double markstyle3 = 7.0; // dashed line double markcolor3 = 779977; // green by default double markthick3 = 4.5; // title printing on first page, and footers for rest of pages int titleQ = 1; int footerQ = 1; // display of timing numbers int notenumbersQ = 1; double notenumbercolor = -1; double notenumbersize = 1.0; // comment string for bottom right of page const char* commentstring = ""; // used with --comment double commenthpos = 180.0; // used with --cp ////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { // process the command-line options checkOptions(options, argc, argv); HumdrumFile infile; if (options.getArgCount() < 1) { infile.read(cin); } else { infile.read(options.getArg(1)); } SigCollection marks; marks.setSize(1); if (strcmp(marksfile, "") != 0) { marks[0].read(marksfile); } if (strcmp(marksfile2, "") != 0) { marks.setSize(2); marks[1].read(marksfile2); } if (strcmp(marksfile3, "") != 0) { marks.setSize(3); marks[2].read(marksfile3); } ScorePage scorepage; processInput(scorepage, infile, marks); if (options.getBoolean("output")) { scorepage.writeBinary(ofile); } else { scorepage.printAscii(cout); } return 0; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // processInput -- // void processInput(ScorePage& scorepage, HumdrumFile& infile, SigCollection& marks) { Array times; times.setSize(infile.getNumLines()); times.setSize(0); Array dyns; dyns.setSize(infile.getNumLines()); dyns.setSize(0); Array pitches; pitches.setSize(infile.getNumLines()); pitches.setSize(0); Array pstaff; pstaff.setSize(infile.getNumLines()); pstaff.setSize(0); Array measures; measures.setSize(infile.getNumLines()); measures.setSize(0); double mintime = 10000000; double maxtime = -10000000; double mindyn = 10000000; double maxdyn = -10000000; int i; double value; int intvalue; int p1, p2; Array auxacci; auxacci.setSize(0); int pindex = 0; if (auxQ) { HumdrumFile auxhum; auxhum.read(auxfile); auxacci.setSize(auxhum.getNumLines()); auxacci.setSize(0); for (i=0; i maxtime) { maxtime = value; } times.append(value); value = 0; sscanf(infile[i][1], "%lf", &value); if (value < mindyn) { mindyn = value; } if (value > maxdyn) { maxdyn = value; } dyns.append(value); value = 0; if (auxQ) { getBetterPitchInfo(infile[i][2], p1, p2, auxacci[pindex++]); } else { getPitchInfo(infile[i][2], p1, p2); } pitches.append(p1); pstaff.append(p2); sscanf(infile[i][4], "%d", &intvalue); measures.append(intvalue); break; case E_humrec_none: case E_humrec_empty: case E_humrec_global_comment: case E_humrec_bibliography: case E_humrec_data_comment: case E_humrec_data_kern_measure: case E_humrec_interpretation: default: break; } } if (auxQ && (times.getSize() != auxacci.getSize())) { // something went wrong: number of notes in primary // and auxiliary file do not match, so throw away // any auxiliary data since it will be invalid. auxQ = 0; auxacci.setSize(0); cerr << "ERROR IN AUXILIARY FILE. GIVING UP" << endl; exit(1); } times.allowGrowth(0); dyns.allowGrowth(0); normalizeDyns(dyns, maxdyn, mindyn); int systems = int((maxtime - mintime) / timewidth + 0.99999); int pages = int((double)systems / systemsperpage + 0.99999); if (systems > 1000) { // reduce page count if time is in milliseconds maxtime = maxtime / 1000.0; mintime = mintime / 1000.0; for (i=0; i 1) && footerQ) { double footerscale = 0.75; if (page == pages) { if (systems % systemsperpage != 0) { // have to shrink footer if there is no // staff present at the bottom of the page: footerscale *= staffscale; } } printFooter(scorepage, 1, infile, footerscale); } if (strcmp(commentstring, "") != 0) { double footerscale = 0.75; if (page == pages) { if (systems % systemsperpage != 0) { // have to shrink footer if there is no // staff present at the bottom of the page: footerscale *= staffscale; } } printComment(scorepage, 1, commentstring, commenthpos, footerscale); } printPage(scorepage, page, maxtime, mintime, times, dyns, pitches, pstaff, measures, marks); } } ////////////////////////////// // // printComment -- // void printComment(ScorePage& scorepage, int basestaff, const char* comment, double hpos, double scale) { ScoreRecord srecord; srecord.setPValue(1, P1_Text); srecord.setPValue(2, basestaff); srecord.setPValue(3, hpos); srecord.setPValue(4, -8.0); srecord.setPValue(6, scale); srecord.setTextData(comment); srecord.setTextFont("_00"); scorepage.addItem(srecord); } ////////////////////////////// // // printFooter -- // void printFooter(ScorePage& scorepage, int basestaff, HumdrumFile& infile, double footerscale) { char buffer[1024] = {0}; char buffer2[1024] = {0}; ScoreRecord srecord; int i; for (i=0; i& dyns, double maxdyn, double mindyn) { int i; double diff = maxdyn - mindyn; for (i=0; i& times, Array& dyns, Array& pitches, Array& pstaff, Array& measures, SigCollection& marks) { int systems = int((maxtime - mintime) / timewidth + 0.99999); int pages = int((double)systems / systemsperpage + 0.99999); if (page > pages || page < 0) { exit(0); } if (page < pages) { systems = systemsperpage; } else { systems = systems - systemsperpage * (pages-1); } int i; Array > marktimes; Array > markmeter; int maxmarks = 10; marktimes.setSize(maxmarks); marktimes.allowGrowth(0); markmeter.setSize(maxmarks); markmeter.allowGrowth(0); for (i=0; i 0)) { minsystime = mintime + systembase * timewidth; maxsystime = minsystime + timewidth; // print any marks before the start of the music if (marktimes.getSize() > 0) { if (marktimes[0].getSize() > 0) { printPreMarks(scorepage, systemsperpage * 2 - 1, marktimes[0], markmeter[0], minsystime, maxsystime, leftmargin, rightmargin, markcolor, markdashsize, markspacesize, markstyle, markthick); } } if (marktimes.getSize() > 1) { if (marktimes[1].getSize() > 0) { printPreMarks(scorepage, systemsperpage * 2 - 1, marktimes[1], markmeter[1], minsystime, maxsystime, leftmargin, rightmargin, markcolor2, markdashsize2, markspacesize2, markstyle2, markthick2); } } if (marktimes.getSize() > 2) { if (marktimes[2].getSize() > 0) { printPreMarks(scorepage, systemsperpage * 2 - 1, marktimes[2], markmeter[2], minsystime, maxsystime, leftmargin, rightmargin, markcolor3, markdashsize3, markspacesize3, markstyle3, markthick3); } } } int lastsystem = 0; for (i=0; i 0) { if (marktimes[0].getSize() > 0) { printMarks(scorepage, basestaff, marktimes[0], markmeter[0], minsystime, maxsystime, leftmargin, rightmargin, markcolor, markdashsize, markspacesize, markstyle, markthick); } } if (marktimes.getSize() > 1) { if (marktimes[1].getSize() > 0) { printMarks(scorepage, basestaff, marktimes[1], markmeter[1], minsystime, maxsystime, leftmargin, rightmargin, markcolor2, markdashsize2, markspacesize2, markstyle2, markthick2); } } if (marktimes.getSize() > 2) { if (marktimes[2].getSize() > 0) { printMarks(scorepage, basestaff, marktimes[2], markmeter[2], minsystime, maxsystime, leftmargin, rightmargin, markcolor3, markdashsize3, markspacesize3, markstyle3, markthick3); } } if (!marksonlyQ) { printSystem(scorepage, basestaff, times, dyns, pitches, pstaff, measures, minsystime, maxsystime, lastsystem); } } } ////////////////////////////// // // getMarkInfo -- // void getMarkInfo(HumdrumFile& marks, Array& marktimes, Array& markmeter) { if (marks.getNumLines() <= 0) { marktimes.setSize(0); markmeter.setSize(0); return; } marktimes.setSize(marks.getNumLines()); marktimes.setSize(0); markmeter.setSize(marks.getNumLines()); markmeter.setSize(0); double value; int intval, intval2; int i; for (i=0; i 1) { intval = 0; if (strchr(marks[i][1], ':') != NULL) { sscanf(marks[i][1], "%d:%d", &intval2, &intval); } else if (strchr(marks[i][1], '.') != NULL) { sscanf(marks[i][1], "%d.%d", &intval2, &intval); } markmeter.append(intval); } else { intval = 0; markmeter.append(intval); } } } } ////////////////////////////// // // printPreMarks -- // void printPreMarks(ScorePage& scorepage, int basestaff, Array& marktimes, Array& markmeter, double starttime, double stoptime, double leftmar, double rightmar, double markcolor, double markdashsize, double markspacesize, double markstyle, double markthick) { double sysdur = stoptime - starttime; double hpos; ScoreRecord srecord; int i; for (i=0; i& marktimes, Array& markmeter, double starttime, double stoptime, double leftmar, double rightmar, double markcolor, double markdashsize, double markspacesize, double markstyle, double markthick) { double sysdur = stoptime - starttime; double hpos; ScoreRecord srecord; int i; for (i=0; i= stoptime) { break; } if (marktimes[i] < starttime) { continue; } hpos = (marktimes[i] - starttime) / sysdur; hpos = hpos * (rightmar - leftmar) + leftmar; if (hpos < 0.0) { continue; } srecord.clear(); srecord.setPValue(1, P1_Barline); srecord.setPValue(2, basestaff); srecord.setPValue(3, hpos); srecord.setPValue(4, 2.0); srecord.setPValue(5, markstyle); if (markmeter[i] == 1) { srecord.setPValue(6, markthick); // make first beat of measure thicker } if (markstyle == 7.0) { srecord.setPValue(8, markdashsize); srecord.setPValue(9, markspacesize); } srecord.setPValue(10, 11.0); srecord.setPValue(11, 3.0); srecord.setPValue(26, markcolor); scorepage.addItem(srecord); } } ////////////////////////////// // // printTicks -- // double printTicks(ScorePage& scorepage, int staff, int vpos, int direction, double starttime, double stoptime, double leftmar, double rightmar, double majdur, int secticks, int terticks, double majticklen, double secticklen, double terticklen) { double sysdur = stoptime - starttime; Array secticktimes; secticktimes.setSize(secticks*2); secticktimes.setSize(0); int i, j; int secfound; double sectime, tertime; // print major ticks: double temp = starttime/majdur; double startmajor; if (temp - int(temp) == 0.0) { startmajor = starttime; } else { startmajor = int(temp+1.0) * majdur; } double output = startmajor; // draw secondary ticks which occur befor the first major tick double hpos = 0; secticktimes.setSize(0); for (i=1; i= starttime) { secticktimes.append(sectime); hpos = (sectime - starttime) / sysdur; hpos = hpos * (rightmar - leftmar) + leftmar; drawtick(scorepage, staff, hpos, vpos, direction, secticklen); } } for (i=1; i= starttime)) { hpos = (tertime - starttime) / sysdur; hpos = hpos * (rightmar - leftmar) + leftmar; drawtick(scorepage, staff, hpos, vpos, direction, terticklen); } } // draw major ticks, secondary ticks and tertiary ticks starting // at the first major tick while (startmajor <= stoptime) { hpos = (startmajor - starttime) / sysdur; hpos = hpos * (rightmar - leftmar) + leftmar; drawtick(scorepage, staff, hpos, vpos, direction, majticklen); secticktimes.setSize(0); for (i=1; i& times, Array& dyns, Array& pitches, Array& pstaff, Array& measures, double minsystime, double maxsystime, int finalbarlineQ) { if (verboseQ) { cout << "@minsystime:\t" << minsystime << endl; cout << "@maxsystime:\t" << maxsystime << endl; cout << "@basestaff:\t" << basestaff << endl; } ScoreRecord srecord; double startmajortick = -1000; if (tickDisplayQ) { startmajortick = printTicks(scorepage, basestaff, 3, -1, minsystime, maxsystime, leftmargin, rightmargin, majortickdur, secondaryticks, tertiaryticks, majorticklen, secticklen, terticklen); // print above treble staff printTicks(scorepage, basestaff+1, 11, 1, minsystime, maxsystime, leftmargin, rightmargin, majortickdur, secondaryticks, tertiaryticks, majorticklen, secticklen, terticklen); if (ticklabelstyle == STYLE_LEFT) { srecord.setPValue(1, P1_Number); srecord.setPValue(2, basestaff); srecord.setPValue(3, 0.0); srecord.setPValue(4, -0.5); srecord.setPValue(5, startmajortick); srecord.setPValue(6, 0.5); scorepage.addItem(srecord); srecord.clear(); } } srecord.setPValue(1, P1_Staff); srecord.setPValue(2, basestaff); if (staffscale != 1.0) { srecord.setPValue(5, staffscale); } scorepage.appendItem(srecord); srecord.setPValue(2, basestaff+1); scorepage.appendItem(srecord); srecord.clear(); srecord.setPValue(1, P1_Clef); srecord.setPValue(2, basestaff+1); srecord.setPValue(3, 1.5); scorepage.appendItem(srecord); srecord.setPValue(2, basestaff); srecord.setPValue(5, 1); // bass cleff scorepage.appendItem(srecord); srecord.clear(); // add basic measure lines srecord.setPValue(1, P1_Barline); srecord.setPValue(2, basestaff); srecord.setPValue(4, 2.0); srecord.setPValue(3, 0.0); scorepage.appendItem(srecord); // barline at start of system srecord.setPValue(5, 8.0); scorepage.appendItem(srecord); // curly-brace at start of system srecord.setPValue(3, 200.0); if (finalbarlineQ) { srecord.setPValue(5, 2.0); // add final barline } else { srecord.setPValue(5, 0.0); } scorepage.appendItem(srecord); // right-side barline srecord.clear(); // notes have to be placed on page so that those // with more ledger lines are printed before those // with fewer ledger lines; otherwise, the ledger lines // of the ones with more will over write those with less. SigCollection > notelayers; notelayers.setSize(50); int i; for (i=0; i maxsystime) { continue; } if (foundfirstnote == 0) { // place the measure number of the first note on the system // on the top-left side of the system: srecord.clear(); srecord.setPValue(1, P1_Number); srecord.setPValue(2, basestaff+1); srecord.setPValue(3, 0.0); srecord.setPValue(4, 15.0); srecord.setPValue(5, measures[i]); srecord.setPValue(7, 1.0); scorepage.addItem(srecord); foundfirstnote = 1; } // cout << times[i] << "\t" << dyns[i] // << "\t" << pitches[i] // << "\t" << pstaff[i] << endl; srecord.clear(); srecord.setPValue(1, P1_Note); P2 = basestaff + pstaff[i]; srecord.setPValue(2, P2); P3 = (times[i] - minsystime) / (maxsystime - minsystime); P3 = (rightmargin - leftmargin) * P3 + leftmargin; srecord.setPValue(3, P3); P4 = Convert::base40ToScoreVPos(pitches[i], !pstaff[i]); srecord.setPValue(4, P4); srecord.setPValue(25, 100 - fabs(P4)); // drawing layers: // outer-most notes first // have to use the following system because P25 system // does not work very well: layer = abs(int(P4+0.999)); if (layer >= 50) { layer = 49; } if (accidentalQ) { int accval = Convert::base40ToAccidental(pitches[i]); switch (accval) { case 0: break; // natural: no accidental case 1: // sharp srecord.setPValue(5, 2); // sharp break; case -1: // flat srecord.setPValue(5, 1); // flat break; case 2: // double-sharp srecord.setPValue(5, 5); // double-sharp break; case -2: // double-flat srecord.setPValue(5, 4); // double-flat break; } if (accval != 0) { srecord.setPValue(29, 0.5); // make accidental 50% than normal } } if (scorever >= 6) { // WinScore version P26 = dyns[i] * (quietnote - loudnote) + loudnote; P26 = int(P26 * 100.0 + 0.5)/100.0; P26 = 1.0 - P26; if (P26 < 0.0) { P26 = 0.0; } if (P26 > 1.0) { P26 = 1.0; } srecord.setPValue(26, P26); } else { // for SCORE 5 (color) P32 = int(100.0 * dyns[i] * (quietnote - loudnote) + loudnote + 0.5); P32 = 100.0 - P32; if (P32 > 99) { P32 = 99; } if (P32 < 1) { P32 = 1; } P32 = P32/100.0; srecord.setPValue(32, P32); } notelayers[layer].append(srecord); // store note for later adding to page if (notenumbersQ) { int notenumber = int(times[i] * 1000.0 + 0.5) % 100; srecord.clear(); srecord.setPValue(1, P1_Text); srecord.setPValue(2, P2); srecord.setPValue(3, P3); if ((int(P4)+100) % 2 == 0) { // note occurs on a space srecord.setPValue(4, P4+0.25-1); // place on space after note } else { // note occurs on a line srecord.setPValue(4, P4+0.25); // place on space above note } char tempbuff[128] = {0}; if (notenumber < 10) { sprintf(tempbuff, "0%d", notenumber); } else { sprintf(tempbuff, "%d", notenumber); } srecord.setTextData(tempbuff); srecord.setTextFont("_08"); // Helvetica-Narrow srecord.setPValue(6, 0.5 * notenumbersize); srecord.setPValue(11, 2.2); // horizontal offset srecord.setPValue(5, 0.9); // squeeze digits together if (notenumbercolor < 0) { double color = P26; // match color of notehead with a minimum if (color > 0.75) { color = 0.75; } srecord.setPValue(16, color); } else { srecord.setPValue(16, notenumbercolor); } srecord.setPValue(14, -1); // needed for colored text // consider adding a printing layer here (P15 for text) notelayers[layer].append(srecord); // store timing number } } for (i=notelayers.getSize()-1; i>=0; i--) { scorepage.addItem(notelayers[i]); // add notes to page in proper ordering } } ////////////////////////////// // // checkOptions -- // void checkOptions(Options& opts, int argc, char* argv[]) { opts.define("c|count|page-count=b","list the number of pages input data produces"); opts.define("comment=s", "comment string for bottom right of page"); opts.define("cp|comment-position=d:180.0", "horz. position of comment"); opts.define("page=i:1", "output data for specified page"); opts.define("s|scale|staff-scale=d:0.75", "staff size factor"); opts.define("l|left|left-margin=d:10.0", "left margin for data"); opts.define("r|right|right-margin=d:195.0", "right margin for data"); opts.define("v|verbose=b", "verbose information for debugging"); opts.define("p|piano=d:0.9", "grayscale for soft notes"); opts.define("f|forte=d:0.0", "grayscale for soft notes"); opts.define("o|output=s", "output file name for binary data"); opts.define("m|marks=s", "auxiliary vertical line data"); opts.define("2|m2|marks2=s", "auxiliary vertical lines (blue color)"); opts.define("3|m3|marks3=s", "auxiliary vertical lines (green color)"); opts.define("markcolor=d:997777", "color for marks"); opts.define("nns|note-number-size=d:1.0", "size of numbers for notes"); opts.define("nnc|note-number-color=d:-1.0", "note number color"); opts.define("aux=s", "auxiliary notes files with accidental info"); opts.define("accidental|acci=b", "display accidental marks"); opts.define("author=b", "author of program"); opts.define("version=b", "compilation info"); opts.define("example=b", "example usages"); opts.define("help=b", "short description"); opts.process(argc, argv); // handle basic options: if (opts.getBoolean("author")) { cout << "Written by Craig Stuart Sapp, " << "craig@ccrma.stanford.edu, Mar 2009" << endl; exit(0); } else if (opts.getBoolean("version")) { cout << argv[0] << ", version: 9 Mar 2009" << endl; cout << "compiled: " << __DATE__ << endl; cout << MUSEINFO_VERSION << endl; exit(0); } else if (opts.getBoolean("help")) { usage(opts.getCommand()); exit(0); } else if (opts.getBoolean("example")) { example(); exit(0); } verboseQ = opts.getBoolean("verbose"); pagecountQ = opts.getBoolean("page-count"); page = opts.getInteger("page"); staffscale = opts.getDouble("staff-scale"); leftmargin = opts.getDouble("left-margin"); rightmargin = opts.getDouble("right-margin"); quietnote = opts.getDouble("piano"); loudnote = opts.getDouble("forte"); if (opts.getBoolean("output")) { ofile = opts.getString("output"); } marksfile = opts.getString("marks"); marksfile2 = opts.getString("marks2"); marksfile3 = opts.getString("marks3"); markcolor = opts.getDouble("markcolor"); auxfile = opts.getString("aux"); auxQ = opts.getBoolean("aux"); accidentalQ = opts.getBoolean("accidental"); notenumbersize = opts.getDouble("note-number-size"); notenumbercolor = opts.getDouble("note-number-color"); commentstring = opts.getString("comment"); commenthpos = opts.getDouble("comment-position"); } ////////////////////////////// // // example -- // void example(void) { } ////////////////////////////// // // usage -- // void usage(const char* command) { } // md5sum: 8e3d8aef0fdfcb6f60bcb07ab0711440 noteheadtime.cpp [20090626]