// // Programmer: Craig Stuart Sapp // Creation Date: Wed Jun 2 23:30:21 PDT 2010 // Last Modified: Fri Dec 24 16:48:57 PST 2010 Continued implementation // Last Modified: Sat Feb 5 14:57:09 PST 2011 Added text for lyrics // Last Modified: Mon Feb 28 06:30:23 PST 2011 Added --footer // Last Modified: Mon Mar 7 19:19:44 PST 2011 Added LO:N:smx/Y;bmx/Y // Last Modified: Fri Mar 25 11:37:46 PDT 2011 Macro expansion adjustments // Last Modified: Fri Apr 1 07:54:48 PDT 2011 Hanging tie endings // Last Modified: Wed Apr 6 19:54:37 PDT 2011 Added --vz // Last Modified: Thu Apr 14 21:44:11 PDT 2011 Added *rscale processing // Last Modified: Thu Aug 18 14:43:25 PDT 2011 Added segno sign on barlines // Last Modified: Wed Aug 31 16:59:11 PDT 2011 Added \+ infront of # text // Last Modified: Sat Sep 24 19:21:17 PDT 2011 Added -x option // Last Modified: Fri May 25 11:02:26 PDT 2012 Added "P C0:x1" print suggestions // Last Modified: Mon Jul 16 16:21:25 PDT 2012 Added unterminated tie handling // Last Modified: Mon Aug 20 14:18:50 PDT 2012 Added -textvadd // Last Modified: Tue Aug 21 09:36:43 PDT 2012 Added line extensions // Last Modified: Mon Aug 19 15:12:09 PDT 2013 Added *elision controls // Last Modified: Fri Jan 3 14:34:21 PST 2014 Added RDF**kern:j tied group // Last Modified: Wed Jan 6 19:51:34 PST 2016 Added left/right quotes // Filename: ...sig/examples/all/hum2muse.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/hum2muse.cpp // Syntax: C++; museinfo // // Description: Converts Humdrum files into MuseData files. // #include "humdrum.h" #include "CheckSum.h" #include "MuseData.h" #include "MuseDataSet.h" #include "PerlRegularExpression.h" #include #include #include /* for current time/date */ #include #include #include #include using namespace std; ////////////////////////////////////////////////////////////////////////// // // Layout codes // #define LO_WEDGE_START "HP" #define LO_WEDGE_END "HPZ" ////////////////////////////////////////////////////////////////////////// class Coord { public: Coord(void) { i = j = -1; } int i; int j; }; ////////////////////////////////////////////////////////////////////////// class LayoutParameters { public: LayoutParameters (void) { clear(); } void clear (void); void parseLayout (HumdrumFile& infile, Array& layout); int getParameter (int codeindex, const char* searchkey); void addEntry (LayoutParameters& param, int index); int getSize (void) {return code.getSize(); } int getCodeSize (int index) {return key[index].getSize();} Array& getCode (int index) { return code[index]; } Array >& getKeys (int index) { return key[index]; } Array >& getValues (int index) { return value[index]; } Array& getKey (int i1, int i2) { return key[i1][i2]; } Array& getValue (int i1, int i2) { return value[i1][i2]; } int appendCode (const char* codeName); int addKeyValue (int index, const char* keystr, const char* valuestr); int hasKeyName (int codeindex, const char* kname); int getKeyValueInt(int codeindex, const char* kname); ostream& print (ostream& out, int index=-1); protected: void prepareCode(const char* xcode, const char* params); private: Array > code; Array > > key; Array > > value; }; ostream& operator<<(ostream& out, LayoutParameters& lp) { return lp.print(out); } ostream& LayoutParameters::print(ostream& out, int index) { int i,j; if (index >= 0) { out << "@LO:" << getCode(index) << ":"; for (j=0; j 0) { out << "=" << value[index][j]; } if (j < getCodeSize(index)-1) { out << ":"; } } out << "\n"; return out; } for (i=0; i 0) { out << "=" << value[i][j]; } if (j < getCodeSize(i)-1) { out << ":"; } } out << "\n"; } return out; } int LayoutParameters::getKeyValueInt(int codeindex, const char* kname) { int i; for (i=0; i& layout) { PerlRegularExpression pre; int i; int ii; int jj; clear(); char codename[128] = {0}; for (i=0; i > tokens; pre.getTokens(tokens, ":", params); key.increase(1); value.increase(1); key.last().setSize(tokens.getSize()); value.last().setSize(tokens.getSize()); PerlRegularExpression pre2; int i; for (i=0; i& outfiles, HumdrumFile& infile); void getKernTracks (Array& tracks, HumdrumFile& infile); void convertTrackToMuseData (MuseData& musedata, int track, HumdrumFile& infile, Array& tpq, int& tickpos, int counter, int total, int printFirstMeasureQ); void getMeasureInfo (Array& measures, HumdrumFile& infile); void getMeasureData (MuseData& tempdata, int track, HumdrumFile& infile, int startline, int stopline, int tpq, int& tickpos, int& measuresuppress, int& founddataQ, Array >& lastVerse); int getMaxVoices (HumdrumFile& infile, int startline, int stopline, int track); void addMeasureEntry (MuseData& tempdata, HumdrumFile& infile, int line, int spine); void processVoice (MuseData& tempdata, HumdrumFile& infile, int startline, int stopline, int track, int voice, int tpq, int& tickpos, int& measuresuppress, int& founddataQ, int starttick, int stoptick, Array >& lastVerse); int getGlobalTicksPerQuarter (HumdrumFile& infile); int getTick (int tpq, HumdrumFile& infile, int line); int addNoteToEntry (MuseData& tempdata, HumdrumFile& infile, int row, int col, int tpq, int voice, int opentie, Array >& lastVerse); int getTickDur (int tpq, HumdrumFile& infile, int row, int col); void addBackup (MuseData& tempdata, int backticks, int tpq); void checkColor (Array& colorchar, MuseRecord& arecord, const char* token, Array& colorout); void setColorCharacters (HumdrumFile& infile, Array& colorchar, Array& colorout); void setNoteheadShape (MuseRecord& arecord, const char* token); void insertHeaderRecords (HumdrumFile& infile, MuseData& tempdata, int track, int counter, int total); void addCopyrightLine (HumdrumFile& infile, MuseData& tempdata); void addControlNumber (HumdrumFile& infile, MuseData& tempdata); void addTimeStamp (MuseData& tempdata); void addDateAndEncoder (HumdrumFile& infile, MuseData& tempdata); void addWorkNumberInfo (HumdrumFile& infile, MuseData& tempdata); void addSourceRecord (HumdrumFile& infile, MuseData& tempdata); void addWorkTitle (HumdrumFile& infile, MuseData& tempdata); void addMovementTitle (HumdrumFile& infile, MuseData& tempdata); void addPartName (HumdrumFile& infile, int track, MuseData& tempdata); void insertDollarRecord (HumdrumFile& infile, int line, MuseData& musedata, int track, int counter, int total, int tpq); int appendKeySignature (char* buffer, HumdrumFile& infile, int line, int track); int appendClef (char* buffer, HumdrumFile& infile, int line, int track); int isBarlineBeforeData (HumdrumFile& infile, int startindex); char getColorCategory (const char* color); int appendTimeSignature (char* buffer, HumdrumFile& infile, int line, int track, int tpq); void printMuse2PsOptions (HumdrumFile& infile); void getPartNames (HumdrumFile& infile, Array >& PartNames); void addChordLevelArtic (MuseData& tempdata, MuseRecord& arecord, MuseRecord& printsuggestion, HumdrumFile& infile, int row, int col, int voice); void addNoteLevelArtic (MuseRecord& arecord, HumdrumFile& infile, int row, int col, int tnum); void setupMusicaFictaVariables(HumdrumFile& infile); void getBeamState (Array > >& beams, Array > >& layout, Array >& glayout, Array >& clefs, HumdrumFile& infile); void countBeamStuff (const char* token, int& start, int& stop, int& flagr, int& flagl); void getMeasureDuration (HumdrumFile& infile, Array& rns); int isPowerOfTwo (RationalNumber& rn); void getTupletState (Array& hasTuplet, Array >& TupletState, Array >& TupletTopNum, Array >& TupletBotNum, HumdrumFile& infile); void primeFactorization (Array& factors, int input); char getBase36 (int value); int getTupletTop (HumdrumFile& infile, int row, int col); void storeLayoutInformation (HumdrumFile& infile, int line, Array > >& laystate, Array > >& layout, int initializeQ); void addLayoutInfo (HumdrumFile& infile, int line, Array > > &laystate); void handleLayoutInfoChord (MuseData& tempdata, HumdrumFile& infile, int row, int col, LayoutParameters& lp, int voice); void insertSlurUpPrintSug (MuseData& data, MuseRecord& prec); void insertSlurDownPrintSug (MuseData& data, MuseRecord& prec); void insertStaccatoSuggestion (MuseData& indata, MuseRecord& prec, int direction); void getPitches (Array& pitches, HumdrumFile& infile, int row, int col); int numbersort (const void* A, const void* B); int numberRsort (const void* A, const void* B); void getChordMapping (Array& chordmapping, HumdrumFile& infile, int row, int col); void getDynamicsAssignment (HumdrumFile& infile, Array& DynamicsAssignment, int& dynamicsQ); void addDynamics (HumdrumFile& infile, int row, int col, MuseData& indata, MuseRecord& prec, Array& DynamicsAssignment, LayoutParameters& lp); void addMovementDesignation (char* buffer, HumdrumFile& infile, int line); void convertKernLODYtoMusePS (char* buffer, Array >& keys, Array >& values); void processDynamicsLayout (int loc, MuseRecord& prec, LayoutParameters& lp); int rnsort (const void* A, const void* B); void getRhythms (Array& rns, HumdrumFile& infile, int startindex, int stopindex); void prepareLocalTickChanges (HumdrumFile& infile, Array& ticks); int LCM (Array& rhythms); int GCD (int a, int b); int getTPQByRhythmSet (Array& rhys); void adjustClefState (HumdrumFile& infile, int line, Array& clefstate); void storeClefInformation (HumdrumFile& infile, int line, Array& clefstate, Array >& clefs); void insertArpeggio (MuseData& tempdata, HumdrumFile& infile, int row, int col); int getScoreStaffVerticalPos (int note, int line, int row, Array >& clefs, HumdrumFile& infile); void processMeasureLayout (MuseData& tempdata, HumdrumFile& infile, int line, int col, LayoutParameters& lp, LayoutParameters& glp); void storeGlobalLayoutInfo (HumdrumFile& infile, int line, Array& glaystate, Array >& glayout); void addGlobalLayoutInfo (HumdrumFile& infile, int line, Array& glaystate); void handleMeasureLayoutParam (MuseData& tempdata, HumdrumFile& infile, LayoutParameters& lp, int index); void addHairpinStops (MuseData& tempdata, HumdrumFile& infile, int row, int col); void addHairpinStarts (MuseData& tempdata, HumdrumFile& infile, int row, int col); void addDecrescendoStop (MuseData& tempdata, LayoutParameters& lp); void addCrescendoStop (MuseData& tempdata, LayoutParameters& lp); void addCrescendoStart (MuseData& tempdata, LayoutParameters& lp); void addDecrescendoStart (MuseData& tempdata, LayoutParameters& lp); void getXxYy (Array& vals, Array& states, Array >& keys, Array >& values); void addPositionInfo (MuseData& tempdata, int column, LayoutParameters lp, const char* code); void addCrescText (MuseData& tempdata, HumdrumFile& infile, int row, int col, int dcol, LayoutParameters& lp, const char* text); void addPositionParameters (MuseData& tempdata, int column, Array >& keys, Array >& values); void addTextLayout (MuseData& tempdata, HumdrumFile& infile, int row, int col, LayoutParameters& lp, const char* code); void addText (MuseData& tempdata, Array >& keys, Array >& values); int addDashing (MuseData& tempdata, int column, int track, LayoutParameters& lp); void addUnDash (MuseData& tempdata, HumdrumFile& infile, int row, int col, int dcol, LayoutParameters& lpd); void setupTextAssignments (HumdrumFile& infile, int& textQ, Array >& TextAssignment, Array >& TextElisions, string& textspines); void track2column (Array& trackcol, HumdrumFile& infile, int row); void addLyrics (MuseRecord& arecord, HumdrumFile& infile, int row, int col, Array >& TextAssignment, Array >& TextElisions, Array >& lastVerse); void convertHumdrumTextToMuseData(Array & text); void convertHtmlTextToMuseData(Array & text); void getWorkAndMovement (Array& work, Array& movment, HumdrumFile& infile); void printWithMd5sum (MuseData& datafile); void appendReferenceRecords (MuseData& musedata, HumdrumFile& infile); RationalNumber getDuration (const char* input, const char* def); RationalNumber getDurationNoDots(const char* input, const char* def); void processLyricsSpines (Array >& spineinfo, HumdrumFile& infile, const char* data); void verifyMuseDataFile (const char* filename); void verifyMuseDataFile (istream& input); void verifyMuseDataFiles (Options& options); int verifyPart (MuseData& part); void updateMuseDataFileTimeStamps(Options& options); void removeOldTimeStamps (MuseData& part); int getTimeStampLine (MuseRecord& arecord, MuseData& part); void getNewLine (Array& newline, MuseData& part); void updateFileTimeStamps (const char* filename, Options& options); void updateFileTimeStamps (istream& input, Options& options); void doUpdateWork (MuseDataSet& mds); void updatePart (MuseData& part); void getCheckSum (Array& checksum, MuseData& part, Array& newline); void appendToEndOfPart (MuseData& md, MuseRecord& arecord); void addHumdrumVeritas (MuseData& musedata, HumdrumFile& infile); void setGracenoteVisualRhythm (MuseRecord& arecord, const char* token); void convertKernLONtoMusePS (char* buffer, Array >& keys, Array >& values, const char* token); void printFooter (MuseData& musedata, HumdrumFile& infile, const char* footertext); void cleanFooterField (Array& footerline, HumdrumFile& infile); void substituteReferenceRecord(Array& string, const char* refstring, const char* extension, HumdrumFile& infile); void getReferenceValue (Array& value, Array& refkey, HumdrumFile& infile); void hideNotesAfterLong (HumdrumFile& infile, int row, int col); void analyzeForwardTieConditions (Array > >& tiestate, HumdrumFile& infile); void flipNameComma (Array& value); void getRscaleState (HumdrumFile& infile, Array >& rscale); void filterOptions (Array& options, const char* filter); void addRepeatLines (MuseData& tempdata, HumdrumFile& infile, LayoutParameters& lp, int index); int isTopStaffOnSystem (HumdrumFile& infile, int row, int col); int isTopStaffOnSystem (int track); int isBottomStaffOnSystem (HumdrumFile& infile, int row, int col); void processGlobalComment (HumdrumFile& infile, int line, MuseData& tempdata, int track); void printRehearsalMark (MuseData& tempdata, LayoutParameters& lp, int index); int printUnterminatedTies (MuseData& tempdata, HumdrumFile& infile, int line, int track); void printPrehangTie (MuseRecord& arecord, const char* buffer, HumdrumFile& infile, int row, int col, int voice); void addTextVertcialSpace (Array& ostring, HumdrumFile& infile); int needsWordExtension (HumdrumFile& infile, int row, int notecol, int versecol, Array& verse); void markAllNotesInTiedGroup (HumdrumFile& infile, char groupchar); void adjustRscales (HumdrumFile& infile, int line, Array& rscales); void getNoteState (Array& states, HumdrumFile& infile, int startline, int endline); void analyzeTacet (Array >& tacet, HumdrumFile& infile); string getInstrumentAbbreviation(HumdrumFile& infile, int track); Array > > TieConditionsForward; // User interface variables: Options options; int debugQ = 0; // used with --debug option int roundQ = 0; // used with --round option int slurQ = 1; // used with --no-slurs option int dynamicsQ = 1; // used with --no-dynamics option int sfzQ = 1; // used with --no-dynamics option int textQ = 1; // used with --no-text option string TextSpines = ""; // used with --text option int metQ = 1; // used with --no-met option int verselimit = 6; // used with --vl option int mensuralQ = 0; // used with --mensural option int abbreviationQ = 0; // used with --abbreviation option int sysabbrQ = 0; // used with -a option int mensural2Q = 0; // used with --mensural2 option int referenceQ = 1; // used with -R option int noinvisibleQ = 0; // used with --no-invisible option int beamQ = 1; // used with --no-beams option int tieQ = 1; // used with --no-ties option int excludeQ = 0; // used with -x option string excludeString = ""; // used with -x option int tupletQ = 1; // used with -no-tuplets option int vzQ = 0; // used with --vz option int hangtieQ = 1; // used with --no-hang-tie option int composerQ = 1; // used with -C option int titleQ = 1; // used with -T option int checksumQ = 0; // used with --no-checksum option int textvaddQ = 0; // used with --textvadd option int sepbracketQ = 0; // used with --sepbracket option int extensionQ = 1; // used with --no-extentions option int ignoreTickError = 0; // used with --dd option int footerQ = 0; // used with --footer option string workNumber = ""; // used with --wk option string movementNumber = ""; // used with --mv option string footertext = ""; // used with --footer option string defaultDur = "4"; // used with --dd option string LyricSpines = ""; // used with --ls option string Encoder = ""; // used with --encoder option int encoderQ = 0; // used with --encoder option int copyrightQ = 0; // used with --copyright option const char* Copyright = ""; // used with --copyright option Array Colorchar; // charcter in **kern data which causes color Array Colorout; // converted charcter in col 14 of MuseData Array > PartNames; int hasFictaQ = 0; // used with !!!RDF**kern: i=musica ficta char FictaChar = 'i'; int hasLongQ = 0; // used with !!!RDF**kern: l=long note char LongChar = 'l'; int tiedgroupQ = 0; // used with !!!RDF**kern: j=single notehead char TiedGroupChar = 'j'; Array > > BeamState; Array hasTuplet; Array > TupletState; Array > TupletTopNum; Array > TupletBotNum; Array MeasureDur; // used to decide centered whole rest Array > > LayoutInfo; Array > GlobalLayoutInfo; int LastTPQPrinted; // used for tpqs that change const char* muse2psoptionstring = ""; // used with --mo option Array NEWLINE; // used to control the newline style Array > TextAssignment; // used to keep track of lyics by staff Array > TextElisions; // used to keep track of elision display Array DynamicsAssignment; Array > ClefState; // which clef is being used for every note Array DashState; // used for turning off dashed lines Array > RscaleState; // used for *rscale processing Array KernTracks; Array > TacetState; int autoTacetQ = 1; // muse2ps =k options: Display alternative options unsigned int kflag = 0; #define k_active (1) #define k_sfz (1 << 1) // on = display as sfz #define k_subedit (1 << 2) // on = editorial data looks like regular data #define k_noedit (1 << 3) // on = hide editorla data from print #define k_roman (1 << 4) // on = editorial marks in times-roman font #define k_ligature (1 << 5) // on = text ligatures #define k_fbass (1 << 6) // on = figured bass above staff #define k_lrbar (1 << 7) // on =:|!|:, off =:!!: #define k_augdot (1 << 8) // on = don't share aug. dots between voices #define k_newkey (1 << 9) // on = allow same key sig to repeat on staff #define k_chord (1 << 10) // on = mixed rhythm chords #define k_hidekey (1 << 11) // on = hide key signatures #define k_clef (1 << 12) // on = clef changes are normal size not cue size ////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { NEWLINE.setSize(3); NEWLINE[0] = (char)0x0d; NEWLINE[1] = (char)0x0a; NEWLINE[2] = '\0'; HumdrumFile infile; // initial processing of the command-line options checkOptions(options, argc, argv); if (options.getArgCount() < 1) { infile.read(cin); } else { infile.read(options.getArg(1).c_str()); } infile.analyzeRhythm("4"); setColorCharacters(infile, Colorchar, Colorout); DashState.setSize(infile.getMaxTracks()+1); DashState.setAll(0); getKernTracks(KernTracks, infile); setupTextAssignments(infile, textQ, TextAssignment, TextElisions, TextSpines); setupMusicaFictaVariables(infile); getBeamState(BeamState, LayoutInfo, GlobalLayoutInfo, ClefState, infile); getTupletState(hasTuplet, TupletState, TupletTopNum, TupletBotNum, infile); getMeasureDuration(infile, MeasureDur); Array outfiles; outfiles.setSize(0); getRscaleState(infile, RscaleState); if (dynamicsQ) { getDynamicsAssignment(infile, DynamicsAssignment, dynamicsQ); // dynamicsQ may become false if no dynamics found } getPartNames(infile, PartNames); analyzeForwardTieConditions(TieConditionsForward, infile); if (autoTacetQ) { analyzeTacet(TacetState, infile); } convertData(outfiles, infile); printMuse2PsOptions(infile); // must come after convertData() // eventual the footer should go here, but it is not currently // echoed with =M option. //if (footerQ) { // printFooter(infile, footertext); //} int i; for (i=0; icleanLineEndings(); printWithMd5sum(*(outfiles[i])); // cout << *(outfiles[i]); delete outfiles[i]; outfiles[i] = NULL; cout << "/eof" << NEWLINE << flush; } // end of all data file marker (two slashes on a line by themselves): cout << "//" << NEWLINE << flush; outfiles.setSize(0); return 0; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // analyzeTacet -- Search each voice between double barlines. If it // is always resting, then force it to be hidden. // void analyzeTacet(Array >& tacet, HumdrumFile& infile) { int maxtrack = infile.getMaxTracks(); tacet.setSize(infile.getNumLines()); int i; for (i=0; i tacetState(maxtrack+1); tacetState.setAll(0); int tacetcount = 0; PerlRegularExpression pre; for (i=0; i 1)) { for (ii=startline; ii=1 if there // is at least one note. Don't need to keep track of >1, but no // easy way to check with this implementation. // void getNoteState(Array& states, HumdrumFile& infile, int startline, int endline) { int i, j; int track; states.setAll(0); for (i=startline; i q). // void getRscaleState(HumdrumFile& infile, Array >& rscale) { int tracks = infile.getMaxTracks(); rscale.setSize(infile.getNumLines()); int i, j; Array current; current.setSize(tracks+1); current.setAll(1); PerlRegularExpression pre; RationalNumber rnum; int track; for (i=0; i > >& tiestate, HumdrumFile& infile) { tiestate.setSize(infile.getNumLines()); Array > currentState; currentState.setSize(infile.getMaxTracks()+1); int i, j, k; for (i=0; i separator; separator.setSize(2); separator[0] = footertext[0]; separator[1] = '\0'; pre.sar(separator, "\\$", "\\$", ""); // escape $ character pre.sar(separator, "\\^", "\\^", ""); // escape ^ character pre.sar(separator, "\\*", "\\*", ""); // escape * character Array > tokens; pre.getTokensWithEmpties(tokens, separator.getBase(), footertext+1); int i; for (i=0; i= 3) { arecord.appendString(tokens[2].getBase()); } musedata.append(arecord); arecord.clear(); arecord.appendString("@FOOT2: \t"); if (tokens.getSize() >= 2) { arecord.appendString(tokens[1].getBase()); } musedata.append(arecord); arecord.clear(); arecord.appendString("@FOOT3: \t"); if (tokens.getSize() >= 1) { arecord.appendString(tokens[0].getBase()); } musedata.append(arecord); arecord.clear(); arecord.appendString("@FOOT1R:\t"); if (tokens.getSize() >= 6) { arecord.appendString(tokens[5].getBase()); } musedata.append(arecord); arecord.clear(); arecord.appendString("@FOOT2R:\t"); if (tokens.getSize() >= 5) { arecord.appendString(tokens[4].getBase()); } musedata.append(arecord); arecord.clear(); arecord.appendString("@FOOT3R:\t"); if (tokens.getSize() >= 4) { arecord.appendString(tokens[3].getBase()); } musedata.append(arecord); arecord.clear(); // extra control parameters, should start with @: if (tokens.getSize() >= 7) { arecord.appendString(tokens[6].getBase()); musedata.append(arecord); arecord.clear(); } arecord.appendString("@END:\tFOOTER"); musedata.append(arecord); } ////////////////////////////// // // cleanFooterField -- // void cleanFooterField(Array& footerline, HumdrumFile& infile) { PerlRegularExpression pre; pre.sar(footerline, "^\\s+", "", ""); // remove leading spaces pre.sar(footerline, "\\s+$", "", ""); // remove trailing spaces Array buf1; while (pre.search(footerline, "(@\\{[^}]+\\})(\\{[^}]*\\})?", "")) { buf1.setSize(strlen(pre.getSubmatch(1))+1); strcpy(buf1.getBase(), pre.getSubmatch(1)); substituteReferenceRecord(footerline, buf1.getBase(), pre.getSubmatch(2), infile); } } ////////////////////////////// // // substituteReferenceRecord -- // void substituteReferenceRecord(Array& string, const char* refstring, const char* extension, HumdrumFile& infile) { Array refkey; refkey.setSize(strlen(refstring)+1); strcpy(refkey.getBase(), refstring); PerlRegularExpression pre; pre.sar(refkey, "^@\\{", "", ""); pre.sar(refkey, "\\}$", "", ""); Array value; value.setSize(1); value[0] = '\0'; getReferenceValue(value, refkey, infile); pre.sar(value, "^\\s+", "", ""); pre.sar(value, "\\s+$", "", ""); Array newref; newref.setSize(strlen(refstring)+1); strcpy(newref.getBase(), refstring); Array tref; tref.setSize(strlen(refstring)+1); strcpy(tref.getBase(), refstring); PerlRegularExpression pre2; if (pre.search(value, "^(20\\d\\d)/0?(\\d+?)\\/0?(\\d+?)\\/?$", "")) { char buffer[1024] = {0}; strcpy(buffer, pre.getSubmatch(3)); int month = atoi(pre.getSubmatch(2)); switch (month) { case 1: strcat(buffer, " Jan"); break; case 2: strcat(buffer, " Feb"); break; case 3: strcat(buffer, " Mar"); break; case 4: strcat(buffer, " Apr"); break; case 5: strcat(buffer, " May"); break; case 6: strcat(buffer, " Jun"); break; case 7: strcat(buffer, " Jul"); break; case 8: strcat(buffer, " Aug"); break; case 9: strcat(buffer, " Sep"); break; case 10: strcat(buffer, " Oct"); break; case 11: strcat(buffer, " Nov"); break; case 12: strcat(buffer, " Dec"); break; } strcat(buffer, " "); strcat(buffer, pre.getSubmatch(1)); pre.sar(value, "^.*$", buffer, ""); } if (pre.search(value, "^\\s*$", "")) { // reference record is empty // remove any conditional text after @{} operator: pre.sar(newref, "$", "\\{[^}]*\\}", ""); char empty = '\0'; newref.append(empty); pre.sar(string, newref.getBase(), value.getBase(), ""); // get rid of marker since it does not exist in bib records. pre.sar(string, refstring, "", ""); // get rid of extra text marker as well pre.sar(string, extension, "", ""); } else { // found reference record // also insert conditional text pre.sar(tref, "$", "\\{([^}]+)\\}", ""); if (pre.search(string, tref.getBase(), "")) { pre2.sar(value, "$", pre.getSubmatch(1), ""); pre2.sar(newref, "$", "\\{[^}]*\\}", ""); } pre.sar(string, newref.getBase(), value.getBase(), ""); } } ////////////////////////////// // // getReferenceValue -- Flip COM & COA around if there is a comma // in the name. // void getReferenceValue(Array& value, Array& refkey, HumdrumFile& infile) { int i; char buffer[1024] = {0}; value.setSize(1); value[0] = '\0'; for (i=0; i refkey2 = refkey; PerlRegularExpression pre; pre.sar(refkey2, "@.*", ""); for (i=0; i& value) { PerlRegularExpression pre; char buffer[1024] = {0}; if (pre.search(value, "^([^,]+)\\s*,\\s*([^,]+)$", "")) { strcpy(buffer, pre.getSubmatch(2)); strcat(buffer, " "); strcat(buffer, pre.getSubmatch(1)); value.setSize(strlen(buffer)+1); strcpy(value.getBase(), buffer); } } ////////////////////////////// // // appendReferenceRecords -- // void appendReferenceRecords(MuseData& musedata, HumdrumFile& infile) { int i; /* Always print reference records if being printed: if no VTS, then * generate one. * int hasbib = 0; for (i=0; i vts; infile.makeVts(vts); arecord.clear(); arecord.append("ss", "@", vts.getBase()); musedata.append(arecord); } if (!hasVtsData) { Array vts; infile.makeVtsData(vts); arecord.clear(); arecord.append("ss", "@", vts.getBase()); musedata.append(arecord); } } ////////////////////////////// // // printWithMd5sum -- add an MD5sum for each file on the third header // record line. // void printWithMd5sum(MuseData& datafile) { stringstream tempstream; int i; for (i=0; i data; data.setSize(strlen(tempstream.str().c_str())+1); strcpy(data.getBase(), tempstream.str().c_str()); PerlRegularExpression pre; if (checksumQ) { Array md5sum; CheckSum::getMD5Sum(md5sum, data); // 0d0a added to indicate the the md5sum was calculated with DOS newlines. char newlinestring[1024] = {0}; char buffer[32] = {0}; for (i=0; i >& TextAssignment, Array >& TextElisions, string& textspines) { int i, j, track; TextAssignment.setSize(infile.getMaxTracks()+1); TextElisions.setSize(infile.getMaxTracks()+1); for (i=0; i 0) { textQ = 1; Array > lyricspines; processLyricsSpines(lyricspines, infile, LyricSpines.c_str()); Array kerntracks; getKernTracks(kerntracks, infile); for (i=0; i& DynamicsAssignment, int& dynamicsQ) { DynamicsAssignment.setSize(infile.getMaxTracks()+1); DynamicsAssignment.setAll(0); Array& da = DynamicsAssignment; dynamicsQ = 0; int i, j; int lastkerntrack = -1; int track; for (i=0; i 0) && (da[lastkerntrack] <= 0)) { da[lastkerntrack] = track; dynamicsQ = 1; if (debugQ) { cout << "ASSIGNING DYNAM TRACK " << track << " TO KERN TRACK " << lastkerntrack << NEWLINE; } } } } break; } } ////////////////////////////// // // getMeasureDuration -- for each line, report the duration of the // current measure. The algorithm only considers one meter for all // parts, and would have to be done by part if the meter is different in // different parts... // void getMeasureDuration(HumdrumFile& infile, Array& rns) { PerlRegularExpression pre; int i, j; int top; int bot; int zbot; RationalNumber current(0,1); rns.setSize(infile.getNumLines()); for (i=0; i=0; i--) { if (!infile[i].isBibliographic()) { continue; } if (pre.search(infile[i][0], "^!!!RDF\\*\\*kern\\s*:\\s*([^\\s=])\\s*=.*musica ficta", "i")) { // "musica ficta" hasFictaQ = 1; FictaChar = pre.getSubmatch(1)[0]; } else if (pre.search(infile[i][0], "^!!!RDF\\*\\*kern\\s*:\\s*([^\\s=])\\s*=.*editorial", "i")) { // "editorial accidental" hasFictaQ = 1; FictaChar = pre.getSubmatch(1)[0]; } if (pre.search(infile[i][0], "^!!!RDF\\*\\*kern\\s*:\\s*([^\\s=])\\s*=.*long", "i")) { hasLongQ = 1; LongChar = pre.getSubmatch(1)[0]; } if (pre.search(infile[i][0], "^!!!RDF\\*\\*kern\\s*:\\s*([^\\s=])\\s*=.*single.*notehead", "i")) { tiedgroupQ = 1; TiedGroupChar = pre.getSubmatch(1)[0]; } } if (tiedgroupQ) { markAllNotesInTiedGroup(infile, TiedGroupChar); } } ////////////////////////////// // // markAllNotesInTiedGroup -- mark notes in tied group which should be hidden // when printing. Will currently not handle spine splits properly. // Will not handle chords properly. // void markAllNotesInTiedGroup(HumdrumFile& infile, char groupchar) { int maxtracks = infile.getMaxTracks(); Array lastpitch(maxtracks+1); lastpitch.setAll(-1); int i, j, track; int base40; Array string; for (i=0; i >& PartNames) { int i, j; PartNames.setSize(infile.getMaxTracks()+1); // 0 = unused for (i=0; i ignore; ignore.setSize(infile.getMaxTracks()+1); ignore.setAll(0); PerlRegularExpression pre; int track; for (i=0; i& options, const char* filter) { int olength = strlen(options.getBase()); Array > singles; singles.setSize(100); singles.setSize(0); char empty = '\0'; int i, j, k; int startindex = 0; for (i=0; i= olength - 1) { break; } if ((i < olength - 1) && (options[i] == '^')) { // the next character is a '^' so start reading until // the next '^' enclosing a string value is found. // First go to '^' character and store it singles.last().append(options[i++]); while (i < olength) { singles.last().append(options[i]); i++; if (options[i] == '^') { singles.last().append(options[i]); break; } } } else if (std::isdigit(options[i+1])) { // read one or more integers, separated by commas while ((i < olength) && (std::isdigit(options[i]) || (options[i] == ','))) { singles.last().append(options[i++]); } if (i <= olength) { i--; } } } //for (i=0; i temp; temp.setSize(options.getSize() + 32); temp.setSize(0); for (i=0; i tempdata; Array temp2; for (i=0; i= 0)) { if (pre.search(infile[titleline][0], "^!!!OTL[^:]*:\\s*(.+)\\s*$", "")) { Array title; title.setSize(strlen(pre.getSubmatch(1))+1); strcpy(title.getBase(), pre.getSubmatch()); // deal with HTML accented characters converted into MuseData // equivalents here... // title cannot contain ^ character pre2.sar(title, "\\^", "", "g"); convertHtmlTextToMuseData(title); cout << "@muse2psv1==T^" << title << "^" << NEWLINE; } } // Print composer if not found in muse2ps options, but found in file. if (composerQ && (!hascomposer) && (composerline >= 0)) { if (pre.search(infile[composerline][0], "^!!!COM[^:]*:\\s*(.*)\\s*$", "")) { Array composer; composer.setSize(strlen(pre.getSubmatch(1))+1); strcpy(composer.getBase(), pre.getSubmatch()); // reverse order of names if there is a comma in the name if (pre.search(composer, "^\\s*(.*?)\\s*,\\s*(.*)", "")) { char buffer[1024] = {0}; strcpy(buffer, pre.getSubmatch(2)); strcat(buffer, " "); strcat(buffer, pre.getSubmatch(1)); composer.setSize(strlen(buffer)+1); strcpy(composer.getBase(), buffer); } // abbreviate the names of famous composers pre2.sar(composer, "Johann Sebastian Bach", "J.S. Bach", ""); pre2.sar(composer, "Wolfgang Amadeus Mozart", "W.A. Mozart", ""); pre2.sar(composer, "Ludwig van Beethoven", "L. van Beethoven", ""); convertHtmlTextToMuseData(composer); // deal with HTML accented characters converted into MuseData // equivalents here... // composer cannot contain ^ character pre2.sar(composer, "\\^", "", "g"); cout << "@muse2psv1==C^" << composer << "^" << NEWLINE; } } Array kerntracks; getKernTracks(kerntracks, infile); Array systemspine; systemspine.setSize(1024); systemspine.setSize(1); systemspine[0] = '\0'; if (kerntracks.getSize() == 1) { //do nothing; } else if (sepbracketQ) { pre.sar(systemspine, "$", "s^[", ""); for (i=0; i 4) { cout << "@muse2psv1==s^[("; for (int ii=0; ii ostring; ostring.setSize(1); ostring[0] = '\0'; if (strlen(muse2psoptionstring) > 0) { ostring.setSize(strlen(muse2psoptionstring) + 1); strcpy(ostring.getBase(), muse2psoptionstring); } if (kflag > 0) { char buffer[32] = {0}; if (kflag < 0xff) { sprintf(buffer, "k^0x%02x^", kflag); } else if (kflag < 0xffff) { sprintf(buffer, "k^0x%04x^", kflag); } else if (kflag < 0xffffff) { sprintf(buffer, "k^0x%06x^", kflag); } else { sprintf(buffer, "k^0x%08x^", kflag); } pre.sar(ostring, "$", buffer, ""); } if (mensuralQ) { pre.sar(ostring, "$", "W1", ""); // thin barlines } if (strlen(ostring.getBase()) > 0) { if (!pre.search(ostring, "^=", "")) { // add muse2ps option marker at start of option string. pre.sar(ostring, "^", "=", ""); } if (!pre.search(ostring, "^=[^=]*=", "")) { // mark as a default type of option. pre.sar(ostring, "^", "=", ""); } cout << "@muse2psv1" << ostring << NEWLINE; } // print global default options globaldefaults << ends; Array globals(strlen(globaldefaults.str().c_str())+1); strcpy(globals.getBase(), globaldefaults.str().c_str()); if ((globals.getSize() > 1) && textvaddQ) { addTextVertcialSpace(globals, infile); } // cout << globaldefaults.str().c_str(); cout << globals; // print local default options localdefaults << ends; cout << localdefaults.str().c_str(); } ////////////////////////////// // // setColorCharacters -- If the Humdrum file has a record in the form: // !!!RDF**kern:\s*([^\s])\s*=\s*match // Then $1 is the marker in **kern data to indicate a match result // from a search red is the default color. If there is a color code // in the RDF marker, the the most dominant color will be used // to mark the note in MuseData. // // This note will be colored red in muse2ps, indicated // by placing "R" in column 14 of the note record, "G" for green, // and "B" for blue. // void setColorCharacters(HumdrumFile& infile, Array& colorchar, Array& colorout) { PerlRegularExpression pre; colorchar.setSize(0); colorout.setSize(0); char value; int i; for (i=0; i 0) { cout << "COLOR MARKERS:\n"; for (i=0; i "; cout << "\t\"" << colorout[i] << "\""; cout << NEWLINE; } } } } ////////////////////////////// // // getColorCategory -- // char getColorCategory(const char* color) { int len = strlen(color); if (len != 6) { return ' '; } char buffer[4] = {0}; int num[3]; buffer[0] = color[0]; buffer[1] = color[1]; num[0] = strtol(buffer, NULL, 16); buffer[0] = color[2]; buffer[1] = color[3]; num[1] = strtol(buffer, NULL, 16); buffer[0] = color[4]; buffer[1] = color[5]; num[2] = strtol(buffer, NULL, 16); if ((num[0] == num[1]) && (num[1] == num[2])) { // grayscale, so print black return ' '; } int max = 0; if (num[1] > num[0]) { max = 1; } if (num[2] > num[max]) { max = 2; } switch (max) { case 0: return 'R'; // red channel is most dominant case 1: return 'G'; // green channel is most dominant case 2: return 'B'; // blue channel is most dominant } return ' '; } ////////////////////////////// // // isBarlineBeforeData -- returns true if there is a barline found // before a dataline. // int isBarlineBeforeData(HumdrumFile& infile, int startindex) { int i; for (i=startindex; i& outfiles, HumdrumFile& infile) { Array kerntracks; getKernTracks(kerntracks, infile); int i; int tickpos; // tick location within score. outfiles.setSize(kerntracks.getSize()); int printfirstmeasureQ = isBarlineBeforeData(infile, 0); int reversei; Array ticksperquarter; prepareLocalTickChanges(infile, ticksperquarter); MuseRecord endrecord; for (i=0; iappend(endrecord); } } ////////////////////////////// // // prepareLocalTickChanges -- check to make sure that ticks never // exceed 999 for rhythmic durations. If they do, then try to change // the Q: record (ticks per quarter) based on measures, If the global // tick setting will not work. If measure-based doesn't work, then // generalized this function to allow Q: to change within measures // (but this is difficult to generalize). // // If this algorithm fails, next thing to try is to encode tpq // for parts separately. If that generalization fails, then // adjust tpq within measures. If that generalization fails, then // too bad. // void prepareLocalTickChanges(HumdrumFile& infile, Array& ticks) { int ticksperquarter = getGlobalTicksPerQuarter(infile); // maybe make measurelines an input parameter since it is also // calculated elsewhere. But it is calculated in an inconvenient // location, so doing it here. Array measurelines; getMeasureInfo(measurelines, infile); // handle case where there are no measure lines in the data: if (measurelines.getSize() == 0) { measurelines.setSize(2); measurelines[0] = 0; measurelines[1] = infile.getNumLines()-1; } Array rhys; getRhythms(rhys, infile, 0, -1); if (rhys.getSize() == 0) { // something funny: no durations in file, so don't try to do anything ticks.setSize(measurelines.getSize()); ticks.setAll(ticksperquarter); return; } RationalNumber maxtick; // the first rhythm is the largest. maxtick = rhys[0].getInversion() * ticksperquarter; if (maxtick < 1000) { // there will never be a tick overflow in the converted data. ticks.setSize(measurelines.getSize()); ticks.setAll(ticksperquarter); return; } // there will be an overflow in the converted data. // try to adjust the ticks by each measure to localize // tuplets or extreme rhythms which are causing the problem. Array mtpq; mtpq.setSize(measurelines.getSize()); mtpq.setAll(0); Array mticks; mticks.setSize(measurelines.getSize()); mticks.setAll(0); int i; for (i=0; i 0) { mtpq[i] = getTPQByRhythmSet(rhys); mticks[i] = rhys[0].getInversion() * mtpq[i]; } else { mticks[i] = 0; if (i > 0) { mtpq[i] = mtpq[i-1]; } } } if (mtpq.getSize() == 0) { // no barlines, too difficult to deal with. ticks.setSize(measurelines.getSize()); ticks.setAll(ticksperquarter); return; } if (mtpq[mtpq.getSize()-2] == 0) { // no non-zero mtpq values, strange, so just give up. ticks.setSize(measurelines.getSize()); ticks.setAll(ticksperquarter); return; } // group measures by their ability to not exceed durations of 999 ticks // for the maximum rhythmic duration in the range. Array newtpq; newtpq.setSize(measurelines.getSize()); newtpq.setAll(0); int curtpq = mtpq[0]; int lcm; RationalNumber maxdur = mticks[0]; // keep track of the maximum rhy value maxdur /= mtpq[0]; RationalNumber tmax; RationalNumber tdur; // test new maximum rhy duration Array twonum(2); for (i=1; i maxdur) { tmax = tdur; } else { tmax = maxdur; } // check to see if the test maximum does not exceed the new // test ticks per quarter setting: if (tmax * lcm < 1000) { // the new measure can be added to the previous grouping // so store the new values (and erase previous setting. maxdur = tmax; curtpq = lcm; newtpq[i] = lcm; if (i < measurelines.getSize()-2) { newtpq[i-1] = 0; } continue; } // the new tpq would be too large to accomodate the largest // duration in the range. So restart the calculation on the // current measure. maxdur = tdur; curtpq = mtpq[i]; newtpq[i] = curtpq; } // travel backwards in newtpq, setting any values that are 0 to the // next higher index value which is non-zero. for (i=newtpq.getSize()-2; i>=0; i--) { if (newtpq[i] == 0) { newtpq[i] = newtpq[i+1]; } } if (debugQ) { RationalNumber rn; for (i=0; i& rhys) { Array intrhy; intrhy.setSize(rhys.getSize()); int i; for (i=0; i rhythms(2); rhythms[0] = rn.getNumerator(); rhythms[1] = 4; int returnval = LCM(rhythms); ignoreTickError = 1; return returnval; } if (debugQ) { cout << "Ticks per quarter: " << rn << NEWLINE; } // should be simplified automatically. return rn.getNumerator() * rn.getDenominator(); } ////////////////////////////// // // convertTrackToMuseData -- Convert a particular track of **kern data into // a MuseData file. Successive measures are processed one at a time. // void convertTrackToMuseData(MuseData& musedata, int track, HumdrumFile& infile, Array& tpq, int& tickpos, int counter, int total, int printFirstMeasureQ) { Array measures; Array > lastVerse; lastVerse.setSize(0); // should probably move to calling function, but keeping here for now // in case the more generalized solution of finding the barlines in each // individual track should be done instead of the global barlines. // The HumdrumFile class likes barlines to be global... getMeasureInfo(measures, infile); int i; MuseData tempdata; insertHeaderRecords(infile, musedata, track, counter, total); LastTPQPrinted = tpq[0]; insertDollarRecord(infile, 0, musedata, track, counter, total, tpq[0]); if (hasTuplet[track]) { // add a style which places no tuplet slur on beamed notes MuseRecord arecord; arecord.insertString(1, "P C0:t2"); musedata.append(arecord); } if (autoTacetQ) { MuseRecord arecord2; if (TacetState[0][track]) { arecord2.insertString(1, "P C0:x1"); } else { arecord2.insertString(1, "P C0:x0"); } musedata.append(arecord2); } if (sysabbrQ) { MuseRecord arecord3; string suggestion = "P C0:z33" + getInstrumentAbbreviation(infile, track); arecord3.insertString(1, suggestion.c_str()); musedata.append(arecord3); } int founddataQ = 0; // needed to suppress first $ record from being // reprinted (the first one was needed above // with the Q: field added). for (i=0; i 0) { sprintf(tempbuf, "Q:%d ", tpq); strcat(buffer, tempbuf); } // time signature if (appendTimeSignature(buffer, infile, line, track, tpq)) { strcat(buffer, " "); } // clefs if (appendClef(buffer, infile, line, track)) { strcat(buffer, " "); } else { // a clef must be present in the first musical attributes line // for a part, so force one here. Make it a treble clef... if (tpq > 0) { // only print if Q: recrod is being printed. Not quite // right, but rare to print Q: record twice in part, so good // enought for now. strcat(buffer, "C:4 "); } } // add a movement designation if this is the first // one (will have to fix so that movement designations which // occur later can be also printed... // if (tpq > 0) { addMovementDesignation(buffer, infile, line); // } // [20111016] If the $ record contains no content, then do not add it // into the output data. arecord.insertString(4, buffer); PerlRegularExpression pre; if (pre.search(arecord.getLine(), "^.\\s*$")) { return; } else { musedata.append(arecord); } } ////////////////////////////// // // addMovementDesignation -- // void addMovementDesignation(char* buffer, HumdrumFile& infile, int line) { int omdline = -1; int i; PerlRegularExpression pre; for (i=line; i=0; i--) { if (infile[i].isData()) { break; } if (!infile[i].isBibliographic()) { continue; } if (pre.search(infile[i][0], "^!!!OMD[^:]*:\\s*(.*)\\s*$", "")) { omdline = i; break; } } } if (omdline >= 0) { strcat(buffer, "D:"); Array movementdesignation(strlen(pre.getSubmatch(1))+1); strcpy(movementdesignation.getBase(), pre.getSubmatch()); convertHtmlTextToMuseData(movementdesignation); strcat(buffer, movementdesignation.getBase()); // probably don't need this, but just in case something is added on line: strcat(buffer, " "); } } ////////////////////////////// // // appendClef -- Append a clef marker to a Music Attribute // record if there is a key signature intepretation somewhere between the // current line and the first data line found after that line -- in the // specified primary track. // int appendClef(char* buffer, HumdrumFile& infile, int line, int track) { int i, j; int row = -1; // row where key signature was found; int col = -1; // col where key signature was found; for (i=line; i= 0) { // a met code has been found, so use that to print a time signature if (strcmp(infile[metrow][metcol], "*met(O)") == 0) { strcat(buffer, "T:11/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(c)") == 0) { strcat(buffer, "T:1/1"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(com)") == 0) { strcat(buffer, "T:1/1"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(common)") == 0) { strcat(buffer, "T:1/1"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(c|)") == 0) { strcat(buffer, "T:0/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(cut)") == 0) { strcat(buffer, "T:0/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O:)") == 0) { // preferred alternate for *met(O..) strcat(buffer, "T:12/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O..)") == 0) { strcat(buffer, "T:12/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O.)") == 0) { strcat(buffer, "T:21/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O:.)") == 0) { strcat(buffer, "T:22/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O...)") == 0) { strcat(buffer, "T:22/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O;)") == 0) { // preferred alternate for *met(O:.) and *met(O...) strcat(buffer, "T:22/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C)") == 0) { strcat(buffer, "T:31/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C.)") == 0) { strcat(buffer, "T:41/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C.3/2)") == 0) { strcat(buffer, "T:42/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C.3/8)") == 0) { strcat(buffer, "T:43/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(Cr)") == 0) { strcat(buffer, "T:51/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(Cr|)") == 0) { strcat(buffer, "T:52/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C|r)") == 0) { strcat(buffer, "T:52/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C|)") == 0) { strcat(buffer, "T:61/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C|/2)") == 0) { strcat(buffer, "T:62/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C|.)") == 0) { strcat(buffer, "T:63/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C.|)") == 0) { strcat(buffer, "T:63/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C2)") == 0) { strcat(buffer, "T:71/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C2/3)") == 0) { strcat(buffer, "T:72/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O2)") == 0) { strcat(buffer, "T:81/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O3/2)") == 0) { strcat(buffer, "T:82/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O|)") == 0) { strcat(buffer, "T:91/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O|3)") == 0) { strcat(buffer, "T:92/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O|3/2)") == 0) { strcat(buffer, "T:93/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C|3)") == 0) { strcat(buffer, "T:101/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(3)") == 0) { strcat(buffer, "T:102/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(3/2)") == 0) { strcat(buffer, "T:103/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C|/3)") == 0) { strcat(buffer, "T:104/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C3)") == 0) { strcat(buffer, "T:105/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(O/3)") == 0) { strcat(buffer, "T:106/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(C|2)") == 0) { strcat(buffer, "T:111/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(2)") == 0) { strcat(buffer, "T:112/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met(Oo)") == 0) { strcat(buffer, "T:121/0"); return 1; } else if (strcmp(infile[metrow][metcol], "*met()") == 0) { // hide the time signature // strcat(buffer, ""); return 1; return 0; } else { // an unknown metric symbol, so try your luck with time signature // data below. } // mensurations which need to be added to MuseData: // *met(O|3/2) } if (timerow >= 0) { if (!pre.search(infile[timerow][timecol], "^\\*M(\\d+)/(\\d+)")) { // something funny happened... return 0; } int timetop = atoi(pre.getSubmatch(1)); int timebot = atoi(pre.getSubmatch(2)); if ((timerow > 0) && (timecol >= 0) && pre.search(infile[timerow][timecol], "^\\*M3/3%2", "")) { // can map to 2/1 or 3/1 depending on context. Only used // with mensurations, and usually best to display in 3/1 // which serves as an shorthand for triplet markers. timetop = 3; timebot = 1; } if ((timebot == 0) && (timetop < 9)) { // Currently reserving 9/0 for a hidden time signature // meters larger than 10/0 are used for mensural signatures // // For time signatures smaller than 9/0, note that // MuseData cannot handle a breve as the beat, so adjust // by shifting the bottom number from 0 to 1 and double the // top number in the time signature. timebot = 1; timetop *= 2; } stringstream temps; temps << "T:"; temps << timetop << "/" << timebot; temps << ends; strcat(buffer, temps.str().c_str()); } else { // no time signature found in interpretation region. // currently print the time signature 9/0 which functions // as an invisible time signature, although this will change // later when the muse2ps program no longer requires a time // signature at the start of the music. if (tpq >= 0) { // no longer needed since muse2ps can now handle an empty time sig. // strcat(buffer, "T:9/0"); } // don't print hidden time signature if no Q record. // return 0; } // *met(.*) or *M\d+/\d+ found in interpretation region. return 1; } ////////////////////////////// // // appendKeySignature -- Append a key signature marker to a Music Attribute // record if there is a key signature intepretation somewhere between the // current line and the first data line found after that line -- in the // specified primary track. // int appendKeySignature(char* buffer, HumdrumFile& infile, int line, int track) { int i, j; PerlRegularExpression pre; int row = -1; // row where key signature was found; int col = -1; // col where key signature was found; for (i=line; i addDateAndEncoder(infile, tempdata); // Record 5: WKn: MVn: addWorkNumberInfo(infile, tempdata); // Record 6: addSourceRecord(infile, tempdata); // Record 7: addWorkTitle(infile, tempdata); // Record 8: addMovementTitle(infile, tempdata); // Record 9: addPartName(infile, track, tempdata); // Record 10: miscellaneous designations // such as [mode], [movement type] and [voice] arecord.clear(); arecord.insertString(1, "Header Record 10"); tempdata.append(arecord); // Record 11: group memberships: . . . arecord.clear(); arecord.insertString(1, "Group memberships: score"); tempdata.append(arecord); // Record 12: : part of stringstream partnum; partnum << "score: part " << (total - counter)+1 << " of " << total; partnum << ends; arecord.clear(); arecord.insertString(1, partnum.str().c_str()); tempdata.append(arecord); } ////////////////////////////// // // addCopyrightLine -- add fixed header record 1 // void addCopyrightLine(HumdrumFile& infile, MuseData& tempdata) { MuseRecord arecord; arecord.insertString(1, "Header Record 1: optional copyright notice"); char buffer [1024] = {0}; int i; for (i=0; itm_year + 1900; int month = current->tm_mon + 1; int day = current->tm_mday; const char* ptr = "JAN"; switch (month) { case 1: ptr = "JAN"; break; case 2: ptr = "FEB"; break; case 3: ptr = "MAR"; break; case 4: ptr = "APR"; break; case 5: ptr = "MAY"; break; case 6: ptr = "JUN"; break; case 7: ptr = "JUL"; break; case 8: ptr = "AUG"; break; case 9: ptr = "SEP"; break; case 10: ptr = "OCT"; break; case 11: ptr = "NOV"; break; case 12: ptr = "DEC"; break; } const char* dptr = ""; if (day < 10) { dptr = "0"; } arecord.append("ssssisis", "TIMESTAMP: ", ptr, "/", dptr, day, "/", year, " []"); tempdata.append(arecord); } ////////////////////////////// // // addDateAndEncoder -- add fixed header record 4 // void addDateAndEncoder(HumdrumFile& infile, MuseData& tempdata) { MuseRecord arecord; int encline = -1; int eedline = -1; int endline = -1; int eevline = -1; int i; for (i=0; i= 0) { if (pre.search(infile[endline][0], "(\\d{4})/0*(\\d+)/0*(\\d+)", "")) { year = atoi(pre.getSubmatch(1)); month = atoi(pre.getSubmatch(2)); day = atoi(pre.getSubmatch(3)); } else if (pre.search(infile[endline][0], "(\\d{4})/0*(\\d+)", "")) { year = atoi(pre.getSubmatch(1)); month = atoi(pre.getSubmatch(2)); day = 0; } else if (pre.search(infile[endline][0], "(\\d{4})", "")) { year = atoi(pre.getSubmatch(1)); month = 0; day = 0; } } else if (eevline >= 0) { if (pre.search(infile[eevline][0], "(\\d{4})/0*(\\d+)/0*(\\d+)", "")) { year = atoi(pre.getSubmatch(1)); month = atoi(pre.getSubmatch(2)); day = atoi(pre.getSubmatch(3)); } else if (pre.search(infile[eevline][0], "(\\d{4})/0*(\\d+)", "")) { year = atoi(pre.getSubmatch(1)); month = atoi(pre.getSubmatch(2)); day = 0; } else if (pre.search(infile[eevline][0], "(\\d{4})", "")) { year = atoi(pre.getSubmatch(1)); month = 0; day = 0; } } char encoder[1024] = {0}; if (year < 0) { // Use today's date: struct tm *current; time_t now; time(&now); current = localtime(&now); year = current->tm_year + 1900; month = current->tm_mon + 1; day = current->tm_mday; } char datebuf[1024] = {0}; char daybuf[16] = {0}; if (day < 10) { sprintf(daybuf, "0%d", day); } else { sprintf(daybuf, "%d", day); } char monthbuf[16] = {0}; if (month < 10) { sprintf(monthbuf, "0%d", month); } else { sprintf(monthbuf, "%d", month); } sprintf(datebuf, "%s/%s/%d ", monthbuf, daybuf, year); if (encline >= 0) { if (pre.search(infile[encline][0], "^!!!ENC:\\s*(.*)\\s*$", "")) { strcpy(encoder, pre.getSubmatch(1)); } } else if (eedline >= 0) { if (pre.search(infile[eedline][0], "^!!!EED:\\s*(.*)\\s*$", "")) { strcpy(encoder, pre.getSubmatch(1)); } } if (encoderQ) { strcpy(encoder, Encoder.c_str()); } else if (strlen(encoder) == 0) { strcpy(encoder, "hum2muse"); } arecord.insertString(1, datebuf); arecord.appendString(encoder); tempdata.append(arecord); } ////////////////////////////// // // addWorkNumberInfo -- add fixed header record 5. This line must start // with "WK#" in order for muse2ps to process the file. // void addWorkNumberInfo(HumdrumFile& infile, MuseData& tempdata) { MuseRecord arecord; Array work; Array movement; getWorkAndMovement(work, movement, infile); arecord.append("ssss", "WK#:", work.getBase(), " MV#:", movement.getBase()); tempdata.append(arecord); } ////////////////////////////// // // getWorkAndMovement -- // void getWorkAndMovement(Array& work, Array& movement, HumdrumFile& infile) { movement.setSize(2); work.setSize(2); strcpy(movement.getBase(), "1"); strcpy(work.getBase(), "1"); if (workNumber[0] != '\0') { work.setSize(strlen(workNumber.c_str())+1); strcpy(work.getBase(), workNumber.c_str()); } if (movementNumber[0] != '\0') { movement.setSize(strlen(movementNumber.c_str())+1); strcpy(movement.getBase(), movementNumber.c_str()); } int omvline = -1; int sctline = -1; int opsline = -1; // int onmline = -1; int i; for (i=0; i= 0) { pre.search(infile[omvline][0], "^!!!OMV[^:]*:\\s*(.*)\\s*$", ""); movement.setSize(strlen(pre.getSubmatch(1)) +1); strcpy(movement.getBase(), pre.getSubmatch()); pre.sar(movement, "^[^\\d]+", "", ""); pre.sar(movement, "\\s*\\.\\s*", "", ""); } if (strlen(movement.getBase()) == 0) { movement.setSize(2); strcpy(movement.getBase(), "1"); } // if there is a BWV in SCT, then use that as the work # and any // slash after the first number after BWV as the movment number. if ((sctline >= 0) && pre.search(infile[sctline][0], "^!!!SCT[^:]*:\\s*(.+)\\s*$", "")) { if (pre2.search(pre.getSubmatch(1), "BWV\\s+(\\d+)/(\\d+)", "i")) { work.setSize(strlen(pre2.getSubmatch(1)) + 1); strcpy(work.getBase(), pre2.getSubmatch()); movement.setSize(strlen(pre2.getSubmatch(2)) + 1); strcpy(movement.getBase(), pre2.getSubmatch()); return; } if (pre2.search(pre.getSubmatch(1), "BWV\\s*(\\d+[^\\s]*)", "i")) { work.setSize(strlen(pre2.getSubmatch(1)) + 1); strcpy(work.getBase(), pre2.getSubmatch()); return; } } // if there is an opus number, then use that as the work number // handle onm line later... if ((opsline >= 0) && pre2.search(infile[opsline][0], "^!!!OPS[^:]*:\\s*(\\d[^\\s]*)", "")) { work.setSize(strlen(pre2.getSubmatch(1)) + 1); strcpy(work.getBase(), pre2.getSubmatch()); return; } } ////////////////////////////// // // addSourceRecord -- add fixed header record 6, which is the // original source for this particular digital encoding. // void addSourceRecord(HumdrumFile& infile, MuseData& tempdata) { MuseRecord arecord; PerlRegularExpression pre; int smsline = -1; int i; for (i=0; i sms; sms.setSize(strlen(pre.getSubmatch(1)) + 1); strcpy(sms.getBase(), pre.getSubmatch()); convertHtmlTextToMuseData(sms); arecord.insertString(1, sms.getBase()); } tempdata.append(arecord); } ////////////////////////////// // // addWorkTitle -- add fixed header record 7 which is the title of the work // (of which this data may be a particular movment). // void addWorkTitle(HumdrumFile& infile, MuseData& tempdata) { MuseRecord arecord; PerlRegularExpression pre; int otlline = -1; int i; for (i=0; i strang(strlen(pre.getSubmatch(1))+1); strcpy(strang.getBase(), pre.getSubmatch(1)); convertHtmlTextToMuseData(strang); arecord.insertString(1, strang.getBase()); } tempdata.append(arecord); } ////////////////////////////// // // addMovementTitle -- add fixed header record 8, which is the movement // name. Change to OMV: OMD, instead of OMD. // void addMovementTitle(HumdrumFile& infile, MuseData& tempdata) { MuseRecord arecord; PerlRegularExpression pre; int omdline = -1; int i; for (i=0; i strang(strlen(pre.getSubmatch(1))+1); strcpy(strang.getBase(), pre.getSubmatch(1)); convertHtmlTextToMuseData(strang); arecord.insertString(1, strang.getBase()); } tempdata.append(arecord); } ////////////////////////////// // // addPartName -- add fixed header record 9 which is the instrumental // name of the part. This name can be set explicitly with *I" tandem // interpretations in the data. Otherwise, the part name will be // (eventually) extracted automatically from the *I instrument // code names. // void addPartName(HumdrumFile& infile, int track, MuseData& tempdata) { MuseRecord arecord; if (strcmp(PartNames[track].getBase(), "") != 0) { arecord.insertString(1, PartNames[track].getBase()); } else { arecord.insertString(1, " "); } tempdata.append(arecord); } ////////////////////////////// // // getMeasureData -- Get MuseData for a particular measure in a particular // part (track). // void getMeasureData(MuseData& tempdata, int track, HumdrumFile& infile, int startline, int stopline, int tpq, int& tickpos, int& measuresuppress, int& founddataQ, Array >& lastVerse) { int maxvoice = getMaxVoices(infile, startline, stopline, track); if (debugQ) { cout << "For line range " << startline+1 << " to " << stopline+1 << " in track " << track << " maxvoices = " << maxvoice << NEWLINE; } if (maxvoice == 0) { // measure has no data maxvoice = 1; } int starttick; int stoptick; starttick = getTick(tpq, infile, startline); stoptick = getTick(tpq, infile, stopline); int voice; for (voice=0; voice >& lastVerse) { int tpqtest = -1; if (LastTPQPrinted != tpq) { tpqtest = tpq; LastTPQPrinted = tpq; // hack for variable tpq: tickpos = -1; } PerlRegularExpression pre; // int starttick = getTick(tpq, infile, startline); // int stoptick = getTick(tpq, infile, stopline); if (debugQ) { cout << "STARTTICK = " << starttick << "\tSTOPTICK = " << stoptick << NEWLINE; } if (tickpos < 0) { // hack for variable tpq tickpos = starttick; } int backQ = 0; // emit a back command to go back to the start of the measure // if this is not the first voice to process... if (voice > 1) { addBackup(tempdata, stoptick - starttick, tpq); tickpos -= stoptick - starttick; backQ = 1; } if ((!ignoreTickError) && (tickpos != starttick)) { cerr << "Error: tick mismatch: " << tickpos << " " << starttick << NEWLINE; cerr << "on line " << startline + 1 << NEWLINE; exit(1); } int i, j; int startingmeasure = 1; for (i=startline; i>0; i--) { if (infile[i].isData()) { startingmeasure = 0; break; } } int firstitem = 1; int dollarprint = 0; int curvoice = 0; for (i=startline; i 0) { MuseRecord dchange; char buffer[128] = {0}; sprintf(buffer, "$ Q:%d", tpqtest); dchange.insertString(1, buffer); tempdata.append(dchange); tpqtest = -1; } int currtick = getTick(tpq, infile, i); if (currtick > tickpos) { // a voice which does not start at the beginning of the measure. MuseRecord forward; forward.setPitch("irst"); // also "irest" can be used forward.setTicks(currtick-tickpos); tempdata.append(forward); tickpos += (currtick - tickpos); } // print a note in the voice. Keeping track of the // time that the next note is expected, and emit a // forward marker if needed. if (strcmp(infile[i][j], ".") != 0) { tickpos += addNoteToEntry(tempdata, infile, i, j, tpq, voice, firstitem & startingmeasure & hangtieQ, lastVerse); firstitem = 0; } } } if (debugQ) { cout << "GOT HERE AAA" << endl; } // emit a forward to get to end of measure in current voice if (tickpos < stoptick) { if (debugQ) { cout << "GOT HERE BBB" << endl; } // cout << "FORWARD " << stoptick - tickpos << NEWLINE; MuseRecord forward; forward.setPitch("irst"); // also "irest" can be used forward.setTicks(stoptick-tickpos); tempdata.append(forward); tickpos += stoptick - tickpos; } else if ((!ignoreTickError) && (tickpos > stoptick)) { cerr << "Error: duration of music is too large on line " << startline+1 << NEWLINE; cerr << "Tickpos = " << tickpos << "\tStoptick = " << stoptick << NEWLINE; cerr << "The first number should be smaller or " << "equal to the second number "<< NEWLINE; exit(1); } if (debugQ) { cout << "GOT HERE CCC" << endl; } } ////////////////////////////// // // processGlobalComments -- // void processGlobalComment(HumdrumFile& infile, int line, MuseData& tempdata, int track) { PerlRegularExpression pre; if (strncmp(infile[line][0], "!!LO:", 5) != 0) { return; } LayoutParameters lp; Array coords(1); coords[0].i = line; coords[0].j = 0; lp.parseLayout(infile, coords); int i; for (i=0; i 0) { jdex--; string value; value = lp.getValue(i, jdex).getBase(); pre.sar(value, ":", ":", "g"); if (value.size() > 0) { value.insert(0, "P "); MuseRecord arecord; arecord.setLine(value.c_str()); tempdata.append(arecord); } } } } } ////////////////////////////// // // printRehearsalMark -- // // col 1: * // col 2-5: blank // col 6-8: time offset (blank) // col 9-12: blank // col 13-15: footnote/editorial level (blank) // col 16: blank // col 17-18: "R " or " R" for rehearsal mark // col 19: "+" for above the staff, blank for below the staff // col 20: blank // col 21-23: numberic parameter (blank) // col 24: staff (blank for 1) // col 25...: text to put in rehearsal mark // // Rehearsal mark is automatically put in a box (no control at the moment). // // void printRehearsalMark(MuseData& tempdata, LayoutParameters& lp, int index) { int jdex = lp.hasKeyName(index, "t"); if (jdex <= 0) { jdex = lp.hasKeyName(index, "text"); } if (jdex <= 0) { // no text to display for rehearsal mark return; } jdex = jdex - 1; MuseRecord arecord; arecord.insertString(25, lp.getValue(index, jdex).getBase()); arecord.getColumn(1) = '*'; arecord.getColumn(17) = 'R'; arecord.getColumn(19) = '+'; tempdata.append(arecord); } ////////////////////////////// // // addBackup -- insert a backup command. If the tick size is greater than // 999, it will have to be split into multiple entries. // void addBackup(MuseData& tempdata, int backticks, int tpq) { if (backticks <= 0) { cerr << "Error with ticks in addBackup: " << backticks << NEWLINE; } MuseRecord arecord; if (backticks < 1000) { arecord.setBack(backticks); tempdata.append(arecord); return; } int increment = tpq * 4; if (increment >= 1000) { increment = 999; } while (backticks > 0) { arecord.clear(); if (backticks <= increment) { arecord.setBack(backticks); tempdata.append(arecord); break; } backticks -= increment; arecord.setBack(increment); tempdata.append(arecord); } } ////////////////////////////// // // getTick -- return the tick position of the given line of music in // the Humdrum score. // int getTick(int tpq, HumdrumFile& infile, int line) { RationalNumber tique = infile[line].getAbsBeatR(); tique *= tpq; if (tique.getDenominator() != 1) { cerr << "Error at line " << line+1 << " in file.\n"; cerr << "Tick position is not an integer: " << tique << NEWLINE; exit(1); } return tique.getNumerator(); } ////////////////////////////// // // getDuration -- return the duration of the note/chord/rest. If the // note is not a grace note (indicated by a q or Q in the record), // and the note does not have a duration, then use the default duration // which can be set by the --dd option (the default duration if none // exists is a quarter note). This is used to display music with no // durations (primarily monophonic, but also polyphonic as long as // all parts have the same rhythmn). // RationalNumber getDuration(const char* input, const char* def) { Array strang; strang.setSize(strlen(input)+1); strcpy(strang.getBase(), input); PerlRegularExpression pre; pre.sar(strang, "\\s.*", "", ""); if (pre.search(strang, "\\d", "")) { // has some sort of numeric value so don't add default return Convert::kernToDurationR(strang.getBase()); } if (pre.search(strang, "q", "i")) { // is a grace note (0 duration, but let kernToDurationR decide) return Convert::kernToDurationR(strang.getBase()); } pre.sar(strang, "$", def); return Convert::kernToDurationR(strang.getBase()); } ////////////////////////////// // // getDurationNoDots -- // RationalNumber getDurationNoDots(const char* input, const char* def) { Array strang; strang.setSize(strlen(input)+1); strcpy(strang.getBase(), input); PerlRegularExpression pre; pre.sar(strang, "\\s.*", "", ""); if (pre.search(strang, "(\\d+)%(\\d+)", "")) { // handle a rational value in the rhythm RationalNumber rn; rn = atoi(pre.getSubmatch(2)); rn /= atoi(pre.getSubmatch(1)); rn *= 4; // convert from whole-note to quarter-note units. return rn; } if (pre.search(strang, "\\d", "")) { // has some sort of numeric value so don't add default return Convert::kernToDurationNoDotsR(strang.getBase()); } if (pre.search(strang, "q", "i")) { // is a grace note (0 duration, but let kernToDurationR decide) return Convert::kernToDurationNoDotsR(strang.getBase()); } pre.sar(strang, "$", def); return Convert::kernToDurationNoDotsR(strang.getBase()); } ///////////////////////////// // // addNoteToEntry -- Add a note or a chord to the data. // int addNoteToEntry(MuseData& tempdata, HumdrumFile& infile, int row, int col, int tpq, int voice, int opentie, Array >& lastVerse) { int& i = row; int& j = col; MuseRecord arecord; MuseRecord psuggestion; if (roundQ) { arecord.setRoundedBreve(); } RationalNumber rn = getDuration(infile[row][col], defaultDur.c_str()); int tokencount = infile[row].getTokenCount(col); char buffer[128] = {0}; int tickdur = getTickDur(tpq, infile, row, col); int k, kk; int hidetieQ = 0; int hidetie = 0; int tiemark = 0; Array temp; LayoutParameters lp; lp.parseLayout(infile, LayoutInfo[row][col]); const char* visual_display = ""; char visbuffer[128] = {0}; // if RscaleState[i][j] is not one, then apply a new visual display if (RscaleState[i][j] != 1) { RationalNumber visrn; visrn = rn * RscaleState[i][j]; Convert::durationRToKernRhythm(visbuffer, visrn); visual_display = visbuffer; } int m; for (m=0; m= 0) { visual_display = lp.getValue(m,ind).getBase(); } if (strcmp(visual_display, "dot") == 0) { // vis=dot case handled in print suggestion do not needed here // vis=dot means replace the noteheat/stem with a dot. visual_display = ""; } // only use the first visual_display setting found, and ignore any other break; } if (mensural2Q) { // if the note is tied, and the second note is 1/2 the duration // of the first note, then make the tie invisible, and make the // second note a dot, only considering the first note listed // in a chord. char tbuffer[32] = {0}; infile[row].getToken(tbuffer, col, 0); if (strchr(tbuffer, '[') != NULL) { // start of tied note: see if the duration of the note is 2/3 of the // tied duration. If so, then hide the tie. A dot will replace // the ending note of tie, so keep the same visual appearance. RationalNumber dur; RationalNumber tdur; dur = Convert::kernToDurationR(tbuffer); tdur = infile.getTiedDuration(row, col, 0); if (dur * 3 / 2 == tdur) { hidetieQ = 1; hidetie = 1; } } else if (strchr(tbuffer, ']') != NULL) { // end of tied note: see if the duration of the note is 1/3 of the // tied duration. If so, then convert the note to a dot. RationalNumber dur; RationalNumber tdur; dur = Convert::kernToDurationR(tbuffer); tdur = infile.getTotalTiedDurationR(row, col, 0); if (dur * 3 == tdur) { visual_display = ""; // turn off any existing visual instruction // add faked LO:N:vis=dot layout directive int cindex = lp.appendCode("N"); lp.addKeyValue(cindex, "vis", "dot"); } } } if (voice == 1) { addHairpinStarts(tempdata, infile, row, col); } // check for global layout text codes add above/below the system. LayoutParameters glp; glp.parseLayout(infile, GlobalLayoutInfo[row]); int ii; LayoutParameters tempparam; if ((glp.getSize() > 0) && isTopStaffOnSystem(infile, row, col)) { // Only display text if placed above the staff for (ii=0; ii 0) && isBottomStaffOnSystem(infile, row, col)) { // Only display text if placed below the staff for (ii=0; ii chordmapping; chordmapping.setSize(tokencount); if (tokencount > 1) { getChordMapping(chordmapping, infile, row, col); } else { chordmapping.setAll(0); } for (kk=0; kk 0) && (voice < 10)) { // set the track number arecord.getColumn(15) = '0' + voice; } checkColor(Colorchar, arecord, buffer, Colorout); if (tickdur > 0) { arecord.setTicks(tickdur); } else { if (kk > 0) { arecord.setTypeGraceChordNote(); } else { arecord.setTypeGraceNote(); } } if (strchr(buffer, 'r') != NULL) { if ((strchr(buffer, ';') != NULL) && (strstr(buffer, ";y") == NULL)) { if (voice != 2) { arecord.addAdditionalNotation('F'); } else{ // put fermata underneath if rest is in the second voice arecord.addAdditionalNotation('E'); } } if ((!noinvisibleQ) && strstr(buffer, "ry") != NULL) { // the rest should not be printed // also don't provide a shape for the rest. arecord.setPitch("irst"); // also "irest" can be used } else { if ((rn == MeasureDur[row]) && (RscaleState[i][j] == 1)) { // the duration of the rest matches the duration of the measure, // so make the rest a centered whole rest. If the duration // is greated than 4 quarter notes, maybe don't center? // To make a centered whole note shaped rests, put a space // in column 17: arecord.getColumn(17) = ' '; // sometimes have problems with blank rhythmic value // on rests [20120124]. So adding an explicit note // shape in certain conditions: if (rn * RscaleState[i][j] == 4) { arecord.getColumn(17) = 'w'; } else if (rn * RscaleState[i][j] == 8) { /* 1. to get a centered whole measure rest, either a whole-note or a breve rest, depending on the length of the measure. YOU SHOULD LAVE A " " (BLANK) IN COLUMN 17, NOT A "B". 2. the correct way to ask mskpage to remove a line that is empty is to use the P C0:x1 print suggestion. Turn it off with P C0:x0 when you want this feature to stop. These are "inline" suggestions and apply only to the part in question. The /END command automatically turns off this feature. 3. Putting a "B" in column 17 SHOULD ONLY BE DONE WHEN YOU WANT A DOWN-BEAT ALIGNED BREVE REST, AND YOU ALSO WANT THIS MEASURE REMOVED IN THE CASE WHERE THE LINE IS EMPTY. If you simply want a down-beat aligned full measure breve rest without this other complication, use "b" instead of "B". */ // for rests b = arecord.getColumn(17) = ' '; } } else { if (strlen(visual_display) > 0) { setNoteheadShape(arecord, visual_display); } else { setNoteheadShape(arecord, buffer); } } arecord.setPitch("rest"); } // added 20110815 so that column 20 tuplet info is filled in for // rests: addNoteLevelArtic(arecord, infile, row, col, k); // added 20110815 so that rests can start/end tuplet brackets addChordLevelArtic(tempdata, arecord, psuggestion, infile, row, col, voice); tempdata.append(arecord); if (kk == 0) { // also handles dynamics which are likely to have layout // information associated with them, and it is easier // to keep track of them in this function: handleLayoutInfoChord(tempdata, infile, row, col, lp, voice); } continue; } if (strlen(visual_display) > 0) { setNoteheadShape(arecord, visual_display); } else { setNoteheadShape(arecord, buffer); } if (hasLongQ) { // Longa notehead shape if (strchr(infile[row][col], LongChar) != NULL) { // Force to display a long regardless of visual_display // if (strlen(visual_display) == 0) { arecord.setNoteheadLong(); // } // current usage of the longa will not desire an // augmentation dot. If it is ever needed, then // there should be an option added to suppress // the dot in other cases. Ideally, the addition of // an augmentation dot for longs should be an option // and the default behavior left as is. arecord.getColumn(18) = ' '; hideNotesAfterLong(infile, row, col); } } tiemark = 0; if (tiedgroupQ && (strchr(infile[row][col], TiedGroupChar) != NULL)) { // hide note if not the first one in the tied note group // & set the shape of the first note in the group to the duration // of the group. if (strchr(infile[row][col], 'r') != NULL) { char doublemark[3]; doublemark[0] = TiedGroupChar; doublemark[1] = TiedGroupChar; doublemark[2] = '\0'; if (strstr(infile[row][col], doublemark) != NULL) { // deal with starting rest of group. // Set its duration to the duration of the rest group } else { // hide secondary rests in group temp.setSize(strlen(infile[row][col]+3)); strcpy(temp.getBase(), infile[row][col]); strcat(temp.getBase(), "yy"); infile[row].setToken(col, temp.getBase()); } } else if (strchr(infile[row][col], '[') != NULL) { // start of tied group: set notehead shape int len = strlen(infile[row][col]); temp.setSize(len+10); temp.setSize(len+1); strcpy(temp.getBase(), infile[row][col]); PerlRegularExpression pre10; pre10.sar(temp, "[[]", "[y", "g"); infile[row].setToken(col, temp.getBase()); RationalNumber tiedur = infile.getTiedDurationR(row, col); // add single augmentation dots (handle simple cases only): RationalNumber dotted4th(3,2); RationalNumber dotted8th(3,4); RationalNumber dotted16th(3,8); RationalNumber dotted32nd(3,16); RationalNumber dotted64th(3,32); RationalNumber dotted128th(3,64); RationalNumber dotted256th(3,128); if (tiedur == 3) { // dotted half note arecord.setDots(1); tiedur = 2; } else if (tiedur == 6) { // dotted whole note arecord.setDots(1); tiedur = 4; } else if (tiedur == 12) { // dotted breve note arecord.setDots(1); tiedur = 8; } else if (tiedur == 24) { // dotted long note arecord.setDots(1); tiedur = 16; } else if (tiedur == 48) { // dotted maxima note arecord.setDots(1); tiedur = 32; } else if (tiedur == dotted4th) { // dotted quarter note arecord.setDots(1); tiedur = 1; } else if (tiedur == dotted8th) { // dotted eighth note arecord.setDots(1); tiedur = 1; tiedur /= 2; } else if (tiedur == dotted16th) { arecord.setDots(1); tiedur = 1; tiedur /= 4; } else if (tiedur == dotted32nd) { arecord.setDots(1); tiedur = 1; tiedur /= 8; } else if (tiedur == dotted64th) { arecord.setDots(1); tiedur = 1; tiedur /= 16; } else if (tiedur == dotted128th) { arecord.setDots(1); tiedur = 1; tiedur /= 32; } else if (tiedur == dotted256th) { arecord.setDots(1); tiedur = 1; tiedur /= 64; } if (RscaleState[row][col] != 1) { RationalNumber scaling; tiedur *= RscaleState[row][col]; } // might need to deal with tuplets... arecord.setNoteheadShape(tiedur); arecord.setTie(1); // hide tie tiemark = 1; } else if ((strchr(infile[row][col], '_') != NULL) || (strchr(infile[row][col], ']') != NULL)) { // tie continuation: hide note temp.setSize(strlen(infile[row][col]+3)); strcpy(temp.getBase(), infile[row][col]); strcat(temp.getBase(), "yy"); infile[row].setToken(col, temp.getBase()); } } if (strchr(buffer, '/') != NULL) { // stem up arecord.setStemUp(); } if (strchr(buffer, '\\') != NULL) { // stem down arecord.setStemDown(); } if (!tiemark) { if (!(hasLongQ && (strchr(infile[row][col], LongChar) != NULL))) { if (strchr(buffer, '[') != NULL) { // tie start if ((strstr(buffer, "yy") != NULL) || (strstr(buffer, "[y") != NULL)) { arecord.setTie(1); } else { if (hidetieQ) { arecord.setTie(hidetie); } else { arecord.setTie(!tieQ); } } } } if (strchr(buffer, '_') != NULL) { // tie continuation if ((strstr(buffer, "yy") != NULL) || (strstr(buffer, "_y") != NULL)) { arecord.setTie(1); } else { if (hidetieQ) { arecord.setTie(hidetie); } else { arecord.setTie(!tieQ); } } } if ((opentie) && (strchr(buffer, ']') != NULL)) { // this closing tie has no opening, so show a tie going // off to the left which is not tied to anything. printPrehangTie(arecord, buffer, infile, row, col, voice); } } if (kk == 0) { if (beamQ) { arecord.setBeamInfo(BeamState[i][j]); } } if (kk == 0) { // handle artculations addChordLevelArtic(tempdata, arecord, psuggestion, infile, row, col, voice); } addNoteLevelArtic(arecord, infile, row, col, k); int graceQ = 0; if (strchr(buffer, 'q') != NULL) { graceQ = 1; } arecord.setPitch(Convert::kernToBase40(buffer), kk, graceQ); if ((strstr(buffer, "-y") == 0) || (strstr(buffer, "#y") == 0) || (strstr(buffer, "ny") == 0)) { // hidden accidental arecord.getColumn(19) = ' '; } // Maybe need to add lyric to every single note in the chord? // Otherwise Buf3056 and Duf3001 are mutually excusive in their display bugs. // if (kk == tokencount - 1) { addLyrics(arecord, infile, row, col, TextAssignment, TextElisions, lastVerse); // } tempdata.append(arecord); if (!psuggestion.isEmpty()) { tempdata.append(psuggestion); } if (kk == 0) { // also handles dynamics which are likely to have layout // information associated with them, and it is easier // to keep track of them in this function: handleLayoutInfoChord(tempdata, infile, row, col, lp, voice); } } if (voice == 1) { addHairpinStops(tempdata, infile, row, col); } return tickdur; } ////////////////////////////// // // printPrehaningTie -- add a hanging tie before a note. // K = slur dipping down // J = slur dipping up // // If single voice on staff, then use stem direction to determine which // of J,K to use. If there is no stem direction, then determine the clef // and then the staff position of the note (but input into this program // required a stem direction). // // If there are two voices, then the first voice will be stem up (use J), // and the second will be down (use K) // // // void printPrehangTie(MuseRecord& arecord, const char* buffer, HumdrumFile& infile, int row, int col, int voice) { if (!tieQ) { return; } int voicecount = 0; int track = infile[row].getPrimaryTrack(col); int ptrack; int j; for (j=0; j 1) { if (voice == 1) { // there are two voices, and this note is in the first voice, so make // the tie bend upwards arecord.addAdditionalNotation('J'); return; } else if (voice == 2) { arecord.addAdditionalNotation('K'); return; } else { // place the tie in the opposite direction of the beam if (stemdir > 0) { arecord.addAdditionalNotation('J'); return; } else { arecord.addAdditionalNotation('K'); return; } } } if (stemdir > 0) { arecord.addAdditionalNotation('K'); } else { arecord.addAdditionalNotation('J'); } } ////////////////////////////// // // isTopStaffOnSystem -- return true if the primary track of the given // cell is the top part on the system. Will not match to secondary tracks // other than the first one. // int isTopStaffOnSystem(int track) { if (track != KernTracks.last()) { return 0; } return 1; } int isTopStaffOnSystem(HumdrumFile& infile, int row, int col) { if (KernTracks.getSize() == 0) { return 0; } // the last entry in KernTracks global variable is the top part int track = infile[row].getPrimaryTrack(col); // only return true if there is no "b" character in the track string if (strchr(infile[row].getSpineInfo(col).c_str(), 'b') != NULL) { return 0; } return isTopStaffOnSystem(track); } ////////////////////////////// // // isBottomStaffOnSystem -- return true if the primary track of the given // cell is the bottom part on the system. Will not match to secondary // tracks other than the first one. // int isBottomStaffOnSystem(HumdrumFile& infile, int row, int col) { if (KernTracks.getSize() == 0) { return 0; } // the first entry in KernTracks global variable is the top part int track = infile[row].getPrimaryTrack(col); if (track != KernTracks[0]) { return 0; } // only return true if there is no "b" character in the track string if (strchr(infile[row].getSpineInfo(col).c_str(), 'b') != NULL) { return 0; } return 1; } ////////////////////////////// // // hidNotesAfterLong -- hides all notes tied after a long note. // also hides the barlines of any barlines which are found while removing. // currently presumes that each track does not have subspines. // void hideNotesAfterLong(HumdrumFile& infile, int row, int col) { if (strchr(infile[row][col], '[') == NULL) { // no ties on the long, so nothing to hide later. return; } int track = infile[row].getPrimaryTrack(col); RationalNumber endtime = infile[row].getAbsBeatR(); endtime += infile.getTiedDurationR(row,col); int j; int i = row+1; char buffer[1024] = {0}; while ((i= infile[i].getAbsBeatR())) { if (infile[i].isMeasure()) { // hide measure for (j=0; j >& TextAssignment, Array >& TextElisions, Array >& lastVerse) { int track = infile[row].getPrimaryTrack(col); int versecount = TextAssignment[track].getSize(); if (versecount == 0) { // no text to print so return return; } // Muse2ps cannot handle more than 6 verses // but limiting to 5 since MuseData cannot handle more than 80 columns if (versecount > verselimit) { versecount = verselimit; } Array trackcol; track2column(trackcol, infile, row); Array > verses; verses.setSize(versecount); int i; int texttrack; int elisionQ = 1; const char* ptr; PerlRegularExpression pre; char buffer[1024] = {0}; int textcol; for (i=0; i= 0) && pre.search(ptr, "^\\s*$")) { if (i == 0) { strcat(buffer, ""); } else { strcat(buffer, "|"); } continue; } } else { if ((i >= 0) && pre.search(ptr, "^\\s*$")) { char extension[2] = {0}; if (lastVerse[i].getSize() > 1) { extension[0] = lastVerse[i][lastVerse[i].getSize()-2]; if (extension[0] != '-') { extension[0] = '_'; } } if (i == 0) { strcat(buffer, extension); } else { strcat(buffer, "|"); strcat(buffer, extension); } continue; } } if (i > 0) { strcat(buffer, "|"); } int extensionneeded = 0; verses[i].setSize(strlen(ptr)+1); strcpy(verses[i].getBase(), ptr); texttrack = TextAssignment[track][i]; PerlRegularExpression pre3; if (infile[row].isExInterp(trackcol[texttrack], "**text")) { convertHumdrumTextToMuseData(verses[i]); convertHtmlTextToMuseData(verses[i]); // if the verse syllable starts with a digit, then it will not be // printed by default in MuseData muse2ps program. Adding // the string "\+" without quote in front of the number will // allow the number to be printed. Likewise, < and . characters // need to be forced to print in a similar manner. if (std::isdigit(verses[i].getBase()[0]) && (strcmp(ptr, "") != 0)) { strcat(buffer, "\\+"); } else if ((verses[i].getBase()[0] == '<') && (strcmp(ptr, "") != 0)) { strcat(buffer, "\\+"); } else if ((verses[i].getBase()[0] == '.') && (strcmp(ptr, "") != 0)) { strcat(buffer, "\\+"); } extensionneeded = 0; if (extensionQ && needsWordExtension(infile, row, col, textcol, verses[i])) { extensionneeded = 1; if (i < lastVerse.getSize()) { lastVerse[i].setSize(lastVerse[i].getSize()+1); strcat(lastVerse[i].getBase(), "_"); } } // removing & at end of line. This means do not extend a line after // the syllable. if (strcmp(verses[i].getBase(), "&") == 0) { pre3.sar(verses[i], "^.*$", "\\+"); } else { pre3.sar(verses[i], "&\\s*$", ""); } // add elision characters if requested, currently forcing // elison characters if the exinterp is **text: // if (elisionQ) { pre3.sar(verses[i], "(?<=[A-Za-z])\\s+(?=[A-Za-z])", "\\0+", "g"); // deal with punctuation, such as ".", ",", ":", ";", etc: // This avoids eliding text such as "1. A" which might be the // hacked number at the start of a verse. pre3.sar(verses[i], "(?<=[A-Za-z].)\\s+(?=[A-Za-z])", "\\0+", "g"); // } strcat(buffer, verses[i].getBase()); if (extensionneeded) { strcat(buffer, "_"); } } else { // treat non **text spines as lyrics: // (Don't apply convertHumdrumTextToMuseData) convertHtmlTextToMuseData(verses[i]); // cannot print backslashes. (don't know why, \\ doesn't work) pre3.sar(verses[i], "\\\\", "", "g"); // cannot print pipes (because it is a verse separator). pre3.sar(verses[i], "\\|", "", "g"); // [change 20111222] pre3.sar(verses[i], "^\\s*$", "\\+", ""); pre3.sar(verses[i], "^\\.$", "\\+", ""); // if the verse starts with a digit, then it will not be // printed by default in MuseData muse2ps program. Adding // the string "\+" without quote in front of the number will // allow the number to be printed. if (std::isdigit(verses[i].getBase()[0]) && (strcmp(ptr, "") != 0)) { strcat(buffer, "\\+"); } // same for "<" else if ((verses[i].getBase()[0] == '<') && (strcmp(ptr, "") != 0)) { strcat(buffer, "\\+"); } extensionneeded = 0; if (extensionQ && needsWordExtension(infile, row, col, textcol, verses[i])) { extensionneeded = 1; } // removing & at end of line. This means do not extend a line after // the syllable. if (strcmp(verses[i].getBase(), "&") == 0) { pre3.sar(verses[i], "^.*$", "\\+"); } else { pre3.sar(verses[i], "&\\s*$", ""); } // add elision characters if requested: if (elisionQ) { pre3.sar(verses[i], "(?=[A-Za-z])\\s+(?=[A-Z][a-z])", "\\0+", "g"); // deal with punctuation, such as ".", ",", ":", ";", etc: // This avoids eliding text such as "1. A" which might be the // hacked number at the start of a verse. pre3.sar(verses[i], "(?=[A-Za-z].)\\s+(?=[A-Z][a-z])", "\\0+", "g"); } strcat(buffer, verses[i].getBase()); if (extensionneeded) { strcat(buffer, "_"); } } } if (lastVerse.getSize() != verses.getSize()) { lastVerse.setSize(verses.getSize()); for (i=0; i 1) { lastVerse[i].setSize(verses[i].getSize()); strcpy(lastVerse[i].getBase(), verses[i].getBase()); } } // Convert spaces to \+ (elison character may have removed some spaces). Array newbuffer; newbuffer.setSize(strlen(buffer)+1); strcpy(newbuffer.getBase(), buffer); if (!pre.search(newbuffer, "^\\s*$")) { pre.sar(newbuffer, "\\s", "\\+", "g"); } pre.sar(newbuffer, "%%%", "", "g"); if (strlen(buffer) > 0) { arecord.insertString(44, newbuffer.getBase()); } } ////////////////////////////// // // needsWordExtension -- // int needsWordExtension(HumdrumFile& infile, int row, int notecol, int versecol, Array& verse) { PerlRegularExpression pre; if (pre.search(verse, "-\\s*$")) { // doesn't need a word extender if not end of word... return 0; } if (pre.search(verse, "&\\s*$")) { // doesn't need a word extender. Using "&" at the end of the syllable // means do not put a line extender. This is an impromptu // method which may change. return 0; } if (!pre.search(verse, "[a-z]", "i")) { // don't print a line extender if there are no letters in the syllable. return 0; } if (strcmp(verse.getBase(), "\\+") == 0) { // don't print if only a space character. return 0; } int i, j; int ntrack = infile[row].getPrimaryTrack(notecol); int vtrack = infile[row].getPrimaryTrack(versecol); int newnote = 0; int newverse = 0; int track; for (i=row+1; i & text) { PerlRegularExpression pre; PerlRegularExpression pre2; if (pre.sar(text, "^\\s*$", " ", "")) { return; } // if (pre.sar(text, "^\\s*$", "\\+", "")) { // // a blank syllable // return; // } pre.sar(text, " \\*", ".", "g"); // convert period " *" -> "." pre.sar(text, " ,", ",", "g"); // convert comma " ," -> "," pre.sar(text, " \\?", "?", "g"); // convert question " ?" -> "?" pre.sar(text, " !", "!", "g"); // convert exclamation " !" -> "!" pre.sar(text, " :", ":", "g"); // convert colon " :" -> ":" pre.sar(text, " ;", ";", "g"); // convert semi-colon " ;" -> ";" pre.sar(text, "~", "-", "g"); // word hypen "~" -> "-" pre.sar(text, "^-", "", ""); // remove staring hyphen marker pre.sar(text, "2a", "\\===a===3", "g"); // umlauts pre.sar(text, "2e", "\\===e===3", "g"); // umlauts pre.sar(text, "2i", "\\===i===3", "g"); // umlauts pre.sar(text, "2o", "\\===o===3", "g"); // umlauts pre.sar(text, "2u", "\\===u===3", "g"); // umlauts pre.sar(text, "2A", "\\===A===3", "g"); // umlauts pre.sar(text, "2E", "\\===E===3", "g"); // umlauts pre.sar(text, "2I", "\\===I===3", "g"); // umlauts pre.sar(text, "2O", "\\===O===3", "g"); // umlauts pre.sar(text, "2U", "\\===U===3", "g"); // umlauts pre.sar(text, "2y", "\\===y===3", "g"); // umlauts pre.sar(text, "2Y", "\\===Y===3", "g"); // umlauts pre.sar(text, "7n", "\\===n===1", "g"); // tildes pre.sar(text, "7N", "\\===N===1", "g"); // tildes pre.sar(text, "7a", "\\===a===1", "g"); // tildes pre.sar(text, "7e", "\\===e===1", "g"); // tildes pre.sar(text, "7i", "\\===i===1", "g"); // tildes pre.sar(text, "7o", "\\===o===1", "g"); // tildes pre.sar(text, "7u", "\\===u===1", "g"); // tildes pre.sar(text, "7A", "\\===A===1", "g"); // tildes pre.sar(text, "7E", "\\===E===1", "g"); // tildes pre.sar(text, "7I", "\\===I===1", "g"); // tildes pre.sar(text, "7O", "\\===O===1", "g"); // tildes pre.sar(text, "7U", "\\===U===1", "g"); // tildes pre.sar(text, "7y", "\\===y===1", "g"); // tildes pre.sar(text, "7Y", "\\===Y===1", "g"); // tildes pre.sar(text, "/a", "\\===a===7", "g"); // acute pre.sar(text, "/e", "\\===e===7", "g"); // acute pre.sar(text, "/i", "\\===i===7", "g"); // acute pre.sar(text, "/o", "\\===o===7", "g"); // acute pre.sar(text, "/u", "\\===u===7", "g"); // acute pre.sar(text, "/A", "\\===A===7", "g"); // acute pre.sar(text, "/E", "\\===E===7", "g"); // acute pre.sar(text, "/I", "\\===I===7", "g"); // acute pre.sar(text, "/O", "\\===O===7", "g"); // acute pre.sar(text, "/U", "\\===U===7", "g"); // acute pre.sar(text, "/y", "\\===y===7", "g"); // acute pre.sar(text, "/Y", "\\===Y===7", "g"); // acute pre.sar(text, "\\\\a", "\\===a===8", "g"); // grave pre.sar(text, "\\\\e", "\\===e===8", "g"); // grave pre.sar(text, "\\\\i", "\\===i===8", "g"); // grave pre.sar(text, "\\\\o", "\\===o===8", "g"); // grave pre.sar(text, "\\\\u", "\\===u===8", "g"); // grave pre.sar(text, "\\\\A", "\\===A===8", "g"); // grave pre.sar(text, "\\\\E", "\\===E===8", "g"); // grave pre.sar(text, "\\\\I", "\\===I===8", "g"); // grave pre.sar(text, "\\\\O", "\\===O===8", "g"); // grave pre.sar(text, "\\\\U", "\\===U===8", "g"); // grave pre.sar(text, "\\\\y", "\\===y===8", "g"); // grave pre.sar(text, "\\\\Y", "\\===Y===8", "g"); // grave pre.sar(text, "\\^a", "\\===a===9", "g"); // circumflex pre.sar(text, "\\^e", "\\===e===9", "g"); // circumflex pre.sar(text, "\\^i", "\\===i===9", "g"); // circumflex pre.sar(text, "\\^o", "\\===o===9", "g"); // circumflex pre.sar(text, "\\^u", "\\===u===9", "g"); // circumflex pre.sar(text, "\\^A", "\\===A===9", "g"); // circumflex pre.sar(text, "\\^E", "\\===E===9", "g"); // circumflex pre.sar(text, "\\^I", "\\===I===9", "g"); // circumflex pre.sar(text, "\\^O", "\\===O===9", "g"); // circumflex pre.sar(text, "\\^U", "\\===U===9", "g"); // circumflex pre.sar(text, "\\^y", "\\===y===9", "g"); // circumflex pre.sar(text, "\\^Y", "\\===Y===9", "g"); // circumflex pre.sar(text, "===", "", "g"); // get rid of buffer characters pre.sar(text, "^\"", "\\0\"", "g"); // lefthand double quote // macron: 1 // cedilla: 5 // pre.sar(text, "\\s+", "\\0", "g"); // word elision character doesn't work pre.sar(text, "\\|", "", "g"); // disable dashes for now. } void convertHtmlTextToMuseData(Array & text) { PerlRegularExpression pre; pre.sar(text, "ä", "\\a3", "g"); // umlaut pre.sar(text, "ë", "\\e3", "g"); // umlaut pre.sar(text, "ï", "\\i3", "g"); // umlaut pre.sar(text, "ö", "\\o3", "g"); // umlaut pre.sar(text, "ü", "\\u3", "g"); // umlaut pre.sar(text, "Ä", "\\A3", "g"); // umlaut pre.sar(text, "Ë", "\\E3", "g"); // umlaut pre.sar(text, "Ï", "\\I3", "g"); // umlaut pre.sar(text, "Ö", "\\O3", "g"); // umlaut pre.sar(text, "Ü", "\\U3", "g"); // umlaut pre.sar(text, "ÿ", "\\y3", "g"); // umlaut pre.sar(text, "Ÿ", "\\Y3", "g"); // umlaut pre.sar(text, "ñ", "\\n1", "g"); // tilde pre.sar(text, "Ñ", "\\N1", "g"); // tilde pre.sar(text, "ã", "\\a1", "g"); // tilde pre.sar(text, "&etilde;", "\\e1", "g"); // tilde pre.sar(text, "ĩ", "\\i1", "g"); // tilde pre.sar(text, "õ", "\\o1", "g"); // tilde pre.sar(text, "ũ", "\\u1", "g"); // tilde pre.sar(text, "Ã", "\\A1", "g"); // tilde pre.sar(text, "&Etilde;", "\\E1", "g"); // tilde pre.sar(text, "Ĩ", "\\I1", "g"); // tilde pre.sar(text, "Õ", "\\O1", "g"); // tilde pre.sar(text, "Ũ", "\\U1", "g"); // tilde pre.sar(text, "&ytilde;", "\\y1", "g"); // tilde pre.sar(text, "&Ytilde;", "\\Y1", "g"); // tilde pre.sar(text, "à", "\\a8", "g"); // grave pre.sar(text, "è", "\\e8", "g"); // grave pre.sar(text, "ì", "\\i8", "g"); // grave pre.sar(text, "ò", "\\o8", "g"); // grave pre.sar(text, "ù", "\\u8", "g"); // grave pre.sar(text, "À", "\\A8", "g"); // grave pre.sar(text, "È", "\\E8", "g"); // grave pre.sar(text, "Ì", "\\I8", "g"); // grave pre.sar(text, "Ò", "\\O8", "g"); // grave pre.sar(text, "Ù", "\\U8", "g"); // grave pre.sar(text, "&ygrave;", "\\y8", "g"); // grave pre.sar(text, "&Ygrave;", "\\Y8", "g"); // grave pre.sar(text, "á", "\\a7", "g"); // acute pre.sar(text, "é", "\\e7", "g"); // acute pre.sar(text, "í", "\\i7", "g"); // acute pre.sar(text, "ó", "\\o7", "g"); // acute pre.sar(text, "ú", "\\u7", "g"); // acute pre.sar(text, "Á", "\\A7", "g"); // acute pre.sar(text, "É", "\\E7", "g"); // acute pre.sar(text, "Í", "\\I7", "g"); // acute pre.sar(text, "Ó", "\\O7", "g"); // acute pre.sar(text, "Ú", "\\U7", "g"); // acute pre.sar(text, "ý", "\\y7", "g"); // acute pre.sar(text, "Ý", "\\Y7", "g"); // acute pre.sar(text, "â", "\\a9", "g"); // circumflex pre.sar(text, "ê", "\\e9", "g"); // circumflex pre.sar(text, "î", "\\i9", "g"); // circumflex pre.sar(text, "ô", "\\o9", "g"); // circumflex pre.sar(text, "û", "\\u9", "g"); // circumflex pre.sar(text, "Â", "\\A9", "g"); // circumflex pre.sar(text, "Ê", "\\E9", "g"); // circumflex pre.sar(text, "Î", "\\I9", "g"); // circumflex pre.sar(text, "Ô", "\\O9", "g"); // circumflex pre.sar(text, "Û", "\\U9", "g"); // circumflex pre.sar(text, "ŷ", "\\y9", "g"); // circumflex pre.sar(text, "Ŷ", "\\Y9", "g"); // circumflex pre.sar(text, "ß", "\\s2", "g"); // ss pre.sar(text, "ç", "\\c2", "g"); // c-cedilla pre.sar(text, "ø", "\\o2", "g"); // o-slash pre.sar(text, "å", "\\a4", "g"); // ring pre.sar(text, "&ering;", "\\e4", "g"); // ring pre.sar(text, "&iring;", "\\i4", "g"); // ring pre.sar(text, "&oring;", "\\o4", "g"); // ring pre.sar(text, "ů", "\\u4", "g"); // ring pre.sar(text, "Å", "\\A4", "g"); // ring pre.sar(text, "&Ering;", "\\E4", "g"); // ring pre.sar(text, "&Iring;", "\\I4", "g"); // ring pre.sar(text, "&Oring;", "\\O4", "g"); // ring pre.sar(text, "Ů", "\\U4", "g"); // ring // 5's are v (hachek) accent (\\s5) // 6's? pre.sar(text, "¿", "\\0?", "g"); // inverted question mark pre.sar(text, ":", ":", "g"); // colon (:) pre.sar(text, "&eqals;", "=", "g"); // equals sign (=) pre.sar(text, "&", "&", "g"); // ampersand (&) pre.sar(text, "^Fine$", "Fine\\+", ""); // workaround to force Fine printing pre.sar(text, "^fine$", "fine\\+", ""); // workaround to force fine printing pre.sar(text, "D\\. C\\.", "D.\\+C.", "g"); // workaround for Da capo // Polish accented letters (no mapping at the moment pre.sar(text, "Ą", "A,", "g"); // A ogon pre.sar(text, "Ę", "E,", "g"); // E ogon pre.sar(text, "ą", "a,", "g"); // a ogon pre.sar(text, "ę", "e,", "g"); // e ogon pre.sar(text, "Ć", "C'", "g"); // C acute pre.sar(text, "Ń", "N'", "g"); // N acute pre.sar(text, "Ś", "S'", "g"); // S acute pre.sar(text, "Ź", "Z'", "g"); // Z acute pre.sar(text, "Ż", "Z.", "g"); // Z dot pre.sar(text, "Ł", "L/", "g"); // L-stroke pre.sar(text, "ć", "c'", "g"); // c acute pre.sar(text, "ń", "n'", "g"); // n acute pre.sar(text, "ś", "s'", "g"); // s acute pre.sar(text, "ź", "z'", "g"); // z acute pre.sar(text, "ż", "z.", "g"); // z dot pre.sar(text, "ł", "l/", "g"); // l-stroke // Unicode UTF-8 for Polish letters: pre.sar(text, "\\xc4\\x84", "A,", "g"); // A ogon pre.sar(text, "\\xc4\\x86", "C'", "g"); // C acute pre.sar(text, "\\xc4\\x98", "E,", "g"); // E ogon pre.sar(text, "\\xc5\\x81", "L/", "g"); // L slash pre.sar(text, "\\xc5\\x83", "N'", "g"); // N acute pre.sar(text, "\\xc3\\x93", "\\O7", "g"); // O acute pre.sar(text, "\\xc5\\x9a", "S'", "g"); // S acute pre.sar(text, "\\xc5\\xb9", "Z'", "g"); // Z acute pre.sar(text, "\\xc5\\xbb", "Z.", "g"); // Z dot pre.sar(text, "\\xc4\\x85", "a,", "g"); // a ogon pre.sar(text, "\\xc4\\x87", "c'", "g"); // c acute pre.sar(text, "\\xc4\\x99", "e,", "g"); // e ogon pre.sar(text, "\\xc5\\x82", "l/", "g"); // l slash pre.sar(text, "\\xc5\\x84", "n'", "g"); // n acute pre.sar(text, "\\xc3\\xb3", "\\o7", "g"); // o acute pre.sar(text, "\\xc5\\x9b", "s'", "g"); // s acute pre.sar(text, "\\xc5\\xba", "z'", "g"); // z acute pre.sar(text, "\\xc5\\xbc", "z.", "g"); // z dot // goes to !42| ... !40| (for 11 point font) // Currently T and u (subtitle) entries have to be on separate option lines. // // Font numbers and sizes: // 31 = regular 8 point // 32 = bold 8 point // 33 = italic 8 point // 34 = regular 9 point // 37 = regular 10 point // 40 = regular 11 point // 43 = regular 12 point // 46 = regular 14 point // 48 = italic 14 point // (you fill in the gaps in this) // if (pre.search(text, "T\\^")) { pre.sar(text, "", "!42|", "g"); pre.sar(text, "", "!40|", "g"); } else if (pre.search(text, "u\\^")) { pre.sar(text, "", "!36|", "g"); pre.sar(text, "", "!34|", "g"); } } ////////////////////////////// // // track2column -- // void track2column(Array& trackcol, HumdrumFile& infile, int row) { trackcol.setSize(infile.getMaxTracks()+1); trackcol.setAll(-1); int j, track; for (j=0; j >& keys, Array >& values) { int i; int italicQ = 0; int boldQ = 0; const char* text = ""; char justification = 'D'; // left justified //justification = 'C'; // center justified //justification = 'B'; // right justified int dsize = 0; for (i=0; i textstring; Array textstring2; textstring.setSize(strlen(text)+1); strcpy(textstring.getBase(), text); convertHtmlTextToMuseData(textstring); int fontnumber = 31; // roman, regular size if (boldQ && !italicQ) { fontnumber = 32; // bold, regular size } else if (italicQ && !boldQ) { fontnumber = 33; // italic, regular size } else if (italicQ && boldQ) { // can't do both italic and bold at the same time? Setting to italic fontnumber = 33; // italic & bold, regular size } fontnumber += dsize; int column = 17; MuseRecord arecord; arecord.appendString("*"); arecord.getColumn(column) = justification; arecord.insertString(25, textstring.getBase()); tempdata.append(arecord); // add font info arecord.clear(); if ((dsize != 0) || italicQ || boldQ) { arecord.append("sisi", "P C", column, ":f" , fontnumber); tempdata.append(arecord); } addPositionParameters(tempdata, column, keys, values); } ////////////////////////////// // // addHairpinStarts -- place cresc and decresc hairpins in music. In theory // the dynamic markings can be added to the start/stop of the // hairpin, but these are handled in handleLayoutInfoChord. // void addHairpinStarts(MuseData& tempdata, HumdrumFile& infile, int row, int col) { PerlRegularExpression pre; Array& da = DynamicsAssignment; int j; int track = infile[row].getPrimaryTrack(col); int targettrack = da[track]; if (targettrack <= 0) { // this **kern track does not have a dynamics assignment. return; } int dcol = -1; for (j=0; j])(?!yy)", "")) { return; } LayoutParameters lpd; lpd.parseLayout(infile, LayoutInfo[row][dcol]); // If there is an X appended to the < or >, then it should also not // be printed as a hairpin since it is a written word. PerlRegularExpression pre2; if (pre2.search(infile[row][dcol], "([\\[\\]]).*[<>]X?", "")) { // there is a stoping crescendo/decrescendo occuring before // the next one start, so stop it before the new one starts. if (strcmp(pre2.getSubmatch(1), "[") == 0) { addCrescendoStop(tempdata, lpd); } else if (strcmp(pre2.getSubmatch(), "]") == 0) { addDecrescendoStop(tempdata, lpd); } } if (!pre.search(infile[row][dcol], "([<>]X?)", "")) { return; } // The "X" character after a crescendo or decrescendo start mark // indidates that a text version of the symbol should be printed. // (note that the crescento or descrescendo stop mark needs to // be suppresed in these cases, so also add an "X" to the end // of the cresc/decresc (otherwise muse2ps will currently // quit without producing any output). if (strcmp(">", pre.getSubmatch(1)) == 0) { addDecrescendoStart(tempdata, lpd); } else if (strcmp("<", pre.getSubmatch(1)) == 0) { addCrescendoStart(tempdata, lpd); } else if (strcmp(">X", pre.getSubmatch(1)) == 0) { addCrescText(tempdata, infile, row, col, dcol, lpd, "decresc."); } else if (strcmp("= 0) { text = lp.getValue(i,pind).getBase(); } } MuseRecord arecord; arecord.getColumn(1) = '*'; int column = 17; // allow all these later: // D = left-justified // C = centered // B = right-justified arecord.getColumn(column) = 'D'; arecord.insertString(25, text); tempdata.append(arecord); int dQ = addDashing(tempdata,column+1, infile[row].getPrimaryTrack(col), lp); int fontnumber = 33; // 33 = italic arecord.clear(); arecord.append("sisi", "P C", column, ":f", fontnumber); tempdata.append(arecord); addPositionInfo(tempdata, column, lp, "TX"); if (dQ) { addPositionInfo(tempdata, column+1, lp, "TX"); } } ////////////////////////////// // // addDashing -- // int addDashing(MuseData& tempdata, int column, int track, LayoutParameters& lp) { // check for a "DY" code with a "dash" key int dashQ = 0; int i, j; for (i=0; i >& keys, Array >& values) { Array vals; Array states; getXxYy(vals, states, keys, values); int& X = vals[0]; int& x = vals[1]; int& Y = vals[2]; int& y = vals[3]; int& XQ = states[0]; int& xQ = states[1]; int& YQ = states[2]; int& yQ = states[3]; char buffer[128] = {0}; char nbuff[32] = {0}; if (!(xQ || XQ || yQ || YQ)) { // no position information so don't do anything return; } if (xQ) { sprintf(nbuff, "%d", x); strcat(buffer, "x"); strcat(buffer, nbuff); } else if (XQ) { sprintf(nbuff, "%d", X); strcat(buffer, "x"); // X does not seem to work, to remap to x strcat(buffer, nbuff); } if (yQ) { sprintf(nbuff, "%d", y); strcat(buffer, "y"); strcat(buffer, nbuff); } else if (YQ) { sprintf(nbuff, "%d", Y); strcat(buffer, "Y"); strcat(buffer, nbuff); } if (strlen(buffer) == 0) { // something strange happend, don't print anything return; } char buffer2[128] = {0}; sprintf(buffer2, " C%d:%s", column, buffer); // if there is already a print suggestion line, then the new // print suggestion must occur on the same line (appended to end). if ((!tempdata.isEmpty()) && (tempdata.last().getColumn(1) == 'P')) { MuseRecord& arecord = tempdata.last(); arecord.appendString(buffer2); } else { MuseRecord arecord; arecord.appendString("P"); arecord.appendString(buffer2); tempdata.append(arecord); } } ////////////////////////////// // // addCrescendoStart -- Add a crescendo hairpin start. Also will // process layout information. // void addCrescendoStart(MuseData& tempdata, LayoutParameters& lp) { MuseRecord arecord; char buffer[32] = {0}; int spread = 0; int column = 18; arecord.getColumn(1) = '*'; arecord.getColumn(column) = 'E'; sprintf(buffer, "%d", spread); arecord.insertStringRight(23, buffer); tempdata.append(arecord); addPositionInfo(tempdata, column, lp, "HP"); } ////////////////////////////// // // addDecrescendoStop -- Add a descrescendo hairpin stop. Also will // process layout information. // void addDecrescendoStop(MuseData& tempdata, LayoutParameters& lp) { MuseRecord arecord; char buffer[32] = {0}; int spread = 0; int column = 17; // can also be 18, but reserve for optional dynamic arecord.getColumn(1) = '*'; arecord.getColumn(column) = 'F'; sprintf(buffer, "%d", spread); arecord.insertStringRight(23, buffer); tempdata.append(arecord); // Note that the MuseData printing system ignores Y adjustments // to the ends of dyanmics hairpins. addPositionInfo(tempdata, column, lp, LO_WEDGE_END); } ////////////////////////////// // // addCrescendoStop -- Add a screscendo hairpin stop. Also will // process layout information. // void addCrescendoStop(MuseData& tempdata, LayoutParameters& lp) { MuseRecord arecord; char buffer[32] = {0}; int spread = 12; int column = 17; // can also be 18, but reserve for optional dynamic arecord.getColumn(1) = '*'; arecord.getColumn(column) = 'F'; sprintf(buffer, "%d", spread); arecord.insertStringRight(23, buffer); tempdata.append(arecord); // Note that the MuseData printing system ignores Y adjustments // to the ends of dyanmics hairpins. addPositionInfo(tempdata, column, lp, LO_WEDGE_END); } ////////////////////////////// // // addHairpinStops -- place cresc and decresc hairpins in music. In theory // the dynamic markings can be added to the start/stop of the // hairpin, but these are handled in handleLayoutInfoChord. // void addHairpinStops(MuseData& tempdata, HumdrumFile& infile, int row, int col) { PerlRegularExpression pre; Array& da = DynamicsAssignment; int j; int track = infile[row].getPrimaryTrack(col); int targettrack = da[track]; if (targettrack <= 0) { // this **kern track does not have a dynamics assignment. return; } int dcol = -1; for (j=0; j]", "")) { // the stop is for the previous crescendo, and a new one is // starting at this note, so don't handle the stop, since it // was handled before the start was printed. However, there // is a possible case which is currently being missed: // if there are two stops, and one is after start, then // need to print the second stop. Add that case later... return; } if (!pre.search(infile[row][dcol], "([\\[\\]]X?)", "")) { return; } LayoutParameters lpd; lpd.parseLayout(infile, LayoutInfo[row][dcol]); // If there is an X appended to the < or >, then it should not // be printed as a hairpin since it is a written word. if (strcmp("[", pre.getSubmatch(1)) == 0) { addCrescendoStop(tempdata, lpd); } else if (strcmp("]", pre.getSubmatch(1)) == 0) { addDecrescendoStop(tempdata, lpd); } else if (strcmp("]X", pre.getSubmatch(1)) == 0) { // suppress printing a * record. addUnDash(tempdata, infile, row, col, dcol, lpd); } else if (strcmp("[X", pre.getSubmatch(1)) == 0) { // suppress printing a * record. addUnDash(tempdata, infile, row, col, dcol, lpd); } } ////////////////////////////// // // getChordMapping -- chords are displayed with the note furthest from // the stem displayed first, then the other notes, with the last // note in the chord list being the one on the stem side of the chord. // void getChordMapping(Array& chordmapping, HumdrumFile& infile, int row, int col) { Array pitches; getPitches(pitches, infile, row, col); int stemdir = +1; if (strchr(infile[row][col], '\\') != NULL) { stemdir = -1; } // do automatic analysis for whole notes here, but this will involve // knowing the clef which the notes are placed... Array tempp; tempp.setSize(pitches.getSize()); int i; for (i=0; i 0) { // stemdir is up, so sort notes from low to high qsort(tempp.getBase(), tempp.getSize(), sizeof(Coord), numbersort); } else { // stemdir is down, so sort notes from high to low qsort(tempp.getBase(), tempp.getSize(), sizeof(Coord), numberRsort); } chordmapping.setSize(pitches.getSize()); for (i=0; i valueb.j) { return -1; } else if (valuea.j < valueb.j) { return +1; } else { return 0; } } ////////////////////////////// // // numbersort -- sort smallest largest first. // int numbersort(const void* A, const void* B) { Coord valuea = *((Coord*)A); Coord valueb = *((Coord*)B); if (valuea.j > valueb.j) { return +1; } else if (valuea.j < valueb.j) { return -1; } else { return 0; } } ////////////////////////////// // // getPitches -- // void getPitches(Array& pitches, HumdrumFile& infile, int row, int col) { int k; int tokencount = infile[row].getTokenCount(col); pitches.setSize(tokencount); pitches.setAll(0); char buffer[128] = {0}; for (k=0; k > pfields; MuseRecord prec; prec.setLine("P"); char notepsbuffer[128] = {0}; char tbuffer[128] = {0}; infile[row].getToken(tbuffer, col, 0); int i, j; if (lp.getSize() != 0 ) { for (i=0; i