// // Programmer: Craig Stuart Sapp // Creation Date: Sat Oct 11 02:27:50 PDT 2008 // Last Modified: Wed Oct 15 08:45:23 PDT 2008 adding tuplet rhythm parsing // Last Modified: Fri Oct 17 19:00:46 PDT 2008 added layers, invisible, caut. // Last Modified: Sun Oct 26 04:58:48 PST 2008 added misc features // Last Modified: Wed Dec 3 20:19:29 PST 2008 end of data format fix // Last Modified: Wed Jun 24 15:37:21 PDT 2009 updated for GCC 4.4 // Last Modified: Mon Jul 20 16:32:01 PDT 2009 added 8ba treble clef // Last Modified: Mon Jul 20 16:32:01 PDT 2009 added M:none time signature // Last Modified: Wed Jul 22 13:03:44 PDT 2009 added editorial slur dashing // Last Modified: Mon Aug 24 14:00:34 PDT 2009 more work on 8ba treble clef // Last Modified: Mon Sep 21 17:13:53 PDT 2009 added --no-tempo option // Last Modified: Mon Sep 21 17:29:50 PDT 2009 accidental spelling by octave // Last Modified: Sun Jan 16 09:56:09 PST 2011 adj. off-by-1 in bar num update // Last Modified: Sun Jan 16 11:25:19 PST 2011 added marks and --no-marks // Last Modified: Sun Jan 16 16:31:23 PST 2011 added --no-slur option // Last Modified: Sat Oct 6 07:45:11 PDT 2012 added --cb and --db options // Last Modified: Sun Oct 21 13:26:51 PDT 2012 added --key for keysig modality // Last Modified: Fri Dec 14 00:39:32 PST 2012 made compiler OS X happy // Filename: ...sig/examples/all/hum2abc.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/hum2abc.cpp // Syntax: C++; museinfo // // Description: Converts Humdrum files into ABC+ (polyphonic ABC) files // // Note: Not completed yet, particularly dynamics and lyrics. // // Todo: // lyrics // add local comments as [r: comment] fields // cross staff stemming/beaming? // up/down stem controls (if possible) // display numeric plots underneath a staff // dynamics // figured bass // suppress accidentals on tied notes after barlines // handle invisible articulations // allow trills on a single note in a chord (e.g. s03-1;m23) // don't display accidental on tied notes (_ and ]) // don't store accidental state of tied notes // some characters with accents (incomplete list implemented) // allow fermatas on barlines (including final / repeats) // // Done in v1.0.0 accidentals // measure numbers can be boxed with --box option // invisible rests using "y" // added option to print invisible rests and other objects // first/second endings // cautionary accidentals (with X) // deal with tenor and etc clef as an initial clef // printing invisible rests in second layer // print the first barnumber if there a pickup and barnum'g = 1 // multi-measure rests // tuplet rhythms (make rhythms more robust) // notes w/no rhythms are displayed as quarter notes w/no stems // break tuplet rhythm groupings by beaming // added veritas checking/marking // breaking subbeams // add brokenrhythms A>B = A3/2B/2 // abc+ extended information records // -sharp -flat -natural map to \# \b \= in titles; \b\= no work // slurs (some bugs in abcm2ps rendering of dashed slurs) // ties // gracenotes (with [q] and without [Q] slashes) // phrases // piano system automatic identification (partially done) // clef changes during music // time signature changes during the piece // add rhythm shorthands / = /2, // = /4 // key signtures changes during piece // beaming // ornaments // articulations // if the initial keysignature comes after the // first barline, but before the music, set it // as the initial meter in the header. // // Notes: // // * abcm2ps src: http://trillian.mit.edu/~jc/music/abc/src/abcm2ps/abc2ps.c // * Format parameters for abcm2ps 5.4.4: // http://br.geocities.com/hfmlacerda/abc/format.html // * Creating new symbols in abcm2ps: // http://br.geocities.com/hfmlacerda/abc/decomanual-en.pdf // http://hudsonlacerda.webs.com/abc/decomanual-en.pdf // * interesting webpage: http://www.ucolick.org/~sla/abcmusic/piano/piano.html // // * http://www.formulus.com/hymns/abcm2ps.txt // alto1= C1 soprano cleff, alto2 = C2 mezzo-soprano clef // bass3 = F3 // // * Use the F: record to encode the File URL // e.g.: hum2abc -F "http://this.location" file.krn > file.abc // #include #include #include #include #include #include #include #include #include #include #include using namespace std; #include "PerlRegularExpression.h" #include "CheckSum.h" #include "humdrum.h" #define EMPTY '\0' #define TOLERANCE 0.0001 #define AA ('A'-'A') #define BB ('B'-'A') #define CC ('C'-'A') #define DD ('D'-'A') #define EE ('E'-'A') #define FF ('F'-'A') #define GG ('G'-'A') #define HH ('H'-'A') #define II ('I'-'A') #define JJ ('J'-'A') #define KK ('K'-'A') #define LL ('L'-'A') #define MM ('M'-'A') #define NN ('N'-'A') #define OO ('O'-'A') #define PP ('P'-'A') #define QQ ('Q'-'A') #define RR ('R'-'A') #define SS ('S'-'A') #define TT ('T'-'A') #define UU ('U'-'A') #define VV ('V'-'A') #define WW ('W'-'A') #define XX ('X'-'A') #define YY ('Y'-'A') #define ZZ ('Z'-'A') ////////////////////////////////////////////////////////////////////////// class Coordinate { public: int row; int col; }; class VoiceMap { public: VoiceMap(void); ~VoiceMap(); int primary; // primary spine of the staff int voicenumber; // voice number in abc output void clear(void); // store dynamics location // store lyrics location(s) }; VoiceMap::VoiceMap(void) { clear(); } VoiceMap::~VoiceMap() { clear(); } void VoiceMap::clear(void) { primary = -1; voicenumber = -1; } class MeasureInfo { public: MeasureInfo(void); ~MeasureInfo(); void clear(void); int startline; // line number in the humdrum file which // start the measure (with a barline) int endline; // line number in the humdrum file which // ends the measure (may be a barline or not) int measurenum; // an explicit number found for the barline int currkey; int newkey; const char* kernkey; // for printing ABC modality with --key option int fullrest; // -1 or 0 if not a full measure of rest // otherwise a count of the number of // full measures of rests including // the current one which have occured // in sequence before the current one. int ending; // -1 if not the start of an ending // 0 if the end of a set of repeat // endings. 1 or greater if the start // of a repeat. }; MeasureInfo::MeasureInfo(void) { clear(); } MeasureInfo::~MeasureInfo() { clear(); } void MeasureInfo::clear(void) { startline = -1; endline = -1; measurenum = -1; newkey = -100; currkey = -100; fullrest = -100; ending = -1; kernkey = NULL; } class TupletInfo { public: TupletInfo(void); ~TupletInfo(); void clear(void); int top; int bot; int shortcut; int count; }; TupletInfo::TupletInfo(void) { clear(); } TupletInfo::~TupletInfo() { clear(); } void TupletInfo::clear(void) { shortcut = count = top = bot = -1; } class BeamInfo { public: BeamInfo(void); ~BeamInfo(); void clear(void); int left; // number of beams on left side of note int right; // number of beams on right side of note // maybe add flags as well }; BeamInfo::BeamInfo(void) { clear(); } BeamInfo::~BeamInfo() { clear(); } void BeamInfo::clear(void) { left = right = -1; } ////////////////////////////////////////////////////////////////////////// // function declarations: void checkOptions (Options& opts, int argc, char** argv, int fcount, HumdrumFile& cinfile); void storeOptionSet (Options& opts); void example (void); void usage (const char* command); void printHeader (ostream& out, HumdrumFile& infile, vector& header, vector& measures, int xval, const char* filename); void parseBibliographic (vector& header, HumdrumFile& infile); void convertHumdrumToAbc (ostream& out, HumdrumFile& infile, int xval, const char* filename); void calculateBestRhythmUnit(HumdrumFile& infile, int& Ltop, int& Lbot); void getBibPieces (const char* string, const char* string2, string& marker, string& contents); void storeHeaderRecord (vector& header, char recordletter, const char* string); void translateSpecialCharacters(string& output, const char* input); void calculateQRecord (string& QRecord, double tempo, const char* omdstring, int top, int bot); void printBody (ostream& out, HumdrumFile& infile, vector& measures); void createVoiceMap (vector& voicemap, HumdrumFile& infile); void printVoiceDeclaration(ostream& out, VoiceMap vmap, int voicenum, HumdrumFile& infile); void getMeasureInfo (vector& measures, HumdrumFile& infile); void printMeasures (ostream& out, HumdrumFile& infile, vector& measures, int startmeasure, int endmeasure, vector& voicemap); void printSingleMeasure (ostream& out, const vector& voiceinfo, int vindex, int vmapsize, vector& measureinfo, int mindex, HumdrumFile& infile, int staffnumber); void buildNoteAddresses (vector >& noteaddresses, HumdrumFile& infile, const VoiceMap& voiceinfo, const MeasureInfo& measureinfo); void printLayer (int layer, ostream& out, const VoiceMap& voiceinfo, const MeasureInfo& measureinfo, HumdrumFile& infile, vector >& address, vector& meterclef); int getMaxLayer (int track, int start, int endd, HumdrumFile& infile); void printMeasureLine (ostream& out, HumdrumFile& infile, int line, int primary, int staffnumber, vector& measureinfo, int mindex, int vindex); void getNoteDurations (vector& notedurations, vector& iindex, int layer, vector >& address, HumdrumFile& infile); void printKernTokenAsAbc (ostream& out, HumdrumFile& infile, int row, int col, vector& accident, double notedur, int brokenq, double brokendur, int top, int bot, int& slursuppress, int groupflag); void getBrokenRhythms (vector& broken, vector& brokendurs, vector& nogracelist, vector& nogracedurs, HumdrumFile& infile); int getBeamState (const char* token); char* base40ToAbcPitch (char* buffer, int base40); int countDots (char* buffer); int getRhythm (const char* buffer); void printKeySignature (ostream& out, HumdrumFile& infile); void setAccidentals (vector& accident, int key); void printAbcKeySignature (ostream& out, int keynum); void printAbcKeySignature (ostream& out, const char* kernkey); void printAccidental (ostream& out, int base40, const char* token, vector& accident); void adjustAccidentalStates(vector& accident, int step, vector >& address, HumdrumFile& infile); void adjustAccidentalStates2(vector& accident, HumdrumFile& infile, int row, int col); void printArticulations (ostream& out, const char* string); void getMeterAndClefChanges(vector& meterchanges, HumdrumFile& infile, const VoiceMap& voiceinfo, const MeasureInfo& measureinfo); void printMeterAndClefChanges(ostream& out, int testrow, vector& meterclef, HumdrumFile& infile); void printAbcClef (ostream& out, const char* string, int track, vector& cleftranspose); void printAbcMeter (ostream& out, const char* string); int checkForAllTies (HumdrumFile& infile, int row, int col); int findRecord (const char* key, string& value, HumdrumFile& infile, vector& bibfields); int findMultipleRecords (const char* key, vector& values, HumdrumFile& infile, vector& bibfields); void printAbcExtendedInformationFields(ostream& out, HumdrumFile& infile); void primeFactorization (vector& factors, int input); void printRhythm1 (ostream& out, char* buffer, int top, int bot); int printRhythm2 (ostream& out, double dur, int brokenstate, double brokendur, int top, int bot, int chordQ, const string& token); void simplifyFraction (int& top, int& bot); void printDurationAsRhythm(ostream& out, double dur, int top, int bot); void getNoGraceList (vector& notelist, vector >& address, int layer, HumdrumFile& infile); void getNoGraceDurs (vector& nogracedurs, vector& notedurs, vector >& address, int layer); void identifyTuplets (vector& tupletstuff, vector& nogracedurs, vector& nogracelist, HumdrumFile& infile, vector& broken, vector& brokendurs, vector& beaminfo); int getTupletInfo (TupletInfo& tupletsutff, Coordinate& nogracelist, HumdrumFile& infile); int getNonDuplePrimes (vector& factors); int getDuplePrimes (vector& factors); int getNextLowerPowerOfTwo(int number); void identifyTupletShortCuts(vector& tupletstuff, vector& beaminfo, vector& nogracedurs, vector& nogracelist, HumdrumFile& infile); void printTupletInfo (ostream& out, TupletInfo& tinfo); void flipCommaParts (string& contents); int roundTempoToNearestStandard(double tempo); int characterCount (const char* string, char character); void getBeamInfo (vector& beaminfo, vector& nogracelist, HumdrumFile& infile); void separateCountsByBeams(vector& tupletstuff, vector& beaminfo, vector& nogracelist, HumdrumFile& infile); void identifyWholeRestsInMeasures(vector& measures, VoiceMap& voicemap, HumdrumFile& infile); int checkForWholeRestInMeasure(int startline, int endline, int primary, HumdrumFile& infile); void printMultiRest (ostream& out, vector& measureinfo, int mindex); void printInvisibleRest (ostream& out, double duration); void getRepeatInfo (vector& segments, vector& repeatinfo, HumdrumFile& infile); int checkForRepeatMeasure(HumdrumFile& infile, vector& segments, vector& repeatinfo, int mindex); int getFirstMeasureNumber(HumdrumFile& infile); void printVoiceClef (ostream& out, VoiceMap voicemap, HumdrumFile& infile); void getRestInfo (vector& rests, vector& nogracelist, HumdrumFile& infile); void printVeritas (ostream& out, HumdrumFile& infile); void prepareBibliographicData(vector& markers, vector& contentses, HumdrumFile& infile); void addContentToString (string& newtitle, string& key, vector& markers, vector& contentses); void printNumberFromString (ostream& out, const char* filename, const char* ending); void printFilenameBase (ostream& out, const string& filename); int norhythm (const char* buffer); void getFileListing (vector& filelist, const char* directoryname, const char* filemask); int sortfiles (const void* a, const void* b); void printGraceRhythm (ostream& out, const char* buffer, int top, int bot, int groupflag); int getGraceNoteGroupFlag (vector& notedurs, int index); void checkForLineBreak (ostream& out, HumdrumFile& infile, int row, int col); void getColorAssignment (double& red, double& green, double& blue, const char* line); void checkMarks (HumdrumFile& infile); int getMarkState (string& marks, HumdrumFile& infile); void printMarks (ostream& out, const char* buffer, string& marks, vector > markcolors, int notecount); void printMarkCodes (ostream& out, string& marks, vector >& markcolors); void printNoteHeadShape (ostream& out, const char* buffer, int mindex); void getNoteShape (RationalNumber&, const char* buffer); // User interface variables: Options options; int debugQ = 0; // used with --debug option int labelQ = 0; // used with --label option int veritasQ = 1; // used with --no-veritas option int continueQ = 1; // used with --no-autoformat option int boxQ = 0; // used with --box option int notespacingQ = 0; // used with --spacing option double notespacing = 0; // used with --spacing option int linemeasure = 4; // used with -m option int barnumberingstyle = 0; // used with -n option int invisibleQ = 1; // used with --no-invisible int footerQ = 0; // used with -f option string footer = ""; // used with -f option int headerQ = 0; // used with -h option string header = ""; // used with -h option int musicscaleQ = 0; // used with -s option double musicscale = 0.75; // used with -s option int landscapeQ = 0; // used with --landscape option string parameterstring = ""; // used with -p option string invisiblerest = "x"; // used with --no-invisible option int filenumQ = 0; // used with --filenum option string filenumstring = ""; // used with --filenum option int filenametitleQ = 0; // used with --filetitle option int titleexpansionQ = 0; // used with --TT option string titleexpansion = ""; // used with --TT option int graceQ = 1; // used with --no-grace option int directoryQ = 0; // used with --dir option string directoryname = "."; // used with --dir option string filemask = ".krn"; // used with --mask option int nonaturalQ = 0; // used with --nn option int linebreakQ = 0; // used with --linebreak option int notempoQ = 0; // used with --no-tempo option int slurQ = 1; // used with --no-slur option int databarnumQ = 0; // used with --data-barnum option int commentbarnumQ = 0; // used with --comment-barnum option int keyQ = 0; // used with --key option // mark data int markQ = 1; // used with --no-mark option int hasmarksQ = 0; // used with markQ string marks; // marking characters in **kern data vector > markcolors; // color for markings in **kern data vector > usedMarks; // keep track of which noteheads used. const int USEDSIZE = 9; int usedAnnotation = 0; // keep track of whether annotation code // needs to be added to header vector Header; // score variables int Ltop = 1; // used for creating rhythm values int Lbot = 4; // used for creating rhythm values int StartKey = 0; vector Voicemap; // used printing polyphonic data vector clefstates; // keep track of transpositions for tenor clef int stemlessQ = 0; // used with -p "%%nostems" spoofing ////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { HumdrumFileSet infiles; storeOptionSet(options); options.process(argc, argv); infiles.read(options); // initial processing of the command-line options checkOptions(options, argc, argv, 1, infiles[0]); int i = 0; int j; if (directoryQ) { vector filelist; getFileListing(filelist, directoryname.c_str(), filemask.c_str()); for (i=0; i<(int)filelist.size(); i++) { infiles.read(filelist[i].c_str()); for (j=0; j 1.0) { red = 1.0; } if (green > 1.0) { green = 1.0; } if (blue > 1.0) { blue = 1.0; } } ////////////////////////////// // // getFileListing -- // void getFileListing(vector& filelist, const char* directoryname, const char* filemask) { filelist.setSize(1000000); filelist.setGrowth(1000000); filelist.setSize(0); int length; int dirlen = strlen(directoryname); const char* connector = "/"; if (directoryname[dirlen-1] == '/') { connector = ""; } int conlen = strlen(connector); int storlen; DIR *d; struct dirent *dir; d = opendir(directoryname); if (!d) { cerr << "Error: could not open directory " << directoryname << " for reading." << endl; exit(1); } while ((dir = readdir(d)) != NULL) { if (strstr(dir->d_name, filemask) == NULL) { // ignore files which do not match to mask. continue; } filelist.resize(filelist.size()+1); filelist.last() = directoryname; filelist.last() += connector; filelist.last() += dir->d_name; } closedir(d); // sort the filelist (it is not sorted currently) qsort(filelist.data(), (int)filelist.size(), sizeof(string), sortfiles); } ////////////////////////////// // // sortfiles -- // typedef string chararray; int sortfiles(const void* a, const void* b) { return strcmp((((chararray*)a))->c_str(), (((chararray*)b))->c_str()); } ////////////////////////////// // // convertHumdrumToAbc -- // void convertHumdrumToAbc(ostream& out, HumdrumFile& infile, int xval, const char* filename) { clefstates.setSize(infile.getMaxTracks()+1); clefstates.setAll(0); clefstates.allowGrowth(0); infile.analyzeRhythm("4"); vector measures; createVoiceMap(Voicemap, infile); getMeasureInfo(measures, infile); stringstream sheader; stringstream smarks; stringstream sbody; printHeader(sheader, infile, Header, measures, xval, filename); printBody(sbody, infile, measures); // if (hasmarksQ) { printMarkCodes(smarks, marks, markcolors); // } sheader << ends; smarks << ends; sbody << ends; out << sheader.str().c_str(); out << smarks.str().c_str(); out << sbody.str().c_str(); out << flush; } ////////////////////////////// // // printBody -- print the musical data for the score. // void printBody(ostream& out, HumdrumFile& infile, vector& measures) { // print on a single line if requested by the // user here; otherwise, currently print in // 4-measure chunks. int i; if (debugQ) { out << "%\n% Measure information in input file:\n"; out << "%\tindex\tnumber\tstart\tstop\tlines\tcurkey\tnewkey\n"; for (i=0; i<(int)measures.size(); i++) { out << "%\tM:" << i << "\t" << measures[i].measurenum << "\t" << measures[i].startline << "\t" << measures[i].endline << "\t" << measures[i].endline - measures[i].startline << "\t" << measures[i].currkey << "\t" << measures[i].newkey << endl; } out << "%\n"; } int increment = linemeasure; int maxmeasure; for (i=0; i<(int)measures.size(); i+=increment) { maxmeasure = i+increment; if (maxmeasure >= (int)measures.size()) { maxmeasure = (int)measures.size(); } printMeasures(out, infile, measures, i, maxmeasure, Voicemap); } } ////////////////////////////// // // printMeasures -- // void printMeasures(ostream& out, HumdrumFile& infile, vector& measures, int startmeasure, int stopmeasure, vector& voicemap) { int i, j; int asize = (int)voicemap.size(); stringstream *pstaves[asize]; for (i=0; i 0) { out << "%BAR: " << measures[i].measurenum << "\n"; } } printSingleMeasure(*(pstaves[j]), voicemap, j, (int)voicemap.size(), measures, i, infile, j); } } for (i=0; i<(int)voicemap.size(); i++) { (*(pstaves[i])) << ends; if ((*(pstaves[i])).str().c_str()[0] == '\0') { continue; } if ((int)voicemap.size() > 1) { out << "[V:" << voicemap[i].voicenumber << "] "; out << (*(pstaves[i])).str().c_str() << "\n"; } else { // multi-line rests will generate blank lines, so // suppress them here when blank lines occur. if (strlen((*(pstaves[i])).str().c_str()) > 0) { out << (*(pstaves[i])).str().c_str() << "\n"; } } } for (i=0; i& voiceinfo, int vindex, int vmapsize, vector& measureinfo, int mindex, HumdrumFile& infile, int staffnumber) { MeasureInfo& MI = measureinfo[mindex]; int maxlayers = getMaxLayer(voiceinfo[vindex].primary, MI.startline, MI.endline, infile); if (keyQ) { if (MI.kernkey != NULL) { out << " [K:"; printAbcKeySignature(out, MI.kernkey); out << "] "; } } else { if (MI.newkey > -10) { out << " [K:"; printAbcKeySignature(out, MI.newkey); out << "] "; } } vector meterclef; getMeterAndClefChanges(meterclef, infile, voiceinfo[vindex], MI); vector > noteaddresses; noteaddresses.setSize(maxlayers); noteaddresses.allowGrowth(0); buildNoteAddresses(noteaddresses, infile, voiceinfo[vindex], MI); if (noteaddresses.size() == 0) { // no notes to print in this measure return; } int multirestQ = 0; if ((vmapsize == 1) && measureinfo[mindex].fullrest > 1) { if (mindex < (int)measureinfo.size() - 1) { if (measureinfo[mindex+1].fullrest < 1) { multirestQ = 1; } else { multirestQ = 0; } } else { multirestQ = 1; } } if (multirestQ) { // print a multi-rest if at the end of a string // of measures of full rests. This algorithm // is not fully generalized, and needs to be // extended eventually. It will absorb repeat // barlines, which is should not do. It will also // count split bars as two measures instead of // one at the moment. printMultiRest(out, measureinfo, mindex); if (infile[MI.endline].getType() == E_humrec_data_measure) { printMeasureLine(out, infile, MI.endline, voiceinfo[vindex].primary, staffnumber, measureinfo, mindex, vindex); } } else if (measureinfo[mindex].fullrest > 1) { // do nothing; } else if ((mindex < (int)measureinfo.size() - 1) && (measureinfo[mindex+1].fullrest > 1) ) { // do nothing; still waiting for end of multi rest } else { printLayer(1, out, voiceinfo[vindex], MI, infile, noteaddresses, meterclef); if (maxlayers > 1) { out << " & "; printLayer(2, out, voiceinfo[vindex], MI, infile, noteaddresses, meterclef); } // don't know how to print more than 2 layers... // so ignore layers greater than 2 if (infile[MI.endline].getType() == E_humrec_data_measure) { printMeasureLine(out, infile, MI.endline, voiceinfo[vindex].primary, staffnumber, measureinfo, mindex, vindex); } } } ////////////////////////////// // // printMultiRest -- // void printMultiRest(ostream& out, vector& measureinfo, int mindex) { int count = 0; int i; for (i=mindex; i>=0; i--) { if (measureinfo[i].fullrest) { count++; } else { break; } } out << "Z"; if (count > 1) { out << count; } } ////////////////////////////// // // getMeterAndClefChanges -- find the location of meter changes in the music // for the particular measure and voice. // void getMeterAndClefChanges(vector& meterchanges, HumdrumFile& infile, const VoiceMap& voiceinfo, const MeasureInfo& measureinfo) { vector temp; temp.setSize(0); Coordinate tcord; int top; int bot; int i, j; for (i=measureinfo.startline; i<=measureinfo.endline; i++) { if (infile[i].getType() == E_humrec_interpretation) { for (j=0; j=0; i--) { tcord = temp[i]; meterchanges.append(tcord); } } ////////////////////////////// // // printMeasureLine -- // .| or : = dashed measure line // | = regular line // [| = !| style for humdrum measures // |[| = |!| style for humdrum measures // :: = :!!: style for humdrum measures // [|] = invisible measurel line (- for humdrum measures) // |] = |! or == for humdrum scores // || = || or (end of second repeat) // [1 = first repeat // [2 = second repeat // |1 = short first repeat // |2 = short second repeat // void printMeasureLine(ostream& out, HumdrumFile& infile, int line, int primary, int staffnumber, vector& measureinfo, int mindex, int vindex) { if (infile[line].getType() != E_humrec_data_measure) { return; } int col = -1; for (col=0; col= 0) { out << "[I:setbarnb " << measureinfo[mindex+1].measurenum << "]"; } } else if ((staffnumber == 0) && ((mindex < (int)measureinfo.size()-1) && (measureinfo[mindex+1].measurenum >= 0)) && ((mdiff > 1) || labelQ)) { out << "[I:setbarnb " << measureinfo[mindex+1].measurenum << "]"; } if (strchr(token, '-') != NULL) { // print an invisible barline out << "[|] "; return; } if ((measureinfo[mindex].ending < 0) || (vindex != 0)) { if (strstr(token, ":|!|:") != NULL) { out << ":|]|: "; } else if (strstr(token, ":|!") != NULL) { out << ":|] "; } else if (strstr(token, "!|:") != NULL) { out << "]|: "; } else if (strstr(token, ":||:") != NULL) { out << ":||: "; } else if (strstr(token, "||") != NULL) { out << "|| "; } else if (strstr(token, "==") != NULL) { out << "|] "; } else { out << "|"; } } else { if (strstr(token, ":|!|:") != NULL) { out << ":|]|:"; } else if (strstr(token, ":|!") != NULL) { out << ":|]"; } else if (strstr(token, "!|:") != NULL) { out << "]|:"; } else if (strstr(token, ":||:") != NULL) { out << ":||:"; } else if (strstr(token, "||") != NULL) { out << "||"; } else if (strstr(token, "==") != NULL) { out << "|]"; } else { if (measureinfo[mindex].ending == 0) { // the second ending must be ended by || barline in abcm2ps // and not a single barline out << "|"; //out << " ||"; } else { out << "|"; } } if (measureinfo[mindex].ending > 0) { out << "["; out << measureinfo[mindex].ending; } } out << " "; } ////////////////////////////// // // getMaxLayer -- returns the maximum number of layers in a // particular voice (staff). // int getMaxLayer(int track, int start, int endd, HumdrumFile& infile) { int i, j; int counter = 0; int maxx = 0; for (i=start; i<=endd; i++) { if (infile[i].getType() != E_humrec_data) { continue; } counter = 0; for (j=0; j maxx) { maxx = counter; } } return maxx; } ////////////////////////////// // // printLayer -- print a single layer of a single staff. But // the accidental states need to be monitored for all // layers in the staff. // void printLayer(int layer, ostream& out, const VoiceMap& voiceinfo, const MeasureInfo& measureinfo, HumdrumFile& infile, vector >& address, vector& meterclef) { if (debugQ) { cout << "\%\% Printing measure"; if (measureinfo.measurenum > 0) { cout << " " << measureinfo.measurenum; } cout << " voice " << voiceinfo.primary << " layer " << layer << "==================\n%"; cout << endl; } // abcm2ps 5.9.1 has problems with slurs originating in // gracenote regions when there is another uncompleted // slur in progress, so suppress all slurs starting // from grace notes (at least for now). In any case, // abcm2ps will automatically attach slurs between // grace notes and the next non-grace note in the // layer unless a flag is set to turn that feature off. int slursuppress = 0; int ilayer = layer - 1; const char* token; vector accident(7*9); accident.allowGrowth(0); accident.setAll(0); setAccidentals(accident, measureinfo.currkey); vector notedurs; // duration of note events in layer, // (including grace notes) vector iindex; getNoteDurations(notedurs, iindex, layer, address, infile); // note list contains a list of notes in the current layer // without the grace notes vector nogracelist; getNoGraceList(nogracelist, address, layer, infile); vector nogracedurs; getNoGraceDurs(nogracedurs, notedurs, address, layer); vector beaminfo; getBeamInfo(beaminfo, nogracelist, infile); // broken rhythms are dotted rhythm patterns vector broken; // stores the brokenness for the first of a broken pair vector brokendurs; // store the revised duration for broken notes getBrokenRhythms(broken, brokendurs, nogracelist, nogracedurs, infile); vector tupletstuff; identifyTuplets(tupletstuff, nogracedurs, nogracelist, infile, broken, brokendurs, beaminfo); int beamstate = 0; int oldbeamstate = 0; int gracestate = 0; int lastrow = -1; int lastcol = -1; int row = -1; int col = -1; int counter = 0; int j; int nogracei = -1; double finaldur; int lastnotegraceQ = 0; double currentbeat = infile[measureinfo.startline].getAbsBeat(); for (j=0; j<(int)address[0].size(); j++) { row = address[ilayer][j].row; col = address[ilayer][j].col; if (linebreakQ) { checkForLineBreak(out, infile, row, col); } if (row >= 0) { lastrow = row; lastcol = col; //if (notedurs[counter] > 0.0) { if ((strchr(infile[row][col], 'q') == NULL) && (strchr(infile[row][col], 'Q') == NULL)) { nogracei++; if (lastnotegraceQ) { // if the last note was a grace note, then do not // place a corrective invisible rest in the layer // To prevent the invisible rest from occurring // set the current beat to the absolute beat of the // note which is currently being processed. currentbeat = infile[row].getAbsBeat(); } lastnotegraceQ = 0; } else { // cout << "GOT HERE LAST NOTE WAS A GRACE NOTE" << endl; notedurs[counter] = 0.0; lastnotegraceQ = 1; } if (currentbeat != infile[row].getAbsBeat()) { //cout << "% current beat = " << currentbeat << " score beat " //<< infile[row].getAbsBeat() << " diff = " //<< infile[row].getAbsBeat() - currentbeat << endl; if ((strchr(infile[row][col], 'q') == NULL) && (strchr(infile[row][col], 'Q') == NULL)) { if (layer != 1) { // don't allow invisible rests to occur in the first layer printInvisibleRest(out, infile[row].getAbsBeat() - currentbeat); } } else { // don't create any invisible rests when printing out // a grace note, since there metrical position in the // score is irrelevant. } } currentbeat = infile[row].getAbsBeat() + notedurs[counter]; if ((layer == 1) && (meterclef.size() > 0) && (row > meterclef[(int)meterclef.size()-1].row)) { printMeterAndClefChanges(out, row, meterclef, infile); } token = infile[row][col]; oldbeamstate = beamstate; beamstate += getBeamState(token); if ((counter > 0) && (oldbeamstate == 0) && (notedurs[counter] < 1.0) && (notedurs[counter-1] < 1.0)) { out << " "; // put a space to prevent beaming } if ((!gracestate) && (nogracei>0) ) { // if not a grace note and note the first note: // It is assumbed that bi[i].left == bi[i-1].right // but might need to check it if this is no longer true. if (beaminfo[nogracei].right > beaminfo[nogracei].left) { if (beaminfo[nogracei-1].right < beaminfo[nogracei-1].left) { if (beaminfo[nogracei].left > 0) { out << "!beambr" << beaminfo[nogracei].left << "!"; } } } } if ((gracestate == 0) && (notedurs[counter] == 0.0)) { // grace notes (q) will have slashes added // groupetto notes (Q) will not have slashes added if (strchr(token, 'q') != NULL) { out << "{/"; //q: start a grace note sequence with a slash } else { out << "{"; //Q: start a grace note sequence without a slash } gracestate = 1; } if ((notedurs[counter] > 0) && (nogracei >= 0)) { printTupletInfo(out, tupletstuff[nogracei]); finaldur = nogracedurs[nogracei]; } else { finaldur = 0.0; } // print pitches of chords or single pitches (with accidentals) // plus articulations for note/chord. int groupflag = getGraceNoteGroupFlag(notedurs, counter); if (nogracei >= 0) { printKernTokenAsAbc(out, infile, row, col, accident, finaldur, broken[nogracei], brokendurs[nogracei], Ltop, Lbot, slursuppress, groupflag); } else { // print grace note printKernTokenAsAbc(out, infile, row, col, accident, finaldur, 0, 0, Ltop, Lbot, slursuppress, groupflag); } if (notedurs[counter] >= 1) { beamstate = 0; // fix any problems with beaming info } if (gracestate) { if (counter == (int)notedurs.size()-1) { out << "}"; gracestate = 0; } else if (notedurs[counter+1] > 0.0) { out << "}"; gracestate = 0; } } counter++; } adjustAccidentalStates(accident, j, address, infile); } if (currentbeat != infile[measureinfo.endline].getAbsBeat()) { // don't do a final correction if the last note was a grace note: if ((lastrow >= 0) && (lastcol >= 0) && (strchr(infile[lastrow][lastcol], 'q') == NULL) && (strchr(infile[lastrow][lastcol], 'Q') == NULL)) { printInvisibleRest(out, infile[measureinfo.endline].getAbsBeat() - currentbeat); } } // print any meter or clef changes at the end of a measure: if ((layer == 1) && (meterclef.size() > 0)) { printMeterAndClefChanges(out, 100000, meterclef, infile); } } ////////////////////////////// // // checkForLineBreak -- // void checkForLineBreak(ostream& out, HumdrumFile& infile, int row, int col) { int i; for (i=row-1; i>=0; i--) { if (infile[i].getType() == E_humrec_data) { return; } if (infile[i].getType() == E_humrec_local_comment) { if (strstr(infile[i][0], "linebreak") != NULL) { out << "\n"; } } } } ////////////////////////////// // // getGraceNoteGroupFlag -- There is probably a bug in abcm2ps // which requires that when more than one grace notes occurs // in a group together, the duration has to be increased by 4.0 // to print the correct graphical rhythm, or increase by 2.0 // when there is only one grace note... // int getGraceNoteGroupFlag(vector& notedurs, int index) { int counter = 1; int i; for (i=index+1; i<(int)notedurs.size(); i++) { if (notedurs[i] < TOLERANCE) { counter++; } else { break; } } for (i=index-1; i>=0; i--) { if (notedurs[i] < TOLERANCE) { counter++; } else { break; } } if (counter == 1) { return 0; } else { return 1; } } ////////////////////////////// // // printInvisibleRest -- // void printInvisibleRest(ostream& out, double duration) { if (duration <= TOLERANCE) { return; } if (duration <= 0.001) { cout << "% INVISIBILE DURATION SUPPRESSED: " << duration << endl; // more aggressive suppression of tiny invisible rests... return; } double value = duration * Lbot / Ltop / 4.0; int a = int(value + TOLERANCE); double fraction = value - a; int top; int bot; int b; if (fraction > TOLERANCE) { b = int(1.0 / fraction + TOLERANCE); top = a * b + 1; bot = b; simplifyFraction(top, bot); } else { top = a; bot = 1; } out << invisiblerest; if (top > 1) { out << top; } if (bot > 1) { out << "/"; if (bot == 2) { } else if (bot == 4) { out << "/"; } else { out << bot; } } } ////////////////////////////// // // getBeamInfo -- // void getBeamInfo(vector& beaminfo, vector& nogracelist, HumdrumFile& infile) { beaminfo.resize(nogracelist.size()); int i; int Lcount; int Jcount; int current = 0; int row; int col; for (i=0; i<(int)nogracelist.size(); i++) { row = nogracelist[i].row; col = nogracelist[i].col; Lcount = characterCount(infile[row][col], 'L'); Jcount = characterCount(infile[row][col], 'J'); if (i == 0) { Jcount = 0; // ignore cross-measure beaming if it occurs } beaminfo[i].left = current; current -= Jcount; current += Lcount; beaminfo[i].right = current; } beaminfo[(int)beaminfo.size()-1].right = 0; } ////////////////////////////// // // characterCount -- // int characterCount(const char* string, char character) { int i = 0; int count = 0; while (string[i] != '\0') { if (string[i] == character) { count++; } i++; } return count; } ////////////////////////////// // // printTupletInfo -- // void printTupletInfo(ostream& out, TupletInfo& tinfo) { if (tinfo.top < 2) { return; } if (tinfo.count < 0) { return; } switch (tinfo.shortcut) { case 2: out << "(2"; return; case 3: out << "(3"; return; case 4: out << "(4"; return; case 5: out << "(5"; return; case 6: out << "(6"; return; // danger: as expected: factor of two off case 7: out << "(7"; return; case 8: out << "(8"; return; case 9: out << "(9"; return; } // print a longcut: out << "(" << tinfo.top << ":" << tinfo.bot << ":" << tinfo.count; } ////////////////////////////// // // identifyTuplets -- find tuplets in the data. The notedurs and // nogracedurs, and brokendurs will be modified by this function. // to remove non-power-of-two rhythms from the list of notes to // print on the layer. // void identifyTuplets(vector& tupletstuff, vector& nogracedurs, vector& nogracelist, HumdrumFile& infile, vector& broken, vector& brokendurs, vector& beaminfo) { tupletstuff.resize(nogracelist.size()); if (tupletstuff.empty()) { // no notes (other than perhaps grace notes) in the measure return; } int dupleprimes; double newdur; int i; if (debugQ) { cout << "%\n% TUPLET INFO INPUT\n"; cout << "%tuptop\ttupbot\tcount\tshort\tnewdur\ttoken\n"; for (i=0; i<(int)tupletstuff.size(); i++) { cout << "%" << tupletstuff[i].top << "\t" << tupletstuff[i].bot << "\t" << tupletstuff[i].count << "\t" << tupletstuff[i].shortcut << "\t" << nogracedurs[i] << "\t" << infile[nogracelist[i].row][nogracelist[i].col] << "\n"; } cout << "%\n"; } int scale; for (i=0; i<(int)nogracelist.size(); i++) { dupleprimes = getTupletInfo(tupletstuff[i], nogracelist[i], infile); if (tupletstuff[i].top > 2) { newdur = 4.0 / (dupleprimes * tupletstuff[i].bot); scale = 0; if (brokendurs[i] > 0) { if (broken[i] < 0) { // the current note is the first note in a broken // rhythm and it is the smaller scale = -broken[i]; } else if (i > 0) { // the previous note was part of the broken rhythm // and was longer than the current note. if (broken[i-1] > 0) { scale = broken[i-1]; } } } if (scale > 0) { newdur = newdur * (1 << scale); } nogracedurs[i] = newdur; } } identifyTupletShortCuts(tupletstuff, beaminfo, nogracedurs, nogracelist, infile); if (debugQ) { cout << "%\n% TUPLET INFO OUTPUT\n"; cout << "%tuptop\ttupbot\tcount\tshort\tbeam\tbrok\tbrkdur\tnewdur\ttoken\n"; for (i=0; i<(int)tupletstuff.size(); i++) { cout << "%" << tupletstuff[i].top << "\t" << tupletstuff[i].bot << "\t" << tupletstuff[i].count << "\t" << tupletstuff[i].shortcut << "\t" << beaminfo[i].right << ":" << beaminfo[i].left << "\t" << broken[i] << "\t" << int(brokendurs[i]*10000.0+0.5)/10000.0 << "\t" << nogracedurs[i] << "\t" << infile[nogracelist[i].row][nogracelist[i].col] << "\n"; } cout << "%\n"; } } ////////////////////////////// // // separateCountsByBeams -- should also be separated by meter // maybe at the same time... // void separateCountsByBeams(vector& tupletstuff, vector& beaminfo, vector& nogracelist, HumdrumFile& infile) { int i, j; // int counter; vector rests; getRestInfo(rests, nogracelist, infile); for (i=0; i<(int)tupletstuff.size(); i++) { if ((tupletstuff[i].count > 0) && ((beaminfo[i].right > 0) || rests[i])) { // counter = 1; for (j=1; j& rests, vector& nogracelist, HumdrumFile& infile) { rests.resize(nogracelist.size()); int i; for (i=0; i<(int)nogracelist.size(); i++) { if (strchr(infile[nogracelist[i].row][nogracelist[i].col], 'r') != NULL) { rests[i] = 1; } else { rests[i] = 0; } } } ////////////////////////////// // // identifyTupletShortCuts -- // void identifyTupletShortCuts(vector& tupletstuff, vector& beaminfo, vector& nogracedurs, vector& nogracelist, HumdrumFile& infile) { int top = -1; int bot = -1; int length = (int)tupletstuff.size(); int counter = 1; top = tupletstuff[length-1].top; bot = tupletstuff[length-1].bot; for (int i=(int)tupletstuff.size()-2; i>= 0; i--) { if ((top == tupletstuff[i].top) && (bot == tupletstuff[i].bot)) { counter++; } else { if (top == -1) { counter = 1; top = tupletstuff[i].top; bot = tupletstuff[i].bot; continue; } else { tupletstuff[i+1].count = counter; counter = 1; top = tupletstuff[i].top; bot = tupletstuff[i].bot; } } } if (tupletstuff[0].top > 0) { tupletstuff[0].count = counter; } vector& t = tupletstuff; // separateCountsByBeams(tupletstuff, beaminfo, nogracelist, infile); //////////////////////////////////////////////////////////////////////// vector& ng = nogracedurs; vector metric; vector frac; metric.resize(nogracelist.size()); frac.resize(nogracelist.size()); for (int i=0; i<(int)nogracelist.size(); i++) { metric[i] = infile[nogracelist[i].row].getAbsBeat(); frac[i] = metric[i] - (int)metric[i]; } vector beamy(nogracelist.size(), 0); int bcount = 0; for (int i=1; i<(int)beaminfo.size(); i++) { if (beaminfo[i].left == 0) { bcount++; } beamy[i] = bcount; } // check for shortcuts for (int i=0; i<(int)t.size(); i++) { if ((t[i].top == 3) && (t[i].bot == 2) && (t[i].count == 3)) { t[i].shortcut = 3; t[i].count = 0; // shortcut, so don't use longcut } // triplet types of notes if ((t[i].top == 3) && (t[i].bot == 2) && (t[i].count % 3 == 0) && (t[i].count > 0)) { if ((t[i].count == 6) && (fabs(ng[i] - 0.5) < TOLERANCE) && (fabs(ng[i+1] - 0.5) < TOLERANCE) && (fabs(ng[i+2] - 0.5) < TOLERANCE)) { // six triplet eighths in a row cannot be given a (6 shortcut) // since it will be treated as six triplet sixteenths. // The durations have already been de-tupletized, so // a triplet eight is now encoded as an eighth t[i].shortcut = 3; t[i+3].shortcut = 3; t[i].count = 0; t[i+3].count = 0; } else if ((t[i].count > 3) && (fabs(frac[i]) < TOLERANCE) && (fabs(frac[i+1] - 1.0/3.0) < TOLERANCE) && (fabs(frac[i+2] - 2.0/3.0) < TOLERANCE)) { // triplet eigths inside of one beat (assuming quarter-note beat) t[i].shortcut= 3; t[i+3].count = t[i].count - 3; t[i].count = 0; // t[i] is now a shortcut so remove the count } else if ((t[i].count > 3) && (fabs(frac[i] - 3.0/6.0) < TOLERANCE) && (fabs(frac[i+1] - 4.0/6.0) < TOLERANCE) && (fabs(frac[i+2] - 5.0/6.0) < TOLERANCE)) { // triplet 16ths on the second half of a beat t[i].shortcut = 3; t[i+3].count = t[i].count - 3; t[i].count = 0; // t[i] is now a shortcut so remove the count } else if ((t[i].count > 3) && (fabs(frac[i] - 0.0/6.0) < TOLERANCE) && (fabs(frac[i+1] - 1.0/6.0) < TOLERANCE) && (fabs(frac[i+2] - 2.0/6.0) < TOLERANCE)) { t[i].shortcut = 3; t[i+3].count = t[i].count - 3; t[i].count = 0; // t[i] is now a shortcut so remove the count } } } // go back and fix tuplets which are beamed together: int lasti = -1; for (i=0; i<(int)nogracelist.size(); i++) { if (t[i].shortcut == 3) { if (lasti < 0) { lasti = i; } else if (beamy[i] == beamy[lasti]) { if ((t[i].shortcut == 3) && (t[lasti].shortcut == 3)) { t[lasti].shortcut = 0; t[lasti].count = 6; t[i].shortcut = 0; t[i].count = -1; } } lasti = i; } } } ////////////////////////////// // // getTupletInfo -- // // identification algorithm for a non-duple tuplet: // (1) prime factorize the rhythm // (2) identify if there are any prime numbers other than 2 // (3) if there are no other prime numbers then the note is // not a tuplet // (4) if there are other prime numbers, then multiply all of // the other prime numbers together and search for the next // lowest power of two. The prime number will be the top // number in the tuplet ratio, and the next lower power of // two will be the bottom part of the ratio // int getTupletInfo(TupletInfo& tupletstuff, Coordinate& nogracelist, HumdrumFile& infile) { int row = nogracelist.row; int col = nogracelist.col; char buffer[32] = {0}; int rhythm; int nondupleprimes; int dupleprimes; int nextlowerpoweroftwo; vector factors; infile[row].getToken(buffer, col, 0, 32); int i=0; while ((buffer[i] != EMPTY) && (!std::isdigit(buffer[i]))) { i++; } if (sscanf(&buffer[i], "%d", &rhythm) == 1) { primeFactorization(factors, rhythm); nondupleprimes = getNonDuplePrimes(factors); if (nondupleprimes <= 2) { tupletstuff.clear(); return -1; } else { // a tuplet has been identified, so mark it. dupleprimes = getDuplePrimes(factors); nextlowerpoweroftwo = getNextLowerPowerOfTwo(nondupleprimes); tupletstuff.top = nondupleprimes; tupletstuff.bot = nextlowerpoweroftwo; return dupleprimes; } } else { tupletstuff.clear(); } return -1; } ////////////////////////////// // // getNextLowerPowerOfTwo -- return the next lower power of two // for the given input number. Example: 25 -> 16, 15 -> 8, 7->4, 5->4 // if you give a power of two, it will return that same power of two. // if you give 1, then it will return 1. if you give zero, // then it will return 0. // int getNextLowerPowerOfTwo(int number) { if (number <= 0) { return 0; } if (number == 1) { return 1; } double value = log((double)number)/log(2.0); int output = int(pow(2.0, int(value+0.00001))); return output; } ////////////////////////////// // // getNonDuplePrimes -- return the product of all numbers // in the list which are greater than two. All numbers // in the list are assumed to be prime numbers. // int getNonDuplePrimes(vector& factors) { int output = 1; for (i=0; i<(int)factors.size(); i++) { if (factors[i] <= 2) { continue; } output = output * factors[i]; } return output; } ////////////////////////////// // // getDuplePrimes -- return the product of all numbers // in the list which are greater than two. All numbers // in the list are assumed to be prime numbers. // int getDuplePrimes(vector& factors) { int i; int output = 1; for (i=0; i<(int)factors.size(); i++) { if (factors[i] != 2) { continue; } output = output * 2; } return output; } ////////////////////////////// // // printRhythm2 -- print the duration, version 2 which does not // yet address the tuplet notation, but does address the broken // rhythms. Returns the number of broken rhythm characters // which should be printed after the ']' character in chords. // int printRhythm2(ostream& out, double dur, int brokenstate, double brokendur, int top, int bot, int chordQ, const char* token) { int output = 0; if (norhythm(token)) { // this will remove stems from noteheads: out << "0"; } if (stemlessQ) { // this will remove stems from noteheads: out << "0"; } if (fabs(dur) <= TOLERANCE) { // don't print durations of grace notes. return 0; } char bch = 'X'; if (brokenstate != 0) { // print Broken rhythm start. //printDurationAsRhythm(out, brokendur, top, bot); printDurationAsRhythm(out, dur, top, bot); if (brokenstate > 0) { bch = '>'; } else { bch = '<'; } int i; if (chordQ) { output = brokenstate; } else { for (i=0; i TOLERANCE) { cout << "% FRACTION = " << fraction << "BUT SHOULD BE 0" << endl; } } durbot = durbot * 4; simplifyFraction(durtop, durbot); if (debugQ) { // cout << "%FINAL OUTPUT: " << durtop << "/" << durbot << endl; } if (durtop > 1) { out << durtop; } if (durbot == 1) { // don't print anything } else if (durbot == 2) { out << "/"; } else if (durbot == 4) { out << "//"; } else { out << "/" << durbot; } } ////////////////////////////// // // simplifyFraction -- // void simplifyFraction(int& top, int& bot) { vector factorstop; vector factorsbot; primeFactorization(factorstop, top); primeFactorization(factorsbot, bot); int i; int j; for (i=0; i<(int)factorstop.size(); i++) { for (j=0; j<(int)factorsbot.size(); j++) { if (factorstop[i] == factorsbot[j]) { factorstop[i] = 1; factorsbot[j] = 1; break; } } } top = 1; for (i=0; i<(int)factorstop.size(); i++) { if (factorstop[i] > 1) { top = top * factorstop[i]; } } bot = 1; for (i=0; i<(int)factorsbot.size(); i++) { if (factorsbot[i] > 1) { bot = bot * factorsbot[i]; } } } ////////////////////////////// // // primeFactorization -- return a list of the prime factors of // the given input number, sorted from smallest to largest // factors. // void primeFactorization(vector& factors, int input) { int i = 3; int c = input; int two = 2; factors.setSize(0); while ((c >= 2) && ((c%2) == 0)) { factors.append(two); c = c/2; } while (i <= (sqrt((double)c)+1)) { if ((c%i) == 0) { factors.append(i); c = c/i; } else { i = i+2; } } if (c > 1) { factors.append(c); } } ////////////////////////////// // // getNoGraceDurs -- get the durations of all notes which are not // grace notes in the current layer. // void getNoGraceDurs(vector& nogracedurs, vector& notedurs, vector >& address, int layer) { int i; int row; // int col; double tdur; int ilayer = layer - 1; int counter = 0; nogracedurs.setSize(0); for (i=0; i<(int)address[ilayer].size(); i++) { row = address[ilayer][i].row; // col = address[ilayer][i].col; if (row >= 0) { if (notedurs[counter] > 0) { tdur = notedurs[counter]; nogracedurs.append(tdur); } counter++; } } } ////////////////////////////// // // getNoGraceList -- get a list of notes for the current layer, // ignoring the grace notes in the layer. // void getNoGraceList(vector& notelist, vector >& address, int layer, HumdrumFile& infile) { Coordinate tcord; int ilayer = layer - 1; int i; notelist.clear(); notelist.allocate(address[0].size()); for (i=0; i<(int)address[ilayer].size(); i++) { tcord.row = address[ilayer][i].row; tcord.col = address[ilayer][i].col; if ((tcord.row > 0) && (strchr(infile[tcord.row][tcord.col], 'q') == NULL) && (strchr(infile[tcord.row][tcord.col], 'Q') == NULL)) { notelist.append(tcord); } } } ////////////////////////////// // // printMeterAndClefChanges -- // void printMeterAndClefChanges(ostream& out, int testrow, vector& meterclef, HumdrumFile& infile) { int row; int col; while ((!meterclef.empty()) && (meterclef[(int)meterclef.size()-1].row < testrow)) { row = meterclef[(int)meterclef.size()-1].row; col = meterclef[(int)meterclef.size()-1].col; if (strncmp(infile[row][col], "*clef", 5) == 0) { out << "[K:clef="; printAbcClef(out, infile[row][col], infile[row].getPrimaryTrack(col), clefstates); out << "]"; } else { // not a clef so must be a meter: out << "[M:"; printAbcMeter(out, infile[row][col]); out << "]"; } // shrink the array since the clef or meter signature at end was printed meterclef.resize((int)meterclef.size()-1); } } /////////////////////////////// // // printAbcMeter -- // void printAbcMeter(ostream& out, const char* string) { int top; int bot; if (sscanf(string, "*M%d/%d", &top, &bot) == 2) { out << top << "/" << bot; return; } if (strcmp(string, "*met(C)") == 0) { out << "C"; return; } if (strcmp(string, "*met(c)") == 0) { out << "C"; return; } if (strcmp(string, "*met(C|)") == 0) { out << "C|"; return; } if (strcmp(string, "*met(c|)") == 0) { out << "C|"; return; } out << "?"; } ////////////////////////////// // // printAbcClef -- // void printAbcClef(ostream& out, const char* string, int track, vector& cleftranspose) { if (strncmp(string, "*clef", 5) != 0) { out << "perc"; cleftranspose[track] = 0; return; } else { if (strcmp(string, "*clefGv2") == 0) { out << "treble-8"; cleftranspose[track] = +40; return; } if (strcmp(string, "*clefG2") == 0) { out << "treble"; cleftranspose[track] = 0; return; } if (strcmp(string, "*clefF4") == 0) { out << "bass"; cleftranspose[track] = 0; return; } if (strcmp(string, "*clefC3") == 0) { out << "alto"; cleftranspose[track] = 0; return; } if (strcmp(string, "*clefC4") == 0) { out << "tenor"; cleftranspose[track] = 0; return; } if ((string[5] == 'F') || (string[5] == 'C') || (string[5] == 'G')) { if (std::isdigit(string[6])) { out << string[5] << string[6]; cleftranspose[track] = 0; return; } } } out << "perc"; cleftranspose[track] = 0; } ////////////////////////////// // // adjustAccidentalStates -- update the accidental states for the // notes at the current time (in all layers) // void adjustAccidentalStates(vector& accident, int step, vector >& address, HumdrumFile& infile) { int i; int row, col; for (i=0; i<(int)address.size(); i++) { if (address[i][step].row < 0) { continue; } row = address[i][step].row; col = address[i][step].col; adjustAccidentalStates2(accident, infile, row, col); } } ////////////////////////////// // // adjustAccidentalStates2 -- // void adjustAccidentalStates2(vector& accident, HumdrumFile& infile, int row, int col) { int count = infile[row].getTokenCount(col); char buffer[128] = {0}; int acci; int diatonic; int base40; int i; int octave; for (i=0; i& accident, int key) { if (key < -7) { return; } #define XCX 0 #define XDX 1 #define XEX 2 #define XFX 3 #define XGX 4 #define XAX 5 #define XBX 6 int octaves = (int)accident.size() / 7; for (int i=0; i= +7) { accident[XBX+i*7] = +1; } if (key >= +6) { accident[XEX+i*7] = +1; } if (key >= +5) { accident[XAX+i*7] = +1; } if (key >= +4) { accident[XDX+i*7] = +1; } if (key >= +3) { accident[XGX+i*7] = +1; } if (key >= +2) { accident[XCX+i*7] = +1; } if (key >= +1) { accident[XFX+i*7] = +1; } } } ////////////////////////////// // // printKernTokenAsAbc -- print the pitch and articulation // data for a note or a chord. // void printKernTokenAsAbc(ostream& out, HumdrumFile& infile, int row, int col, vector& accident, double notedur, int brokenq, double brokendur, int top, int bot, int& slursuppress, int groupflag) { int count = infile[row].getTokenCount(col); char buffer[128] = {0}; char pitchbuffer[128] = {0}; int base40; int alltie = 0; if (count == 1) { alltie = 1; } else { alltie = checkForAllTies(infile, row, col); } const char* token = infile[row][col]; if (slurQ) { // phrase start, the ' marker after the // dashed slur indicates that it should // be placed above the staff // Warning: if a phrase and slur start on the // same token, then the slur will also be // dashed in abcm2ps (probably a bug). if (strchr(token, '{') != NULL) { out << ".('"; } // slur start const char* tptr; if ((tptr = strchr(token, '(')) != NULL) { if (notedur == 0.0) { slursuppress++; } else { if ((tptr+1)[0] == 'x') { // The slur mark is an editorial slur, so make it dashed out << "."; } out << "("; } } // multiple slurs and phrase markings cannot // be elided, but can be nested in ABC. } infile[row].getToken(buffer, col, 0); printArticulations(out, buffer); int chordQ = 0; int brokencount = 0; // used for printing broken rhythms with chords if (count > 1) { // chord of more than one note out << "["; chordQ = 1; } int i; for (i=0; i 0.00000001) { brokencount = printRhythm2(out, notedur, brokenq, brokendur, top, bot, chordQ, buffer); } else { // the note is a grace note, so suppress the // printing of any duration (maybe change later // to control the graphical view of the grace note). // Here is the change: printGraceRhythm(out, buffer, Ltop, Lbot, groupflag); } // print rhythm here... if ((alltie == 0) && (count != 1)) { if (strchr(buffer, '[') != NULL) { out << "-"; } else if (strchr(buffer, '_') != NULL) { out << "-"; } } } if (count > 1) { // chord of more than one note out << "]"; if (brokencount > 0) { for (i=0; i'; } } else if (brokencount < 0) { for (i=0; i<-brokencount; i++) { out << '<'; } } } // slur end if (slurQ) { if (strchr(infile[row][col], ')') != NULL) { if (slursuppress > 0) { slursuppress--; } else { out << ")"; } } // phrasing end if (strchr(infile[row][col], '}') != NULL) { out << ")"; } } if (alltie) { infile[row].getToken(buffer, col, 0); if (strchr(buffer, '[') != NULL) { out << "-"; } else if (strchr(buffer, '_') != NULL) { out << "-"; } } } ////////////////////////////// // // printMarks -- print marks accoridng to the color and size . // only the first mark found will be printed, since the mark // colors the note head (this may change later). Rests cannot // be marked (the codes used are changes in note-head shape). // // Marked gracenotes which are halfnotes/wholenote etc probably don't work. // void printMarks(ostream& out, const char* buffer, string& marks, vector > markcolors, int notecount) { if (strchr(buffer, 'r') != NULL) { // no rests allowed. return; } if (strcmp(buffer, ".") == 0) { // just in case: ignore null tokens return; } stringstream firstmark; // print the first mark found on the note as a colored notehead // subsequence marks are circles on the note with increasing // diameter. int i = 0; double markdiameter = 6.0; int m; int markcount = 0; RationalNumber rn; while (buffer[i] != '\0') { for (m=0; m 2.0, then the notehead // is a wholenote or larger, so add one to the horizontal offset // so that the circle is centered on the note. // add this text to code at this point: // exch 1 add exch getNoteShape(rn, buffer); if (rn > 2) { out << "exch 1 add exch "; } out << markcolors[m][0] << " "; out << markcolors[m][1] << " "; out << markcolors[m][2] << " "; out << "setrgbcolor newpath "; out << markdiameter; out << " 360 0 arcn stroke grestore\""; markdiameter += 2.0; } else if (buffer[i] == marks[m]) { // mark a chord note } } i++; } firstmark << ends; out << firstmark.str().c_str(); } ////////////////////////////// // // printNoteHeadShape -- print the head of the note // 0: hd = solid head (quarter note and smaller durantion) // 1: Hd = half-note notehead // 2: HD = whole-note notehead // 3: HDD = rounded breve // 4: breve = square breve // 5: longa = longa // 6: pshhd = sharp note when clef is perc (percussion sharp head) // 7: pflhd = flat note when clef is perc (percussion flat head) // 8: ghd = black head for grace notes (ornaments) // void printNoteHeadShape(ostream& out, const char* buffer, int mindex) { RationalNumber rn; getNoteShape(rn, buffer); if (rn == 0) { // grace note out << "ghd"; usedMarks[mindex][8] = 1; } else if (rn <= 1) { // quarer note or smaller out << "hd"; usedMarks[mindex][0] = 1; } else if (rn <= 2) { // half note out << "Hd"; usedMarks[mindex][1] = 1; } else if (rn <= 4) { // whole note out << "HD"; usedMarks[mindex][2] = 1; } else if (rn <= 8) { // square breve (always not round) out << "breve"; usedMarks[mindex][4] = 1; } else if (rn <= 16) { // longa out << "longa"; usedMarks[mindex][5] = 1; } else { // don't know what large rhythm is... out << "hd"; usedMarks[mindex][0] = 1; } } ////////////////////////////// // // getNoteShape -- return the rhythm of the note with no augmentation dots. // Should do a secondary check on grace notes: If the grace note is a // whole note should be considered? // void getNoteShape(RationalNumber& rn, const char* buffer) { int dotcount = 0; int i = 0; while (buffer[i] != '\0') { if (buffer[i] == '.') { dotcount++; } i++; } rn = Convert::kernToDurationR(buffer); if (dotcount > 0) { rn = rn * (int)(pow(2.0, dotcount)); rn = rn / (int)(pow(2.0, dotcount+1) - 1); } } ////////////////////////////// // // printGraceRhythm -- print the graphically displayed rhythm // for a grace note (not the logical rhythm which is zero). // groupflag: true if the grace note is part of a larger group of // grace notes than just a single grace note. // void printGraceRhythm(ostream& out, const char* buffer, int top, int bot, int groupflag) { int length = strlen(buffer); string buff2; buff2.reserve(length+1+20); int digitQ = 0; char ch; int i; for (i=0; i& accident) { if (base40 < 0) { return; // ignore rests } int octave = base40 / 40; if (strchr(token, 'n') != NULL) { out << "="; return; } if (strchr(token, '_') != NULL) { // don't display the accidental on a continuation tied note return; } if (strchr(token, ']') != NULL) { // don't display the accidental on an ending tied note return; } int cautionary = 0; if (strchr(token, 'X') != NULL) { cautionary = 1; } int acci = Convert::base40ToAccidental(base40); int diatonic = Convert::base40ToDiatonic(base40) % 7; if ((!cautionary) && (acci == accident[diatonic+7*octave])) { return; // the accidental matches the current display, so don't print } switch (acci) { case +2: out << "^^"; return; case +1: out << "^"; return; case 0: if (!nonaturalQ) { out << "="; } return; case -1: out << "_"; return; case -2: out << "__"; return; } } ////////////////////////////// // // printRhythm1 -- handle tuplets later... still buggy at the moment // void printRhythm1(ostream& out, char* buffer, int top, int bot) { int rhythm = getRhythm(buffer); int dots = countDots(buffer); int rtop = 1; int rbot = rhythm; rtop = rtop * bot; rbot = rbot * top; int rtemptop; int rtempbot; if (dots == 1) { rtemptop = 3 * rtop * rbot; rtempbot = 2 * rtop * rbot; rtop = rtemptop; rbot = rtempbot; } // handle other rhythms later if (((rtop % 8) == 0) && ((rbot % 8) == 0)) { rtop /= 8; rbot /= 8; } if (((rtop % 4) == 0) && ((rbot % 4) == 0)) { rtop /= 4; rbot /= 4; } if (((rtop % 2) == 0) && ((rbot % 2) == 0)) { rtop /= 2; rbot /= 2; } if (((rtop % 2) == 0) && ((rbot % 2) == 0)) { rtop /= 2; rbot /= 2; } if (rtop > 1) { out << rtop; } if (rbot > 1) { if (rbot == 2) { out << "/"; // shorthand for a /2 rhythm } else if (rbot == 4) { out << "//"; // shorthand for a /4 rhythm } else { out << "/" << rbot; } } } ////////////////////////////// // // getRhythm -- // int getRhythm(const char* buffer) { int rhythm = 0; int length = strlen(buffer); int i; for (i=0; i& broken, vector& brokendurs, vector& nogracelist, vector& nogracedurs, HumdrumFile& infile) { broken.setSize(nogracelist.getSize()); brokendurs.setSize(nogracelist.getSize()); broken.setAll(0); brokendurs.setAll(0); int i; int row; int col; int row2; int col2; for (i=0; i& broken, vector& brokenrhythm, vector& notedurations, HumdrumFile& infile) { vector& ND = notedurations; broken.setSize(ND.getSize()); broken.setAll(0); brokenrhythm.setSize(ND.getSize()); brokenrhythm.setAll(0); int i; for (i=0; i& notedurations, vector& iindex, int layer, vector >& address, HumdrumFile& infile) { int ilayer = layer - 1; notedurations.setSize(address[ilayer].getSize()); notedurations.setSize(0); // iindex is a list of the indices into address for each sequential // note in the layer. iindex.setSize(address[ilayer].getSize()); iindex.setSize(0); char buffer[128] = {0}; double dur; int row, col; int i; for (i=0; i= 0) { row = address[ilayer][i].row; col = address[ilayer][i].col; infile[row].getToken(buffer, col, 0); if (norhythm(buffer)) { dur = 1.0; // assign quarter note } else { dur = Convert::kernToDuration(buffer); } notedurations.append(dur); iindex.append(i); } } if (debugQ) { int tcount = 0; cout << "%dur\ttoken\n"; for (i=0; i >& noteaddresses, HumdrumFile& infile, const VoiceMap& voiceinfo, const MeasureInfo& measureinfo) { int maxlines = measureinfo.endline - measureinfo.startline + 1; int i, j; // empty the previous contents, if any // the first dimension is given by the calling function and // represents the maximum voice layers in the part for (i=0; i rowinfo; rowinfo.setSize(noteaddresses.getSize()); rowinfo.allowGrowth(0); int notesfound; int layercounter; Coordinate coord; for (i=measureinfo.startline; i<=measureinfo.endline; i++) { if (infile[i].getType() != E_humrec_data) { continue; } rowinfo.setAll(-1); notesfound = 0; layercounter = -1; for (j=0; j= 0) { coord.row = i; coord.col = rowinfo[j]; } noteaddresses[j].append(coord); } } } } ////////////////////////////// // // getMeasureInfo -- only check the primary layer for a staff // Also checks for repeat endings in a particular format. // void getMeasureInfo(vector& measures, HumdrumFile& infile) { int i; int lastmeasure = -1; double duration; PerlRegularExpression pre; measures.setSize(infile.getNumLines()); measures.setSize(0); MeasureInfo mtemp; int measureno; int lastmeasureno = -1; //infile.analyzeRhythm("4"); int currentKey = -100; int datafound = 0; int sindex; vector repeatinfo; vector segments; getRepeatInfo(segments, repeatinfo, infile); for (i=0; i= 0) { mtemp.ending = repeatinfo[sindex]; } else { mtemp.ending = -1; } if (duration > 0) { measures.append(mtemp); } else if (datafound && (mtemp.endline - mtemp.startline > 1)) { // also have to allow for non-rhythm notation // such as Gregorian chant... measures.append(mtemp); } currentKey = -100; } if (sscanf(infile[i][0], "=%d", &measureno) == 1) { lastmeasureno = measureno; } else { lastmeasureno = -1; } } else if (infile[i].getType() == E_humrec_interpretation) { if (strncmp(infile[i][0], "*k[", 3) == 0) { currentKey = Convert::kernKeyToNumber(infile[i][0]); } if (pre.search(infile[i][0], "^\\*[A-G-a-g][-#]?:")) { mtemp.kernkey = infile[i][0]; } } } /// This code is now obsolete, since StartKey is a naughty global variable, /// and the getMeasureInfo is now called before StartKey is set... // if (measures.getSize() > 0) { // measures[0].currkey = StartKey; // } for (i=0; i -50) { measures[i].currkey = measures[i].newkey; } else if (i > 0) { measures[i].currkey = measures[i-1].currkey; } } // a key signature in the first measure is controlled by the // header K: field, so turn off any new key markers in the // first measure. if (measures.getSize() > 0) { measures[0].newkey = -100; } if (Voicemap.getSize() == 1) { identifyWholeRestsInMeasures(measures, Voicemap[0], infile); for (i=1; i& segments, vector& repeatinfo, int mindex) { int i; double measuredur = infile[mindex].getAbsBeat(); double sdur; for (i=0; i& segments, vector& repeatinfo, HumdrumFile& infile) { int i; int length; segments.setSize(100); segments.setSize(0); for (i=0; i", 2) == 0) { segments.append(i); } } } repeatinfo.setSize(segments.getSize()); repeatinfo.setAll(-1); int number; int base; int baselength; for (i=1; i baselength) && (sscanf(&infile[segments[i]][0][length-1], "%d", &number) == 1) && (strncmp(infile[segments[i]][0], infile[segments[base]][0], baselength) == 0)) { repeatinfo[i] = number; i++; } } } for (i=1; i 0) && (repeatinfo[i] < 0)) { repeatinfo[i] = 0; } } if (debugQ) { cout << "% MUSIC SEGMENTS: " << endl; for (i=0; i& measures, VoiceMap& voicemap, HumdrumFile& infile) { int i; for (i=0; i& header, vector& measures, int xval, const char* filename) { parseBibliographic(header, infile); // first indicate the required tune number // which must come as the first record in the header // (excluding comment/blank lines) // Use the number 1 if there is no command-line input if (xval > 0) { out << "X: " << xval << "\n"; } else if (strcmp(header[XX], "") == 0) { out << "X: 1\n"; } else { out << "X: " << header[XX] << "\n"; } int firstmeasure = getFirstMeasureNumber(infile); // display the title next (if given) if (strcmp(header[TT], "") != 0) { out << "T:"; if (filenumQ) { printNumberFromString(out, filename, filenumstring.c_str()); } if (filenametitleQ) { out << " "; printFilenameBase(out, filename); } out << " " << header[TT] << "\n"; } else if (filenametitleQ) { out << "T:"; out << " "; printFilenameBase(out, filename); out << "\n"; } // display the composer next if given if (strcmp(header[CC], "") != 0) { out << "C: " << header[CC] << "\n"; } printAbcExtendedInformationFields(out, infile); if ((barnumberingstyle == 1) && (fabs(infile.getPickupDuration()) > TOLERANCE)) { // do something so that the first bar number will be printed // if there is a pickup measure } if (firstmeasure > 1) { out << "%%setbarnb " << firstmeasure << "\n"; } if (Voicemap.getSize() > 5) { // automatically suppress staff lines in music with more // than 5 staves when the line of music contains only // rests // Doesn't seem to work: //out << "%%staffnonote" << "\n"; } int i; for (i=0; i<26; i++) { switch (i+'A') { case 'X': // number of piece, must come at start of header case 'T': // title already printed case 'C': // composer already printed case 'K': // key of piece, must come at end of header case 'V': // ignore user input of this field (Voice declarations) // ignore break; case 'L': // rhythmic unit already printed // determine and display the rhythmic unit (L: record) if (strcmp(header[LL], "") == 0) { calculateBestRhythmUnit(infile, Ltop, Lbot); if (Lbot == 1) { out << "L: " << Ltop << "\n"; } else { out << "L: " << Ltop << "/" << Lbot << "\n"; } } else { // L field is specified in command-line arguments if (sscanf(header[LL], "%d/%d", &Ltop, &Lbot) == 2) { out << "L: " << Ltop << "/" << Lbot << "\n"; } else if (sscanf(header[LL], "%d", &Ltop) == 1) { Lbot = 1; out << "L: " << Ltop << "\n"; } else { calculateBestRhythmUnit(infile, Ltop, Lbot); if (Lbot == 1) { out << "L: " << Ltop << "\n"; } else { out << "L: " << Ltop << "/" << Lbot << "\n"; } } } break; default: if (strcmp(header[i], "") != 0) { out << char(i+'A') << ": " << header[i] << "\n"; } } } if (Voicemap.getSize() > 1) { // polyphonic music if (Voicemap.getSize() == 2) { // presume a piano staff brace. refine the decision later out << "%%staves {1 2}\n"; } else if (Voicemap.getSize() == 4) { // presume a string-quartet-like system with a brace out << "%%staves [1 2 3 4]\n"; } for (i=0; i=0; i--) { if (filename[i] == '/') { slashi = i + 1; break; } } } else { slashi = 0; } for (i=slashi; i bibfields; bibfields.setSize(infile.getNumLines()); bibfields.setSize(0); int i; for (i=0; i values; string value; value[0] = EMPTY; value.setSize(0); out << "%%abc-version 2.0" << "\n"; out << "%%abcx-abcm2ps-target-version 5.9.1 (29 Sep 2008)" << "\n"; out << "%%abc-creator hum2abc 1.0\n"; struct tm *current; time_t now; time(&now); current = localtime(&now); out << "%%abcx-conversion-date "; out << current->tm_year + 1900 << "/"; if (current->tm_mon+1 < 10) { out << "0"; } out << current->tm_mon + 1 << "/"; if (current->tm_mday < 10) { out << "0"; } out << current->tm_mday << " "; if (current->tm_hour < 10) { out << "0"; } out << current->tm_hour << ":"; if (current->tm_min < 10) { out << "0"; } out << current->tm_min << ":"; if (current->tm_sec < 10) { out << "0"; } out << current->tm_sec << "\n"; // YEC = copyright notice if (findRecord("YEC", value, infile, bibfields)) { out << "%%abc-copyright " << value.getBase() << "\n"; } if (findMultipleRecords("EED", values, infile, bibfields)) { for (i=0; i 1) && graceQ) { if ((!parameterstring.empty()) && (strstr(parameterstring.c_str(), "gracespace") == NULL)) { // only print default gracespace if not in -p option out << "%%gracespace 0 6 6\n"; } } // it would be nice to be able to control the indent from the // data file, but the following does not work in abcm2ps 5.9.1: // out << "%indent 2.25 cm\n"; if (notespacingQ) { // a value of 2.0 will increase the density of the music // a value of 1.0 will stretch out the music // the default value is the square-root of 2 (1.414) out << "%%notespacingfactor " << notespacing << "\n"; } if (veritasQ) { printVeritas(out, infile); } if (landscapeQ) { out << "%%landscape" << "\n"; } if (footerQ) { out << "%%footer \"" << footer << "\"" << "\n"; } if (headerQ) { out << "%%header \"" << footer << "\"" << "\n"; } if (musicscaleQ) { out << "%%scale " << musicscale << "\n"; } if (continueQ) { out << "%%continueall 1" << "\n"; } if (strlen(parameterstring.c_str()) > 0) { string cleanedstring; translateSpecialCharacters(cleanedstring, parameterstring.c_str()); out << cleanedstring.getBase() << "\n"; } // by default measure numbering will be turned on here. // add a user-option control to turn off the bar numbering. // %%barnumbers 0 = number the start of each system // %%barnumbers -1 = don't number measures // %%barnumbers 5 = number every fifth measure // %%barnumbers 1 = number every measure out << "%%barnumbers " << barnumberingstyle << "\n"; if (boxQ) { out << "%%measurebox 1" << "\n"; } // print postscript code for marks if there are any. //if (hasmarksQ) { // printMarkCodes(out, marks, markcolors); // } } ////////////////////////////// // // printMarkCodes -- print codes to use for marks // 0: hd = solid head (quarter note and smaller durantion) // 1: Hd = half-note notehead // 2: HD = whole-note notehead // 3: HDD = rounded breve // 4: breve = square breve // 5: longa = longa // 6: pshhd = sharp note when clef is perc (percussion sharp head) // 7: pflhd = flat note when clef is perc (percussion flat head) // 8: ghd = black head for grace notes (ornaments) void printMarkCodes(ostream& out, string& marks, vector >& markcolors) { if (usedAnnotation) { out << "%%postscript /cp {currentpoint}!\n"; out << "%%postscript /anshow { dup 0 get (:) 0 get ne\n"; out << "%%postscript {/gchshow load cshow}\n"; out << "%%postscript {dup length 1 sub 1 exch "; out << "getinterval cvx exec}ifelse}!\n"; } int i; for (i=0; i 1) { getBibPieces(infile[i][0], infile[i][1], marker, contents); } else { getBibPieces(infile[i][0], "", marker, contents); } sscanf(contents.getBase(), "%lu", &veritas); // don't store this line in the buffers since // it is not counted when calculating the veritas... continue; } } // printing with 0x0a which is the linux version // of newlines. That is the only newline which // I am considering valid. Apple will tend to // used 0x0d in text files, and Windows will use // both to make things difficult. So in any case, // you should have converted the newlines in the // file to linux format before calculating your VTS // entry in the original file if you want hum2abc // to validate the file properly. alllines << infile[i] << char(0x0a); if (infile[i].getType() == E_humrec_data) { onlydata << infile[i] << char(0x0a); } } alllines << ends; onlydata << ends; int lenall = strlen(alllines.str().c_str()); int lendata = strlen(onlydata.str().c_str()); unsigned long crcall = CheckSum::crc32(alllines.str().c_str(), lenall); unsigned long crcdata = CheckSum::crc32(onlydata.str().c_str(), lendata); if (veritas != 0) { if (veritas == crcall) { out << "%%humdrum-veritas " << veritas << "\n"; } else { out << "%%humdrum-veritas " << crcall << "\n"; out << "%%humdrum-veritas-invalid: " << veritas << "\n"; } } else { out << "%%humdrum-veritas " << crcall << "\n"; } out << "%%humdrum-veritas-data " << crcdata << "\n"; } ////////////////////////////// // // findMultipleRecords -- find multiple records with the same key // int findMultipleRecords(const char* key, vector& values, HumdrumFile& infile, vector& bibfields) { values.setSize(bibfields.getSize()); values.setSize(0); string marker; string tempv; tempv.setSize(1); tempv[0] = EMPTY; tempv.setSize(0); const char* ptr; int keylen = strlen(key); int i; for (i=0; i 1) { getBibPieces(infile[bibfields[i]][0], infile[bibfields[i]][1], marker, tempv); } else { getBibPieces(infile[bibfields[i]][0], "", marker, tempv); } if (tempv.getSize() > 1) { values.append(tempv); tempv.setSize(1); tempv[0] = EMPTY; tempv.setSize(0); } else { // do nothing since the record is empty } } } return values.getSize(); } ////////////////////////////// // // findRecord -- // int findRecord(const char* key, string& value, HumdrumFile& infile, vector& bibfields) { string marker; value.setSize(1); value[0] = EMPTY; value.setSize(0); const char* ptr; int keylen = strlen(key); int i; for (i=0; i 1) { getBibPieces(infile[bibfields[i]][0], infile[bibfields[i]][1], marker, value); } else { getBibPieces(infile[bibfields[i]][0], "", marker, value); } if (value.getSize() > 0) { return 1; } else { return 0; } } } return 0; } ////////////////////////////// // // printKeySignature -- // void printKeySignature(ostream& out, HumdrumFile& infile) { int i; int keynum; PerlRegularExpression pre; for (i=0; i& voicemap, HumdrumFile& infile) { voicemap.setSize(200); voicemap.setGrowth(200); voicemap.setSize(0); int i, j; VoiceMap vtemp; for (i=0; i=0; j--) { if (infile[i].getExInterpNum(j) == E_KERN_EXINT) { vtemp.primary = j+1; // primary tracks start at 1 voicemap.append(vtemp); } } break; } for (i=0; i C (composer) // !!!OTL --> T (title) (also XEN = Title Translated into English) // ??? --> A (Area) // ??? --> B (Book) // ??? --> D (Discography) // ??? --> F (Filename) // ??? --> G (Group) // ??? --> H (History) // ??? --> I (Information) // *?: --> K ([initial] Key) // ??? --> N (Notes) // *M?/? --> M ([initial] Meter) // ??? --> O (Origin) // ??? --> R (Rhythm) // ??? --> S (Source) // ??? --> Z (Transcription notes) // Notes: // Only the first !!OTL/XEN record in the file will be used. // Only the first !!COM record in the file will be used. // Only the last !!OMD record in the file will be used. // void parseBibliographic(vector& header, HumdrumFile& infile) { int i; string marker; marker.setSize(100); marker.setGrowth(100); marker.setSize(0); string contents; contents.setSize(100); contents.setGrowth(100); contents.setSize(0); int length; char omdstring[1024] = {0}; vector markers; vector contentses; prepareBibliographicData(markers, contentses, infile); for (i=0; i 0)) || ((strcmp(omdstring, "") != 0))) { if (options.getBoolean("q")) { calculateQRecord(QRecord, -1, omdstring, top, bot); } else { calculateQRecord(QRecord, tempo, omdstring, top, bot); } } if (QRecord.getSize() > 0) { storeHeaderRecord(header, 'Q', QRecord.getBase()); } } } } ////////////////////////////// // // parseBibliographicData -- read in the bibliographic data from the // Humdrum file, and interpret any !!!title: record and place // its parsed contents into the !!!OTL: record. // void prepareBibliographicData(vector& markers, vector& contentses, HumdrumFile& infile) { markers.setSize(infile.getNumLines()); markers.setSize(0); contentses.setSize(infile.getNumLines()); contentses.setSize(0); string marker; string contents; int length; int titleindex = -1; int OTLindex = -1; int i; for (i=0; i 1) { getBibPieces(infile[i][0], infile[i][1], marker, contents); } else { getBibPieces(infile[i][0], "", marker, contents); } length = strlen(contents.getBase()); if (length == 0) { continue; } markers.append(marker); contentses.append(contents); if (strcmp(marker.getBase(), "title") == 0) { titleindex = markers.getSize()-1; } else if (strncmp(marker.getBase(), "OTL", 3) == 0) { OTLindex = markers.getSize()-1; } // cout << "% REF = " << marker.getBase() // << " : " << contents.getBase() // << endl; } if (titleexpansionQ) { length = strlen("title"); marker.setSize(length+1); strcpy(marker.getBase(), "title"); markers.append(marker); length = titleexpansion.size(); contents.setSize(length+1); strcpy(contents.getBase(), titleexpansion.c_str()); contentses.append(contents); titleindex = markers.getSize() - 1; } if (titleindex < 0) { return; } string& ct = contentses[titleindex]; int keyq = 0; string key; key.setSize(100); key.setGrowth(100); key.setSize(0); string newtitle; newtitle.setSize(1000); newtitle.setGrowth(1000); newtitle.setSize(0); char ch; for (i=0; i& markers, vector& contentses) { int i; int j; char ch; for (i=0; i0) { if (isspace(buffer[i-1])) { i--; } else { break; } } buffer[i++] = ' '; while ((*ptr3 != EMPTY) && isspace(*ptr3)) { ptr3++; } while ((i0) { if (isspace(buffer[i-1])) { i--; } else { break; } } buffer[i] = EMPTY; length = strlen(buffer); contents.setSize(length+1); strcpy(contents.getBase(), buffer); } /////////////////////////////// // // calculateQRecord -- assemble the tempo and movement designation // to be stored on a Q: line. Traditional tempo markings are: // 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 63, 66, 69, // 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, // 126, 132, 138, 144, 152, 160, 168, 176, 184, 192, 200, and 208. // Round to the nearest of these markings when converting to a // base duration other than quarter notes. Probably should have a // user flag which prevents this rounding from occurring if an // explicit non-standard tempo marking is desired. (or allow // decimal digits in the tempo as well?) // void calculateQRecord(string& QRecord, double tempo, const char* omdstring, int top, int bot) { char ch; int i; char tempostring[1024] = {0}; QRecord.setSize(1024); QRecord.setSize(0); char textbuffer[1024] = {0}; int length; // probably check the omdstring for quotes and remove/handle them length = strlen(omdstring); if (strlen(omdstring) > 0) { strcpy(textbuffer, "\""); strcat(textbuffer, omdstring); strcat(textbuffer, "\""); translateSpecialCharacters(QRecord, textbuffer); QRecord.setSize(QRecord.getSize()-1); // remove string terminator if (tempo > 0) { ch = ' '; QRecord.append(ch); } } int temportop = 1; int temporbot = 4; double fraction = (double)bot / (double)temporbot; temporbot = bot; tempo = tempo * fraction; if (top % 3 == 0) { // check for compound tempo display if (tempo >= 180) { tempo = tempo / 3.0; temportop = temportop * 3; } } else { // duple type meters if (tempo < 40) { // don't print a ridiculously slow tempo // this is for a duple meter, also do for triple meters... tempo *= 2; temporbot *= 2; } else if (tempo > 208) { // don't print a ridiculously fast tempo // this is for a duple meter, also do for triple meters... tempo /= 2; temportop *= 2; } } simplifyFraction(temportop, temporbot); int tempoint; tempoint = roundTempoToNearestStandard(tempo); // tempoint = int(tempo + 0.5); sprintf(tempostring, "%d/%d=%d", temportop, temporbot, tempoint); if ((!notempoQ) && (tempoint > 0)) { length = strlen(tempostring); for (i=0; i 209.0) { return int(tempo+0.5); } vector standardtempos; standardtempos.setSize(100); int stm; // a standard tempo mark standardtempos.setSize(0); stm = 40; standardtempos.append(stm); stm = 42; standardtempos.append(stm); stm = 44; standardtempos.append(stm); stm = 46; standardtempos.append(stm); stm = 48; standardtempos.append(stm); stm = 50; standardtempos.append(stm); stm = 52; standardtempos.append(stm); stm = 54; standardtempos.append(stm); stm = 56; standardtempos.append(stm); stm = 58; standardtempos.append(stm); stm = 60; standardtempos.append(stm); stm = 63; standardtempos.append(stm); stm = 66; standardtempos.append(stm); stm = 69; standardtempos.append(stm); stm = 72; standardtempos.append(stm); stm = 76; standardtempos.append(stm); stm = 80; standardtempos.append(stm); stm = 84; standardtempos.append(stm); stm = 88; standardtempos.append(stm); stm = 92; standardtempos.append(stm); stm = 96; standardtempos.append(stm); stm = 100; standardtempos.append(stm); stm = 104; standardtempos.append(stm); stm = 108; standardtempos.append(stm); stm = 112; standardtempos.append(stm); stm = 116; standardtempos.append(stm); stm = 120; standardtempos.append(stm); stm = 126; standardtempos.append(stm); stm = 132; standardtempos.append(stm); stm = 138; standardtempos.append(stm); stm = 144; standardtempos.append(stm); stm = 152; standardtempos.append(stm); stm = 160; standardtempos.append(stm); stm = 168; standardtempos.append(stm); stm = 176; standardtempos.append(stm); stm = 184; standardtempos.append(stm); stm = 192; standardtempos.append(stm); stm = 200; standardtempos.append(stm); stm = 208; standardtempos.append(stm); vector& ST = standardtempos; double diff1; double diff2; int i; for (i=1; i flat sign does not work... } else if (strncmp(&(input[i]), "\\n", strlen("\\n")) == 0) { ch = '\n'; output.append(ch); i += strlen("\\n"); } else if (strncmp(&(input[i]), "-flat", strlen("-flat")) == 0) { ch = '\\'; output.append(ch); ch = 'b'; output.append(ch); i += strlen("-flat"); // \# --> sharp sign in titles does work. } else if (strncmp(&(input[i]), "-sharp", strlen("-sharp")) == 0) { ch = '\\'; output.append(ch); ch = '#'; output.append(ch); i += strlen("-sharp"); // Haven't tested if \= --> natural sign works. } else if (strncmp(&(input[i]), "-natural", strlen("-natural")) == 0) { ch = '\\'; output.append(ch); ch = '='; output.append(ch); i += strlen("-natural"); // other accented characters } else if (strncmp(&(input[i]), "ç", strlen("ç")) == 0) { ch = '\\'; output.append(ch); ch = 'c'; output.append(ch); ch = 'c'; output.append(ch); i += strlen("ç"); } else if (strncmp(&(input[i]), "&Ccdeil;", strlen("&Ccdeil;")) == 0) { ch = '\\'; output.append(ch); ch = 'C'; output.append(ch); ch = 'C'; output.append(ch); i += strlen("&Ccdeil;"); } else if (strncmp(&(input[i]), "ß", strlen("ß")) == 0) { ch = '\\'; output.append(ch); ch = 's'; output.append(ch); ch = 's'; output.append(ch); i += strlen("ß"); } // other accented characters which map to non-number strings: /* Need to add these * Ntilde = \~N, ntilde = \~n * oslash = \/o, Oslash = \/O * ocirc = \^o, Ocirc = \^O * aelig = \ae, AElig = \AE, OElig = \OE, oelig = \oelig * Also, others less common ones like thorn, Atilde, etc. * -sharp -> sharp sign, likewise for -flat and -natural */ ch = input[i]; output.append(ch); } ch = EMPTY; output.append(ch); } ////////////////////////////// // // storeHeaderRecord -- // typedef char const * charstring; void storeHeaderRecord(vector& header, char recordletter, const char* inputstring) { string parsedstring; translateSpecialCharacters(parsedstring, inputstring); int length = strlen(parsedstring.getBase()); int index = recordletter - 'A'; int startindex = 0; while (startindex0; i--) { if (isspace(header[index][i])) { header[index][i] = EMPTY; } else { break; } } } ////////////////////////////// // // getBibPieces -- // void getBibPieces(const char* string, const char* string2, string& marker, string& contents) { char bufftemp[strlen(string) + strlen(string2)]; strcpy(bufftemp, string); strcat(bufftemp, string2); char ch; char empty = EMPTY; marker.setSize(1); contents.setSize(1); marker[0] = EMPTY; contents[0] = EMPTY; marker.setSize(0); contents.setSize(0); int length = strlen(bufftemp); if (length < 4) { return; } int i = 3; while ((i0; i--) { if (isspace(contents[i])) { contents[i] = empty; contents.setSize(i); } else if (contents[i] == 0x0d) { contents[i] = empty; contents.setSize(i); } else if (contents[i] == 0x0a) { contents[i] = empty; contents.setSize(i); } else if (contents[i] == EMPTY) { contents[i] = empty; contents.setSize(i); } else { break; } } contents.append(empty); char buffer[1024]; strcpy(buffer, contents.getBase()); translateSpecialCharacters(contents, buffer); } ////////////////////////////// // // calculateBestRhythmUnit -- // void calculateBestRhythmUnit(HumdrumFile& infile, int& Ltop, int& Lbot) { int i, j; int tcount; char buffer[128] = {0}; vector rhythms; rhythms.setSize(17); rhythms.allowGrowth(0); rhythms.setAll(0); int rhythm; double duration; for (i=0; i 1) { infile[i].getToken(buffer, j, 0); if (norhythm(buffer)) { duration = 1.0; } else { duration = Convert::kernToDuration(buffer); } } else { if (norhythm(buffer)) { duration = 1.0; } else { duration = Convert::kernToDuration(infile[i][j]); } } Convert::durationToKernRhythm(buffer, duration); if (sscanf(buffer, "%d", &rhythm) == 1) { if (rhythm >= 16) { rhythm = 16; } else if (rhythm >= 8) { rhythm = 8; } else if (rhythm >= 4) { rhythm = 4; } else { rhythm = 0; } rhythms[rhythm] += tcount; } } } int maxx = 8; if (rhythms[4] > rhythms[8]) { maxx = 4; } if (rhythms[16] > rhythms[maxx]) { maxx = 16; } if ((maxx == 4) && (rhythms[4] < rhythms[8] + rhythms[16])) { maxx = 8; } Ltop = 1; Lbot = maxx; } ////////////////////////////// // // base40ToAbcPitch -- convert a base40 pitch to ABC format // C=c4, c=c5, c'=c6, C,=c3, C,,=c2, c''=c7 // The pitch is really ambiguous, since in a bass clef, c can // be c3 rather than c5. // char* base40ToAbcPitch(char* buffer, int base40) { if (base40 < 0) { strcpy(buffer, "z"); return buffer; } int octave = base40 / 40; int diatonic = Convert::base40ToDiatonic(base40) % 7; // int accidental = Convert::base40ToAccidental( // the accidental will not be printed with this // function, you will have to decide from the // musical context if the accidental should be // displayed or suppressed based on the key // signature; char dianame[2]; strcpy(dianame, "x"); switch (diatonic) { case 0: strcpy(dianame, "c"); break; case 1: strcpy(dianame, "d"); break; case 2: strcpy(dianame, "e"); break; case 3: strcpy(dianame, "f"); break; case 4: strcpy(dianame, "g"); break; case 5: strcpy(dianame, "a"); break; case 6: strcpy(dianame, "b"); break; } if (octave < 5) { dianame[0] = std::toupper(dianame[0]); } strcpy(buffer, dianame); switch (octave) { case 6: strcat(buffer, "'"); break; case 7: strcat(buffer, "''"); break; case 8: strcat(buffer, "'''"); break; case 9: strcat(buffer, "''''"); break; case 3: strcat(buffer, ","); break; case 2: strcat(buffer, ",,"); break; case 1: strcat(buffer, ",,,"); break; case 0: strcat(buffer, ",,,,"); break; } return buffer; } ////////////////////////////// // // storeOptionSet -- // void storeOptionSet(Options& opts) { opts.define("debug=b", "Print extract debugging statements"); opts.define("m|measures-per-line=i:1", "Number of measures to print on a text line"); opts.define("header=s:", "Command string for printing headers on pages"); opts.define("f|footer=s:", "Command string for printing footers on pages"); opts.define("label=b", "Explicitly write measure number for every bar"); opts.define("dir|directory=s", "directory for reading files from"); opts.define("mask=s:.krn", "filemask for reading from a directory"); opts.define("no-invisible=b", "Print all invisible items as visible"); opts.define("no-veritas=b", "Don't calculate veritas data"); opts.define("filenum=s:", "Prepend a filenumber value infront of title"); opts.define("filetitle=b", "Print filename at start of title field"); opts.define("TT=s:", "Title expansion"); opts.define("no-grace=b", "Suppress gracespace redefinition"); opts.define("no-tempo=b", "Suppress tempo marking at start of music"); opts.define("no-slur|no-slurs|noslur|noslurs|ns=b", "Suppress displaying all slurs in music"); opts.define("no-mark|no-marks|nomark|nomarks=b", "Suppress marks"); opts.define("nn|no-auto-natural=b", "Suppress automatic naturals"); opts.define("linebreak=b", "Break lines at !linebreak tokens"); opts.define("db|data-barnum=b", "display all bar numbers in data"); opts.define("cb|comment-barnum=b", "display all bar numbers as comments"); opts.define("k|key=b", "use key designation as key signature"); // abcm2ps parameter settings opts.define("box=b", "Put boxes around measure numbers"); opts.define("no-autoformat=b", "Linewrap music same as in text"); opts.define("spacing=d:1.141", "Note spacing factor"); opts.define("n|barnums=i:0", "Measure numbering control"); opts.define("s|scale=d:0.75", "Set the music scaling factor"); opts.define("landscape=b", "Rotate the music 90 degrees"); opts.define("p=s:", "Parameter strings to echo into header"); opts.define("no-indent=b", "Not actually used directly"); opts.define("A=s", "A header record (Area -- origin inside of country)"); opts.define("B=s", "B header record (Book -- source publication)"); opts.define("C=s", "C header record (Composer)"); opts.define("D=s", "D header record (Discography)"); opts.define("E=s", "E header record"); opts.define("F=s", "F header record (Filename)"); opts.define("G=s", "G header record (Group -- instrumentation)"); opts.define("H=s", "H header record (History -- source notes)"); opts.define("I=s", "I header record (Information)"); opts.define("J=s", "J header record"); opts.define("K=s", "K header record (Key signature)"); opts.define("L=s", "L header record"); opts.define("M=s", "M header record (Meter)"); opts.define("N=s", "N header record (Notes)"); opts.define("O=s", "O header record (Origin -- country of origin)"); opts.define("P=s", "P header record"); opts.define("Q=s", "Q header record"); opts.define("q=b", "don't display tempo, only OMD record"); opts.define("R=s", "R header record (Rhythm)"); opts.define("S=s", "S header record (Source -- source of data)"); opts.define("T=s", "T header record (Title)"); opts.define("U=s", "U header record"); opts.define("V=s", "V header record"); opts.define("W=s", "W header record"); opts.define("X=s", "X header record (tune serial number)"); opts.define("Y=s", "Y header record"); opts.define("Z=s", "Z header record (Transcription note)"); opts.define("author=b", "author of program"); opts.define("version=b", "compilation info"); opts.define("example=b", "example usages"); opts.define("h|help=b", "short description"); } ////////////////////////////// // // checkOptions -- // void checkOptions(Options& opts, int argc, char* argv[], int fcount, HumdrumFile& cinfile) { opts.reset(); storeOptionSet(opts); opts.setOptions(argc, argv); opts.process(); debugQ = opts.getBoolean("debug"); // needed before final processing int tdirectoryQ = opts.getBoolean("directory"); HumdrumFileSet tempfile; if (opts.getArgumentCount() == 0) { if (!tdirectoryQ) { tempfile.read(std::cin); } } else { tempfile.read(opts.getArg(fcount)); } int i; string marker; string contents; opts.reset(); storeOptionSet(opts); opts.setOptions(1, argv); // store only the command name if (!tdirectoryQ) { for (i=0; i 1) { getBibPieces(tempfile[0][i][0], tempfile[0][i][1], marker, contents); } else { getBibPieces(tempfile[0][i][0], "", marker, contents); } if (debugQ) { cout << "%Command-line addition: " << marker.getBase() << " :: " << contents.getBase() << endl; } opts.appendOptions(contents.getBase()); } } } } opts.appendOptions(argc-1, argv+1); // exclude the command name opts.process(); // handle basic options: if (opts.getBoolean("author")) { cout << "Written by Craig Stuart Sapp, " << "craig@ccrma.stanford.edu, October 2008" << endl; exit(0); } else if (opts.getBoolean("version")) { cout << argv[0] << ", version: 11 October 2008" << endl; cout << "compiled: " << __DATE__ << endl; cout << MUSEINFO_VERSION << endl; exit(0); } else if (opts.getBoolean("help")) { usage(opts.getCommand().c_str()); exit(0); } else if (opts.getBoolean("example")) { example(); exit(0); } // vector header; Header.setSize(26); for (i=0; i 0) { delete [] Header[i]; Header[i] = new char[length+1]; } storeHeaderRecord(Header, char('A'+i), opts.getString(optchar).c_str()); } debugQ = opts.getBoolean("debug"); slurQ = !opts.getBoolean("no-slur"); linemeasure = opts.getInteger("measures-per-line"); if (linemeasure < 0) { linemeasure = 1; } barnumberingstyle = opts.getInteger("barnums"); if (strcmp(opts.getString("barnums").c_str(), "none") == 0) { barnumberingstyle = -1; } continueQ = !opts.getBoolean("no-autoformat"); footerQ = opts.getBoolean("footer"); headerQ = opts.getBoolean("header"); if (footerQ) { footer = opts.getString("footer").c_str(); } if (headerQ) { header = opts.getString("header").c_str(); } labelQ = opts.getBoolean("label"); graceQ = !opts.getBoolean("no-grace"); notempoQ = opts.getBoolean("no-tempo"); markQ = !opts.getBoolean("no-mark"); veritasQ = !opts.getBoolean("no-veritas"); boxQ = opts.getBoolean("box"); databarnumQ = opts.getBoolean("data-barnum"); commentbarnumQ = opts.getBoolean("comment-barnum"); keyQ = opts.getBoolean("key"); if (strchr(opts.getString("barnums").c_str(), 'b') != NULL) { // the presence of a b after the measure number option // indicates that the user wants the bar numbers enclosed // in a box. boxQ = 1; } notespacingQ = opts.getBoolean("spacing"); notespacing = opts.getDouble("spacing"); invisibleQ = !opts.getBoolean("no-invisible"); if (!invisibleQ) { invisiblerest = "z"; } directoryQ = opts.getBoolean("directory"); directoryname = opts.getString("directory").c_str(); filemask = opts.getString("mask").c_str(); landscapeQ = opts.getBoolean("landscape"); musicscaleQ = opts.getBoolean("scale"); musicscale = opts.getDouble("scale"); parameterstring = opts.getString("p").c_str(); if (strstr(parameterstring.c_str(), "nostems") != NULL) { stemlessQ = 1; } filenumQ = opts.getBoolean("filenum"); filenumstring = opts.getString("filenum").c_str(); filenametitleQ = opts.getBoolean("filetitle"); titleexpansionQ = opts.getBoolean("TT"); titleexpansion = opts.getString("TT").c_str(); nonaturalQ = opts.getBoolean("no-auto-natural"); linebreakQ = opts.getBoolean("linebreak"); if (linebreakQ) { continueQ = 0; // don't allow abcm2ps to reformat the lines of music linemeasure = 10000; // set measures per line very high } } ////////////////////////////// // // example -- example function calls to the program. // void example(void) { } ////////////////////////////// // // usage -- command-line usage description and brief summary // void usage(const char* command) { } // md5sum: ed4503ca347bcafb5950c561c3ab3533 hum2abc.cpp [20140731]