From 262eeb0c6f3f37599ec198cae1556f9f06282089 Mon Sep 17 00:00:00 2001 From: Craig Sapp Date: Sun, 29 Sep 2019 11:41:47 -0700 Subject: [PATCH 001/140] Update humlib (adds MuseData importer). --- include/hum/humlib.h | 684 +++- src/hum/humlib.cpp | 7844 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 8517 insertions(+), 11 deletions(-) diff --git a/include/hum/humlib.h b/include/hum/humlib.h index 9f3ecc825dd..6f86728ed77 100644 --- a/include/hum/humlib.h +++ b/include/hum/humlib.h @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Fri Sep 20 06:46:55 PDT 2019 +// Last Modified: Sun Sep 29 08:05:25 PDT 2019 // Filename: humlib.h // URL: https://github.com/craigsapp/humlib/blob/master/include/humlib.h // Syntax: C++11 @@ -336,6 +336,7 @@ class HumNum { std::ostream& printMixedFraction (std::ostream& out = std::cout, std::string separator = "_") const; std::ostream& printList (std::ostream& out) const; + std::ostream& printTwoPart (std::ostream& out, const std::string& spacer = "+") const; protected: void reduce (void); @@ -2462,6 +2463,604 @@ class HumdrumFile : public HUMDRUMFILE_PARENT { +// Reference: Beyond Midi, page 410. +#define E_muserec_note_regular 'N' + // 'A' --> use type E_muserec_note_regular + // 'B' --> use type E_muserec_note_regular + // 'C' --> use type E_muserec_note_regular + // 'D' --> use type E_muserec_note_regular + // 'E' --> use type E_muserec_note_regular + // 'F' --> use type E_muserec_note_regular + // 'G' --> use type E_muserec_note_regular +#define E_muserec_note_chord 'C' +#define E_muserec_note_cue 'c' +#define E_muserec_note_grace 'g' +#define E_muserec_note_grace_chord 'G' +#define E_muserec_print_suggestion 'P' +#define E_muserec_sound_directives 'S' +#define E_muserec_end '/' +#define E_muserec_endtext 'T' +#define E_muserec_append 'a' +#define E_muserec_backspace 'b' +#define E_muserec_back 'b' +#define E_muserec_backward 'b' +#define E_muserec_figured_harmony 'f' +#define E_muserec_rest_invisible 'i' +#define E_muserec_forward 'i' +#define E_muserec_measure 'm' +#define E_muserec_rest 'r' +#define E_muserec_musical_attributes '$' +#define E_muserec_comment_toggle '&' +#define E_muserec_comment_line '@' +#define E_muserec_musical_directions '*' +#define E_muserec_header_1 '1' // reserved for copyright notice +#define E_muserec_header_2 '2' // reserved for identification +#define E_muserec_header_3 '3' // reserved +#define E_muserec_header_4 '4' // +#define E_muserec_header_5 '5' // WK#: MV#: +#define E_muserec_header_6 '6' // +#define E_muserec_header_7 '7' // +#define E_muserec_header_8 '8' // +#define E_muserec_header_9 '9' // +#define E_muserec_header_part_name '9' // +#define E_muserec_header_10 '0' // misc designations +#define E_muserec_header_11 'A' // group memberships +#define E_muserec_group_memberships 'A' // group memberships +// multiple musered_head_12 lines can occur: +#define E_muserec_header_12 'B' // : part of +#define E_muserec_unknown 'U' // unknown record type +#define E_muserec_empty 'E' // nothing on line and not header + // or multi-line comment +#define E_muserec_deleted 'D' // deleted line +// non-standard record types for MuseDataSet +#define E_muserec_filemarker '+' +#define E_muserec_filename 'F' +#define E_musrec_header 1000 +#define E_musrec_footer 2000 + + +class MuseRecordBasic { + public: + MuseRecordBasic (void); + MuseRecordBasic (const std::string& aLine, int index = -1); + MuseRecordBasic (MuseRecordBasic& aRecord); + ~MuseRecordBasic (); + + void clear (void); + int isEmpty (void); + void cleanLineEnding (void); + std::string extract (int start, int stop); + char& getColumn (int index); + std::string getColumns (int startcol, int endcol); + void setColumns (std::string& data, int startcol, + int endcol); + int getLength (void) const; + std::string getLine (void); + int getLineIndex (void) { return m_lineindex; } + void setLineIndex (int index); + int getLineNumber (void) { return m_lineindex+1; } + int getType (void) const; + void setTypeGraceNote (void); + void setTypeGraceChordNote(void); + + MuseRecordBasic& operator= (MuseRecordBasic& aRecord); + MuseRecordBasic& operator= (MuseRecordBasic* aRecord); + MuseRecordBasic& operator= (const std::string& aRecord); + char& operator[] (int index); + void setLine (const std::string& aString); + void setType (int aType); + void shrink (void); + void insertString (int column, const std::string& strang); + void insertStringRight (int column, const std::string& strang); + void setString (std::string& strang); + void appendString (const std::string& strang); + void appendInteger (int value); + void appendRational (HumNum& value); + void append (const char* format, ...); + + // mark-up accessor functions: + + void setAbsBeat (HumNum value); + void setAbsBeat (int topval, int botval = 1); + HumNum getAbsBeat (void); + + void setLineDuration (HumNum value); + void setLineDuration (int topval, int botval = 1); + HumNum getLineDuration (void); + + void setNoteDuration (HumNum value); + void setNoteDuration (int topval, int botval = 1); + HumNum getNoteDuration (void); + void setRoundedBreve (void); + + void setMarkupPitch (int aPitch); + int getMarkupPitch (void); + + + // tied note functions: + int isTied (void); + int getLastTiedNoteLineIndex(void); + int getNextTiedNoteLineIndex(void); + void setLastTiedNoteLineIndex(int index); + void setNextTiedNoteLineIndex(int index); + + // boolean type fuctions: + bool isAttributes (void); + bool isBarline (void); + bool isChordGraceNote (void); + bool isChordNote (void); + bool isCueNote (void); + bool isFiguredHarmony (void); + bool isGraceNote (void); + bool isNote (void); + bool isPartName (void); + bool isRest (void); + + protected: + std::string m_recordString; // actual characters on line + + // mark-up data for the line: + int m_lineindex; // index into original file + int m_type; // category of MuseRecordBasic record + HumNum m_absbeat; // dur in quarter notes from start + HumNum m_lineduration; // duration of line + HumNum m_noteduration; // duration of note + + int m_b40pitch; // base 40 pitch + int m_nexttiednote; // line number of next note tied to + // this one (-1 if no tied note) + int m_lasttiednote; // line number of previous note tied + // to this one (-1 if no tied note) + int m_roundBreve; +}; + + +std::ostream& operator<<(std::ostream& out, MuseRecordBasic& aRecord); + + + + + +class MuseRecord : public MuseRecordBasic { + public: + MuseRecord (void); + MuseRecord (const std::string& aLine); + MuseRecord (MuseRecord& aRecord); + ~MuseRecord (); + + + ////////////////////////////// + // functions which work with regular note, cue note, and grace note records + // (A..G, c, g) + + // columns 1 -- 5: pitch field information + std::string getNoteField (void); + int getOctave (void); + std::string getOctaveString (void); + int getPitch (void); + std::string getPitchString (void); + int getPitchClass (void); + std::string getPitchClassString (void); + int getAccidental (void); + std::string getAccidentalString (void); + int getBase40 (void); + void setPitch (int base40, int chordnote = 0, + int gracenote = 0); + void setPitch (const std::string& pitchname); + void setPitchAtIndex (int index, + const std::string& pitchname); + void setChordPitch (const std::string& pitchname); + void setGracePitch (const std::string& pitchname); + void setGraceChordPitch (const std::string& pitchname); + void setCuePitch (const std::string& pitchname); + void setStemDown (void); + void setStemUp (void); + + // columns 6 -- 9: duration field information + std::string getTickDurationField (void); + std::string getTickDurationString (void); + int getTickDuration (void); + int getLineTickDuration (void); + int getNoteTickDuration (void); + std::string getTieString (void); + int getTie (void); + int setTie (int hidden = 0); + int tieQ (void); + int getTicks (void); + void setTicks (int value); + void setBack (int value); + void setDots (int value); + void setNoteheadShape (HumNum duration); + void setNoteheadShapeMensural (HumNum duration); + void setNoteheadMaxima (void); + void setNoteheadLong (void); + void setNoteheadBreve (void); + void setNoteheadBreveSquare (void); + void setNoteheadBreveRound (void); + + void setNoteheadWhole (void); + void setNoteheadHalf (void); + void setNoteheadQuarter (void); + void setNotehead8th (void); + void setNotehead16th (void); + void setNotehead32nd (void); + void setNotehead64th (void); + void setNotehead128th (void); + void setNotehead256th (void); + + void setNoteheadBreveMensural (void); + void setNoteheadWholeMensural (void); + void setNoteheadHalfMensural (void); + void setNoteheadQuarterMensural (void); + void setNotehead8thMensural (void); + void setNotehead16thMensural (void); + void setNotehead32ndMensural (void); + void setNotehead64thMensural (void); + void setNotehead128thMensural (void); + void setNotehead256thMensural (void); + + // columns 10 -- 12 ---> blank + + // columns 13 -- 80: graphical and interpretive information + + // column 13: footnote flag + std::string getFootnoteFlagField (void); + std::string getFootnoteFlagString (void); + int getFootnoteFlag (void); + int footnoteFlagQ (void); + + // column 14: level number + std::string getLevelField (void); + std::string getLevelString (void); + int getLevel (void); + int levelQ (void); + + // column 15: track number + std::string getTrackField (void); + std::string getTrackString (void); + int getTrack (void); + int trackQ (void); + + // column 16 ---> blank + + // column 17: graphic note type + std::string getGraphicNoteTypeField (void); + std::string getGraphicNoteTypeString (void); + int getGraphicNoteType (void); + int getGraphicNoteTypeSize (void); + int graphicNoteTypeQ (void); + + // column 18: dots of prolongation + std::string getProlongationField (void); + std::string getProlongationString (void); + int getProlongation (void); + std::string getStringProlongation (void); + int prolongationQ (void); + + // column 19: actual notated accidentals + std::string getNotatedAccidentalField (void); + std::string getNotatedAccidentalString (void); + int getNotatedAccidental (void); + int notatedAccidentalQ (void); + + // columns 20 -- 22: time modification + std::string getTimeModificationField (void); + std::string getTimeModification (void); + std::string getTimeModificationLeftField (void); + std::string getTimeModificationLeftString(void); + int getTimeModificationLeft (void); + std::string getTimeModificationRightField(void); + std::string getTimeModificationRightString(void); + int getTimeModificationRight (void); + int timeModificationQ (void); + int timeModificationLeftQ (void); + int timeModificationRightQ (void); + + // column 23 + std::string getStemDirectionField (void); + std::string getStemDirectionString (void); + int getStemDirection (void); + int stemDirectionQ (void); + + // column 24 + std::string getStaffField (void); + std::string getStaffString (void); + int getStaff (void); + int staffQ (void); + + // column 25 ---> blank + + // columns 26 - 31: beam codes + std::string getBeamField (void); + int beamQ (void); + char getBeam8 (void); + char getBeam16 (void); + char getBeam32 (void); + char getBeam64 (void); + char getBeam128 (void); + char getBeam256 (void); + int beam8Q (void); + int beam16Q (void); + int beam32Q (void); + int beam64Q (void); + int beam128Q (void); + int beam256Q (void); + std::string getKernBeamStyle (void); + void setBeamInfo (std::string& strang); + + // columns 32 -- 43: additional notation + std::string getAdditionalNotationsField (void); + int additionalNotationsQ (void); + int getAddCount (void); + std::string getAddItem (int elementIndex); + int addAdditionalNotation (char symbol); + int addAdditionalNotation (const std::string& symbol); + int getAddItemLevel (int elementIndex); + std::string getEditorialLevels (void); + int addEditorialLevelQ (void); + // protected: getAddElementIndex + int findField (const std::string& key); + int findField (char key, int mincol, + int maxcol); + // int getNotationLevel + int getSlurStartColumn (void); + std::string getSlurParameterRegion (void); + void getSlurInfo (string& slurstarts, string& slurends); + + // columns 44 -- 80: text underlay + std::string getTextUnderlayField (void); + int textUnderlayQ (void); + int getVerseCount (void); + std::string getVerse (int index); + + // general functions for note records: + std::string getKernNoteStyle (int beams = 0, int stems = 0); + std::string getKernNoteAccents (void); + + + ////////////////////////////// + // functions which work with basso continuo figuration records ('f'): + + // column 2: number of figure fields + std::string getFigureCountField (void); + std::string getFigureCountString (void); + int getFigureCount (void); + + // columns 3 -- 5 ---> blank + + // columns 6 -- 8: figure division pointer advancement (duration) + std::string getFigurePointerField (void); + int figurePointerQ (void); + // same as note records: getDuration + + // columns 9 -- 12 ---> blank + + // columns 13 -- 15: footnote and level information + // column 13 --> footnote: uses same functions as note records in col 13. + // column 14 --> level: uses same functions as note records on column 14. + // column 15 ---> blank + + // columns 17 -- 80: figure fields + std::string getFigureFields (void); + std::string getFigureString (void); + int figureFieldsQ (void); + std::string getFigure (int index = 0); + + + ////////////////////////////// + // functions which work with combined records ('b', 'i'): + + + ////////////////////////////// + // functions which work with measure records ('m'): + + // columns 1 -- 7: measure style information + std::string getMeasureTypeField (void); + + // columns 9 -- 12: measure number (left justified) + std::string getMeasureNumberField (void); + std::string getMeasureNumberString (void); + int getMeasureNumber (void); + int measureNumberQ (void); + + // columns 17 -- 80: measure flags + int measureFermataQ (void); + int measureFlagQ (const std::string& key); + void addMeasureFlag (const std::string& strang); + + // general functions for measure records: + std::string getKernMeasureStyle (void); + + + ////////////////////////////// + // functions which work with musical attributes records ('$'): + + std::string getAttributes (void); + void getAttributeMap (std::map& amap); + int attributeQ (const std::string& attribute); + int getAttributeInt (char attribute); + int getAttributeField (std::string& output, const std::string& attribute); + + + // + ////////////////////////////// + + std::string getKernRestStyle (int quarter = 16); + + protected: + void allowNotesOnly (const std::string& functioName); + void allowMeasuresOnly (const std::string& functioName); + void allowFigurationOnly (const std::string& functioName); + void allowFigurationAndNotesOnly (const std::string& functioName); + int getAddElementIndex (int& index, std::string& output, + const std::string& input); + void zerase (std::string& inout, int num); +}; + + + + +// A MuseEventSet is a timestamp and then a list of pointers to all +// lines in the original file that occur at that time. +// The MuseData class contains a variable called "sequence" which is +// a list of MuseEventSet object pointers which are sorted by time. + +class MuseEventSet { + public: + MuseEventSet (void); + MuseEventSet (HumNum atime); + ~MuseEventSet () { clear(); } + + void clear (void); + void setTime (HumNum abstime); + HumNum getTime (void); + void appendRecord (MuseRecord* arecord); + MuseRecord& operator[] (int index); + MuseEventSet operator= (MuseEventSet& anevent); + int getEventCount (void); + + protected: + HumNum absbeat; // starting time of events + std::vector events; // list of events on absbeat +}; + + + +class MuseData { + public: + MuseData (void); + MuseData (MuseData& input); + ~MuseData (); + + void setFilename (const std::string& filename); + std::string getFilename (void); + std::string getPartName (void); + int isMember (const std::string& mstring); + int getMembershipPartNumber(const std::string& mstring); + void selectMembership (const std::string& selectstring); + MuseData& operator= (MuseData& input); + int getLineCount (void); + int getNumLines (void) { return getLineCount(); } + MuseRecord& last (void); + int isEmpty (void); + int append (MuseRecord& arecord); + int append (MuseData& musedata); + int append (std::string& charstring); + void insert (int index, MuseRecord& arecord); + void clear (void); + int getInitialTpq (void); + + int read (std::istream& input); + int readString (const std::string& filename); + int readFile (const std::string& filename); + + // aliases for access to MuseRecord objects based on line indexes: + std::string getLine (int index); + bool isPartName (int index); + + // additional mark-up analysis functions for post-processing: + void doAnalyses (void); + void analyzeType (void); + void analyzeRhythm (void); + void analyzeTies (void); + void analyzePitch (void); + + // line-based (file-order indexing) accessor functions: + MuseRecord& operator[] (int lindex); + MuseRecord& getRecord (int lindex); + HumNum getTiedDuration (int lindex); + + HumNum getAbsBeat (int lindex); + HumNum getFileDuration (void); + + int getLineTickDuration (int lindex); + + // event-based (time-order indexing) accessor functions: + MuseEventSet& getEvent (int eindex); + int getEventCount (void); + HumNum getEventTime (int eindex); + MuseRecord& getRecord (int eindex, int erecord); + int getLineIndex (int eindex, int erecord); + HumNum getLineDuration (int eindex, int erecord); + HumNum getNoteDuration (int eindex, int erecord); + int getLastTiedNoteLineIndex(int eindex, int erecord); + int getNextTiedNoteLineIndex(int eindex, int erecord); + HumNum getTiedDuration (int eindex, int erecord); + int getType (int eindex, int erecord); + void cleanLineEndings (void); + std::string getError (void); + bool hasError (void); + + private: + std::vector m_data; + std::vector m_sequence; + std::string m_name; + std::string m_error; + + protected: + void clearError (void); + void setError (const std::string& error); + void processTie (int eventindex, int recordindex, + int lastindex); + int searchForPitch (int eventindex, int b40, + int track); + int getNextEventIndex (int startindex, + HumNum target); + void constructTimeSequence(void); + void insertEventBackwards (HumNum atime, + MuseRecord* arecord); + int getPartNameIndex (void); + std::string getPartName (int index); +}; + + +std::ostream& operator<<(std::ostream& out, MuseData& musedata); + + + + +class MuseDataSet { + public: + MuseDataSet (void); + MuseDataSet (MuseDataSet& input); + ~MuseDataSet () { clear(); } + + void clear (void); + int readPartFile (const std::string& filename); + int readPartString (const std::string& data); + int readPart (std::istream& input); + int readFile (const std::string& filename); + int readString (const std::string& data); + int read (std::istream& input); + MuseData& operator[] (int index); + int getPartCount (void); + void deletePart (int index); + void cleanLineEndings (void); + + std::string getError (void); + bool hasError (void); + void clearError (void); + + private: + std::vector m_part; + std::string m_error; + + protected: + int appendPart (MuseData* musedata); + void analyzeSetType (std::vector& types, + std::vector& lines); + void analyzePartSegments (std::vector& startindex, + std::vector& stopindex, + std::vector& lines); + void setError (const std::string& error); + +}; + + +std::ostream& operator<<(std::ostream& out, MuseDataSet& musedata); + + + #define GRIDREST NAN class NoteGrid; @@ -2726,6 +3325,15 @@ class Convert { HumNum scale = 4, const std::string& separator = " "); + // MuseData conversions in Convert-musedata.cpp + static int museToBase40 (const std::string& pitchString); + static std::string musePitchToKernPitch(const std::string& museInput); + static std::string museClefToKernClef(const std::string& mclef); + static std::string museKeySigToKernKeySig(const std::string& mkeysig); + static std::string museTimeSigToKernTimeSig(const std::string& mtimesig); + static std::string museMeterSigToKernMeterSig(const std::string& mtimesig); + static std::string museFiguredBassToKernFiguredBass(const std::string& mfb); + // Harmony processing, defined in Convert-harmony.cpp static std::vector minorHScaleBase40(void); static std::vector majorScaleBase40 (void); @@ -2994,6 +3602,11 @@ class GridPart : public std::vector, public GridSide { public: GridPart(void); ~GridPart(); + + private: + std::string m_partName; + + }; std::ostream& operator<<(std::ostream& output, GridPart* part); @@ -3064,6 +3677,8 @@ class GridMeasure : public std::list { void addLayoutParameter(GridSlice* slice, int partindex, const std::string& locomment); void addDynamicsLayoutParameters(GridSlice* slice, int partindex, const std::string& locomment); void addFiguredBassLayoutParameters(GridSlice* slice, int partindex, const std::string& locomment); + GridSlice* addFiguredBass(HTp token, HumNum timestamp, int part, int maxstaff); + GridSlice* addFiguredBass(const std::string& tok, HumNum timestamp, int part, int maxstaff); bool isInvisible(void); bool isSingleChordMeasure(void); bool isMonophonicMeasure(void); @@ -3166,6 +3781,7 @@ class GridSlice : public std::vector { std::ostream& operator<<(std::ostream& output, GridSlice* slice); +std::ostream& operator<<(std::ostream& output, GridSlice& slice); @@ -3235,6 +3851,8 @@ class HumGrid : public std::vector { int getPartCount (void); int getStaffCount (int partindex); void deleteMeasure (int index); + void setPartName (int index, const string& name); + std::string getPartName (int index); protected: void calculateGridDurations (void); @@ -3244,6 +3862,7 @@ class HumGrid : public std::vector { GridSlice& slice); void insertPartIndications (HumdrumFile& outfile); void insertStaffIndications (HumdrumFile& outfile); + void insertPartNames (HumdrumFile& outfile); void addNullTokens (void); void addNullTokensForGraceNotes (void); void addNullTokensForClefChanges (void); @@ -3283,6 +3902,8 @@ class HumGrid : public std::vector { int staff); void insertSideStaffInfo (HumdrumLine* line, int part, int staff, int staffnum); + void insertSideNullInterpretations (HumdrumLine* line, + int part, int staff); void getMetricBarNumbers (std::vector& barnums); string createBarToken (int m, int barnum, GridMeasure* measure); @@ -3308,6 +3929,8 @@ class HumGrid : public std::vector { std::vector m_figured_bass; std::vector m_harmony; + std::vector m_partnames; + // options: bool m_recip; // include **recip spine in output bool m_musicxmlbarlines; // use measure numbers from element @@ -3765,6 +4388,7 @@ class HumTool : public Options { bool hasError (void); std::string getError (void); ostream& getError (ostream& out); + void setError (const string& message); protected: std::stringstream m_humdrum_text; // output text in Humdrum syntax. @@ -4046,6 +4670,16 @@ class Tool_autobeam : public HumTool { void addBeams (HumdrumFile& infile); void removeBeams (HumdrumFile& infile); void removeEdgeRests (HTp& startnote, HTp& endnote); + void breakBeamsByLyrics(HumdrumFile& infile); + void processStrandForLyrics(HTp stok, HTp etok); + bool hasSyllable (HTp token); + void splitBeam (HTp tok, HTp stok, HTp etok); + void splitBeam2 (vector& group, HTp tok); + void getBeamedNotes(vector& toks, HTp tok, HTp stok, HTp etok); + bool isLazy (vector& group); + void splitBeamLazy (vector& group, HTp tok); + void splitBeamNotLazy(vector& group, HTp tok); + void removeBeamCharacters(HTp token); private: std::vector > > m_timesigs; @@ -4053,6 +4687,7 @@ class Tool_autobeam : public HumTool { bool m_overwriteQ; int m_track; bool m_includerests = false; + int m_splitcount = 0; }; @@ -5585,6 +6220,53 @@ class Tool_msearch : public HumTool { }; +class Tool_musedata2hum : public HumTool { + public: + Tool_musedata2hum (void); + ~Tool_musedata2hum () {} + + bool convertFile (ostream& out, const string& filename); + bool convertString (ostream& out, const string& input); + bool convert (ostream& out, MuseDataSet& mds); + bool convert (ostream& out, istream& input); + + void setOptions (int argc, char** argv); + void setOptions (const std::vector& argvlist); + Options getOptionDefinitions (void); + void setInitialOmd (const string& omd); + + protected: + void initialize (void); + void convertLine (GridMeasure* gm, MuseRecord& mr); + bool convertPart (HumGrid& outdata, MuseDataSet& mds, int index); + int convertMeasure (HumGrid& outdata, MuseData& part, int startindex); + GridMeasure* getMeasure (HumGrid& outdata, HumNum starttime); + void setTimeSigDurInfo (const std::string& mtimesig); + void setMeasureStyle (GridMeasure* gm, MuseRecord& mr); + void storePartName (HumGrid& outdata, MuseData& part, int index); + void addNoteDynamics (GridSlice* slice, int part, + MuseRecord& mr); + void addFiguredHarmony (MuseRecord& mr, GridMeasure* gm, + HumNum timestamp, int part, int maxstaff); + + private: + // options: + Options m_options; + bool m_stemsQ = false; // used with -s option + bool m_recipQ = false; // used with -r option + std::string m_omd = ""; // initial tempo designation (store for later output) + + // state variables: + int m_tpq = 1; // Ticks per quarter note + int m_part = 0; // staff index currently being processed + int m_maxstaff = 0; // total number of staves (parts) + HumNum m_timesigdur = 4; // duration of current time signature in quarter notes + HTp m_lastfigure = NULL; // last figured bass token + +}; + + + class MusicXmlHarmonyInfo { public: HTp token; diff --git a/src/hum/humlib.cpp b/src/hum/humlib.cpp index 91682a4279a..0611e073c62 100644 --- a/src/hum/humlib.cpp +++ b/src/hum/humlib.cpp @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Fri Sep 20 06:46:55 PDT 2019 +// Last Modified: Sun Sep 29 08:05:25 PDT 2019 // Filename: /include/humlib.cpp // URL: https://github.com/craigsapp/humlib/blob/master/src/humlib.cpp // Syntax: C++11 @@ -1219,6 +1219,322 @@ string Convert::mensToRecip(const string& mensdata, HumNum scale, + +////////////////////////////// +// +// Convert::museToBase40 -- Convert a MuseData pitch into base-40 representation. +// + +int Convert::museToBase40(const string& pitchString) { + string temp = pitchString; + int octave; + int i = (int)temp.size() - 1; + while (i >= 0 && !isdigit(temp[i])) { + i--; + } + + if (i <= 0) { + cerr << "Error: could not find octave in string: " << pitchString << endl; + exit(1); + } + + octave = temp[i] - '0'; + temp.resize(i); + + for (int i=0; i<(int)temp.size(); i++) { + if (temp[i] == 'f') { + temp[i] = '-'; + } + } + int kb40 = Convert::kernToBase40(temp); + if (kb40 < 0) { + return kb40; + } + return kb40 % 40 + 40 * octave; +} + + + +////////////////////////////// +// +// Convert::musePitchToKernPitch -- +// + +string Convert::musePitchToKernPitch(const string& museInput) { + return base40ToKern(museToBase40(museInput)); +} + + + +////////////////////////////// +// +// Convert::museClefToKernClef -- +// + +string Convert::museClefToKernClef(const string& mclef) { + if (mclef == "4") { // treble clef + return "*clefG2"; + } else if (mclef == "22") { // bass clef + return "*clefF4"; + } else if (mclef == "13") { // alto clef + return "*clefC3"; + } else if (mclef == "12") { // tenor clef + return "*clefC4"; + } else if (mclef == "15") { // soprano clef + return "*clefC1"; + } else if (mclef == "14") { // mezzo-soprano clef + return "*clefC2"; + } else if (mclef == "14") { // baritone clef + return "*clefC5"; + } else if (mclef == "5") { // French violin clef + return "*clefG1"; + } else if (mclef == "3") { + return "*clefG3"; + } else if (mclef == "2") { + return "*clefG4"; + } else if (mclef == "1") { + return "*clefG5"; + } else if (mclef == "25") { + return "*clefF1"; + } else if (mclef == "24") { + return "*clefF2"; + } else if (mclef == "23") { + return "*clefF3"; + } else if (mclef == "21") { + return "*clefF5"; + } else if (mclef == "35") { + return "*clefGv1"; + } else if (mclef == "34") { // vocal tenor clef + return "*clefGv2"; + } else if (mclef == "33") { + return "*clefGv3"; + } else if (mclef == "32") { + return "*clefGv3"; + } else if (mclef == "31") { + return "*clefGv5"; + } + // percussion clef? + // return unknown clef: + return "*"; +} + + + +////////////////////////////// +// +// Convert::museKeySigToKernKeySig -- +// + +string Convert::museKeySigToKernKeySig(const string& mkeysig) { + if (mkeysig == "0") { + return "*k[]"; + } else if (mkeysig == "1") { + return "*k[f#]"; + } else if (mkeysig == "-1") { + return "*k[b-]"; + } else if (mkeysig == "2") { + return "*k[f#c#]"; + } else if (mkeysig == "-2") { + return "*k[b-e-]"; + } else if (mkeysig == "3") { + return "*k[f#c#g#]"; + } else if (mkeysig == "-3") { + return "*k[b-e-a-]"; + } else if (mkeysig == "4") { + return "*k[f#c#g#d#]"; + } else if (mkeysig == "-4") { + return "*k[b-e-a-d-]"; + } else if (mkeysig == "5") { + return "*k[f#c#g#d#a#]"; + } else if (mkeysig == "-5") { + return "*k[b-e-a-d-g-]"; + } else if (mkeysig == "6") { + return "*k[f#c#g#d#a#e#]"; + } else if (mkeysig == "-6") { + return "*k[b-e-a-d-g-c-]"; + } else if (mkeysig == "7") { + return "*k[f#c#g#d#a#e#b#]"; + } else if (mkeysig == "-7") { + return "*k[b-e-a-d-g-c-f-]"; + } + return "*"; +} + + + +////////////////////////////// +// +// Convert::museTimeSigToKernTimeSig -- +// + +string Convert::museTimeSigToKernTimeSig(const string& mtimesig) { + if (mtimesig == "11/0") { + return "*M3/1"; // "*met(O) + } else if (mtimesig == "1/1") { + return "*M4/4"; // "*met(c) + } else if (mtimesig == "0/0") { + return "*M2/2"; // "*met(c) + } else if (mtimesig == "12/0") { + return ""; // "*met(O:) + } else if (mtimesig == "21/0") { + return ""; // "*met(O.) + } else if (mtimesig == "22/0") { + return ""; // "*met(O;) + } else if (mtimesig == "31/0") { + return "*M2/1"; // "*met(C) + } else if (mtimesig == "41/0") { + return ""; // "*met(C.) + } else if (mtimesig == "42/0") { + return ""; // "*met(C.3/2) + } else if (mtimesig == "43/0") { + return ""; // "*met(C.3/8) + } else if (mtimesig == "51/0") { + return ""; // "*met(Cr) + } else if (mtimesig == "52/0") { + return ""; // "*met(Cr|) + } else if (mtimesig == "61/0") { + return "*M2/1"; // "*met(C|) + } else if (mtimesig == "62/0") { + return ""; // "*met(C|/2) + } else if (mtimesig == "63/0") { + return ""; // "*met(C|.) + } else if (mtimesig == "71/0") { + return ""; // "*met(C2) + } else if (mtimesig == "72/0") { + return ""; // "*met(C2/3) + } else if (mtimesig == "81/0") { + return ""; // "*met(O2) + } else if (mtimesig == "82/0") { + return ""; // "*met(O3/2) + } else if (mtimesig == "91/0") { + return "*M3/1"; // "*met(O|) + } else if (mtimesig == "92/0") { + return ""; // "*met(O|3) + } else if (mtimesig == "93/0") { + return ""; // "*met(O|3/2) + } else if (mtimesig == "101/0") { + return ""; // "*met(C|3) + } else if (mtimesig == "102/0") { + return ""; // "*met(3) + } else if (mtimesig == "103/0") { + return ""; // "*met(3/2) + } else if (mtimesig == "104/0") { + return ""; // "*met(C|/3) + } else if (mtimesig == "105/0") { + return ""; // "*met(C3) + } else if (mtimesig == "106/0") { + return ""; // "*met(O/3) + } else if (mtimesig == "111/0") { + return ""; // "*met(C|2) + } else if (mtimesig == "112/0") { + return ""; // "*met(2) + } else if (mtimesig == "121/0") { + return ""; // "*met(Oo) + } + string output = "*M" + mtimesig; + return output; +} + + + +////////////////////////////// +// +// Convert::museMeterSigToKernMeterSig -- +// + +string Convert::museMeterSigToKernMeterSig(const string& mtimesig) { + if (mtimesig == "11/0") { + return "*met(O)"; + } else if (mtimesig == "1/1") { + return "*met(c)"; + } else if (mtimesig == "0/0") { + return "*met(c)"; + } else if (mtimesig == "12/0") { + return "*met(O:)"; + } else if (mtimesig == "21/0") { + return "*met(O.)"; + } else if (mtimesig == "22/0") { + return "*met(O;)"; + } else if (mtimesig == "31/0") { + return "*met(C)"; + } else if (mtimesig == "41/0") { + return "*met(C.)"; + } else if (mtimesig == "42/0") { + return "*met(C.3/2)"; + } else if (mtimesig == "43/0") { + return "*met(C.3/8)"; + } else if (mtimesig == "51/0") { + return "*met(Cr)"; + } else if (mtimesig == "52/0") { + return "*met(Cr|)"; + } else if (mtimesig == "61/0") { + return "*met(C|)"; + } else if (mtimesig == "62/0") { + return "*met(C|/2)"; + } else if (mtimesig == "63/0") { + return "*met(C|.)"; + } else if (mtimesig == "71/0") { + return "*met(C2)"; + } else if (mtimesig == "72/0") { + return "*met(C2/3)"; + } else if (mtimesig == "81/0") { + return "*met(O2)"; + } else if (mtimesig == "82/0") { + return "*met(O3/2)"; + } else if (mtimesig == "91/0") { + return "*met(O|)"; + } else if (mtimesig == "92/0") { + return "*met(O|3)"; + } else if (mtimesig == "93/0") { + return "*met(O|3/2)"; + } else if (mtimesig == "101/0") { + return "*met(C|3)"; + } else if (mtimesig == "102/0") { + return "*met(3)"; + } else if (mtimesig == "103/0") { + return "*met(3/2)"; + } else if (mtimesig == "104/0") { + return "*met(C|/3)"; + } else if (mtimesig == "105/0") { + return "*met(C3)"; + } else if (mtimesig == "106/0") { + return "*met(O/3)"; + } else if (mtimesig == "111/0") { + return "*met(C|2)"; + } else if (mtimesig == "112/0") { + return "*met(2)"; + } else if (mtimesig == "121/0") { + return "*met(Oo)"; + } + return ""; +} + + + +////////////////////////////// +// +// Convert::museFiguredBassToKernFiguredBass -- +// + +string Convert::museFiguredBassToKernFiguredBass(const string& mfb) { + string output; + for (int i=0; i<(int)mfb.size(); i++) { + if (mfb[i] == 'b') { // blank spot in figure stack + output += 'X'; + } else if ((mfb[i] == '&') && (i < (int)mfb.size()-1) && (mfb[i+1] == '0')) { + output += ":"; + i++; + } else { + output += mfb[i]; + } + } + return output; +} + + + + + ////////////////////////////// // // Convert::kernToScientificPitch -- Convert a **kern pitch to @@ -3399,7 +3715,7 @@ GridSlice* GridMeasure::addTempoToken(const string& tok, HumNum timestamp, break; } else if (((*iterator)->getTimestamp() == timestamp) && (*iterator)->isDataSlice()) { // found the correct timestamp, but no clef slice at the timestamp - // so add the clef slice before the data slice (eventually keepping + // so add the clef slice before the data slice (eventually keeping // track of the order in which the other non-data slices should be placed). gs = new GridSlice(this, timestamp, SliceType::Tempos, maxstaff); gs->addToken(tok, part, staff, voice); @@ -3454,7 +3770,7 @@ GridSlice* GridMeasure::addTimeSigToken(const string& tok, HumNum timestamp, break; } else if (((*iterator)->getTimestamp() == timestamp) && (*iterator)->isDataSlice()) { // found the correct timestamp, but no clef slice at the timestamp - // so add the clef slice before the data slice (eventually keepping + // so add the clef slice before the data slice (eventually keeping // track of the order in which the other non-data slices should be placed). gs = new GridSlice(this, timestamp, SliceType::TimeSigs, maxstaff); gs->addToken(tok, part, staff, voice); @@ -3509,7 +3825,7 @@ GridSlice* GridMeasure::addKeySigToken(const string& tok, HumNum timestamp, break; } else if (((*iterator)->getTimestamp() == timestamp) && (*iterator)->isDataSlice()) { // found the correct timestamp, but no clef slice at the timestamp - // so add the clef slice before the data slice (eventually keepping + // so add the clef slice before the data slice (eventually keeping // track of the order in which the other non-data slices should be placed). gs = new GridSlice(this, timestamp, SliceType::KeySigs, maxstaff); gs->addToken(tok, part, staff, voice); @@ -3648,7 +3964,7 @@ GridSlice* GridMeasure::addTransposeToken(const string& tok, HumNum timestamp, break; } else if (((*iterator)->getTimestamp() == timestamp) && (*iterator)->isDataSlice()) { // found the correct timestamp, but no clef slice at the timestamp - // so add the clef slice before the data slice (eventually keepping + // so add the clef slice before the data slice (eventually keeping // track of the order in which the other non-data slices should be placed). gs = new GridSlice(this, timestamp, SliceType::Transpositions, maxstaff); gs->addToken(tok, part, staff, voice); @@ -3703,7 +4019,7 @@ GridSlice* GridMeasure::addClefToken(const string& tok, HumNum timestamp, break; } else if (((*iterator)->getTimestamp() == timestamp) && (*iterator)->isDataSlice()) { // found the correct timestamp, but no clef slice at the timestamp - // so add the clef slice before the data slice (eventually keepping + // so add the clef slice before the data slice (eventually keeping // track of the order in which the other non-data slices should be placed). gs = new GridSlice(this, timestamp, SliceType::Clefs, maxstaff); gs->addToken(tok, part, staff, voice); @@ -3731,6 +4047,124 @@ GridSlice* GridMeasure::addClefToken(const string& tok, HumNum timestamp, +////////////////////////////// +// +// GridMeasure::addFiguredBass -- +// +GridSlice* GridMeasure::addFiguredBass(HTp token, HumNum timestamp, int part, int maxstaff) { + GridSlice* gs = NULL; + bool processed = false; + + if (this->empty() || (this->back()->getTimestamp() < timestamp)) { + // add a new GridSlice to an empty list or at end of list if timestamp + // is after last entry in list. + gs = new GridSlice(this, timestamp, SliceType::Notes, maxstaff); + int staff = 0; + int voice = 0; + string null = "."; + gs->addToken(null, part, staff, voice); + gs->at(part)->setFiguredBass(token); + this->push_back(gs); + processed = true; + } else { + // search for existing line with same timestamp and the same slice type + GridSlice* target = NULL; + auto iterator = this->begin(); + while (iterator != this->end()) { + if (((*iterator)->getTimestamp() == timestamp) && (*iterator)->isDataSlice()) { + target = *iterator; + target->at(part)->setFiguredBass(token); + processed = true; + break; + } else if ((*iterator)->getTimestamp() > timestamp) { + // Need to add figured bass data where there are note notes, + // so add an emtpy data line and add the figure bass contnet. + gs = new GridSlice(this, timestamp, SliceType::Notes, maxstaff); + int staff = 0; + int voice = 0; + string null = "."; + gs->addToken(null, part, staff, voice); + gs->at(part)->setFiguredBass(token); + this->insert(iterator, gs); + processed = true; + break; + } + iterator++; + } + + if (!processed) { + cerr << "Error: could not insert figured bass: " << token << endl; + } else { + HumGrid* hg = getOwner(); + if (hg) { + hg->setFiguredBassPresent(part); + } + } + } + + return gs; +} + +////////////////////////////// +// +// GridMeasure::addFiguredBass -- +// +GridSlice* GridMeasure::addFiguredBass(const string& tok, HumNum timestamp, int part, int maxstaff) { + GridSlice* gs = NULL; + bool processed = false; + + if (this->empty() || (this->back()->getTimestamp() < timestamp)) { + // add a new GridSlice to an empty list or at end of list if timestamp + // is after last entry in list. + gs = new GridSlice(this, timestamp, SliceType::Notes, maxstaff); + int staff = 0; + int voice = 0; + string null = "."; + gs->addToken(null, part, staff, voice); + gs->at(part)->setFiguredBass(tok); + this->push_back(gs); + processed = true; + } else { + // search for existing line with same timestamp and the same slice type + GridSlice* target = NULL; + auto iterator = this->begin(); + while (iterator != this->end()) { + if (((*iterator)->getTimestamp() == timestamp) && (*iterator)->isDataSlice()) { + target = *iterator; + target->at(part)->setFiguredBass(tok); + processed = true; + break; + } else if ((*iterator)->getTimestamp() > timestamp) { + // Need to add figured bass data where there are note notes, + // so add an emtpy data line and add the figure bass contnet. + gs = new GridSlice(this, timestamp, SliceType::Notes, maxstaff); + int staff = 0; + int voice = 0; + string null = "."; + gs->addToken(null, part, staff, voice); + gs->at(part)->setFiguredBass(tok); + this->insert(iterator, gs); + processed = true; + break; + } + iterator++; + } + + if (!processed) { + cerr << "Error: could not inser figured bass: " << tok << endl; + } else { + HumGrid* hg = getOwner(); + if (hg) { + hg->setFiguredBassPresent(part); + } + } + } + + return gs; +} + + + ////////////////////////////// // // GridMeasure::addGlobalComment -- Add a global comment at the given @@ -4398,6 +4832,7 @@ ostream& operator<<(ostream& output, GridPart& part) { + ////////////////////////////// // // GridSide::GridSide -- Constructor. @@ -5486,6 +5921,11 @@ GridMeasure* GridSlice::getMeasure(void) { // operator<< -- print token content of a slice // +ostream& operator<<(ostream& output, GridSlice& slice) { + return output << &slice; +} + + ostream& operator<<(ostream& output, GridSlice* slice) { if (slice == NULL) { output << "{n}"; @@ -6665,6 +7105,7 @@ bool HumGrid::transferTokens(HumdrumFile& outfile, int startbarnum) { cleanupManipulators(); } + insertPartNames(outfile); insertStaffIndications(outfile); insertPartIndications(outfile); insertExclusiveInterpretationLine(outfile); @@ -8312,9 +8753,14 @@ void HumGrid::extendDurationToken(int slicei, int parti, int staffi, slicedur = nextts - currts; type = m_allslices[s]->getType(); + if (staffi == (int)m_allslices.at(s)->at(parti)->size()) { + cerr << "WARNING: staff index " << staffi << " is probably incorrect: increasing staff count for part to " << staffi + 1 << endl; + m_allslices.at(s)->at(parti)->resize(m_allslices.at(s)->at(parti)->size() + 1); + m_allslices.at(s)->at(parti)->at(staffi) = new GridStaff(); + } gs = m_allslices.at(s)->at(parti)->at(staffi); if (gs == NULL) { - cerr << "Strange error2 in extendDurationToken()" << endl; + cerr << "Strange error6 in extendDurationToken()" << endl; return; } @@ -8343,8 +8789,6 @@ void HumGrid::extendDurationToken(int slicei, int parti, int staffi, } // walk through zero-dur items and fill them in, but stop at // a token (likely a grace note which should not be erased). - - } @@ -8522,6 +8966,47 @@ void HumGrid::insertExInterpSides(HumdrumLine* line, int part, int staff) { +////////////////////////////// +// +// HumGrid::insertPartNames -- +// + +void HumGrid::insertPartNames(HumdrumFile& outfile) { + if (m_partnames.size() == 0) { + return; + } + HumdrumLine* line = new HumdrumLine; + HTp token; + + if (m_recip) { + token = new HumdrumToken("*"); + line->appendToken(token); + } + + string text; + GridSlice& slice = *this->at(0)->front(); + int p; // part index + int s; // staff index + for (p=(int)slice.size()-1; p>=0; p--) { + GridPart& part = *slice[p]; + for (s=(int)part.size()-1; s>=0; s--) { + text = "*"; + string pname = m_partnames[p]; + if (!pname.empty()) { + text += "I\""; + text += pname; + } + token = new HumdrumToken(text); + line->appendToken(token); + insertSideNullInterpretations(line, p, s); + } + insertSideNullInterpretations(line, p, -1); + } + outfile.insertLine(0, line); +} + + + ////////////////////////////// // // HumGrid::insertPartIndications -- Currently presumes @@ -8532,6 +9017,7 @@ void HumGrid::insertExInterpSides(HumdrumLine* line, int part, int staff) { // void HumGrid::insertPartIndications(HumdrumFile& outfile) { + if (this->size() == 0) { return; } @@ -8561,6 +9047,46 @@ void HumGrid::insertPartIndications(HumdrumFile& outfile) { insertSidePartInfo(line, p, -1); // insert part sides } outfile.insertLine(0, line); + +} + + + +////////////////////////////// +// +// HumGrid::insertSideNullInterpretations -- +// + +void HumGrid::insertSideNullInterpretations(HumdrumLine* line, + int part, int staff) { + HTp token; + string text; + + if (staff < 0) { + + if (hasDynamics(part)) { + token = new HumdrumToken("*"); + line->appendToken(token); + } + + if (hasFiguredBass(part)) { + token = new HumdrumToken("*"); + line->appendToken(token); + } + + int harmcount = getHarmonyCount(part); + for (int i=0; iappendToken(token); + } + + } else { + int versecount = getVerseCount(part, staff); + for (int i=0; iappendToken(token); + } + } } @@ -9051,6 +9577,42 @@ void HumGrid::deleteMeasure(int index) { +////////////////////////////// +// +// HumGrid::setPartName -- +// + +void HumGrid::setPartName(int index, const string& name) { + if (index < 0) { + return; + } else if (index < (int)m_partnames.size()) { + m_partnames[index] = name; + } else if (index < 100) { + // grow the array and then store name + m_partnames.resize(index+1); + m_partnames.back() = name; + } +} + + + +////////////////////////////// +// +// HumGrid::getPartName -- +// + +std::string HumGrid::getPartName(int index) { + if (index < 0) { + return ""; + } else if (index < (int)m_partnames.size()) { + return m_partnames[index]; + } else { + return ""; + } +} + + + ////////////////////////////// // // operator<< -- Debugging printing of Humgrid Contents. @@ -9067,6 +9629,10 @@ ostream& operator<<(ostream& out, HumGrid& grid) { + + + + //////////////////////////////// // // HumParameter::HumParameter -- HumParameter constructor. @@ -11536,6 +12102,53 @@ ostream& HumNum::printMixedFraction(ostream& out, +////////////////////////////// +// +// HumNum::printTwoPart -- +// default value: spacer = "+" +// + +ostream& HumNum::printTwoPart(ostream& out, const string& spacer) const { + int tnum = top; + int tden = bot; + int sign = 1; + if (tnum < 0) { + tnum = -tnum; + sign = -sign; + } + if (tden < 0) { + tden = -tden; + sign = -sign; + } + + if (tnum < tden) { + out << *this; + return out; + } + + int integ = tnum / tden; + tnum = tnum - tden * integ; + + if (sign < 0) { + out << '-'; + } + if (integ > 0) { + out << integ; + if (tnum > 0) { + out << spacer; + HumNum newone(tnum, tden); + out << newone; + } + } else { + HumNum newone(tnum, tden); + out << newone; + } + + return out; +} + + + ////////////////////////////// // // HumNum::printList -- Print as a list of two numbers, such as @@ -13016,6 +13629,19 @@ void HumTool::clearOutput(void) { +/////////////////////////////// +// +// HumTool::setError -- +// + +void HumTool::setError(const string& message) { + m_error_text << message << endl; +} + + + + + ////////////////////////////// // // HumdrumFile::HumdrumFile -- HumdrumFile constructor. @@ -26219,6 +26845,6224 @@ void HumdrumToken::setNullResolution(HTp resolution) { + +/////////////////////////////////////////////////////////////////////////// +// +// MuseEventSet class functions -- +// + + +////////////////////////////// +// +// MuseEventSet::MuseEventSet -- +// + +MuseEventSet::MuseEventSet (void) { + events.reserve(20); + clear(); +} + + +MuseEventSet::MuseEventSet(HumNum atime) { + setTime(atime); + events.reserve(20); +} + + + +////////////////////////////// +// +// MuseData::operator= -- +// + +MuseData& MuseData::operator=(MuseData& input) { + if (this == &input) { + return *this; + } + m_data.resize(input.m_data.size()); + MuseRecord* temprec; + int i; + for (i=0; i<(int)m_data.size(); i++) { + temprec = new MuseRecord; + *temprec = *(input.m_data[i]); + m_data[i] = temprec; + } + // do something with m_sequence... + m_name = input.m_name; + return *this; +} + + + +////////////////////////////// +// +// MuseEventSet::clear -- +// + +void MuseEventSet::clear(void) { + events.clear(); + absbeat.setValue(0,1); +} + + + +////////////////////////////// +// +// MuseEventSet::setTime -- +// + +void MuseEventSet::setTime(HumNum abstime) { + absbeat = abstime; +} + + + +////////////////////////////// +// +// MuseEventSet::getTime -- +// + +HumNum MuseEventSet::getTime(void) { + return absbeat; +} + + + +////////////////////////////// +// +// MuseEventSet::appendRecord -- still have to sort after insertion... +// also add a removeEvent function so deleted elements can be removed +// gracefully. +// + +void MuseEventSet::appendRecord(MuseRecord* arecord) { + events.push_back(arecord); +} + + + +////////////////////////////// +// +// MuseEventSet::operator[] -- +// + +MuseRecord& MuseEventSet::operator[](int eindex) { + return *(events[eindex]); +} + + + +////////////////////////////// +// +// MuseEventSet::operator= -- +// + +MuseEventSet MuseEventSet::operator=(MuseEventSet& anevent) { + if (&anevent == this) { + return *this; + } + + this->absbeat = anevent.absbeat; + this->events.resize(anevent.events.size()); + int i; + for (i=0; i<(int)this->events.size(); i++) { + this->events[i] = anevent.events[i]; + } + return *this; +} + + + +////////////////////////////// +// +// MuseEventSet::getEventCount -- +// + +int MuseEventSet::getEventCount(void) { + return (int)events.size(); +} + + + +/////////////////////////////////////////////////////////////////////////// +// +// MuseData class functions -- +// + + +////////////////////////////// +// +// MuseData::MuseData -- +// + +MuseData::MuseData(void) { + m_data.reserve(100000); +} + +MuseData::MuseData(MuseData& input) { + m_data.resize(input.m_data.size()); + MuseRecord* temprec; + int i; + for (i=0; i<(int)m_data.size(); i++) { + temprec = new MuseRecord; + *temprec = *(input.m_data[i]); + m_data[i] = temprec; + } + m_sequence.resize(input.m_sequence.size()); + for (i=0; i<(int)input.m_sequence.size(); i++) { + m_sequence[i] = new MuseEventSet; + *(m_sequence[i]) = *(input.m_sequence[i]); + } + + m_name = input.m_name; +} + + + +////////////////////////////// +// +// MuseData::~MuseData -- +// + +MuseData::~MuseData() { + int i; + for (i=0; i<(int)m_data.size(); i++) { + if (m_data[i] != NULL) { + delete m_data[i]; + m_data[i] = NULL; + } + } + m_data.resize(0); + m_name = ""; +} + + + +////////////////////////////// +// +// MuseData::getLineCount -- return the number of lines in the MuseData file. +// + +int MuseData::getLineCount(void) { + return (int)m_data.size(); +} + + + +////////////////////////////// +// +// MuseData::append -- add a MuseRecord to end of file. +// + +int MuseData::append(MuseRecord& arecord) { + MuseRecord* temprec; + temprec = new MuseRecord; + *temprec = arecord; + m_data.push_back(temprec); + return (int)m_data.size()-1; +} + + +int MuseData::append(MuseData& musedata) { + int oldsize = (int)m_data.size(); + int newlinecount = musedata.getLineCount(); + if (newlinecount <= 0) { + return -1; + } + + m_data.resize((int)m_data.size()+newlinecount); + for (int i=0; isetString(charstring); + temprec->setType(E_muserec_unknown); + temprec->setAbsBeat(0); + m_data.push_back(temprec); + return (int)m_data.size()-1; +} + + + +////////////////////////////// +// +// MuseData::insert -- add a MuseRecord to middle of file. Not the most +// efficient, but not too bad as long as the file is not too long, the +// insertion is close to the end of the file, and you don't use this +// method to add a set of sequential lines (write a different function +// for that). +// + +void MuseData::insert(int lindex, MuseRecord& arecord) { + MuseRecord* temprec; + temprec = new MuseRecord; + *temprec = arecord; + + m_data.resize(m_data.size()+1); + for (int i=(int)m_data.size()-1; i>lindex; i--) { + m_data[i] = m_data[i-1]; + } + m_data[lindex] = temprec; +} + + + +////////////////////////////// +// +// MuseData::clear -- +// + +void MuseData::clear(void) { + for (int i=0; i<(int)m_data.size(); i++) { + if (m_data[i] != NULL) { + delete m_data[i]; + m_data[i] = NULL; + } + } + for (int i=0; i<(int)m_sequence.size(); i++) { + m_sequence[i]->clear(); + delete m_sequence[i]; + m_sequence[i] = NULL; + } + m_error.clear(); + m_data.clear(); + m_sequence.clear(); + m_name = ""; +} + + + +////////////////////////////// +// +// MuseData::operator[] -- +// + +MuseRecord& MuseData::operator[](int lindex) { + return *(m_data[lindex]); +} + + + +////////////////////////////// +// +// MuseData::getRecord -- +// + +MuseRecord& MuseData::getRecord(int lindex) { + return *(m_data[lindex]); +} + + +////////////////////////////// +// +// MuseData::getRecord -- This version with two index inputs is +// used to access a data line based on the event time index (time sorted +// viewpoint) and the particular record index for that event time. +// + +MuseRecord& MuseData::getRecord(int eindex, int erecord) { + return *(m_data[getEvent(eindex)[erecord].getLineIndex()]); +} + + + +////////////////////////////// +// +// MuseData::read -- read a MuseData file from a file or input stream. +// 0x0a = unix +// 0x0d = apple +// 0x0d 0x0a = dos +// + +int MuseData::read(istream& input) { + m_error.clear(); + string dataline; + dataline.reserve(256); + int character; + char value; + int isnewline; + char lastvalue = 0; + + while (!input.eof()) { + character = input.get(); + if (input.eof()) { + // end of file found without a newline termination on last line. + if (dataline.size() > 0) { + MuseData::append(dataline); + dataline.clear(); + break; + } + } + value = (char)character; + if ((value == 0x0d) || (value == 0x0a)) { + isnewline = 1; + } else { + isnewline = 0; + } + + if (isnewline && (value == 0x0a) && (lastvalue == 0x0d)) { + // ignore the second newline character in a dos-style newline. + lastvalue = value; + continue; + } else { + lastvalue = value; + } + + if (isnewline) { + MuseData::append(dataline); + dataline.clear(); + } else { + dataline.push_back(value); + } + } + + for (int i=0; i<(int)m_data.size(); i++) { + m_data[i]->setLineIndex(i); + } + + doAnalyses(); + if (hasError()) { + cerr << m_error << endl; + return 0; + } else { + return 1; + } +} + + +int MuseData::readFile(const string& filename) { + ifstream infile(filename); + return MuseData::read(infile); +} + +int MuseData::readString(const string& data) { + stringstream ss; + ss << data; + return MuseData::read(ss); +} + + + +////////////////////////////// +// +// MuseData::doAnalyses -- perform post-processing analysis of the data file +// (in the correct order). +// + +void MuseData::doAnalyses(void) { + analyzeType(); + if (hasError()) { return; } + analyzeRhythm(); + if (hasError()) { return; } + constructTimeSequence(); + if (hasError()) { return; } + analyzePitch(); + if (hasError()) { return; } + analyzeTies(); + if (hasError()) { return; } +} + + + +////////////////////////////// +// +// MuseData::analyzePitch -- calculate the pitch of all notes in terms +// of their base40 value. +// + +void MuseData::analyzePitch() { + for (int i=0; i<(int)m_data.size(); i++) { + m_data[i]->setMarkupPitch(m_data[i]->getBase40()); + } +} + + + +////////////////////////////// +// +// MuseData::analyzeTies -- identify which notes are tied to each other. +// + +void MuseData::analyzeTies(void) { + for (int i=0; i<(int)m_sequence.size(); i++) { + for (int j=0; jgetEventCount(); j++) { + if (!getEvent(i)[j].tieQ()) { + continue; + } + processTie(i, j, -1); + } + } +} + + + +////////////////////////////// +// +// MuseData::processTie -- follow a tied note to the last note +// in the tied m_sequence, filling in the tie information along the way. +// Hanging ties (particularly ties note at the ends of first repeats, etc.) +// still need to be considered. +// + +void MuseData::processTie(int eindex, int rindex, int lastindex) { + int& i = eindex; + int& j = rindex; + + + // lineindex = index of line in original file for the current line. + int lineindex = getEvent(i)[j].getLineIndex(); + + + if ((lastindex < 0) && + (m_data[lineindex]->getLastTiedNoteLineIndex() >= 0)) { + // If there previously tied note already marked in the data, then + // this note has already been processed for ties, so exit function + // without doing any further processing. + return; + } + + // store the location of the note tied to previously: + m_data[lineindex]->setLastTiedNoteLineIndex(lastindex); + + // If the current note contains a tie marker, then there is + // another tied note in the future, so go look for it. + if (!m_data[lineindex]->tieQ()) { + m_data[lineindex]->setNextTiedNoteLineIndex(-1); + return; + } + + // There is another note tied to this one in the future, so + // first get the absolute time location of the future tied note + HumNum abstime = m_data[lineindex]->getAbsBeat(); + HumNum notedur = m_data[lineindex]->getNoteDuration(); + HumNum searchtime = abstime + notedur; + + // Get the event index which occurs at the search time: + int nexteindex = getNextEventIndex(eindex, abstime + notedur); + + if (nexteindex < 0) { + // Couldn't find any data at that absolute time index, so give up: + m_data[lineindex]->setNextTiedNoteLineIndex(-1); + return; + } + + // The pitch of the tied note should match this one; otherwise, it + // would not be a tied note... + int base40 = m_data[lineindex]->getPitch(); + + // The tied note will preferrably be found in the same track as the + // current note (but there could be a cross-track tie occurring, so + // check for that if there is no same-track tie): + int track = m_data[lineindex]->getTrack(); + + int nextrindex = searchForPitch(nexteindex, base40, track); + if (nextrindex < 0) { + // Didn't find specified note at the given event index in the given + // track, so search for the same pitch in any track at the event time: + nextrindex = searchForPitch(nexteindex, base40, -1); + } + + if (nextrindex < 0) { + // Failed to find a note at the target event time which could be + // tied to the current note (no pitches at that time which match + // the pitch of the current note). This is a haning tie which is + // either a data error, or is a tie to a note at an earlier time + // in the music (such as at the beginning of a repeated section). + // for now just ignore the hanging tie, but probably mark it in + // some way in the future. + m_data[lineindex]->setNextTiedNoteLineIndex(-1); + return; + } + + // now the specific note to which this one is tied to is known, so + // go and process that note: + + int nextindex = getEvent(nexteindex)[nextrindex].getLineIndex(); + + m_data[lineindex]->setNextTiedNoteLineIndex(nextindex); + + processTie(nexteindex, nextrindex, lineindex); +} + + + +////////////////////////////// +// +// MuseData::searchForPitch -- search for a matching pitch in the given +// track at the specified event index. If the track is negative, then +// find the first matching pitch in any track. +// +// Will also have to separate by category, so that grace notes are +// searched for separately from regular notes / chord notes, and +// cue notes as well. +// + +int MuseData::searchForPitch(int eventindex, int b40, int track) { + int targettrack; + int targetpitch; + int targettype; + + for (int j=0; jgetEventCount(); j++) { + targettype = getEvent(eventindex)[j].getType(); + if ((targettype != E_muserec_note_regular) && + (targettype != E_muserec_note_chord) ) { + // ignore non-note data (at least ones without durations): + continue; + } + targettrack = getEvent(eventindex)[j].getTrack(); + if ((track >= 0) && (track != targettrack)) { + continue; + } + targetpitch = getEvent(eventindex)[j].getPitch(); + if (targetpitch == b40) { + return j; + } + } + + return -1; +} + + + +////////////////////////////// +// +// MuseData::getNextEventIndex -- return the event index for the given +// absolute time value. The starting index is given first, and it +// is assumed that the target absolute time occurs on or after the +// starting index value. Returns -1 if that absolute time is not +// found in the data (or occurs before the start index. +// + +int MuseData::getNextEventIndex(int startindex, HumNum target) { + int output = -1; + for (int i=startindex; i<(int)m_sequence.size(); i++) { + if (m_sequence[i]->getTime() == target) { + output = i; + break; + } + } + return output; +} + + +////////////////////////////// +// +// MuseData::last -- return the last record in the data. Make sure +// that isEmpty() is not true before calling this function. +// + +MuseRecord& MuseData::last(void) { + return (*this)[getNumLines()-1]; +} + + + +////////////////////////////// +// +// MuseData::isEmpty -- return true if there are no MuseRecords in the +// object; otherwise returns true; +// + +int MuseData::isEmpty(void) { + return m_data.empty(); +} + + + +////////////////////////////// +// +// MuseData::analyzeType -- +// + +void MuseData::analyzeType(void) { + int commentQ = 0; + int h = 0; + MuseData& thing = *this; + int foundattributes = 0; + + int foundend = 0; + + for (int i=0; i 0) { + if (thing[i][0] == '@') { + thing[i].setType(E_muserec_comment_line); + continue; + } + if (thing[i][0] == '&') { + // start or end of multi-line comment; + commentQ = !commentQ; + if (!commentQ) { + thing[i].setType(E_muserec_comment_toggle); + continue; + } + } + if (commentQ) { + thing[i].setType(E_muserec_comment_toggle); + continue; + } + } + h++; + if (h==1) { thing[i].setType(E_muserec_header_1); continue; } + else if (h==2) { thing[i].setType(E_muserec_header_2); continue; } + else if (h==3) { thing[i].setType(E_muserec_header_3); continue; } + else if (h==4) { thing[i].setType(E_muserec_header_4); continue; } + else if (h==5) { thing[i].setType(E_muserec_header_5); continue; } + else if (h==6) { thing[i].setType(E_muserec_header_6); continue; } + else if (h==7) { thing[i].setType(E_muserec_header_7); continue; } + else if (h==8) { thing[i].setType(E_muserec_header_8); continue; } + else if (h==9) { thing[i].setType(E_muserec_header_9); continue; } + else if (h==10) { thing[i].setType(E_muserec_header_10); continue; } + else if (h==11) { thing[i].setType(E_muserec_header_11); continue; } + else if (h==12) { thing[i].setType(E_muserec_header_12); continue; } + + if (thing[i].getLength() == 0) { + thing[i].setType(E_muserec_empty); + continue; + } + + if ((h > 12) && (thing[i][0] != '$') && (foundattributes == 0)) { + thing[i].setType(E_muserec_header_12); + continue; + } + + if (foundend && thing[i][0] != '/') { + thing[i].setType(E_muserec_endtext); + continue; + } + + switch (thing[i][0]) { + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': thing[i].setType(E_muserec_note_regular); break; + case ' ': thing[i].setType(E_muserec_note_chord); break; + case 'c': thing[i].setType(E_muserec_note_cue); break; + case 'g': thing[i].setType(E_muserec_note_grace); break; + case 'P': thing[i].setType(E_muserec_print_suggestion); break; + case 'S': thing[i].setType(E_muserec_sound_directives); break; + case '/': thing[i].setType(E_muserec_end); + foundend = 1; + break; + case 'a': thing[i].setType(E_muserec_append); break; + case 'b': thing[i].setType(E_muserec_backspace); break; + case 'f': thing[i].setType(E_muserec_figured_harmony); break; + case 'i': thing[i].setType(E_muserec_rest_invisible); break; + case 'm': thing[i].setType(E_muserec_measure); break; + case 'r': thing[i].setType(E_muserec_rest); break; + case '*': thing[i].setType(E_muserec_musical_directions); break; + case '$': thing[i].setType(E_muserec_musical_attributes); + foundattributes = 1; + break; + } + } +} + + + +////////////////////////////// +// +// MuseData::analyzeRhythm -- calculate the start time in quarter notes +// for each note/rest in the file. +// +// Secondary chord notes may or may not have a duration listed. +// If they do not, then the duration of the note is the same +// as the primary note of the chord. +// +// char* getTickDurationField (char* output); +// + +void MuseData::analyzeRhythm(void) { + HumNum cumulative(0,1); + HumNum linedur(0,1); + int tpq = 1; + HumRegex hre; + HumNum figadj = 0; // needed for figured harmony + HumNum primarychordnoteduration(0,1); // needed for chord notes + + for (int i=0; i<(int)m_data.size(); i++) { + if (m_data[i]->isAttributes()) { + if (hre.search(m_data[i]->getLine(), "Q:(\\d+)", "")) { + tpq = hre.getMatchInt(1); + } + } + + if (m_data[i]->isChordNote()) { + // insert an automatic back command for chord tones + // also deal with cue-size note chords? + m_data[i]->setAbsBeat(cumulative - primarychordnoteduration); + + // Check to see if the secondary chord note has a duration. + // If so, then set the note duration to that value; otherwise, + // set the note duration to the duration of the primary chord + // note (first note before the current note which is not a chord + // note). + string buffer = m_data[i]->getTickDurationField(); + if (hre.search(buffer, "\\d", "")) { + m_data[i]->setNoteDuration(m_data[i]->getNoteTickDuration(), tpq); + } else { + m_data[i]->setNoteDuration(primarychordnoteduration); + } + m_data[i]->setLineDuration(0); + } else if (m_data[i]->isFiguredHarmony()) { + // Tick values on figured harmony lines do not advance the + // cumulative timestamp; instead they temporarily advance + // the time placement of the next figure if it occurs + // during the same note as the previous figure. + m_data[i]->setAbsBeat(cumulative + figadj); + HumNum tick = m_data[i]->getLineTickDuration(); + if (tick == 0) { + figadj = 0; + } else { + HumNum dur = tick; + dur /= tpq; + figadj += dur; + } + } else { + m_data[i]->setAbsBeat(cumulative); + m_data[i]->setNoteDuration(m_data[i]->getNoteTickDuration(), tpq); + m_data[i]->setLineDuration(m_data[i]->getNoteDuration()); + linedur.setValue(m_data[i]->getLineTickDuration(), tpq); + cumulative += linedur; + } + + switch (m_data[i]->getType()) { + case E_muserec_note_regular: + // should also worry about cue and grace note chords? + primarychordnoteduration = linedur; + } + } + + // adjust Sound and Print records so that they occur at the same + // absolute time as the note they affect. + for (int i=1; i<(int)m_data.size(); i++) { + switch (m_data[i]->getType()) { + case E_muserec_print_suggestion: + case E_muserec_sound_directives: + m_data[i]->setAbsBeat(m_data[i-1]->getAbsBeat()); + } + } + +} + + + +////////////////////////////// +// +// MuseData::getInitialTpq -- return the Q: field in the first $ record +// at the top of the file. +// + +int MuseData::getInitialTpq(void) { + int output = 0; + if (m_data.empty()) { + return output; + } + HumRegex hre; + int i; + if (m_data[0]->getType() == E_muserec_unknown) { + // search for first line which starts with '$': + for (i=0; i<(int)m_data.size(); i++) { + if (m_data[i]->getLength() <= 0) { + continue; + } + if ((*m_data[i])[0] == '$') { + if (hre.search(m_data[i]->getLine(), "Q:(\\d+)", "")) { + output = hre.getMatchInt(1); + } + break; + } + } + } else { + for (int i=0; i<(int)m_data.size(); i++) { + if (m_data[i]->getType() == E_muserec_musical_attributes) { + if (hre.search(m_data[i]->getLine(), "Q:(\\d+)", "")) { + output = hre.getMatchInt(1); + } + break; + } + } + } + + return output; +} + + + +////////////////////////////// +// +// constructTimeSequence -- Make a list of the lines in the file +// sorted by the absolute time at which they occur. +// + +void MuseData::constructTimeSequence(void) { + // * clear old event set + // * allocate the size to match number of lines (* 2 probably). + + MuseData& thing = *this; + for (int i=0; i<(int)m_data.size(); i++) { + insertEventBackwards(thing[i].getAbsBeat(), &thing[i]); + if (hasError()) { + return; + } + } +} + + + +/////////////////////////////// +// +// MuseData::getEventCount -- returns the number of unique times +// at which data line occur in the file. +// + +int MuseData::getEventCount(void) { + return (int)m_sequence.size(); +} + + + +/////////////////////////////// +// +// MuseData::getEvent -- +// + +MuseEventSet& MuseData::getEvent(int eindex) { + return *(m_sequence[eindex]); +} + + + +/////////////////////////////////////////////////////////////////////////// +// +// private functions +// + + + +////////////////////////////// +// +// MuseData::insertEventBackwards -- insert an event into a time-sorted +// organization of the file. Searches for the correct time location to +// insert event starting at the end of the list (since MuseData files +// are mostly sorted in time. +// +// + +void printSequenceTimes(vector& m_sequence) { + for (int i=0; i<(int)m_sequence.size(); i++) { + cout << m_sequence[i]->getTime().getFloat() << " "; + } + cout << endl; +} + + +void MuseData::insertEventBackwards(HumNum atime, MuseRecord* arecord) { + + if (m_sequence.empty()) { + MuseEventSet* anevent = new MuseEventSet; + anevent->setTime(atime); + anevent->appendRecord(arecord); + m_sequence.push_back(anevent); + return; + } + + for (int i=(int)m_sequence.size()-1; i>=0; i--) { + if (m_sequence[i]->getTime() == atime) { + m_sequence[i]->appendRecord(arecord); + return; + } else if (m_sequence[i]->getTime() < atime) { + // insert new event entry after the current one since it occurs later. + MuseEventSet* anevent = new MuseEventSet; + anevent->setTime(atime); + anevent->appendRecord(arecord); + if (i == (int)m_sequence.size()-1) { + // just append the event at the end of the list + m_sequence.push_back(anevent); + return; + } else { + // event has to be inserted before end of list, so move + // later ones up in list. + m_sequence.resize(m_sequence.size()+1); + for (int j=(int)m_sequence.size()-1; j>i+1; j--) { + m_sequence[j] = m_sequence[j-1]; + } + // store the newly created event entry in m_sequence: + m_sequence[i+1] = anevent; + return; + } + } + } + stringstream ss; + ss << "Funny error occurred at time " << atime; + setError(ss.str()); +} + + + +////////////////////////////// +// +// MuseData::getTiedDuration -- these version acess the record lines +// via the time-sorted event index. +// + +HumNum MuseData::getTiedDuration(int eindex, int erecord) { + return getTiedDuration(getLineIndex(eindex, erecord)); +} + + + +////////////////////////////// +// +// MuseData:getTiedDuration -- +// + +HumNum MuseData::getTiedDuration(int index) { + HumNum output(0,1); + + // if the line is not a regular/chord note with duration, then + // return the duration field. + if ((getRecord(index).getType() != E_muserec_note_chord) && + (getRecord(index).getType() != E_muserec_note_regular) ) { + return output; + } + + // if the note is tied to a previous note, then return a + // duration of 0 (behavior may change in the future). + if (getRecord(index).getLastTiedNoteLineIndex() >= 0) { + return output; + } + + // if the note is not tied to anything into the future, then + // gives it actual duration + if (getRecord(index).getNextTiedNoteLineIndex() < 0) { + return getRecord(index).getNoteDuration(); + } + + // this is start of a group of tied notes. Start figuring out + // how long the duration of the tied group is. + output = getRecord(index).getNoteDuration(); + int myindex = index; + while (getRecord(myindex).getNextTiedNoteLineIndex() >= 0) { + myindex = getRecord(myindex).getNextTiedNoteLineIndex(); + output += getRecord(myindex).getNoteDuration(); + } + + return output; +} + + + +////////////////////////////// +// +// MuseData::getLineIndex -- return the line number of a particular +// event/record index pair (the line index is the same as the index +// into the list of lines). +// + +int MuseData::getLineIndex(int eindex, int erecord) { + return getRecord(eindex, erecord).getLineIndex(); +} + + + +////////////////////////////// +// +// MuseData::getLineDuration -- return the duration of an isolated line. +// + +HumNum MuseData::getLineDuration(int eindex, int erecord) { + return getRecord(eindex, erecord).getLineDuration(); +} + + + +////////////////////////////// +// +// MuseData::getNoteDuration -- return the duration of an isolated note. +// + +HumNum MuseData::getNoteDuration(int eindex, int erecord) { + return getRecord(eindex, erecord).getNoteDuration(); +} + + + +////////////////////////////// +// +// MuseData::getLastTiedNoteLineIndex -- return the line index of the +// previous note to which this one is tied to. Returns -1 if there +// is no previous tied note. +// + +int MuseData::getLastTiedNoteLineIndex(int eindex, int erecord) { + return getRecord(eindex, erecord).getLastTiedNoteLineIndex(); +} + + + +////////////////////////////// +// +// MuseData::getNextTiedNoteLineIndex -- returns the line index of the +// next note to which this one is tied to. Returns -1 if there +// is no previous tied note. +// + +int MuseData::getNextTiedNoteLineIndex(int eindex, int erecord) { + return getRecord(eindex, erecord).getNextTiedNoteLineIndex(); +} + + + +////////////////////////////// +// +// MuseData::getType -- return the record type of a particular event +// record. +// + +int MuseData::getType(int eindex, int erecord) { + return getRecord(eindex, erecord).getType(); +} + + + +////////////////////////////// +// +// MuseData::getAbsBeat -- return the absolute beat time (quarter +// note durations from the start of the music until the current +// object. +// + +HumNum MuseData::getAbsBeat(int lindex) { + return m_data[lindex]->getAbsBeat(); +} + + + +////////////////////////////// +// +// MuseData::getLineTickDuration -- +// + +int MuseData::getLineTickDuration(int lindex) { + return m_data[lindex]->getLineTickDuration(); +} + + +////////////////////////////// +// +// MuseData::setFilename -- +// + +void MuseData::setFilename(const string& filename) { + m_name = filename; +} + + + +////////////////////////////// +// +// MuseData::getFilename -- +// + +string MuseData::getFilename(void) { + return m_name; +} + + +/* + +////////////////////////////// +// +// MuseData::getPartName -- +// + +string MuseData::getPartName(void) { + string output; + for (int i=0; i=0; i--) { + if (isspace(output[i])) { + output.resize((int)output.size() - 1); + } else { + break; + } + } + return output; +} + +*/ + + +////////////////////////////// +// +// MuseData::getPartName -- return name of the part +// + +string MuseData::getPartName(void) { + int line = getPartNameIndex(); + if (line < 0) { + return ""; + } + HumRegex hre; + string output = m_data[line]->getLine(); + hre.replaceDestructive(output, "", "^\\s+"); + hre.replaceDestructive(output, "", "\\s+$"); + return output; +} + + + +////////////////////////////// +// +// MuseData::getPartNameIndex -- Search for the part name in the header. +// search the entire file if it is missing (which it should not be. +// + +int MuseData::getPartNameIndex(void) { + int output = -1; + for (int i=0; i<(int)m_data.size(); i++) { + if (m_data[i]->isPartName()) { + return i; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseData::isMember -- returns true if the file belongs to the +// given membership string. Example memberships are "score", +// "skore", "part", "sound". +// + +int MuseData::isMember(const string& mstring) { + for (int i=0; i<(int)m_data.size(); i++) { + if (m_data[i]->getType() == E_muserec_group_memberships) { + if (strstr(m_data[i]->getLine().c_str(), mstring.c_str()) != NULL) { + return 1; + } else { + return 0; + } + } + if (m_data[i]->getType() == E_muserec_musical_attributes) { + break; + } + } + return 0; +} + + + +////////////////////////////// +// +// MuseData::getMembershipPartNumber -- returns the part number within +// a given membership for the data. +// Example: +// sound: part 1 of 4 +// in this case the value returned is 1. +// + +int MuseData::getMembershipPartNumber(const string& mstring) { + string searchstring = "^"; + searchstring += mstring; + searchstring += ":"; + + HumRegex hre; + for (int i=0; i<(int)m_data.size(); i++) { + if (m_data[i]->getType() == E_muserec_header_12) { + if (hre.search(m_data[i]->getLine(), searchstring, "")) { + if (hre.search(m_data[i]->getLine(), + "part\\s*(\\d+)\\s*of\\s*(\\d+)")) { + string partnum = hre.getMatch(1); + return hre.getMatchInt(1); + } + } + } + if (m_data[i]->getType() == E_muserec_musical_attributes) { + break; + } + } + return 0; +} + + + +//////////////////////////////// +// +// MuseData::selectMembership -- +// + +void MuseData::selectMembership(const string& selectstring) { + if (!isMember(selectstring)) { + // not a member of the given membership, so can't select that + // membership. + return; + } + string buffer; + buffer = "Group memberships: "; + buffer += selectstring; + + for (int i=0; igetLineCount(); i++) { + (*this)[i].cleanLineEnding(); + } +} + + + +////////////////////////////// +// +// MuseData::getError -- +// + +std::string MuseData::getError(void) { + return m_error; +} + + + +////////////////////////////// +// +// MuseData::hasError -- +// + +bool MuseData::hasError(void) { + return !m_error.empty(); +} + + +////////////////////////////// +// +// MuseData::clearError -- +// + +void MuseData::clearError(void) { + m_error.clear(); +} + + + +////////////////////////////// +// +// MuseData:::etError -- +// + +void MuseData::setError(const string& error) { + m_error = error; +} + + + +////////////////////////////// +// +// MuseData::getFileDuration -- +// + +HumNum MuseData::getFileDuration(void) { + return getRecord(getLineCount()-1).getAbsBeat(); +} + + + +////////////////////////////// +// +// MuseData::getLine -- return the textual content of the given line index. +// + +string MuseData::getLine(int index) { + return getRecord(index).getLine(); +} + + + +////////////////////////////// +// +// MuseData::isPartName -- return true if partname line. +// + +bool MuseData::isPartName(int index) { + return getRecord(index).isPartName(); +} + + + +/////////////////////////////////////////////////////////////////////////// +// +// friendly functions +// + + +////////////////////////////// +// +// operator<< -- +// + +ostream& operator<<(ostream& out, MuseData& musedata) { + for (int i=0; ireadFile(filename); + md->setFilename(filename); + return appendPart(md); +} + +int MuseDataSet::readPartString(const string& data) { + stringstream ss; + ss << data; + return readPart(ss); +} + + +int MuseDataSet::readPart(istream& input) { + MuseData* md = new MuseData; + md->read(input); + return appendPart(md); +} + + + +////////////////////////////// +// +// MuseDataSet::read -- read potentially Multiple parts from a single file. +// First clear the contents of any previous part data. +// + +int MuseDataSet::readFile(const string& filename) { + MuseDataSet::clear(); + ifstream infile(filename); + return MuseDataSet::read(infile); +} + +int MuseDataSet::readString(const string& data) { + stringstream ss; + ss << data; + return MuseDataSet::read(ss); +} + + +int MuseDataSet::read(istream& infile) { + vector datalines; + datalines.reserve(100000); + string thing; + + while (!infile.eof()) { + getline(infile, thing); + if (infile.eof() && (thing.length() == 0)) { + // last line was not terminated by a newline character + break; + } + datalines.push_back(thing); + } + + vector startindex; + vector stopindex; + analyzePartSegments(startindex, stopindex, datalines); + + stringstream *sstream; + MuseData* md; + for (int i=0; i<(int)startindex.size(); i++) { + sstream = new stringstream; + for (int j=startindex[i]; j<=stopindex[i]; j++) { + (*sstream) << datalines[j] << '\n'; + } + md = new MuseData; + md->read(*sstream); + appendPart(md); + delete sstream; + } + return 1; +} + + + +////////////////////////////// +// +// MuseDataSet::appendPart -- append a MuseData pointer to the end of the +// parts list and return the index number of the new part. +// + +int MuseDataSet::appendPart(MuseData* musedata) { + int index = (int)m_part.size(); + m_part.resize(m_part.size()+1); + m_part[index] = musedata; + return index; +} + + + +////////////////////////////// +// +// MuseData::analyzePartSegments -- Calculate the starting line index +// and the ending line index for each part in the input. +// + +void MuseDataSet::analyzePartSegments(vector& startindex, + vector& stopindex, vector& lines) { + + startindex.clear(); + stopindex.clear(); + startindex.reserve(1000); + stopindex.reserve(1000); + + vector types; + // MuseData& thing = *this; + + types.resize(lines.size()); + std::fill(types.begin(), types.end(), E_muserec_unknown); + + // first identify lines which are multi-line comments so they will + // not cause confusion in the next step + int commentstate = 0; + for (int i=0; i<(int)lines.size(); i++) { + if (lines[i].c_str()[0] == '&') { + types[i] = E_muserec_comment_toggle; + commentstate = !commentstate; + continue; + } + if (commentstate) { + types[i] = E_muserec_comment_line; + } + } + + // search the data for "Group memberships:" lines which are required + // to be in the header of each part. + vector groupmemberships; + groupmemberships.reserve(1000); + int len = strlen("Group memberships:"); + for (int i=0; i<(int)lines.size(); i++) { + if (strncmp("Group memberships:", lines[i].c_str(), len) == 0) { + if (types[i] != E_muserec_comment_line) { + groupmemberships.push_back(i); + } + } + } + + // search backwards from "Group memberships:" until the end of the + // header, sucking any comments which occurs just before the start + // of the header. (currently only multi-line comments, but also need + // to add single-line comments) + int value; + int headerline; + int found = 0; + for (int ii=0; ii<(int)groupmemberships.size(); ii++) { + int i = groupmemberships[ii]; + types[i] = E_muserec_group_memberships; + found = 0; + headerline = 11; + for (int j=i-1; j>=0; j--) { + if (j < 0) { + break; + } + if (lines[j].compare(0, 4, "/eof") == 0) { + // end of previous file + found = 1; + value = j + 1; + startindex.push_back(value); + break; + } + if ((types[j] == E_muserec_comment_line) || + (types[j] == E_muserec_comment_toggle)) { +// j--; + continue; + } + if (j < 0) { + break; + } + headerline--; + + if (headerline == 0) { + while ((j>= 0) && (lines[j][0] == '@')) { + j--; + } + value = j+1; + //value = j+2; + found = 1; + startindex.push_back(value); + break; + } + + if ((j >= 0) && (headerline == 0)) { + value = j+1; + found = 1; + startindex.push_back(value); + break; + } + if (j<0) { + value = 0; + found = 1; + startindex.push_back(value); + continue; + } + switch (headerline) { + case 11: types[j] = E_muserec_header_11; break; + case 10: types[j] = E_muserec_header_10; break; + case 9: types[j] = E_muserec_header_9; break; + case 8: types[j] = E_muserec_header_8; break; + case 7: types[j] = E_muserec_header_7; break; + case 6: types[j] = E_muserec_header_6; break; + case 5: types[j] = E_muserec_header_5; break; + case 4: types[j] = E_muserec_header_4; break; + case 3: types[j] = E_muserec_header_3; break; + case 2: types[j] = E_muserec_header_2; break; + case 1: types[j] = E_muserec_header_1; break; + } + } + if (!found) { + value = 0; + startindex.push_back(value); + } + } + + // now calculate the stopindexes: + stopindex.resize(startindex.size()); + stopindex[(int)stopindex.size()-1] = (int)lines.size()-1; + for (int i=0; i<(int)startindex.size()-1; i++) { + stopindex[i] = startindex[i+1]-1; + } +} + + + +////////////////////////////// +// +// MuseDataSet::getPartCount -- return the number of parts found +// in the MuseDataSet +// + +int MuseDataSet::getPartCount(void) { + return (int)m_part.size(); +} + + + +////////////////////////////// +// +// MuseDataSet::deletePart -- remove a particular part from the data set. +// + +void MuseDataSet::deletePart(int index) { + if (index < 0 || index > (int)m_part.size()-1) { + cerr << "Trying to delete a non-existent part" << endl; + return; + } + + delete m_part[index]; + int i; + for (i=index+1; i<(int)m_part.size(); i++) { + m_part[i-1] = m_part[i]; + } + m_part.resize(m_part.size()-1); +} + + + +////////////////////////////// +// +// MuseDataSet::cleanLineEndings -- remove spaces for ends of lines. +// + +void MuseDataSet::cleanLineEndings(void) { + for (int i=0; i<(int)m_part.size(); i++) { + m_part[i]->cleanLineEndings(); + } +} + + + +////////////////////////////// +// +// MuseDataSet::clearError -- +// + +void MuseDataSet::clearError(void) { + m_error = ""; +} + + + +////////////////////////////// +// +// MuseDataSet::hasError -- +// + +bool MuseDataSet::hasError(void) { + return !m_error.empty(); +} + + +////////////////////////////// +// +// MuseDataSet::setError -- +// + +void MuseDataSet::setError(const string& error) { + m_error = error; +} + + + +/////////////////////////////////////////////////////////////////////////// + +////////////////////////////// +// +// operator<< -- print out all parts in sequential order +// + +ostream& operator<<(ostream& out, MuseDataSet& musedataset) { + for (int i=0; i= (int)recordInfo.size()) { + cerr << "Error: no octave specification in note field: " << recordInfo + << endl; + return 0; + } + return recordInfo[index] - '0'; +} + + +string MuseRecord::getOctaveString(void) { + string recordInfo = getNoteField(); + int index = 0; + while ((index < (int)recordInfo.size()) && !std::isdigit(recordInfo[index])) { + index++; + } + if (index >= (int)recordInfo.size()) { + cerr << "Error: no octave specification in note field: " << recordInfo + << endl; + return ""; + } + string output; + output += recordInfo[index]; + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getPitch -- int version returns the base40 representation +// + +int MuseRecord::getPitch(void) { + string recordInfo = getNoteField(); + return Convert::museToBase40(recordInfo); +} + + +string MuseRecord::getPitchString(void) { + string output = getNoteField(); + int len = (int)output.size(); + int index = len-1; + while (index >= 0 && output[index] == ' ') { + output.resize(index); + index--; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getPitchClass -- returns the pitch without the octave information +// + +int MuseRecord::getPitchClass(void) { + return getPitch() % 40; +} + + +string MuseRecord::getPitchClassString(void) { + string output = getNoteField(); + int index = 0; + while ((index < (int)output.size()) && !std::isdigit(output[index])) { + index++; + } + output.resize(index); + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getAccidental -- int version return -2 for double flat, +// -1 for flat, 0 for natural, +1 for sharp, +2 for double sharp +// + +int MuseRecord::getAccidental(void) { + string recordInfo = getNoteField(); + int output = 0; + int index = 0; + while ((index < (int)recordInfo.size()) && (index < 16)) { + if (recordInfo[index] == 'f') { + output--; + } else if (recordInfo[index] == '#') { + output++; + } + index++; + } + return output; +} + + +string MuseRecord::getAccidentalString(void) { + string output; + int type = getAccidental(); + switch (type) { + case -2: output = "ff"; break; + case -1: output = "f"; break; + case 0: output = ""; break; + case 1: output = "#"; break; + case 2: output = "##"; break; + default: + output = getNoteField(); + cerr << "Error: unknown type of accidental: " << output << endl; + return ""; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getBase40 -- return the base40 pitch value of the data +// line. Middle C set to 40 * 4 + 2; Returns -100 for non-pitched items. +// (might have to update for note_cur_chord and note_grace_chord which +// do not exist yet. +// + +int MuseRecord::getBase40(void) { + switch (getType()) { + case E_muserec_note_regular: + case E_muserec_note_chord: + case E_muserec_note_cue: + case E_muserec_note_grace: + break; + default: + return -100; + } + return getPitch(); +} + + + +////////////////////////////// +// +// MuseRecord::setStemDown -- +// + +void MuseRecord::setStemDown(void) { + getColumn(23) = 'd'; +} + + + +////////////////////////////// +// +// MuseRecord::setStemUp -- +// + +void MuseRecord::setStemUp(void) { + getColumn(23) = 'u'; +} + + + +////////////////////////////// +// +// MuseRecord::setPitch -- input is a base40 value which gets converted +// to a diatonic pitch name. +// Default value: chordnote = 0 +// Default value: gracenote = 0 +// + +void MuseRecord::setPitch(int base40, int chordnote, int gracenote) { + string diatonic; + switch (Convert::base40ToDiatonic(base40) % 7) { + case 0: diatonic = 'C'; break; + case 1: diatonic = 'D'; break; + case 2: diatonic = 'E'; break; + case 3: diatonic = 'F'; break; + case 4: diatonic = 'G'; break; + case 5: diatonic = 'A'; break; + case 6: diatonic = 'B'; break; + default: diatonic = 'X'; + } + + string octave; + octave += char('0' + base40 / 40); + + string accidental; + int acc = Convert::base40ToAccidental(base40); + switch (acc) { + case -2: accidental = "ff"; break; + case -1: accidental = "f"; break; + case +1: accidental = "#"; break; + case +2: accidental = "##"; break; + } + string pitchname = diatonic + accidental + octave; + + if (chordnote) { + if (gracenote) { + setGraceChordPitch(pitchname); + } else { + setChordPitch(pitchname); + } + } else { + setPitch(pitchname); + } +} + + +void MuseRecord::setChordPitch(const string& pitchname) { + getColumn(1) = ' '; + setPitchAtIndex(1, pitchname); +} + +void MuseRecord::setGracePitch(const string& pitchname) { + getColumn(1) = 'g'; + setPitchAtIndex(1, pitchname); +} + +void MuseRecord::setGraceChordPitch(const string& pitchname) { + getColumn(1) = 'g'; + getColumn(2) = ' '; + setPitchAtIndex(2, pitchname); +} + +void MuseRecord::setCuePitch(const string& pitchname) { + getColumn(1) = 'c'; + setPitchAtIndex(1, pitchname); +} + + +void MuseRecord::setPitch(const string& pitchname) { + int start = 0; + // If the record is already set to a grace note or a cue note, + // then place pitch information starting at column 2 (index 1). + if ((getColumn(1) == 'g') || (getColumn(1) == 'c')) { + start = 1; + } + setPitchAtIndex(start, pitchname); +} + + +void MuseRecord::setPitchAtIndex(int index, const string& pitchname) { + int len = (int)pitchname.size(); + if ((len > 4) && (pitchname != "irest")) { + cerr << "Error in MuseRecord::setPitchAtIndex: " << pitchname << endl; + return; + } + insertString(index+1, pitchname); + + // Clear any text fields not used by current pitch data. + for (int i=4-len-1; i>=0; i--) { + (*this)[index + len + i] = ' '; + } +} + + + +////////////////////////////// +// +// MuseRecord::getTickDurationField -- returns the string containing the +// duration, and tie information. +// + +string MuseRecord::getTickDurationField(void) { + switch (getType()) { + case E_muserec_figured_harmony: + case E_muserec_note_regular: + case E_muserec_note_chord: + case E_muserec_rest: + case E_muserec_backward: + case E_muserec_forward: + return extract(6, 9); + break; + // these record types do not have duration, per se: + case E_muserec_note_cue: + case E_muserec_note_grace: + default: + return ""; + // cerr << "Error: cannot use getTickDurationField function on line: " + // << getLine() << endl; + // return ""; + } + return ""; +} + + + +////////////////////////////// +// +// MuseRecord::getTickDurationString -- returns the string containing the duration, +// + +string MuseRecord::getTickDurationString(void) { + string output = getTickDurationField(); + int length = (int)output.size(); + int i = length - 1; + while (i>0 && (output[i] == '-' || output[i] == ' ')) { + output.resize(i); + i--; + length--; + } + + int start = 0; + while (output[start] == ' ') { + start++; + } + + if (start != 0) { + for (i=0; i 16) { // maxima + setNoteheadMaxima(); + } else if (duration > 8) { // long + setNoteheadLong(); + } else if (duration > 4) { // breve + if (m_roundBreve) { + setNoteheadBreveRound(); + } else { + setNoteheadBreve(); + } + } else if (duration > 2) { // whole note + setNoteheadWhole(); + } else if (duration > 1) { // half note + setNoteheadHalf(); + } else if (duration > note8th) { // quarter note + setNoteheadQuarter(); + } else if (duration > note16th) { // eighth note + setNotehead8th(); + } else if (duration > note32nd) { // 16th note + setNotehead16th(); + } else if (duration > note64th) { // 32nd note + setNotehead32nd(); + } else if (duration > note128th) { // 64th note + setNotehead64th(); + } else if (duration > note256th) { // 128th note + setNotehead128th(); + } else if (duration == note256th) { // 256th note + // not allowing tuplets on the 256th note level. + setNotehead256th(); + } else { + cerr << "Error in duration: " << duration << endl; + return; + } +} + + + +////////////////////////////// +// +// MuseRecord::setNoteheadShape -- Duration with augmentation dot component +// removed. Duration of 1 is quarter note. +// + +void MuseRecord::setNoteheadShapeMensural(HumNum duration) { + HumNum note8th(1, 2); + HumNum note16th(1, 4); + HumNum note32th(1, 8); + HumNum note64th(1, 16); + HumNum note128th(1, 32); + HumNum note256th(1, 64); + + if (duration > 16) { // maxima + setNoteheadMaxima(); + } else if (duration > 8) { // long + setNoteheadLong(); + } else if (duration > 4) { // breve + setNoteheadBreve(); + } else if (duration > 2) { // whole note + setNoteheadWholeMensural(); + } else if (duration > 1) { // half note + setNoteheadHalfMensural(); + } else if (duration > note8th) { // quarter note + setNoteheadQuarterMensural(); + } else if (duration > note16th) { // eighth note + setNotehead8thMensural(); + } else if (duration > note32th) { // 16th note + setNotehead16thMensural(); + } else if (duration > note64th) { // 32nd note + setNotehead32ndMensural(); + } else if (duration > note128th) { // 64th note + setNotehead64thMensural(); + } else if (duration > note256th) { // 128th note + setNotehead128thMensural(); + } else if (duration >= note256th) { // 256th note + // don't allow tuplets on 256th note level. + setNotehead256thMensural(); + } else { + cerr << "Error in duration: " << duration << endl; + return; + } +} + +void MuseRecord::setNoteheadMaxima(void) { + if ((*this)[0] == 'c' || ((*this)[0] == 'g')) { + cerr << "Error: cue/grace notes cannot be maximas in setNoteheadLong" + << endl; + return; + } else { + getColumn(17) = 'M'; + } +} + +void MuseRecord::setNoteheadLong(void) { + if ((*this)[0] == 'c' || ((*this)[0] == 'g')) { + cerr << "Error: cue/grace notes cannot be longs in setNoteheadLong" + << endl; + return; + } else { + getColumn(17) = 'L'; + } +} + +void MuseRecord::setNoteheadBreve(void) { + setNoteheadBreveSquare(); +} + +void MuseRecord::setNoteheadBreveSquare(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = 'A'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = 'A'; + } else { // normal note + getColumn(17) = 'B'; + } +} + +void MuseRecord::setNoteheadBreveRound(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = 'A'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = 'A'; + } else { // normal note + getColumn(17) = 'b'; + } +} + +void MuseRecord::setNoteheadBreveMensural(void) { + setNoteheadBreveSquare(); +} + +void MuseRecord::setNoteheadWhole(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '9'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '9'; + } else { // normal note + getColumn(17) = 'w'; + } +} + +void MuseRecord::setNoteheadWholeMensural(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '9'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '9'; + } else { // normal note + getColumn(17) = 'W'; + } +} + +void MuseRecord::setNoteheadHalf(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '8'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '8'; + } else { // normal note + getColumn(17) = 'h'; + } +} + +void MuseRecord::setNoteheadHalfMensural(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '8'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '8'; + } else { // normal note + getColumn(17) = 'H'; + } +} + +void MuseRecord::setNoteheadQuarter(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '7'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '7'; + } else { // normal note + getColumn(17) = 'q'; + } +} + +void MuseRecord::setNoteheadQuarterMensural(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '7'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '7'; + } else { // normal note + getColumn(17) = 'Q'; + } +} + +void MuseRecord::setNotehead8th(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '6'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '6'; + } else { // normal note + getColumn(17) = 'e'; + } +} + +void MuseRecord::setNotehead8thMensural(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '6'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '6'; + } else { // normal note + getColumn(17) = 'E'; + } +} + +void MuseRecord::setNotehead16th(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '5'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '5'; + } else { // normal note + getColumn(17) = 's'; + } +} + +void MuseRecord::setNotehead16thMensural(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '5'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '5'; + } else { // normal note + getColumn(17) = 'S'; + } +} + +void MuseRecord::setNotehead32nd(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '4'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '4'; + } else { // normal note + getColumn(17) = 't'; + } +} + +void MuseRecord::setNotehead32ndMensural(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '4'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '4'; + } else { // normal note + getColumn(17) = 'T'; + } +} + +void MuseRecord::setNotehead64th(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '3'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '3'; + } else { // normal note + getColumn(17) = 'x'; + } +} + +void MuseRecord::setNotehead64thMensural(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '3'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '3'; + } else { // normal note + getColumn(17) = 'X'; + } +} + +void MuseRecord::setNotehead128th(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '2'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '2'; + } else { // normal note + getColumn(17) = 'y'; + } +} + +void MuseRecord::setNotehead128thMensural(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '2'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '2'; + } else { // normal note + getColumn(17) = 'Y'; + } +} + +void MuseRecord::setNotehead256th(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '1'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '1'; + } else { // normal note + getColumn(17) = 'z'; + } +} + +void MuseRecord::setNotehead256thMensural(void) { + if ((*this)[0] == 'g') { // grace note + getColumn(8) = '1'; + } else if ((*this)[0] == 'c') { // cue-sized note (with duration) + getColumn(17) = '1'; + } else { // normal note + getColumn(17) = 'Z'; + } +} + + +///////////////////////////// +// +// MuseRecord::setBack -- +// + +void MuseRecord::setBack(int value) { + insertString(1, "back"); + setTicks(value); +} + + + +///////////////////////////// +// +// MuseRecord::setTicks -- return the numeric value in columns 6-9. +// + +void MuseRecord::setTicks(int value) { + if ((value < 0) || (value >= 1000)) { + cerr << "@ Error: ticks out of range in MuseRecord::setTicks" << endl; + } + stringstream ss; + ss << value; + int len = (int)ss.str().size(); + insertString(5+3-len+1, ss.str()); +} + + + +////////////////////////////// +// +// MuseRecord::getTie -- +// + +string MuseRecord::getTieString(void) { + string output; + output += getColumn(9); + if (output == " ") { + output = ""; + } + return output; +} + + +int MuseRecord::getTie(void) { + return tieQ(); +} + + +////////////////////////////// +// +// MuseRecord::getTie -- Set a tie marker in column 9. Currently +// the function does not check the type of data, so will overr-write any +// data found in column 9 (such as if the record is not for a note). +// +// If the input parameter hidden is true, then the visual tie is not +// displayed, but the sounding tie is displayed. +// + +int MuseRecord::setTie(int hidden) { + getColumn(9) = '-'; + if (!hidden) { + return addAdditionalNotation('-'); + } else { + return -1; + } +} + + + +////////////////////////////// +// +// MuseRecord::addAdditionalNotation -- ties, slurs and tuplets. +// Currently not handling editorial levels. +// + +int MuseRecord::addAdditionalNotation(char symbol) { + // search columns 32 to 43 for the specific symbol. + // if it is found, then don't add. If it is not found, + // then do add. + int i; + int blank = -1; + int nonempty = 0; // true if a non-space character was found. + + for (i=43; i>=32; i--) { + if (getColumn(i) == symbol) { + return i; + } else if (!nonempty && (getColumn(i) == ' ')) { + blank = i; + } else { + nonempty = i; + } + } + + if (symbol == '-') { + // give preferential treatment to placing only ties in + // column 32 + if (getColumn(32) == ' ') { + getColumn(32) = '-'; + return 32; + } + } + + if (blank < 0) { + cerr << "Error in MuseRecord::addAdditionalNotation: " + << "no empty space for notation" << endl; + return 0; + } + + if ((blank <= 32) && (getColumn(33) == ' ')) { + // avoid putting non-tie items in column 32. + blank = 33; + } + + getColumn(blank) = symbol; + return blank; +} + + +// add a multi-character additional notation (such as a dynamic like mf): + +int MuseRecord::addAdditionalNotation(const string& symbol) { + int len = (int)symbol.size(); + // search columns 32 to 43 for the specific symbol. + // if it is found, then don't add. If it is not found, + // then do add. + int i, j; + int blank = -1; + int found = 0; + int nonempty = 0; // true if a non-space character was found. + + for (i=43-len; i>=32; i--) { + found = 1; + for (j=0; j rests also + if (getLength() < 18) { + return " "; + } else { + return extract(18, 18); + } +} + + + +////////////////////////////// +// +// MuseRecord::getProlongationString -- +// + +string MuseRecord::getProlongationString(void) { + string output = getProlongationField(); + if (output[0] == ' ') { + output = ""; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getProlongation -- +// + +int MuseRecord::getProlongation(void) { + int output = 0; + string recordInfo = getProlongationField(); + switch (recordInfo[0]) { + case ' ': output = 0; break; + case '.': output = 1; break; + case ':': output = 2; break; + default: + cerr << "Error: unknon prologation character (column 18): " + << getLine() << endl; + return 0; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getStringProlongation -- +// + +string MuseRecord::getStringProlongation(void) { + switch (getProlongation()) { + case 0: return ""; break; + case 1: return "."; break; + case 2: return ".."; break; + case 3: return "..."; break; + case 4: return "...."; break; + default: + cerr << "Error: unknown number of prolongation dots (column 18): " + << getLine() << endl; + return ""; + } + return ""; +} + + + +////////////////////////////// +// +// MuseRecord::prolongationQ -- +// + +int MuseRecord::prolongationQ(void) { + return getProlongation(); +} + + +////////////////////////////// +// +// MuseRecord::getNotatedAccidentalField -- actual notated accidental is +// stored in column 19. +// + +string MuseRecord::getNotatedAccidentalField(void) { + allowNotesOnly("getNotatedAccidentalField"); + if (getLength() < 19) { + return " "; + } else { + string temp; + temp += getColumn(19); + return temp; + } +} + + + +////////////////////////////// +// +// MuseRecord::getNotatedAccidentalString -- +// + +string MuseRecord::getNotatedAccidentalString(void) { + string output = getNotatedAccidentalField(); + if (output[0] == ' ') { + output = ""; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getNotatedAccidental -- +// + +int MuseRecord::getNotatedAccidental(void) { + int output = 0; + string recordInfo = getNotatedAccidentalField(); + switch (recordInfo[0]) { + case ' ': output = 0; break; + case '#': output = 1; break; + case 'n': output = 0; break; + case 'f': output = -1; break; + case 'x': output = 2; break; + case 'X': output = 2; break; + case '&': output = -2; break; + case 'S': output = 1; break; + case 'F': output = -1; break; + default: + cerr << "Error: unknown accidental: " << recordInfo[0] << endl; + return 0; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::notatedAccidentalQ -- +// + +int MuseRecord::notatedAccidentalQ(void) { + int output; + string recordInfo = getNotatedAccidentalField(); + if (recordInfo[0] == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +/////////////////////////////// +// +// MuseRecord::getTimeModificationField -- return columns 20 -- 22. +// + +string MuseRecord::getTimeModificationField(void) { +// allowNotesOnly("getTimeModificationField"); ---> rests also + if (getLength() < 20) { + return " "; + } else { + return extract(20, 22); + } +} + + + +////////////////////////////// +// +// MuseRecord::getTimeModification -- +// + +string MuseRecord::getTimeModification(void) { + string output = getTimeModificationField(); + int index = 2; + while (index >= 0 && output[index] == ' ') { + output.resize(index); + index--; + } + if (output.size() > 2) { + if (output[0] == ' ') { + output[0] = output[1]; + output[1] = output[2]; + output.resize(2); + } + } + if (output.size() > 1) { + if (output[0] == ' ') { + output[0] = output[1]; + output.resize(1); + } + } + if (output[0] == ' ') { + cerr << "Error: funny error occured in time modification " + << "(columns 20-22): " << getLine() << endl; + return ""; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getTimeModificationLeftField -- return column 20 +// + +string MuseRecord::getTimeModificationLeftField(void) { + string output = getTimeModificationField(); + output.resize(1); + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getTimeModificationLeftString -- +// + +string MuseRecord::getTimeModificationLeftString(void) { + string output = getTimeModificationField(); + if (output[0] == ' ') { + output = ""; + } else { + output.resize(1); + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getTimeModificationLeft -- +// + +int MuseRecord::getTimeModificationLeft(void) { + int output = 0; + string recordInfo = getTimeModificationLeftString(); + if (recordInfo[0] == ' ') { + output = 0; + } else { + output = std::strtol(recordInfo.c_str(), NULL, 36); + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getTimeModificationRightField -- return column 20 +// + +string MuseRecord::getTimeModificationRightField(void) { + string output = getTimeModificationField(); + output = output[2]; + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getTimeModificationRight -- +// + +string MuseRecord::getTimeModificationRightString(void) { + string output = getTimeModificationField(); + if (output[2] == ' ') { + output = ""; + } else { + output = output[2]; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getTimeModificationRight -- +// + +int MuseRecord::getTimeModificationRight(void) { + int output = 0; + string recordInfo = getTimeModificationRightString(); + if (recordInfo[2] == ' ') { + output = 0; + } else { + string temp = recordInfo.substr(2); + output = std::strtol(temp.c_str(), NULL, 36); + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::timeModificationQ -- +// + +int MuseRecord::timeModificationQ(void) { + int output = 0; + string recordInfo = getTimeModificationField(); + if (recordInfo[0] != ' ' || recordInfo[1] != ' ' || recordInfo[2] != ' ') { + output = 1; + } else { + output = 0; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::timeModificationLeftQ -- +// + +int MuseRecord::timeModificationLeftQ(void) { + int output = 0; + string recordInfo = getTimeModificationField(); + if (recordInfo[0] == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::timeModificationRightQ -- +// + +int MuseRecord::timeModificationRightQ(void) { + int output = 0; + string recordInfo = getTimeModificationField(); + if (recordInfo[2] == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getStemDirectionField -- +// + +string MuseRecord::getStemDirectionField(void) { + allowNotesOnly("getStemDirectionField"); + if (getLength() < 23) { + return " "; + } else { + string temp; + temp += getColumn(23); + return temp; + } +} + + + +////////////////////////////// +// +// MuseRecord::getStemDirectionString -- +// + +string MuseRecord::getStemDirectionString(void) { + string output = getStemDirectionField(); + if (output[0] == ' ') { + output = ""; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getStemDirection -- +// + +int MuseRecord::getStemDirection(void) { + int output = 0; + string recordInfo = getStemDirectionField(); + switch (recordInfo[0]) { + case 'u': output = 1; break; + case 'd': output = -1; break; + case ' ': output = 0; break; + default: + cerr << "Error: unknown stem direction: " << recordInfo[0] << endl; + return 0; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::stemDirectionQ -- +// + +int MuseRecord::stemDirectionQ(void) { + int output = 0; + string recordInfo = getStemDirectionField(); + if (recordInfo[0] == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getStaffField -- returns column 24. +// + +string MuseRecord::getStaffField(void) { + allowNotesOnly("getStaffField"); + if (getLength() < 24) { + return " "; + } else { + string temp; + temp += getColumn(24); + return temp; + } +} + + + +////////////////////////////// +// +// MuseRecord::getStaffString -- +// + +string MuseRecord::getStaffString(void) { + string output = getStaffField(); + if (output[0] == ' ') { + output = ""; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getStaff -- +// + +int MuseRecord::getStaff(void) { + int output = 1; + string recordInfo = getStaffField(); + if (recordInfo[0] == ' ') { + output = 1; + } else { + output = std::strtol(recordInfo.c_str(), NULL, 36); + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::staffQ -- +// + +int MuseRecord::staffQ(void) { + int output = 0; + string recordInfo = getStaffField(); + if (recordInfo[0] == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getBeamField -- +// + +string MuseRecord::getBeamField(void) { + allowNotesOnly("getBeamField"); + if (getLength() < 26) { + return " "; + } else { + return extract(26, 31); + } +} + + + +////////////////////////////// +// +// MuseRecord::setBeamInfo -- +// + +void MuseRecord::setBeamInfo(string& strang) { + setColumns(strang, 26, 31); +} + + + +////////////////////////////// +// +// MuseRecord::beamQ -- +// + +int MuseRecord::beamQ(void) { + int output = 0; + allowNotesOnly("beamQ"); + if (getLength() < 26) { + output = 0; + } else { + for (int i=26; i<=31; i++) { + if (getColumn(i) != ' ') { + output = 1; + break; + } + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getBeam8 -- column 26 +// + +char MuseRecord::getBeam8(void) { + allowNotesOnly("getBeam8"); + return getColumn(26); +} + + + +////////////////////////////// +// +// MuseRecord::getBeam16 -- column 27 +// + +char MuseRecord::getBeam16(void) { + allowNotesOnly("getBeam16"); + return getColumn(27); +} + + + +////////////////////////////// +// +// MuseRecord::getBeam32 -- column 28 +// + +char MuseRecord::getBeam32(void) { + allowNotesOnly("getBeam32"); + return getColumn(28); +} + + + +////////////////////////////// +// +// MuseRecord::getBeam64 -- column 29 +// + +char MuseRecord::getBeam64(void) { + allowNotesOnly("getBeam64"); + return getColumn(29); +} + + + +////////////////////////////// +// +// MuseRecord::getBeam128 -- column 30 +// + +char MuseRecord::getBeam128(void) { + allowNotesOnly("getBeam128"); + return getColumn(30); +} + + + +////////////////////////////// +// +// MuseRecord::getBeam256 -- column 31 +// + +char MuseRecord::getBeam256(void) { + allowNotesOnly("getBeam256"); + return getColumn(31); +} + + + +////////////////////////////// +// +// MuseRecord::beam8Q -- +// + +int MuseRecord::beam8Q(void) { + int output = 0; + if (getBeam8() == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::beam16Q -- +// + +int MuseRecord::beam16Q(void) { + int output = 0; + if (getBeam16() == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::beam32Q -- +// + +int MuseRecord::beam32Q(void) { + int output = 0; + if (getBeam32() == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::beam64Q -- +// + +int MuseRecord::beam64Q(void) { + int output = 0; + if (getBeam64() == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::beam128Q -- +// + +int MuseRecord::beam128Q(void) { + int output = 0; + if (getBeam128() == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::beam256Q -- +// + +int MuseRecord::beam256Q(void) { + int output = 0; + if (getBeam256() == ' ') { + output = 0; + } else { + output = 1; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getKernBeamStyle -- +// + +string MuseRecord::getKernBeamStyle(void) { + string output; + string beams = getBeamField(); // 6 characters wide + for (int i=0; i<6; i++) { + switch (beams[i]) { + case '[': // start beam + output += "L"; + break; + case '=': // continue beam + // do nothing + break; + case ']': // end beam + output += "J"; + break; + case 'K': // forward hook + output += "K"; + break; + case 'k': // backward hook + output += "k"; + break; + default: + ; // do nothing + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getAdditionalNotationsField -- returns the contents +// of columns 32-43. +// + +string MuseRecord::getAdditionalNotationsField(void) { + allowNotesOnly("getAdditionalNotationsField"); + return extract(32, 43); +} + + + +////////////////////////////// +// +// MuseRecord::additionalNotationsQ -- +// + +int MuseRecord::additionalNotationsQ(void) { + int output = 0; + if (getLength() < 32) { + output = 0; + } else { + for (int i=32; i<=43; i++) { + if (getColumn(i) != ' ') { + output = 1; + break; + } + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getAddCount -- returns the number of items +// in the additional notations field +// + +int MuseRecord::getAddCount(void) { + string addString = getAdditionalNotationsField(); + string addElement; // element from the notation field + + int count = 0; + int index = 0; + while (getAddElementIndex(index, addElement, addString)) { + count++; + } + + return count; +} + + + +////////////////////////////// +// +// MuseRecord::getAddItem -- returns the specified item +// in the additional notations field +// + +string MuseRecord::getAddItem(int elementIndex) { + string output; + int count = 0; + int index = 0; + string addString = getAdditionalNotationsField(); + + while (count <= elementIndex) { + getAddElementIndex(index, output, addString); + count++; + } + + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getAddItemLevel -- returns the specified item's +// editorial level in the additional notations field +// + +int MuseRecord::getAddItemLevel(int elementIndex) { + int count = 0; + int index = 0; + string number; + string addString = getAdditionalNotationsField(); + string elementString; // element field + + while (count < elementIndex) { + getAddElementIndex(index, elementString, addString); + count++; + } + + int output = -1; +repeating: + while (addString[index] != '&' && index >= 0) { + index--; + } + if (addString[index] == '&' && !isalnum(addString[index+1])) { + index--; + goto repeating; + } else if (addString[index] == '&') { + number = addString[index+1]; + output = std::strtol(number.c_str(), NULL, 36); + } + + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getEditorialLevels -- returns a string containing the +// edit levels given in the additional notation fields +// + +string MuseRecord::getEditorialLevels(void) { + string output; + string addString = getAdditionalNotationsField(); + for (int index = 0; index < 12-1; index++) { + if (addString[index] == '&' && isalnum(addString[index+1])) { + output += addString[index+1]; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::addEditorialLevelQ -- returns true if there are any editorial +// levels present in the additional notations fields +// + +int MuseRecord::addEditorialLevelQ(void) { + string addString = getAdditionalNotationsField(); + int output = 0; + for (int i=0; i<12-1; i++) { // minus one for width 2 (&0) + if (addString[i] == '&' && isalnum(addString[i+1])) { + output = 1; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::findField -- returns true when it finds the first +// instance of the key in the additional fields record. +// + +int MuseRecord::findField(const string& key) { + int len = (int)key.size(); + string notations = getAdditionalNotationsField(); + int output = 0; + for (int i=0; i<12-len; i++) { + if (notations[i] == key[0]) { + output = 1; + for (int j=0; j stop) { + return -1; + } + + if (maxcol < stop) { + stop = maxcol; + } + + int i; + for (i=start; i<=stop; i++) { + if (m_recordString[i-1] == key) { + return i; // return the column which is offset from 1 + } + } + + return -1; +} + + + +////////////////////////////// +// +// MuseRecord::getSlurParameterRegion -- +// + +string MuseRecord::getSlurParameterRegion(void) { + return getColumns(31, 43); +} + + + +////////////////////////////// +// +// MuseRecord::getSlurStartColumn -- search column 32 to 43 for a slur +// marker. Returns the first one found from left to right. +// returns -1 if a slur character was not found. +// + +int MuseRecord::getSlurStartColumn(void) { + int start = 31; + int stop = getLength() - 1; + if (stop >= 43) { + stop = 42; + } + int i; + for (i=start; i<=stop; i++) { + switch (m_recordString[i]) { + case '(': // slur level 1 + case '[': // slur level 2 + case '{': // slur level 3 + case 'z': // slur level 4 + return i+1; // column is offset from 1 + } + } + + return -1; +} + + + +////////////////////////////// +// +// MuseRecord::getTextUnderlayField -- returns the contents +// of columns 44-80. +// + +string MuseRecord::getTextUnderlayField(void) { + allowNotesOnly("getTextUnderlayField"); + return extract(44, 80); +} + + + +////////////////////////////// +// +// MuseRecord::textUnderlayQ -- +// + +int MuseRecord::textUnderlayQ(void) { + int output = 0; + if (getLength() < 44) { + output = 0; + } else { + for (int i=44; i<=80; i++) { + if (getColumn(i) != ' ') { + output = 1; + break; + } + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getVerseCount -- +// + +int MuseRecord::getVerseCount(void) { + if (!textUnderlayQ()) { + return 0; + } + + int count = 1; + for (int i=44; i<=getLength() && i <= 80; i++) { + if (getColumn(i) == '|') { + count++; + } + } + + return count; +} + + + +////////////////////////////// +// +// MuseRecord::getVerse -- +// + +string MuseRecord::getVerse(int index) { + string output; + if (!textUnderlayQ()) { + return output; + } + int verseCount = getVerseCount(); + if (index >= verseCount) { + return output; + } + + int tindex = 44; + int c = 0; + while (c < index && tindex < 80) { + if (getColumn(tindex) == '|') { + c++; + } + tindex++; + } + + while (tindex <= 80 && getColumn(tindex) != '|') { + output += getColumn(tindex++); + } + + // remove trailing spaces + int zindex = (int)output.size() - 1; + while (output[zindex] == ' ') { + zindex--; + } + zindex++; + output.resize(zindex); + + // remove leading spaces + int spacecount = 0; + while (output[spacecount] == ' ') { + spacecount++; + } + + // problem here? + for (int rr = 0; rr <= zindex-spacecount; rr++) { + output[rr] = output[rr+spacecount]; + } + + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getKernNoteStyle -- +// default values: beams = 0, stems = 0 +// + +string MuseRecord::getKernNoteStyle(int beams, int stems) { + string output; + + // place the rhythm + stringstream tempdur; + int notetype = getGraphicNoteType(); + if (timeModificationLeftQ()) { + notetype = notetype / 4 * getTimeModificationLeft(); + if (timeModificationRightQ()) { + notetype = notetype * getTimeModificationRight(); + } else { + notetype = notetype * 2; + } + } + tempdur << notetype; + output = tempdur.str(); + + // add any dots of prolongation to the output string + output += getStringProlongation(); + + // add the pitch to the output string + string musepitch = getPitchString(); + string kernpitch = Convert::musePitchToKernPitch(musepitch); + output += kernpitch; + + // if there is a notated natural sign, then add it now: + string temp = getNotatedAccidentalField(); + if (temp == "n") { + output += "n"; + } + + // check if a grace note + if (getType() == 'g') { + output += "Q"; + } + + // if stems is true, then show stem directions + if (stems && stemDirectionQ()) { + switch (getStemDirection()) { + case 1: // 'u' = up + output += "/"; + break; + case -1: // 'd' = down + output += "\\"; + default: + ; // nothing // ' ' = no stem (if stage 2) + } + } + + // if beams is true, then show any beams + if (beams && beamQ()) { + temp = getKernBeamStyle(); + output += temp; + } + + if (isTied()) { + string tiestarts; + string tieends; + int lasttie = getLastTiedNoteLineIndex(); + int nexttie = getNextTiedNoteLineIndex(); + int state = 0; + if (lasttie >= 0) { + state = 2; + } + if (nexttie >= 0) { + state = 1; + } + switch (state) { + case 1: + tiestarts += "["; + break; + case 2: + tieends += "]"; + break; + case 3: + tieends += "_"; + break; + } + if (state) { + output = tiestarts + output + tieends; + } + } + + string slurstarts; + string slurends; + getSlurInfo(slurstarts, slurends); + if ((!slurstarts.empty()) || (!slurends.empty())) { + output = slurstarts + output + slurends; + } + + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getSlurInfo -- +// +// ( ) = regular slur +// [ ] = second levels slur, convert to &( and &) +// { } = third level slur, convert to &&( and &&) +// Z = fourth level slur (how to close?) +// + +void MuseRecord::getSlurInfo(string& slurstarts, string& slurends) { + slurstarts.clear(); + slurends.clear(); + + string data = getSlurParameterRegion(); + for (int i=0; i<(int)data.size(); i++) { + if (data[i] == '(') { + slurstarts += '('; + } else if (data[i] == ')') { + slurends += ')'; + } else if (data[i] == '[') { + slurstarts += "&{"; + } else if (data[i] == ']') { + slurends += "&)"; + } else if (data[i] == '{') { + slurstarts += "&&("; + } else if (data[i] == '}') { + slurends += "&&)"; + } + } +} + + + +////////////////////////////// +// +// MuseRecord::getKernNoteAccents -- +// + +string MuseRecord::getKernNoteAccents(void) { + string output; + int addnotecount = getAddCount(); + for (int i=0; i': output += "^"; break; // horizontal accent + case '.': output += "'"; break; // staccato + case '_': output += "~"; break; // tenuto + case '=': output += "~'"; break; // detached legato + case 'i': output += "s"; break; // spiccato + case '\'': output += ","; break; // breath mark + case 'F': output += ";"; break; // fermata up + case 'E': output += ";"; break; // fermata down + case 'S': output += ":"; break; // staccato + case 't': output += "O"; break; // trill (to generic) + case 'r': output += "S"; break; // turn + case 'k': output += "O"; break; // delayed turn (to generic) + case 'w': output += "O"; break; // shake (to generic) + case 'M': output += "O"; break; // mordent (to generic) + case 'j': output += "H"; break; // glissando (slide) + } + } + + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getKernRestStyle -- +// + +string MuseRecord::getKernRestStyle(int quarter) { + string output; + string rhythmstring; + + // place the rhythm + stringstream tempdur; + + int notetype; + if (graphicNoteTypeQ()) { + notetype = getGraphicNoteType(); + + if (timeModificationLeftQ()) { + notetype = notetype / 4 * getTimeModificationLeft(); + } + if (timeModificationRightQ()) { + notetype = notetype * getTimeModificationRight() / 2; + } + tempdur << notetype; + output = tempdur.str(); + + // add any dots of prolongation to the output string + output += getStringProlongation(); + } else { // stage 1 data: + HumNum dnotetype(getTickDuration(), quarter); + rhythmstring = Convert::durationToRecip(dnotetype); + output += rhythmstring; + } + + // add the pitch to the output string + output += "r"; + + return output; +} + + + +////////////////////////////////////////////////////////////////////////// +// +// functions that work with measure records +// + + +////////////////////////////// +// +// MuseRecord::getMeasureNumberField -- columns 9-12 +// + +string MuseRecord::getMeasureNumberField(void) { + allowMeasuresOnly("getMeasureNumberField"); + return extract(9, 12); +} + + + +////////////////////////////// +// +// MuseRecord::getMeasureTypeField -- columns 1 -- 7 +// + +string MuseRecord::getMeasureTypeField(void) { + allowMeasuresOnly("getMeasureTypeField"); + return extract(1, 7); +} + + + +////////////////////////////// +// +// MuseRecord::getMeasureNumberString -- +// + +string MuseRecord::getMeasureNumberString(void) { + string output = getMeasureNumberField(); + for (int i=3; i>=0; i--) { + if (output[i] == ' ') { + output.resize(i); + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getMeasureNumber -- +// + +int MuseRecord::getMeasureNumber(void) { + string measureInfo = getMeasureNumberField(); + return std::stoi(measureInfo); +} + + + +////////////////////////////// +// +// MuseRecord::measureNumberQ -- +// + +int MuseRecord::measureNumberQ(void) { + string temp = getMeasureNumberString(); + int i = 0; + int output = 0; + while (temp[i] != '\0') { + if (temp[i] != ' ') { + output = 1; + break; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::measureFermataQ -- returns true if there is a +// fermata above or below the measure +// + +int MuseRecord::measureFermataQ(void) { + int output = 0; + for (int i=17; i<=80 && i<= getLength(); i++) { + if (getColumn(i) == 'F' || getColumn(i) == 'E') { + output = 1; + break; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::measureFlagQ -- Returns true if there are non-space +// characters in columns 17 through 80. A more smarter way of +// doing this is checking the allocated length of the record, and +// do not search non-allocated columns for non-space characters... +// + +int MuseRecord::measureFlagQ(const string& key) { + int output = 0; + int len = (int)key.size(); + for (int i=17; i<=80-len && i& amap) { + amap.clear(); + // Should be "3" on the next line, but "1" or "2" might catch poorly formatted data. + string contents = getLine().substr(2); + if (contents.empty()) { + return; + } + int i = 0; + string key; + string value; + int state = 0; // 0 outside, 1 = in key, 2 = in value + while (i < (int)contents.size()) { + switch (state) { + case 0: // outside of key or value + if (!isspace(contents[i])) { + if (contents[i] == ':') { + // Strange: should not happen + key.clear(); + state = 2; + } else { + state = 1; + key += contents[i]; + } + } + break; + case 1: // in key + if (!isspace(contents[i])) { + if (contents[i] == ':') { + value.clear(); + state = 2; + } else { + // Add to key, such as "C2" for second staff clef. + key += contents[i]; + } + } + break; + case 2: // in value + if (key == "D") { + value += contents[i]; + } else if (isspace(contents[i])) { + // store parameter and clear variables + amap[key] = value; + state = 0; + key.clear(); + value.clear(); + } else { + value += contents[i]; + } + break; + } + i++; + } + + if ((!key.empty()) && (!value.empty())) { + amap[key] = value; + } +} + + + +////////////////////////////// +// +// MuseRecord::getAttributes -- +// + +string MuseRecord::getAttributes(void) { + string output; + switch (getType()) { + case E_muserec_musical_attributes: + break; + default: + cerr << "Error: cannot use getAttributes function on line: " + << getLine() << endl; + return ""; + } + + int ending = 0; + int tempcol; + for (int column=4; column <= getLength(); column++) { + if (getColumn(column) == ':') { + tempcol = column - 1; + while (tempcol > 0 && getColumn(tempcol) != ' ') { + tempcol--; + } + tempcol++; + while (tempcol <= column) { + output += getColumn(tempcol); + if (output.back() == 'D') { + ending = 1; + } + tempcol++; + } + } + if (ending) { + break; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::attributeQ -- +// + +int MuseRecord::attributeQ(const string& attribute) { + switch (getType()) { + case E_muserec_musical_attributes: + break; + default: + cerr << "Error: cannot use getAttributes function on line: " + << getLine() << endl; + return 0; + } + + + string attributelist = getAttributes(); + + int output = 0; + int attstrlength = (int)attributelist.size(); + int attlength = (int)attribute.size(); + + for (int i=0; i 0 && getColumn(tempcol) != ' ') { + tempcol--; + } + tempcol++; + while (tempcol <= column) { + if (getColumn(tempcol) == attribute) { + ending = 2; + } else if (getColumn(tempcol) == 'D') { + ending = 1; + } + tempcol++; + index++; + } + } + if (ending) { + break; + } + } + + if (ending == 0 || ending == 1) { + return output; + } else { + output = std::stoi(&getColumn(column+1)); + return output; + } +} + + + +////////////////////////////// +// +// MuseRecord::getAttributeField -- returns true if found attribute +// + +int MuseRecord::getAttributeField(string& value, const string& key) { + switch (getType()) { + case E_muserec_musical_attributes: + break; + default: + cerr << "Error: cannot use getAttributeInt function on line: " + << getLine() << endl; + return 0; + } + + int returnValue = 0; + int ending = 0; + int index = 0; + int tempcol; + int column; + for (column=4; column <= getLength(); column++) { + if (getColumn(column) == ':') { + tempcol = column - 1; + while (tempcol > 0 && getColumn(tempcol) != ' ') { + tempcol--; + } + tempcol++; + while (tempcol <= column) { + if (getColumn(tempcol) == key[0]) { + ending = 2; + } else if (getColumn(tempcol) == 'D') { + ending = 1; + } + tempcol++; + index++; + } + } + if (ending) { + break; + } + } + + value.clear(); + if (ending == 0 || ending == 1) { + return returnValue; + } else { + returnValue = 1; + column++; + while (getColumn(column) != ' ') { + value += getColumn(column++); + } + return returnValue; + } +} + + + +/////////////////////////////////////////////////////////////////////////// +// +// functions that work with basso continuo figuration records (f): +// + + +////////////////////////////// +// +// MuseRecord::getFigureCountField -- column 2. +// + +string MuseRecord::getFigureCountField(void) { + allowFigurationOnly("getFigureCountField"); + return extract(2, 2); +} + + + +////////////////////////////// +// +// MuseRecord::getFigurationCountString -- +// + +string MuseRecord::getFigureCountString(void) { + allowFigurationOnly("getFigureCount"); + string output = extract(2, 2); + if (output[0] == ' ') { + output = ""; + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getFigurationCount -- +// + +int MuseRecord::getFigureCount(void) { + allowFigurationOnly("getFigureCount"); + string temp = getFigureCountString(); + int output = std::strtol(temp.c_str(), NULL, 36); + return output; +} + + + +////////////////////////////// +// +// getFigurePointerField -- columns 6 -- 8. +// + +string MuseRecord::getFigurePointerField(void) { + allowFigurationOnly("getFigurePointerField"); + return extract(6, 8); +} + + +////////////////////////////// +// +// figurePointerQ -- +// + +int MuseRecord::figurePointerQ(void) { + allowFigurationOnly("figurePointerQ"); + int output = 0; + for (int i=6; i<=8; i++) { + if (getColumn(i) != ' ') { + output = 1; + break; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getFigureString -- +// + +string MuseRecord::getFigureString(void) { + string output = getFigureFields(); + for (int i=(int)output.size()-1; i>= 0; i--) { + if (isspace(output[i])) { + output.resize((int)output.size() - 1); + } else { + break; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::getFigureFields -- columns 17 -- 80 +// + +string MuseRecord::getFigureFields(void) { + allowFigurationOnly("getFigureFields"); + return extract(17, 80); +} + + +////////////////////////////// +// +// MuseRecord::figureFieldsQ -- +// + +int MuseRecord::figureFieldsQ(void) { + allowFigurationOnly("figureFieldsQ"); + int output = 0; + if (getLength() < 17) { + output = 0; + } else { + for (int i=17; i<=80; i++) { + if (getColumn(i) != ' ') { + output = 1; + break; + } + } + } + return output; +} + + + +////////////////////////////// +// +// getFigure -- +// + +string MuseRecord::getFigure(int index) { + string output; + allowFigurationOnly("getFigure"); + if (index >= getFigureCount()) { + return output; + } + string temp = getFigureString(); + if (index == 0) { + return temp; + } + HumRegex hre; + vector pieces; + hre.split(pieces, temp, " +"); + if (index < (int)pieces.size()) { + output = pieces[index]; + } + return output; +} + + + +/////////////////////////////////////////////////////////////////////////// +// +// protected functions +// + + +////////////////////////////// +// +// MuseRecord::allowFigurationOnly -- +// + +void MuseRecord::allowFigurationOnly(const string& functionName) { + switch (getType()) { + case E_muserec_figured_harmony: + break; + default: + cerr << "Error: can only access " << functionName + << " on a figuration record. Line is: " << getLine() << endl; + return; + } +} + + + +////////////////////////////// +// +// MuseRecord::allowFigurationAndNotesOnly -- +// + +void MuseRecord::allowFigurationAndNotesOnly(const string& functionName) { + switch (getType()) { + case E_muserec_figured_harmony: + case E_muserec_note_regular: + case E_muserec_note_chord: + case E_muserec_note_grace: + case E_muserec_note_cue: + break; + default: + cerr << "Error: can only access " << functionName + << " on a figuration record. Line is: " << getLine() << endl; + return; + } +} + + + +////////////////////////////// +// +// MuseRecord::allowMeasuresOnly -- +// + +void MuseRecord::allowMeasuresOnly(const string& functionName) { + switch (getType()) { + case E_muserec_measure: + break; + default: + cerr << "Error: can only access " << functionName + << " on a measure record. Line is: " << getLine() << endl; + return; + } +} + + + +////////////////////////////// +// +// MuseRecord::allowNotesOnly -- +// + +void MuseRecord::allowNotesOnly(const string& functionName) { + switch (getType()) { + case E_muserec_note_regular: + case E_muserec_note_chord: + case E_muserec_note_grace: + case E_muserec_note_cue: + break; + default: + cerr << "Error: can only access " << functionName + << " on a note record. Line is: " << getLine() << endl; + return; + } +} + + + +////////////////////////////// +// +// MuseRecord::getAddElementIndex -- get the first element pointed +// to by the specified index in the additional notations field. +// returns 0 if no element was found, or 1 if an element was found. +// + +int MuseRecord::getAddElementIndex(int& index, string& output, const string& input) { + int finished = 0; + int count = 0; + output.clear(); + + while (!finished) { + switch (input[index]) { + case '&': // editorial level marker + // there is exactly one character following an editorial + // marker. neither the '&' nor the following character + // is counted if the following character is in the set + // [0..9, A..Z, a..z] + index++; + if (isalnum(input[index])) { + index++; + } else { + // count '&' as an element + count++; + output += '&'; + } + break; + + case 'p': case 'f': // piano and forte + // any sequence of 'p' and 'f' is considered one element + count++; + output += input[index++]; + while (input[index] == 'p' || input[index] == 'f') { + output += input[index++]; + } + break; + + case 'Z': // sfz, or Zp = sfp + // elements starting with 'Z': + // Z = sfz + // Zp = sfp + count++; + output += input[index++]; + if (input[index] == 'p') { + output += input[index++]; + } + break; + + case 'm': // mezzo + // a mezzo marking MUST be followed by a 'p' or an 'f'. + count++; + output += input[index++]; + if (input[index] == 'p' || input[index] == 'f') { + output += input[index++]; + } else { + cout << "Error at \'m\' in notation field: " << input << endl; + return 0; + } + break; + + case 'S': // arpeggiation + // elements starting with 'S': + // S = arpeggiate (up) + // Sd = arpeggiate down) + count++; + output += input[index++]; + if (input[index] == 'd') { + output += input[index++]; + } + break; + + case '1': // fingering + case '2': // fingering + case '3': // fingering + case '4': // fingering + case '5': // fingering + // case ':': // finger substitution + // keep track of finger substitutions + count++; + output += input[index++]; + if (input[index] == ':') { + output += input[index++]; + output += input[index++]; + } + break; + + ////////////////////////////// + // Ornaments + // + case 't': // trill (tr.) + case 'r': // turn + case 'k': // delayed turn + case 'w': // shake + case '~': // trill wavy line extension + case 'c': // continued wavy line + case 'M': // mordent + case 'j': // slide (Schleifer) + // ornaments can be modified by accidentals: + // s = sharp + // ss = double sharp + // f = flat + // ff = double flat + // h = natural + // u = next accidental is under the ornament + // any combination of these characters following a + // ornament is considered one element. + // + count++; + index++; + while (input[index] == 's' || input[index] == 'f' || + input[index] == 'h' || input[index] == 'u') { + output += input[index++]; + } + break; + + ////////////////////////////////////////////////////////////// + // The following chars are uniquely SINGLE letter items: // + // // + // // + case '-': // tie // + case '(': // open slur #1 // + case ')': // close slur #1 // + case '[': // open slur #2 // + case ']': // close slur #2 // + case '{': // open slur #3 // + case '}': // close slur #3 // + case 'z': // open slur #4 // + case 'x': // close slur #4 // + case '*': // start written tuplet // + case '!': // end written tuplet // + case 'v': // up bow // + case 'n': // down bow // + case 'o': // harmonic // + case 'O': // open string // + case 'Q': // thumb position // + case 'A': // accent (^) // + case 'V': // accent (v) // + case '>': // accent (>) // + case '.': // staccatto // + case '_': // tenuto // + case '=': // detached tenuto // + case 'i': // spiccato // + case '\'': // breath mark // + case 'F': // upright fermata // + case 'E': // inverted fermata // + case 'R': // rfz // + case '^': // editorial accidental // + case '+': // cautionary accidental // + count++; // + output += input[index++]; // + break; // + // // + // // + ////////////////////////////////////////////////////////////// + case ' ': + // ignore blank spaces and continue counting elements + index++; + break; + default: + cout << "Error: unknown additional notation: " + << input[index] << endl; + return 0; + } + if (count != 0 || index >= 12) { + finished = 1; + } + } // end of while (!finished) loop + + return count; +} + + + +//////////////////// +// +// MuseRecord::zerase -- removes specified number of characters from +// the beginning of the string. +// + +void MuseRecord::zerase(string& inout, int num) { + int len = (int)inout.size(); + if (num >= len) { + inout = ""; + } else { + for (int i=num; i<=len; i++) { + inout[i-num] = inout[i]; + } + } + inout.resize(inout.size() - num); +} + + + + + +////////////////////////////// +// +// MuseRecordBasic::MuseRecordBasic -- +// + +MuseRecordBasic::MuseRecordBasic(void) { + m_recordString.reserve(81); + setType(E_muserec_unknown); + + m_lineindex = -1; + m_absbeat = 0; + m_lineduration = 0; + m_noteduration = 0; + m_b40pitch = -100; + m_nexttiednote = -1; + m_lasttiednote = -1; + m_roundBreve = 0; +} + + +// default value: index = -1; +MuseRecordBasic::MuseRecordBasic(const string& aLine, int index) { + m_recordString.reserve(81); + setLine(aLine); + setType(E_muserec_unknown); + m_lineindex = index; + + m_absbeat = 0; + m_lineduration = 0; + m_noteduration = 0; + m_b40pitch = -100; + m_nexttiednote = -1; + m_lasttiednote = -1; + m_roundBreve = 0; +} + + +MuseRecordBasic::MuseRecordBasic(MuseRecordBasic& aRecord) { + *this = aRecord; +} + + + +////////////////////////////// +// +// MuseRecordBasic::~MuseRecordBasic -- +// + +MuseRecordBasic::~MuseRecordBasic() { + m_recordString.resize(0); + + m_absbeat = 0; + m_lineduration = 0; + m_noteduration = 0; + m_b40pitch = -100; + m_nexttiednote = -1; + m_lasttiednote = -1; + m_roundBreve = 0; +} + + + +////////////////////////////// +// +// MuseRecordBasic::clear -- remove content of record. +// + +void MuseRecordBasic::clear(void) { + m_recordString.clear(); +} + + + +///////////////////////////// +// +// MuseRecordBasic::isEmpty -- returns true if only spaces on line, ignoring +// non-printable characters (which may no longer be necessary). +// + +int MuseRecordBasic::isEmpty(void) { + for (int i=0; i<(int)m_recordString.size(); i++) { + if (!std::isprint(m_recordString[i])) { + continue; + } + if (!std::isspace(m_recordString[i])) { + return 0; + } + } + return 1; +} + + + +////////////////////////////// +// +// MuseRecordBasic::extract -- extracts the character columns from the +// storage string. Appends a null character to the end of the +// copied string. +// + +string MuseRecordBasic::extract(int start, int end) { + string output; + int count = end - start + 1; + for (int i=0; i= 80) { + // the new limit is somewhere above 900, but limit to 180 + if (realindex < 0 || realindex >= 180) { + cerr << "Error trying to access column: " << columnNumber << endl; + cerr << "CURRENT DATA: ===============================" << endl; + cerr << (*this); + static char x = ' '; + return x; + } else if (realindex >= (int)m_recordString.size()) { + m_recordString.resize(realindex+1); + for (int i=length; i<=realindex; i++) { + m_recordString[i] = ' '; + } + } + return m_recordString[realindex]; +} + + + +////////////////////////////// +// +// MuseRecordBasic::getColumns -- +// + +string MuseRecordBasic::getColumns(int startcol, int endcol) { + string output; + int charcount = endcol - startcol + 1; + if (charcount <= 0) { + return output; + } + for (int i=startcol; i<=endcol; i++) { + output += getColumn(i); + } + return output; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setColumns -- +// + +void MuseRecordBasic::setColumns(string& data, int startcol, int endcol) { + if (startcol > endcol) { + int temp = startcol; + startcol = endcol; + endcol = temp; + } + + int dsize = (int)data.size(); + getColumn(endcol) = ' '; // allocate space if not already done + int i; + int ii; + for (i=startcol; i<=endcol; i++) { + ii = i - startcol; + if (ii < dsize) { + getColumn(i) = data[ii]; + } else { + break; + } + } + +} + + + +////////////////////////////// +// +// MuseRecordBasic::getLength -- returns the size of the +// character string being stored. A number between +// 0 and 80. +// + +int MuseRecordBasic::getLength(void) const { + return (int)m_recordString.size(); +} + + + +////////////////////////////// +// +// MuseRecordBasic::getLine -- returns a pointer to data record +// + +string MuseRecordBasic::getLine(void) { + return m_recordString; +} + + + +////////////////////////////// +// +// MuseRecordBasic::getType -- returns the type of the record. +// + +int MuseRecordBasic::getType(void) const { + return m_type; +} + + + +////////////////////////////// +// +// MuseRecordBasic::operator= +// + +MuseRecordBasic& MuseRecordBasic::operator=(MuseRecordBasic& aRecord) { + // don't copy onto self + if (&aRecord == this) { + return *this; + } + + setLine(aRecord.getLine()); + setType(aRecord.getType()); + m_lineindex = aRecord.m_lineindex; + + m_absbeat = aRecord.m_absbeat; + m_lineduration = aRecord.m_lineduration; + m_noteduration = aRecord.m_noteduration; + + m_b40pitch = aRecord.m_b40pitch; + m_nexttiednote = aRecord.m_nexttiednote; + m_lasttiednote = aRecord.m_lasttiednote; + + return *this; +} + + +MuseRecordBasic& MuseRecordBasic::operator=(MuseRecordBasic* aRecord) { + *this = *aRecord; + return *this; +} + + +MuseRecordBasic& MuseRecordBasic::operator=(const string& aLine) { + setLine(aLine); + setType(aLine[0]); + + m_lineindex = -1; + m_absbeat = 0; + m_lineduration = 0; + m_noteduration = 0; + m_b40pitch = -100; + m_nexttiednote = -1; + m_lasttiednote = -1; + + return *this; +} + + + +////////////////////////////// +// +// MuseRecordBasic::operator[] -- character array offset from 0. +// + +char& MuseRecordBasic::operator[](int index) { + return getColumn(index+1); +} + + + +////////////////////////////// +// +// MuseRecordBasic::setLine -- sets the record to a (new) string +// + +void MuseRecordBasic::setLine(const string& aLine) { + m_recordString = aLine; + // Line lengths should not exceed 80 characters according + // to MuseData standard, so maybe have a warning or error if exceeded. +} + + + +////////////////////////////// +// +// MuseRecordBasic::setType -- sets the type of the record +// + +void MuseRecordBasic::setType(int aType) { + m_type = aType; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setTypeGraceNote -- put a "g" in the first column. +// shift pitch information over if it exists? Maybe later. +// Currently will destroy any pitch information. +// + +void MuseRecordBasic::setTypeGraceNote(void) { + setType(E_muserec_note_grace); + (*this)[0] = 'g'; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setTypeGraceChordNote -- put a "g" in the first column, +// and a space in the second column. Shift pitch information over if +// it exists? Maybe later. Currently will destroy any pitch information. +// + +void MuseRecordBasic::setTypeGraceChordNote(void) { + setType(E_muserec_note_grace_chord); + (*this)[0] = 'g'; + (*this)[1] = ' '; +} + + + +////////////////////////////// +// +// MuseRecordBasic::shrink -- removes trailing spaces in a MuseData record +// + +void MuseRecordBasic::shrink(void) { + int i = (int)m_recordString.size() - 1; + while (i >= 0 && m_recordString[i] == ' ') { + m_recordString.resize((int)m_recordString.size()-1); + i--; + } +} + + + +////////////////////////////// +// +// MuseRecordBasic::insertString -- +// + +void MuseRecordBasic::insertString(int column, const string& strang) { + int len = (int)strang.size(); + if (len == 0) { + return; + } + int index = column - 1; + // make sure that record has text data up to the end of sring in + // final location by preallocating the end location of string: + (*this)[index+len-1] = ' '; + int i; + for (i=0; i 0) { + appendString(FormatData.s); + } + break; + + case 'r': + FormatData.r = va_arg(valist, int *); + rn.setValue(FormatData.r[0], FormatData.r[1]); + appendRational(rn); + break; + + default: + // don't put any character other than "i", "r" or "s" + // in the format string + break; + } + } + + va_end(valist); +} + + + +////////////////////////////// +// +// MuseRecordBasic::setString -- +// + +void MuseRecordBasic::setString(string& astring) { + m_recordString = astring; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setAbsBeat -- +// + + +void MuseRecordBasic::setAbsBeat(HumNum value) { + m_absbeat = value; +} + + +// default value botval = 1 +void MuseRecordBasic::setAbsBeat(int topval, int botval) { + m_absbeat.setValue(topval, botval); +} + + + +////////////////////////////// +// +// MuseRecordBasic::getAbsBeat -- +// + +HumNum MuseRecordBasic::getAbsBeat(void) { + return m_absbeat; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setLineDuration -- set the duration of the line +// in terms of quarter notes. +// + +void MuseRecordBasic::setLineDuration(HumNum value) { + m_lineduration = value; +} + + +// default value botval = 1 +void MuseRecordBasic::setLineDuration(int topval, int botval) { + m_lineduration.setValue(topval, botval); +} + + + +////////////////////////////// +// +// MuseRecordBasic::getLineDuration -- set the duration of the line +// in terms of quarter notes. +// + +HumNum MuseRecordBasic::getLineDuration(void) { + return m_lineduration; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setNoteDuration -- set the duration of the note +// in terms of quarter notes. If the line does not represent a note, +// then the note duration should probably be 0... +// + +void MuseRecordBasic::setNoteDuration(HumNum value) { + m_noteduration = value; +} + + +// default value botval = 1 +void MuseRecordBasic::setNoteDuration(int topval, int botval) { + m_noteduration.setValue(topval, botval); +} + + + +////////////////////////////// +// +// MuseRecordBasic::getNoteDuration -- get the duration of the note +// in terms of quarter notes. If the line does not represent a note, +// then the note duration should probably be 0... +// + +HumNum MuseRecordBasic::getNoteDuration(void) { + return m_noteduration; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setLineIndex -- +// + +void MuseRecordBasic::setLineIndex(int index) { + m_lineindex = index; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isTied -- True if the note is tied to a note +// before or after it. +// 0 = no ties +// 1 = tied to previous note +// 2 = tied to future note +// 3 = tied to both previous and future note +// + +int MuseRecordBasic::isTied(void) { + int output = 0; + if (getLastTiedNoteLineIndex() >= 0) { + output += 1; + } + + if (getNextTiedNoteLineIndex() >= 0) { + output += 2; + } + + return output; +} + + + +////////////////////////////// +// +// MuseRecordBasic::getLastTiedNoteLineIndex -- +// + + +int MuseRecordBasic::getLastTiedNoteLineIndex(void) { + return m_lasttiednote; +} + + + +////////////////////////////// +// +// MuseRecordBasic::getNextTiedNoteLineIndex -- +// + + +int MuseRecordBasic::getNextTiedNoteLineIndex(void) { + return m_nexttiednote; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setLastTiedNoteLineIndex -- +// + + +void MuseRecordBasic::setLastTiedNoteLineIndex(int index) { + m_lasttiednote = index; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setNextTiedNoteLineIndex -- +// + + +void MuseRecordBasic::setNextTiedNoteLineIndex(int index) { + m_nexttiednote = index; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setRoundedBreve -- set double whole notes rounded flag. +// + +void MuseRecordBasic::setRoundedBreve(void) { + m_roundBreve = 1; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setMarkupPitch -- set the base-40 pitch information +// in the markup area. Does not change the original pitch in +// the text line of the data. +// + +void MuseRecordBasic::setMarkupPitch(int aPitch) { + m_b40pitch = aPitch; +} + + + +////////////////////////////// +// +// MuseRecordBasic::getMarkupPitch -- get the base-40 pitch information +// in the markup area. Does not look at the original pitch in +// the text line of the data. A negative value is a rest (or invalid). +// + +int MuseRecordBasic::getMarkupPitch(void) { + return m_b40pitch; +} + + + +////////////////////////////// +// +// MuseRecordBasic::cleanLineEnding -- remove spaces at the end of the +// line; +// + +void MuseRecordBasic::cleanLineEnding(void) { + int i = (int)m_recordString.size() - 1; + // Don't remove first space on line. + while ((i > 0) && (m_recordString[i] == ' ')) { + m_recordString.resize((int)m_recordString.size() - 1); + i = (int)m_recordString.size() - 1; + } +} + + + +////////////////////////////// +// +// MuseRecordBasic::isPartName -- +// + +bool MuseRecordBasic::isPartName(void) { + return m_type == E_muserec_header_part_name; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isAttributes -- +// + +bool MuseRecordBasic::isAttributes(void) { + return m_type == E_muserec_musical_attributes; +} + + +////////////////////////////// +// +// MuseRecordBasic::isBarline -- +// + +bool MuseRecordBasic::isBarline(void) { + return m_type == E_muserec_measure; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isChordNote -- Is a regular note that is a seoncdary +// note in a chord (not the first note in the chord). +// + +bool MuseRecordBasic::isChordNote(void) { + return m_type == E_muserec_note_chord; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isGraceNote -- A grace note, either a single note or +// the first note in a gracenote chord. +// + +bool MuseRecordBasic::isGraceNote(void) { + return m_type == E_muserec_note_grace; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isCueNote -- +// + +bool MuseRecordBasic::isCueNote(void) { + return m_type == E_muserec_note_cue; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isChordNote -- +// + +bool MuseRecordBasic::isChordGraceNote(void) { + return m_type == E_muserec_note_grace_chord; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isFiguredHarmony -- +// + +bool MuseRecordBasic::isFiguredHarmony(void) { + return m_type == E_muserec_figured_harmony; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isNote -- +// + +bool MuseRecordBasic::isNote(void) { + switch (m_type) { + case E_muserec_note_regular: + case E_muserec_note_chord: + case E_muserec_note_cue: + case E_muserec_note_grace: + case E_muserec_note_grace_chord: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isRest -- +// + +bool MuseRecordBasic::isRest(void) { + switch (m_type) { + case E_muserec_rest_invisible: + case E_muserec_rest: + return true; + } + return false; +} + + + +/////////////////////////////////////////////////////////////////////////// + + +////////////////////////////// +// +// operator<< +// + +ostream& operator<<(ostream& out, MuseRecordBasic& aRecord) { + aRecord.shrink(); // have to shrink automatically because + // muse2ps program chokes on line 9 of header + // if it has more than one space on a blank line. + out << aRecord.getLine(); + return out; +} + + + class MxmlMeasure; class MxmlPart; @@ -32214,6 +39058,8 @@ Tool_autobeam::Tool_autobeam(void) { define("t|track=i:0", "process specific track number"); define("r|remove=b", "remove all beams"); define("o|overwrite=b", "over-write existing beams"); + define("l|lyric|lyrics=b", "break beam by lyric syllables"); + define("L|lyric-info=b", "return the number of breaks needed"); define("rest|include-rests=b", "include rests in beam edges"); } @@ -32263,6 +39109,12 @@ bool Tool_autobeam::run(HumdrumFile& infile) { initialize(infile); if (getBoolean("remove")) { removeBeams(infile); + } else if (getBoolean("lyrics")) { + breakBeamsByLyrics(infile); + } else if (getBoolean("lyric-info")) { + breakBeamsByLyrics(infile); + m_free_text << m_splitcount << endl; + return true; } else { addBeams(infile); } @@ -32335,6 +39187,494 @@ void Tool_autobeam::removeBeams(HumdrumFile& infile) { } +////////////////////////////// +// +// Tool_autobeam::breakBeamsByLyrics -- +// + + +void Tool_autobeam::breakBeamsByLyrics(HumdrumFile& infile) { + infile.analyzeNonNullDataTokens(); + int strands = infile.getStrandCount(); + int track; + for (int i=0; i 0) { + track = infile.getStrandStart(i)->getTrack(); + if (track != m_track) { + continue; + } + } + HTp starttok = infile.getStrandStart(i); + if (!starttok->isKern()) { + continue; + } + HTp curtok = starttok->getNextFieldToken(); + bool hastext = false; + while (curtok && !curtok->isKern()) { + if (curtok->isDataType("**text")) { + hastext = true; + break; + } + curtok = starttok->getNextFieldToken(); + } + if (!hastext) { + continue; + } + processStrandForLyrics(infile.getStrandStart(i), infile.getStrandEnd(i)); + } +} + + + +////////////////////////////// +// +// Tool_autobeam::processStrandForLyrics -- +// + +void Tool_autobeam::processStrandForLyrics(HTp stok, HTp etok) { + HTp current = stok; + current = current->getNextNNDT(); + while (current && (current != etok)) { + if (hasSyllable(current)) { + splitBeam(current, stok, etok); + } + current = current->getNextNNDT(); + } +} + + + +////////////////////////////// +// +// Tool_autobeam::splitBeam -- +// + +void Tool_autobeam::splitBeam(HTp tok, HTp stok, HTp etok) { + HumNum duration = Convert::recipToDuration(tok); + if (duration >= 1) { + // Should not be beamed + return; + } + vector seq; + getBeamedNotes(seq, tok, stok, etok); + if (seq.size() == 0) { + // note is larger than eighth note + return; + } + if (seq.size() == 1) { + // Single note with no beam possible + return; + } + splitBeam2(seq, tok); +} + + + +////////////////////////////// +// +// Tool_autobeam::splitBeam2 -- +// + +void Tool_autobeam::splitBeam2(vector& group, HTp tok) { + int target = -1; + for (int i=0; i<(int)group.size(); i++) { + if (group[i] == tok) { + target = i; + break; + } + } + + if (target <= 0) { + // problem or at start of beam, so do not modify beam. + return; + } + + m_splitcount++; + if (group.size() <= 2) { + // remove beam completely + for (int i=0; i<(int)group.size(); i++) { + string value = *group[i]; + string newvalue; + for (int j=0; j<(int)value.size(); j++) { + if ((value[j] == 'L') || (value[j] == 'J') || (toupper(value[j]) == 'K')) { + continue; + } + newvalue += value[j]; + } + group[i]->setText(newvalue); + } + return; + } + int lazyQ = isLazy(group); + if (lazyQ) { + splitBeamLazy(group, tok); + } else { + splitBeamNotLazy(group, tok); + } +} + + + +////////////////////////////// +// +// Tool_autobeam::splitBeamNotLazy -- +// + +void Tool_autobeam::splitBeamNotLazy(vector& group, HTp tok) { + int target = -1; + + for (int i=0; i<(int)group.size(); i++) { + if (tok == group[i]) { + target = i; + break; + } + } + if (target < 0) { + return; + } + + vector sbeam(group.size(), 0); + vector ebeam(group.size(), 0); + + for (int i=0; i<(int)group.size(); i++) { + string value = *group[i]; + int Lcount = 0; + int Jcount = 0; + for (int j=0; j<(int)value.size(); j++) { + if (value[j] == 'L') { + Lcount++; + } + if (value[j] == 'J') { + Jcount++; + } + } + sbeam[i] = Lcount; + ebeam[i] = Jcount; + } + + vector sum(group.size(), 0); + sum[0] = sbeam[0] - ebeam[0]; + for (int i=1; i<(int)sum.size(); i++) { + sum[i] = sum[i-1] + sbeam[i] - ebeam[i]; + } + vector rsum(group.size(), 0); + int rsize = (int)rsum.size(); + rsum[rsize - 1] = ebeam[rsize - 1] - sbeam[rsize - 1]; + for (int i=rsize-2; i>=0; i--) { + rsum[i] = rsum[i+1] - sbeam[i] + ebeam[i]; + } + + if (target == 1) { + // remove the first note from a beam group + removeBeamCharacters(group[0]); + string value = *group[1]; + for (int i=0; isetText(value); + } else if (target == (int)group.size() - 1) { + // remove the last note from the beam + removeBeamCharacters(group[(int)group.size() - 1]); + string value = *group[(int)group.size()-2]; + for (int i=0; isetText(value); + } else { + // split beam into two beams + string value = *group[target]; + for (int i=0; isetText(value); + + value = *group[target-1]; + for (int i=0; isetText(value); + } +} + + + +////////////////////////////// +// +// Tool_autobeam::splitBeamLazy -- Input will have more than two notes in the beam. +// + +void Tool_autobeam::splitBeamLazy(vector& group, HTp tok) { + + int target = -1; + for (int i=0; i<(int)group.size(); i++) { + if (tok == group[i]) { + target = i; + break; + } + } + if (target < 0) { + return; + } + if (target == 1) { + // remove the first note from a beam group + removeBeamCharacters(group[0]); + string value = *group[1]; + value += 'L'; + group[1]->setText(value); + } else if (target == (int)group.size() - 2) { + // remove the last note from the beam + removeBeamCharacters(group[(int)group.size() - 1]); + string value = *group[(int)group.size()-2]; + value += 'J'; + group[(int)group.size() - 2]->setText(value); + } else { + // split beam into two beams + string value = *group[target]; + value += 'L'; + group[target]->setText(value); + value = *group[target-1]; + value += 'J'; + group[target-1]->setText(value); + } + +} + + + +////////////////////////////// +// +// Tool_autobeam::removeBeamCharacters -- +// + +void Tool_autobeam::removeBeamCharacters(HTp token) { + string value = *token; + string newvalue; + for (int i=0; i<(int)value.size(); i++) { + if ((value[i] == 'L') || (value[i] == 'J') || (toupper(value[i]) == 'K')) { + continue; + } + newvalue += value[i]; + } + if (newvalue.size()) { + token->setText(newvalue); + } else { + token->setText("."); + } +} + + + +////////////////////////////// +// +// Tool_autobeam::isLazy -- Return if just a single beam encoded +// (even if should only have one beam). +// + +bool Tool_autobeam::isLazy(vector& group) { + int Lcount = 0; + int Jcount = 0; + int Kcount = 0; + for (int i=0; i<(int)group.size(); i++) { + string value = *group[i]; + for (int j=0; j<(int)value.size(); j++) { + if (value[j] == 'L') { + Lcount++; + } else if (value[j] == 'J') { + Jcount++; + } else if (toupper(value[j]) == 'K') { + Kcount++; + } + } + } + if ((Lcount == 1) && (Jcount == 1) && (Kcount == 0)) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// Tool_autobeam::getBeamedNotes -- +// + +void Tool_autobeam::getBeamedNotes(vector& toks, HTp tok, HTp stok, HTp etok) { + toks.resize(0); + vector backward; + vector forward; + vector seq; + HTp current = tok; + while (current && !current->isBarline()) { + if (current->isNull()) { + current = current->getNextToken(); + if (current && (current == etok)) { + break; + } + } + HumNum dur = Convert::recipToDuration(current); + if (dur >= 1) { + // No beams should be on this note + break; + } + forward.push_back(current); + current = current->getNextToken(); + if (current && (current == etok)) { + break; + } + } + + current = tok->getPreviousToken(); + while (current && !current->isBarline()) { + if (current->isNull()) { + if (current == stok) { + break; + } + current = current->getPreviousToken(); + if (current && (current == stok)) { + break; + } + continue; + } + HumNum dur = Convert::recipToDuration(current); + if (dur >= 1) { + // No beams should be on this note + break; + } + backward.push_back(current); + if (current == stok) { + break; + } + current = current->getPreviousToken(); + } + + seq.clear(); + for (int i=(int)backward.size() - 1; i>=0; i--) { + seq.push_back(backward[i]); + } + for (int i=0; i<(int)forward.size(); i++) { + seq.push_back(forward[i]); + } + + if (seq.size() < 2) { + // no beams possible + return; + } + + vector sbeam(seq.size(), 0); + vector ebeam(seq.size(), 0); + + for (int i=0; i<(int)seq.size(); i++) { + string value = *seq[i]; + int Lcount = 0; + int Jcount = 0; + for (int j=0; j<(int)value.size(); j++) { + if (value[j] == 'L') { + Lcount++; + } + if (value[j] == 'J') { + Jcount++; + } + } + sbeam[i] = Lcount; + ebeam[i] = Jcount; + } + + vector sum(seq.size(), 0); + sum[0] = sbeam[0] - ebeam[0]; + for (int i=1; i<(int)sum.size(); i++) { + sum[i] = sum[i-1] + sbeam[i] - ebeam[i]; + } + + int target = -1; + for (int i=0; i<(int)sum.size(); i++) { + if (seq[i] == tok) { + target = i; + break; + } + } + + + if ((target == 0) && (sum[0] == 0)) { + // no beam on note + return; + } + + int sindex = -1; + int eindex = -1; + if (sum[target] == 0) { + if (sum[target - 1] == 0) { + // no beam on note + return; + } else { + // There is a beam on target note and currently at + // the end of the beam, so find the start: + eindex = target; + sindex = target; + for (int i=target-1; i>=0; i--) { + if (sum[i] != 0) { + sindex = i; + } else { + break; + } + } + } + } else { + // In the middle of a beam so expand outwards to find + // the start and end of the beam group. + for (int i=target; i>=0; i--) { + if (sum[i] != 0) { + sindex = i; + } else { + break; + } + } + for (int i=target; i<(int)sum.size(); i++) { + if (sum[i] == 0) { + eindex = i; + break; + } + } + } + + if (eindex < 0) { + // in case where beam is not closed properly, assume last note is beam end. + eindex = (int)sum.size() - 1; + } + if (sindex < 0) { + // in case where beam is not opened properly, assume last note is beam end. + sindex = 0; + } + + toks.clear(); + for (int i=sindex; i<=eindex; i++) { + toks.push_back(seq[i]); + } +} + + + +////////////////////////////// +// +// Tool_autobeam::hasSyllable -- Only checking the first verse. +// + +bool Tool_autobeam::hasSyllable(HTp token) { + HTp current = token->getNextFieldToken(); + while (current && !current->isKern()) { + if (current->isDataType("**text")) { + if (current->isNull()) { + return false; + } else { + return true; + } + } + current = token->getNextFieldToken(); + } + return false; +} + + ////////////////////////////// // @@ -32369,6 +39709,7 @@ void Tool_autobeam::addBeams(HumdrumFile& infile) { // void Tool_autobeam::initialize(HumdrumFile& infile) { + m_splitcount = 0; m_kernspines = infile.getKernSpineStartList(); vector& ks = m_kernspines; m_timesigs.resize(infile.getTrackCount() + 1); @@ -32465,7 +39806,7 @@ void Tool_autobeam::processMeasure(vector& measure) { beatpos.push_back(measure[i]->getDurationFromBarline() / beatdur); } - // Now identify notes which should be beamed together + // Now identify notes that should be beamed together // (using lazy beaming for now). HumNum eighthnote(1, 2); int beat1; @@ -52179,6 +59520,489 @@ void Tool_msearch::fillMusicQuery(vector& query, +////////////////////////////// +// +// Tool_musedata2hum::Tool_musedata2hum -- +// + +Tool_musedata2hum::Tool_musedata2hum(void) { + // Options& options = m_options; + // options.define("k|kern=b","display corresponding **kern data"); + + define("r|recip=b", "output **recip spine"); + define("s|stems=b", "include stems in output"); +} + + + +////////////////////////////// +// +// initialize -- +// + +void Tool_musedata2hum::initialize(void) { + m_stemsQ = getBoolean("stems"); + m_recipQ = getBoolean("recip"); +} + + + +////////////////////////////// +// +// Tool_musedata2hum::setOptions -- +// + +void Tool_musedata2hum::setOptions(int argc, char** argv) { + m_options.process(argc, argv); +} + + +void Tool_musedata2hum::setOptions(const vector& argvlist) { + m_options.process(argvlist); +} + + + +////////////////////////////// +// +// Tool_musedata2hum::getOptionDefinitions -- Used to avoid +// duplicating the definitions in the test main() function. +// + +Options Tool_musedata2hum::getOptionDefinitions(void) { + return m_options; +} + + + +////////////////////////////// +// +// Tool_musedata2hum::convert -- Convert a MusicXML file into +// Humdrum content. +// + +bool Tool_musedata2hum::convertFile(ostream& out, const string& filename) { + MuseDataSet mds; + int result = mds.readFile(filename); + if (!result) { + cerr << "\nMuseData file [" << filename << "] has syntax errors\n"; + cerr << "Error description:\t" << mds.getError() << "\n"; + exit(1); + } + + return convert(out, mds); +} + + +bool Tool_musedata2hum::convert(ostream& out, istream& input) { + MuseDataSet mds; + mds.read(input); + return convert(out, mds); +} + + +bool Tool_musedata2hum::convertString(ostream& out, const string& input) { + MuseDataSet mds; + int result = mds.readString(input); + if (!result) { + cout << "\nXML content has syntax errors\n"; + cout << "Error description:\t" << mds.getError() << "\n"; + exit(1); + } + return convert(out, mds); +} + + + +bool Tool_musedata2hum::convert(ostream& out, MuseDataSet& mds) { + initialize(); + + HumGrid outdata; + int partcount = mds.getPartCount(); + bool status = true; + for (int i=0; isetBarStyle(MeasureStyle::Plain); + int i = startindex; + for (i=startindex; i= part.getLineCount()) { + endtime = part[i-1].getAbsBeat(); + } else { + endtime = part[i].getAbsBeat(); + } + + // set duration of measures (so it will be printed in conversion to Humdrum): + gm->setDuration(endtime - starttime); + gm->setTimestamp(starttime); + gm->setTimeSigDur(m_timesigdur); + + if ((i < part.getLineCount()) && part[i].isBarline()) { + setMeasureStyle(outdata.back(), part[i]); + } + + return i; +} + + + +////////////////////////////// +// +// Tool_musedata2hum::setMeasureStyle -- +// + +void Tool_musedata2hum::setMeasureStyle(GridMeasure* gm, MuseRecord& mr) { + string line = mr.getLine(); + if (line.compare(0, 7, "mheavy2") == 0) { + gm->setStyle(MeasureStyle::Final); + } +} + + +////////////////////////////// +// +// Tool_musedata2hum::convertLine -- +// + +void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { + int tpq = m_tpq; + int part = m_part; + int staff = 0; + int maxstaff = m_maxstaff; + int voice = 0; + HumNum timestamp = mr.getAbsBeat(); + string tok; + + if (mr.isBarline()) { + tok = mr.getKernMeasureStyle(); + } else if (mr.isAttributes()) { + map attributes; + mr.getAttributeMap(attributes); + + string mtempo = attributes["D"]; + if (!mtempo.empty()) { + if (timestamp != 0) { + string value = "!!!OMD: " + mtempo; + gm->addGlobalComment(value, timestamp); + } else { + setInitialOmd(mtempo); + } + } + + string mclef = attributes["C"]; + if (!mclef.empty()) { + string kclef = Convert::museClefToKernClef(mclef); + gm->addClefToken(kclef, timestamp, part, staff, voice, maxstaff); + } + + string mkeysig = attributes["K"]; + if (!mkeysig.empty()) { + string kkeysig = Convert::museKeySigToKernKeySig(mkeysig); + gm->addKeySigToken(kkeysig, timestamp, part, staff, voice, maxstaff); + } + + string mtimesig = attributes["T"]; + if (!mtimesig.empty()) { + string ktimesig = Convert::museTimeSigToKernTimeSig(mtimesig); + gm->addTimeSigToken(ktimesig, timestamp, part, staff, voice, maxstaff); + setTimeSigDurInfo(ktimesig); + } + + } else if (mr.isNote()) { + tok = mr.getKernNoteStyle(1, 1); + GridSlice* slice; + slice = gm->addDataToken(tok, timestamp, part, staff, voice, maxstaff); + addNoteDynamics(slice, part, mr); + } else if (mr.isFiguredHarmony()) { + addFiguredHarmony(mr, gm, timestamp, part, maxstaff); + } else if (mr.isChordNote()) { + cerr << "PROCESS CHORD NOTE HERE: " << mr << endl; + } else if (mr.isCueNote()) { + cerr << "PROCESS CUE NOTE HERE: " << mr << endl; + } else if (mr.isGraceNote()) { + cerr << "PROCESS GRACE NOTE HERE: " << mr << endl; + } else if (mr.isChordGraceNote()) { + cerr << "PROCESS GRACE CHORD NOTE HERE: " << mr << endl; + } else if (mr.isRest()) { + tok = mr.getKernRestStyle(tpq); + gm->addDataToken(tok, timestamp, part, staff, voice, maxstaff); + } +} + + + +////////////////////////////// +// +// Tool_musedata2hum::addFiguredHarmony -- +// + +void Tool_musedata2hum::addFiguredHarmony(MuseRecord& mr, GridMeasure* gm, + HumNum timestamp, int part, int maxstaff) { + string fh = mr.getFigureString(); + fh = Convert::museFiguredBassToKernFiguredBass(fh); + if (fh.find(":") == string::npos) { + HTp fhtok = new HumdrumToken(fh); + m_lastfigure = fhtok; + gm->addFiguredBass(fhtok, timestamp, part, maxstaff); + return; + } + + if (!m_lastfigure) { + HTp fhtok = new HumdrumToken(fh); + m_lastfigure = fhtok; + gm->addFiguredBass(fhtok, timestamp, part, maxstaff); + return; + } + + // For now assuming only one line extension needs to be transferred. + + // Has a line extension that should be moved to the previous token: + int position = 0; + int colpos = -1; + if (fh[0] == ':') { + colpos = 0; + } else { + for (int i=1; i<(int)fh.size(); i++) { + if (isspace(fh[i]) && !isspace(fh[i-1])) { + position++; + } + if (fh[i] == ':') { + colpos = i; + break; + } + } + } + + string lastfh = m_lastfigure->getText(); + vector pieces; + int state = 0; + for (int i=0; i<(int)lastfh.size(); i++) { + if (state) { + if (isspace(lastfh[i])) { + state = 0; + } else { + pieces.back() += lastfh[i]; + } + } else { + if (isspace(lastfh[i])) { + // do nothing + } else { + pieces.resize(pieces.size()+1); + pieces.back() += lastfh[i]; + state = 1; + } + } + } + + if (pieces.empty() || (position >= (int)pieces.size())) { + HTp fhtok = new HumdrumToken(fh); + m_lastfigure = fhtok; + gm->addFiguredBass(fhtok, timestamp, part, maxstaff); + return; + } + + pieces[position] += ':'; + string oldtok; + for (int i=0; i<(int)pieces.size(); i++) { + oldtok += pieces[i]; + if (i<(int)pieces.size() - 1) { + oldtok += ' '; + } + } + + m_lastfigure->setText(oldtok); + + fh.erase(colpos, 1); + HTp newtok = new HumdrumToken(fh); + m_lastfigure = newtok; + gm->addFiguredBass(newtok, timestamp, part, maxstaff); +} + + + +////////////////////////////// +// +// Tool_musedata2hum::addNoteDynamics -- +// + +void Tool_musedata2hum::addNoteDynamics(GridSlice* slice, int part, + MuseRecord& mr) { + string notations = mr.getAdditionalNotationsField(); + vector dynamics(1); + int state = 0; + for (int i=0; i<(int)notations.size(); i++) { + if (state) { + switch (notations[i]) { + case 'p': + case 'm': + case 'f': + dynamics.back() += notations[i]; + break; + default: + state = 0; + dynamics.resize(dynamics.size() + 1); + } + } else { + switch (notations[i]) { + case 'p': + case 'm': + case 'f': + state = 1; + dynamics.back() = notations[i]; + break; + } + } + } + + for (int i=0; i<(int)dynamics.size(); i++) { + if (dynamics[i].empty()) { + continue; + } + slice->at(part)->setDynamics(dynamics[i]); + break; // only one dynamic allowed (at least for now) + } + + HumGrid* grid = slice->getOwner(); + if (grid) { + grid->setDynamicsPresent(part); + } +} + + + +////////////////////////////// +// +// Tool_musedata2hum::setTimeSigDurInfo -- +// + +void Tool_musedata2hum::setTimeSigDurInfo(const string& ktimesig) { + HumRegex hre; + if (hre.search(ktimesig, "(\\d+)/(\\d+)")) { + int top = hre.getMatchInt(1); + int bot = hre.getMatchInt(2); + HumNum value = 1; + value /= bot; + value *= top; + value.invert(); + value *= 4; // convert from whole notes to quarter notes + m_timesigdur = value; + } +} + + + +////////////////////////////// +// +// Tool_musedata2hum::getMeasure -- Could be imporoved by NlogN search. +// + +GridMeasure* Tool_musedata2hum::getMeasure(HumGrid& outdata, HumNum starttime) { + for (int i=0; i<(int)outdata.size(); i++) { + if (outdata[i]->getTimestamp() == starttime) { + return outdata[i]; + } + } + // Did not find measure in data, so append to end of list. + // Assuming that unknown measures are at a later timestamp + // than those in current list, but should fix this later perhaps. + GridMeasure* gm = new GridMeasure(&outdata); + outdata.push_back(gm); + return gm; +} + + + +////////////////////////////// +// +// Tool_musedata2hum::setInitialOmd -- +// + +void Tool_musedata2hum::setInitialOmd(const string& omd) { + m_omd = omd; +} + + + + + ////////////////////////////// // // Tool_musicxml2hum::Tool_musicxml2hum -- From 33affbde36821432d41181f1cc644603591ab38e Mon Sep 17 00:00:00 2001 From: Craig Sapp Date: Sun, 29 Sep 2019 11:44:36 -0700 Subject: [PATCH 002/140] Add MuseData importer. --- include/vrv/toolkit.h | 1 + src/toolkit.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/include/vrv/toolkit.h b/include/vrv/toolkit.h index 72ad4952212..a619eeb3938 100644 --- a/include/vrv/toolkit.h +++ b/include/vrv/toolkit.h @@ -32,6 +32,7 @@ enum FileFormat { MUSICXML, MUSICXMLHUM, MEIHUM, + MUSEDATAHUM, ESAC, MIDI, TIMEMAP diff --git a/src/toolkit.cpp b/src/toolkit.cpp index 31d9c55243a..68d0eb752bb 100644 --- a/src/toolkit.cpp +++ b/src/toolkit.cpp @@ -149,6 +149,15 @@ bool Toolkit::SetFormat(std::string const &informat) else if ((informat == "musicxml") || (informat == "xml")) { m_format = MUSICXML; } + else if (informat == "md") { + m_format = MUSEDATAHUM; + } + else if (informat == "musedata") { + m_format = MUSEDATAHUM; + } + else if (informat == "musedata-hum") { + m_format = MUSEDATAHUM; + } else if (informat == "musicxml-hum") { m_format = MUSICXMLHUM; } @@ -183,6 +192,13 @@ FileFormat Toolkit::IdentifyInputFormat(const std::string &data) if (data[0] == 0) { return UNKNOWN; } + std::string excerpt = data.substr(0, 2000); + std::string::size_type found = excerpt.find("Group memberships:"); + if (found != std::string::npos) { + // MuseData may contain '@' as first character, so needs + // to be checked before PAE identification. + return MUSEDATAHUM; + } if (data[0] == '@') { return PAE; } @@ -468,6 +484,34 @@ bool Toolkit::LoadData(const std::string &data) input = new MeiInput(&m_doc, ""); } + else if (inputFormat == MUSEDATAHUM) { + // This is the indirect converter from MuseData to MEI using iohumdrum: + hum::Tool_musedata2hum converter; + stringstream conversion; + bool status = converter.convertString(conversion, data); + if (!status) { + LogError("Error converting MuseData data"); + return false; + } + std::string buffer = conversion.str(); + SetHumdrumBuffer(buffer.c_str()); + + // Now convert Humdrum into MEI: + Doc tempdoc; + tempdoc.SetOptions(m_doc.GetOptions()); + FileInputStream *tempinput = new HumdrumInput(&tempdoc, ""); + if (!tempinput->ImportString(conversion.str())) { + LogError("Error importing Humdrum data (4)"); + delete tempinput; + return false; + } + MeiOutput meioutput(&tempdoc, ""); + meioutput.SetScoreBasedMEI(true); + newData = meioutput.GetOutput(); + delete tempinput; + input = new MeiInput(&m_doc, ""); + } + else if (inputFormat == ESAC) { // This is the indirect converter from EsAC to MEI using iohumdrum: hum::Tool_esac2hum converter; @@ -485,7 +529,7 @@ bool Toolkit::LoadData(const std::string &data) tempdoc.SetOptions(m_doc.GetOptions()); FileInputStream *tempinput = new HumdrumInput(&tempdoc, ""); if (!tempinput->ImportString(conversion.str())) { - LogError("Error importing Humdrum data (4)"); + LogError("Error importing Humdrum data (5)"); delete tempinput; return false; } From e98b17545e4cb238399c3cb0b653964bf4ff9f37 Mon Sep 17 00:00:00 2001 From: Craig Sapp Date: Sun, 6 Oct 2019 22:31:26 -0700 Subject: [PATCH 003/140] Humlib update (for musedata2hum converter) --- include/hum/humlib.h | 123 ++- src/hum/humlib.cpp | 2049 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 2020 insertions(+), 152 deletions(-) diff --git a/include/hum/humlib.h b/include/hum/humlib.h index 6f86728ed77..d41e2e58782 100644 --- a/include/hum/humlib.h +++ b/include/hum/humlib.h @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Sun Sep 29 08:05:25 PDT 2019 +// Last Modified: Sat Oct 5 22:33:07 PDT 2019 // Filename: humlib.h // URL: https://github.com/craigsapp/humlib/blob/master/include/humlib.h // Syntax: C++11 @@ -43,8 +43,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include +#include #include #include #include @@ -2493,14 +2495,21 @@ class HumdrumFile : public HUMDRUMFILE_PARENT { #define E_muserec_comment_toggle '&' #define E_muserec_comment_line '@' #define E_muserec_musical_directions '*' +#define E_muserec_copyright '1' // reserved for copyright notice #define E_muserec_header_1 '1' // reserved for copyright notice #define E_muserec_header_2 '2' // reserved for identification +#define E_muserec_id '2' // reserved for identification #define E_muserec_header_3 '3' // reserved #define E_muserec_header_4 '4' // +#define E_muserec_encoder '4' // #define E_muserec_header_5 '5' // WK#: MV#: +#define E_muserec_work_info '5' // WK#: MV#: #define E_muserec_header_6 '6' // +#define E_muserec_source '6' // #define E_muserec_header_7 '7' // +#define E_muserec_work_title '7' // #define E_muserec_header_8 '8' // +#define E_muserec_movement_title '8' // #define E_muserec_header_9 '9' // #define E_muserec_header_part_name '9' // #define E_muserec_header_10 '0' // misc designations @@ -2508,6 +2517,7 @@ class HumdrumFile : public HUMDRUMFILE_PARENT { #define E_muserec_group_memberships 'A' // group memberships // multiple musered_head_12 lines can occur: #define E_muserec_header_12 'B' // : part of +#define E_muserec_group 'B' // : part of #define E_muserec_unknown 'U' // unknown record type #define E_muserec_empty 'E' // nothing on line and not header // or multi-line comment @@ -2542,6 +2552,13 @@ class MuseRecordBasic { int getType (void) const; void setTypeGraceNote (void); void setTypeGraceChordNote(void); + void setHeaderState (int state); + + // Humdrum conversion variables + void setToken (HTp token); + HTp getToken (void); + void setVoice (GridVoice* voice); + GridVoice* getVoice (void); MuseRecordBasic& operator= (MuseRecordBasic& aRecord); MuseRecordBasic& operator= (MuseRecordBasic* aRecord); @@ -2576,6 +2593,8 @@ class MuseRecordBasic { void setMarkupPitch (int aPitch); int getMarkupPitch (void); + void setLayer (int layer); + int getLayer (void); // tied note functions: int isTied (void); @@ -2584,17 +2603,41 @@ class MuseRecordBasic { void setLastTiedNoteLineIndex(int index); void setNextTiedNoteLineIndex(int index); + std::string getLayoutVis (void); + // boolean type fuctions: + bool isAnyNote (void); + bool isAnyNoteOrRest (void); bool isAttributes (void); + bool isBackup (void); bool isBarline (void); + bool isBodyRecord (void); bool isChordGraceNote (void); bool isChordNote (void); + bool isAnyComment (void); + bool isLineComment (void); + bool isBlockComment (void); + bool isCopyright (void); bool isCueNote (void); + bool isEncoder (void); bool isFiguredHarmony (void); bool isGraceNote (void); - bool isNote (void); + bool isGroup (void); + bool isGroupMembership (void); + bool isHeaderRecord (void); + bool isId (void); + bool isMovementTitle (void); bool isPartName (void); - bool isRest (void); + bool isRegularNote (void); + bool isAnyRest (void); + bool isRegularRest (void); + bool isInvisibleRest (void); + bool isSource (void); + bool isWorkInfo (void); + bool isWorkTitle (void); + bool hasTpq (void); + int getTpq (void); + void setTpq (int value); protected: std::string m_recordString; // actual characters on line @@ -2612,10 +2655,19 @@ class MuseRecordBasic { int m_lasttiednote; // line number of previous note tied // to this one (-1 if no tied note) int m_roundBreve; + int m_header = -1; // -1 = undefined, 0 = no, 1 = yes + int m_layer = 0; // voice/layer (track info but may be analyzed) + int m_tpq = 0; // ticks-per-quarter for durations + std::string m_graphicrecip; // graphical duration of note/rest + GridVoice* m_voice = NULL; // conversion structure that token is stored in. + + public: + static std::string trimSpaces (std::string input); }; std::ostream& operator<<(std::ostream& out, MuseRecordBasic& aRecord); +std::ostream& operator<<(std::ostream& out, MuseRecordBasic* aRecord); @@ -2670,6 +2722,7 @@ class MuseRecord : public MuseRecordBasic { void setTicks (int value); void setBack (int value); void setDots (int value); + int getDotCount (void); void setNoteheadShape (HumNum duration); void setNoteheadShapeMensural (HumNum duration); void setNoteheadMaxima (void); @@ -2729,6 +2782,7 @@ class MuseRecord : public MuseRecordBasic { int getGraphicNoteType (void); int getGraphicNoteTypeSize (void); int graphicNoteTypeQ (void); + std::string getGraphicRecip (void); // column 18: dots of prolongation std::string getProlongationField (void); @@ -2805,7 +2859,8 @@ class MuseRecord : public MuseRecordBasic { // int getNotationLevel int getSlurStartColumn (void); std::string getSlurParameterRegion (void); - void getSlurInfo (string& slurstarts, string& slurends); + void getSlurInfo (std::string& slurstarts, + std::string& slurends); // columns 44 -- 80: text underlay std::string getTextUnderlayField (void); @@ -2864,6 +2919,7 @@ class MuseRecord : public MuseRecordBasic { int measureNumberQ (void); // columns 17 -- 80: measure flags + std::string getMeasureFlagsString (void); int measureFermataQ (void); int measureFlagQ (const std::string& key); void addMeasureFlag (const std::string& strang); @@ -2889,6 +2945,7 @@ class MuseRecord : public MuseRecordBasic { protected: void allowNotesOnly (const std::string& functioName); + void allowNotesAndRestsOnly (const std::string& functionName); void allowMeasuresOnly (const std::string& functioName); void allowFigurationOnly (const std::string& functioName); void allowFigurationAndNotesOnly (const std::string& functioName); @@ -2953,10 +3010,40 @@ class MuseData { int read (std::istream& input); int readString (const std::string& filename); int readFile (const std::string& filename); + void analyzeLayers (void); + int analyzeLayersInMeasure(int startindex); // aliases for access to MuseRecord objects based on line indexes: std::string getLine (int index); + + bool isCopyright (int index); + bool isEncoder (int index); + bool isId (int index); + bool isMovementTitle (int index); + bool isAnyNote (int index); + bool isRegularNote (int index); bool isPartName (int index); + bool isSource (int index); + bool isWorkInfo (int index); + bool isWorkTitle (int index); + bool isHeaderRecord (int index); + bool isBodyRecord (int index); + + // header information + std::string getComposer (void); + std::string getComposerDate (void); + std::string getCopyright (void); + std::string getEncoder (void); + std::string getEncoderDate (void); + std::string getEncoderName (void); + std::string getId (void); + std::string getMovementTitle (void); + std::string getSource (void); + std::string getWorkInfo (void); + std::string getWorkTitle (void); + std::string getOpus (void); + std::string getNumber (void); + std::string getMovementNumber (void); // additional mark-up analysis functions for post-processing: void doAnalyses (void); @@ -2964,6 +3051,7 @@ class MuseData { void analyzeRhythm (void); void analyzeTies (void); void analyzePitch (void); + void analyzeTpq (void); // line-based (file-order indexing) accessor functions: MuseRecord& operator[] (int lindex); @@ -2991,6 +3079,7 @@ class MuseData { std::string getError (void); bool hasError (void); + private: std::vector m_data; std::vector m_sequence; @@ -3011,6 +3100,8 @@ class MuseData { MuseRecord* arecord); int getPartNameIndex (void); std::string getPartName (int index); + std::string trimSpaces (std::string); + void assignHeaderBodyState(void); }; @@ -3626,6 +3717,8 @@ class GridMeasure : public std::list { int part, int staff, int voice, int maxstaff); GridSlice* addTimeSigToken(const std::string& tok, HumNum timestamp, int part, int staff, int voice, int maxstaff); + GridSlice* addMeterSigToken(const std::string& tok, HumNum timestamp, + int part, int staff, int voice, int maxstaff); GridSlice* addKeySigToken (const std::string& tok, HumNum timestamp, int part, int staff, int voice, int maxstaff); GridSlice* addClefToken (const std::string& tok, HumNum timestamp, @@ -3640,6 +3733,8 @@ class GridMeasure : public std::list { int maxstaff); GridSlice* addDataToken (const std::string& tok, HumNum timestamp, int part, int staff, int voice, int maxstaff); + GridSlice* addDataSubtoken(const std::string& tok, HumNum timestamp, + int part, int staff, int voice); GridSlice* addGraceToken (const std::string& tok, HumNum timestamp, int part, int staff, int voice, int maxstaff, int gracenumber); @@ -3663,6 +3758,8 @@ class GridMeasure : public std::list { void setFinalBarlineStyle(void) { setStyle(MeasureStyle::Final); } void setRepeatEndStyle(void) { setStyle(MeasureStyle::RepeatBackward); } void setRepeatBackwardStyle(void) { setStyle(MeasureStyle::RepeatBackward); } + void setMeasureNumber(int value); + int getMeasureNumber(void); bool isDouble(void) {return m_style == MeasureStyle::Double;} @@ -3694,6 +3791,7 @@ class GridMeasure : public std::list { HumNum m_timestamp; HumNum m_timesigdur; MeasureStyle m_style; + int m_barnum = -1; }; std::ostream& operator<<(std::ostream& output, GridMeasure& measure); @@ -3853,6 +3951,11 @@ class HumGrid : public std::vector { void deleteMeasure (int index); void setPartName (int index, const string& name); std::string getPartName (int index); + void addInvisibleRestsInFirstTrack(void); + void setPartStaffDimensions (std::vector>& nextevent, + GridSlice* startslice); + void addInvisibleRest (std::vector>& nextevent, + int index, int p, int s); protected: void calculateGridDurations (void); @@ -3868,11 +3971,11 @@ class HumGrid : public std::vector { void addNullTokensForClefChanges (void); void addNullTokensForLayoutComments(void); - void FillInNullTokensForGraceNotes(GridSlice* graceslice, GridSlice* lastnote, + void fillInNullTokensForGraceNotes(GridSlice* graceslice, GridSlice* lastnote, GridSlice* nextnote); - void FillInNullTokensForLayoutComments(GridSlice* layoutslice, GridSlice* lastnote, + void fillInNullTokensForLayoutComments(GridSlice* layoutslice, GridSlice* lastnote, GridSlice* nextnote); - void FillInNullTokensForClefChanges (GridSlice* clefslice, + void fillInNullTokensForClefChanges (GridSlice* clefslice, GridSlice* lastnote, GridSlice* nextnote); void adjustClefChanges (void); bool buildSingleList (void); @@ -6239,15 +6342,17 @@ class Tool_musedata2hum : public HumTool { void initialize (void); void convertLine (GridMeasure* gm, MuseRecord& mr); bool convertPart (HumGrid& outdata, MuseDataSet& mds, int index); - int convertMeasure (HumGrid& outdata, MuseData& part, int startindex); + int convertMeasure (HumGrid& outdata, MuseData& part, int partindex, int startindex); GridMeasure* getMeasure (HumGrid& outdata, HumNum starttime); void setTimeSigDurInfo (const std::string& mtimesig); void setMeasureStyle (GridMeasure* gm, MuseRecord& mr); + void setMeasureNumber (GridMeasure* gm, MuseRecord& mr); void storePartName (HumGrid& outdata, MuseData& part, int index); void addNoteDynamics (GridSlice* slice, int part, MuseRecord& mr); void addFiguredHarmony (MuseRecord& mr, GridMeasure* gm, HumNum timestamp, int part, int maxstaff); + std::string trimSpaces (std::string input); private: // options: @@ -6262,6 +6367,8 @@ class Tool_musedata2hum : public HumTool { int m_maxstaff = 0; // total number of staves (parts) HumNum m_timesigdur = 4; // duration of current time signature in quarter notes HTp m_lastfigure = NULL; // last figured bass token + int m_lastbarnum = -1; // barnumber carried over from previous bar + HTp m_lastnote = NULL; // for dealing with chords. }; diff --git a/src/hum/humlib.cpp b/src/hum/humlib.cpp index 0611e073c62..37590276264 100644 --- a/src/hum/humlib.cpp +++ b/src/hum/humlib.cpp @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Sun Sep 29 08:05:25 PDT 2019 +// Last Modified: Sat Oct 5 22:33:07 PDT 2019 // Filename: /include/humlib.cpp // URL: https://github.com/craigsapp/humlib/blob/master/src/humlib.cpp // Syntax: C++11 @@ -1235,10 +1235,11 @@ int Convert::museToBase40(const string& pitchString) { if (i <= 0) { cerr << "Error: could not find octave in string: " << pitchString << endl; - exit(1); - } - - octave = temp[i] - '0'; + cerr << "Assigning to octave 4" << endl; + octave = 4; + } else { + octave = temp[i] - '0'; + } temp.resize(i); for (int i=0; i<(int)temp.size(); i++) { @@ -1524,6 +1525,27 @@ string Convert::museFiguredBassToKernFiguredBass(const string& mfb) { } else if ((mfb[i] == '&') && (i < (int)mfb.size()-1) && (mfb[i+1] == '0')) { output += ":"; i++; + } else if ((mfb[i] == '/')) { // assuming means flat + output += "-/"; + } else if ((mfb[i] == '\\')) { // assuming means sharp + output += "#/"; + } else if ((mfb[i] == '+')) { // assuming means sharp + output += "#|"; + } else if (isdigit(mfb[i]) && (i < (int)mfb.size() - 1) && (mfb[i+1] == '#')) { + output += mfb[i]; + output += mfb[i+1]; + output += 'r'; + i++; + } else if (isdigit(mfb[i]) && (i < (int)mfb.size() - 1) && (mfb[i+1] == 'f')) { + output += mfb[i]; + output += mfb[i+1]; + output += 'r'; + i++; + } else if (isdigit(mfb[i]) && (i < (int)mfb.size() - 1) && (mfb[i+1] == 'n')) { + output += mfb[i]; + output += mfb[i+1]; + output += 'r'; + i++; } else { output += mfb[i]; } @@ -3798,6 +3820,63 @@ GridSlice* GridMeasure::addTimeSigToken(const string& tok, HumNum timestamp, +////////////////////////////// +// +// GridMeasure::addMeterSigToken -- Add a meter signature token in the data slice at +// the given timestamp (or create a new timesig slice at that timestamp), placing the +// token at the specified part, staff, and voice index. +// +// To do: +// The meter signtature should occur immediately after a time signature line. +// + +GridSlice* GridMeasure::addMeterSigToken(const string& tok, HumNum timestamp, + int part, int staff, int voice, int maxstaff) { + GridSlice* gs = NULL; + if (this->empty() || (this->back()->getTimestamp() < timestamp)) { + // add a new GridSlice to an empty list or at end of list if timestamp + // is after last entry in list. + gs = new GridSlice(this, timestamp, SliceType::MeterSigs, maxstaff); + gs->addToken(tok, part, staff, voice); + this->push_back(gs); + } else { + // search for existing line with same timestamp and the same slice type + GridSlice* target = NULL; + auto iterator = this->begin(); + while (iterator != this->end()) { + if (((*iterator)->getTimestamp() == timestamp) && (*iterator)->isMeterSigSlice()) { + target = *iterator; + target->addToken(tok, part, staff, voice); + break; + } else if (((*iterator)->getTimestamp() == timestamp) && (*iterator)->isDataSlice()) { + // found the correct timestamp, but no clef slice at the timestamp + // so add the clef slice before the data slice (eventually keeping + // track of the order in which the other non-data slices should be placed). + gs = new GridSlice(this, timestamp, SliceType::MeterSigs, maxstaff); + gs->addToken(tok, part, staff, voice); + this->insert(iterator, gs); + break; + } else if ((*iterator)->getTimestamp() > timestamp) { + gs = new GridSlice(this, timestamp, SliceType::MeterSigs, maxstaff); + gs->addToken(tok, part, staff, voice); + this->insert(iterator, gs); + break; + } + iterator++; + } + + if (iterator == this->end()) { + // Couldn't find a place for the key signature, so place at end of measure. + gs = new GridSlice(this, timestamp, SliceType::MeterSigs, maxstaff); + gs->addToken(tok, part, staff, voice); + this->insert(iterator, gs); + } + + } + return gs; +} + + ////////////////////////////// // // GridMeasure::addKeySigToken -- Add a key signature token in a key sig slice at @@ -4091,14 +4170,14 @@ GridSlice* GridMeasure::addFiguredBass(HTp token, HumNum timestamp, int part, in } iterator++; } + } - if (!processed) { - cerr << "Error: could not insert figured bass: " << token << endl; - } else { - HumGrid* hg = getOwner(); - if (hg) { - hg->setFiguredBassPresent(part); - } + if (!processed) { + cerr << "Error: could not insert figured bass: " << token << endl; + } else { + HumGrid* hg = getOwner(); + if (hg) { + hg->setFiguredBassPresent(part); } } @@ -4149,14 +4228,14 @@ GridSlice* GridMeasure::addFiguredBass(const string& tok, HumNum timestamp, int } iterator++; } + } - if (!processed) { - cerr << "Error: could not inser figured bass: " << tok << endl; - } else { - HumGrid* hg = getOwner(); - if (hg) { - hg->setFiguredBassPresent(part); - } + if (!processed) { + cerr << "Error: could not inser figured bass: " << tok << endl; + } else { + HumGrid* hg = getOwner(); + if (hg) { + hg->setFiguredBassPresent(part); } } @@ -4168,7 +4247,9 @@ GridSlice* GridMeasure::addFiguredBass(const string& tok, HumNum timestamp, int ////////////////////////////// // // GridMeasure::addGlobalComment -- Add a global comment at the given -// timestamp (before any data line at the same timestamp). +// timestamp (before any data line at the same timestamp). Suppress +// adding the comment if it matches to another global comment at the +// same timestamp with the same text. // GridSlice* GridMeasure::addGlobalComment(const string& tok, HumNum timestamp) { @@ -4190,8 +4271,9 @@ GridSlice* GridMeasure::addGlobalComment(const string& tok, HumNum timestamp) { // before the data slice. But don't add if the previous // grid slice is a global comment with the same text. if ((iterator != this->end()) && (*iterator)->isGlobalComment()) { - if (tok == *(*iterator)->at(0)->at(0)->at(0)->getToken()) { + if (tok == (*iterator)->at(0)->at(0)->at(0)->getToken()->getText()) { // do not insert duplicate global comment + gs = *iterator; break; } } @@ -4301,14 +4383,17 @@ void GridMeasure::appendInitialBarline(HumdrumFile& infile, int startbarline) { // strange case which should never happen. return; } + if (getMeasureNumber() > 0) { + startbarline = getMeasureNumber(); + } int fieldcount = infile.back()->getFieldCount(); HumdrumLine* line = new HumdrumLine; string tstring = "="; - if (startbarline) { - tstring += to_string(startbarline); - } else { - tstring += "1"; - } +// if (startbarline) { +// tstring += to_string(startbarline); +// } else { +// tstring += "1"; +// } // probably best not to start with an invisible barline since // a plain barline would not be shown before the first measure anyway. // tstring += "-"; @@ -4415,7 +4500,8 @@ void GridMeasure::setTimeSigDur(HumNum duration) { // GridMeasure::addLayoutParameter -- // -void GridMeasure::addLayoutParameter(GridSlice* slice, int partindex, const string& locomment) { +void GridMeasure::addLayoutParameter(GridSlice* slice, int partindex, + const string& locomment) { auto iter = this->rbegin(); if (iter == this->rend()) { // something strange happened: expecting at least one item in measure. @@ -4742,6 +4828,28 @@ GridSlice* GridMeasure::getLastSpinedSlice(void) { +////////////////////////////// +// +// GridMeasure::setMeasureNumber -- +// + +void GridMeasure::setMeasureNumber(int value) { + m_barnum = value; +} + + + +////////////////////////////// +// +// GridMeasure::getMeasureNumber -- +// + +int GridMeasure::getMeasureNumber(void) { + return m_barnum; +} + + + ////////////////////////////// // // operator<< -- @@ -5262,7 +5370,6 @@ GridSlice::~GridSlice(void) { - ////////////////////////////// // // GridSlice::addToken -- Will not allocate part array, but will @@ -5394,7 +5501,7 @@ void GridSlice::transferTokens(HumdrumFile& outfile, bool recip) { voice = this->at(0)->at(0)->at(0); empty = (string)*voice->getToken(); } else { - empty = "="; + empty = "=YYYYYY"; } } } else if (isInterpretationSlice()) { @@ -5416,7 +5523,7 @@ void GridSlice::transferTokens(HumdrumFile& outfile, bool recip) { voice = this->at(0)->at(0)->at(0); token = new HumdrumToken((string)*voice->getToken()); } else { - token = new HumdrumToken("="); + token = new HumdrumToken("=XXXXX"); } empty = (string)*token; } else if (isInterpretationSlice()) { @@ -7098,6 +7205,7 @@ bool HumGrid::transferTokens(HumdrumFile& outfile, int startbarnum) { calculateGridDurations(); addNullTokens(); + addInvisibleRestsInFirstTrack(); addMeasureLines(); buildSingleList(); addLastMeasure(); @@ -8031,7 +8139,11 @@ void HumGrid::addMeasureLines(void) { lcount = 1; } for (int v=0; vgetMeasureNumber(); + if (m < (int)barnums.size() - 1) { + num = barnums[m+1]; + } + token = createBarToken(m, num, measure); gv = new GridVoice(token, 0); mslice->at(p)->at(s)->push_back(gv); } @@ -8249,7 +8361,6 @@ bool HumGrid::buildSingleList(void) { dur = (ts2 - ts1); // whole-note units m_allslices[i]->setDuration(dur); } - return !m_allslices.empty(); } @@ -8293,7 +8404,7 @@ void HumGrid::addNullTokensForGraceNotes(void) { continue; } - FillInNullTokensForGraceNotes(m_allslices[i], lastnote, nextnote); + fillInNullTokensForGraceNotes(m_allslices[i], lastnote, nextnote); } } @@ -8337,7 +8448,7 @@ void HumGrid::addNullTokensForLayoutComments(void) { continue; } - FillInNullTokensForLayoutComments(m_allslices[i], lastnote, nextnote); + fillInNullTokensForLayoutComments(m_allslices[i], lastnote, nextnote); } } @@ -8381,7 +8492,7 @@ void HumGrid::addNullTokensForClefChanges(void) { continue; } - FillInNullTokensForClefChanges(m_allslices[i], lastnote, nextnote); + fillInNullTokensForClefChanges(m_allslices[i], lastnote, nextnote); } } @@ -8389,10 +8500,10 @@ void HumGrid::addNullTokensForClefChanges(void) { ////////////////////////////// // -// HumGrid::FillInNullTokensForClefChanges -- +// HumGrid::fillInNullTokensForClefChanges -- // -void HumGrid::FillInNullTokensForClefChanges(GridSlice* clefslice, +void HumGrid::fillInNullTokensForClefChanges(GridSlice* clefslice, GridSlice* lastnote, GridSlice* nextnote) { if (clefslice == NULL) { return; } @@ -8446,10 +8557,10 @@ void HumGrid::FillInNullTokensForClefChanges(GridSlice* clefslice, ////////////////////////////// // -// HumGrid::FillInNullTokensForLayoutComments -- +// HumGrid::fillInNullTokensForLayoutComments -- // -void HumGrid::FillInNullTokensForLayoutComments(GridSlice* layoutslice, +void HumGrid::fillInNullTokensForLayoutComments(GridSlice* layoutslice, GridSlice* lastnote, GridSlice* nextnote) { if (layoutslice == NULL) { return; } @@ -8507,10 +8618,10 @@ void HumGrid::FillInNullTokensForLayoutComments(GridSlice* layoutslice, ////////////////////////////// // -// HumGrid::FillInNullTokensForGraceNotes -- +// HumGrid::fillInNullTokensForGraceNotes -- // -void HumGrid::FillInNullTokensForGraceNotes(GridSlice* graceslice, GridSlice* lastnote, +void HumGrid::fillInNullTokensForGraceNotes(GridSlice* graceslice, GridSlice* lastnote, GridSlice* nextnote) { if (graceslice == NULL) { @@ -8631,11 +8742,145 @@ void HumGrid::addNullTokens(void) { +////////////////////////////// +// +// HumGrid::setPartStaffDimensions -- +// + +void HumGrid::setPartStaffDimensions(vector>& nextevent, + GridSlice* startslice) { + nextevent.clear(); + for (int i=0; i<(int)m_allslices.size(); i++) { + if (!m_allslices[i]->isNoteSlice()) { + continue; + } + GridSlice* slice = m_allslices[i]; + nextevent.resize(slice->size()); + for (int p=0; p<(int)slice->size(); p++) { + nextevent.at(p).resize(slice->at(p)->size()); + for (int j=0; j<(int)nextevent.at(p).size(); j++) { + nextevent.at(p).at(j) = startslice; + } + } + break; + } +} + + + +////////////////////////////// +// +// HumGrid::addInvisibleRestsInFirstTrack -- If there are any +// timing gaps in the first track of a **kern spine, then +// fill in with invisible rests. +// + +void HumGrid::addInvisibleRestsInFirstTrack(void) { + int i; // slice index + int p; // part index + int s; // staff index + int v = 0; // only looking at first voice + + vector> nextevent; + GridSlice* lastslice = m_allslices.back(); + setPartStaffDimensions(nextevent, lastslice); + + for (i=(int)m_allslices.size()-1; i>=0; i--) { + GridSlice& slice = *m_allslices.at(i); + if (!slice.isNoteSlice()) { + continue; + } + for (p=0; p<(int)slice.size(); p++) { + GridPart& part = *slice.at(p); + for (s=0; s<(int)part.size(); s++) { + GridStaff& staff = *part.at(s); + if (!staff.at(v)) { + // in theory should not happen + continue; + } + GridVoice& gv = *staff.at(v); + if (gv.isNull()) { + continue; + } + + // Found a note/rest. Check if its duration matches + // the next non-null data token. If not, then add + // an invisible rest somewhere between the two + + // first check to see if the previous item is a + // NULL. If so, then store and continue. + if (nextevent[p][s] == NULL) { + nextevent[p][s] = &slice; + continue; + } + addInvisibleRest(nextevent, i, p, s); + } + } + } +} + + + +////////////////////////////// +// +// HumGrid::addInvisibleRest -- +// + +void HumGrid::addInvisibleRest(vector>& nextevent, + int index, int p, int s) { + GridSlice *ending = nextevent[p][s]; + if (ending == NULL) { + cerr << "Not handling this case yet at end of data." << endl; + return; + } + HumNum endtime = ending->getTimestamp(); + + GridSlice* starting = m_allslices.at(index); + HumNum starttime = starting->getTimestamp(); + HTp token = starting->at(p)->at(s)->at(0)->getToken(); + HumNum duration = Convert::recipToDuration(token); + HumNum difference = endtime - starttime; + HumNum gap = difference - duration; + if (gap == 0) { + // nothing to do + nextevent[p][s] = starting; + return; + } + HumNum target = starttime + duration; + + string kern = Convert::durationToRecip(gap); + kern += "ryy"; + + for (int i=index+1; i<(int)m_allslices.size(); i++) { + GridSlice* slice = m_allslices[i]; + if (!slice->isNoteSlice()) { + continue; + } + HumNum timestamp = slice->getTimestamp(); + if (timestamp < target) { + continue; + } + if (timestamp > target) { + cerr << "Cannot deal with this slice addition case yet..." << endl; + nextevent[p][s] = starting; + return; + } + // At timestamp for adding new token. + m_allslices.at(i)->at(p)->at(s)->at(0)->setToken(kern); + break; + } + + // Store the current event in the buffer + nextevent[p][s] = starting; +} + + + ////////////////////////////// // // HumGrid::adjustClefChanges -- If a clef change starts at the -// beginning of a meausre, move it to before the measure (unless -// the measure has zero duration). +// beginning of a meausre, move it to before the measure (unless +// the measure has zero duration). // void HumGrid::adjustClefChanges(void) { @@ -9629,10 +9874,6 @@ ostream& operator<<(ostream& out, HumGrid& grid) { - - - - //////////////////////////////// // // HumParameter::HumParameter -- HumParameter constructor. @@ -12594,6 +12835,11 @@ void HumRegex::unsetGlobal(void) { // input string. Returns the character position + 1 of the first match if any found. // Search results can be accessed with .getSubmatchCount() and .getSubmatch(index). // +// Warning: a temporary string cannot be used as input to the search function +// if you want to call getMatch() later. If you do a memory leak will occur. +// If you have a temporary string, first save it to a variable which remains +// in scope while accesssing a match with getMatch(). +// int HumRegex::search(const string& input, const string& exp) { m_regex = regex(exp, m_regexflags); @@ -12719,15 +12965,22 @@ string HumRegex::getMatch(int index) { ////////////////////////////// // // HumRegex::getMatchInt -- Get the match interpreted as a integer. +// returns 0 if match does not start with a valid number. // int HumRegex::getMatchInt(int index) { string value = m_matches.str(index); + int output = 0; if (value.size() > 0) { - return stoi(value); - } else { - return 0; + if (isdigit(value[0])) { + output = std::stoi(value); + } else if (value[0] == '-') { + output = std::stoi(value); + } else if (value[0] == '+') { + output = std::stoi(value); + } } + return output; } @@ -27025,15 +27278,7 @@ MuseData::MuseData(MuseData& input) { // MuseData::~MuseData() { - int i; - for (i=0; i<(int)m_data.size(); i++) { - if (m_data[i] != NULL) { - delete m_data[i]; - m_data[i] = NULL; - } - } - m_data.resize(0); - m_name = ""; + clear(); } @@ -27206,7 +27451,7 @@ int MuseData::read(istream& input) { } else { isnewline = 0; } - + if (isnewline && (value == 0x0a) && (lastvalue == 0x0d)) { // ignore the second newline character in a dos-style newline. lastvalue = value; @@ -27214,7 +27459,7 @@ int MuseData::read(istream& input) { } else { lastvalue = value; } - + if (isnewline) { MuseData::append(dataline); dataline.clear(); @@ -27258,7 +27503,10 @@ int MuseData::readString(const string& data) { void MuseData::doAnalyses(void) { analyzeType(); + analyzeTpq(); if (hasError()) { return; } + assignHeaderBodyState(); + analyzeLayers(); analyzeRhythm(); if (hasError()) { return; } constructTimeSequence(); @@ -27271,6 +27519,32 @@ void MuseData::doAnalyses(void) { +////////////////////////////// +// +// MuseData::analyzeTpq -- Read $ records for Q: field values and store +// the ticks-per-quarter note value on each line after that $ record. +// This is used later to extract the logical duration of notes and rests. +// + +void MuseData::analyzeTpq(void) { + HumRegex hre; + int ticks = 0; + for (int i=0; iisAttributes()) { + mr->setTpq(ticks); + continue; + } + string line = getLine(i); + if (hre.search(line, " Q:(\\d+)")) { + ticks = hre.getMatchInt(1); + } + mr->setTpq(ticks); + } +} + + + ////////////////////////////// // // MuseData::analyzePitch -- calculate the pitch of all notes in terms @@ -27368,7 +27642,7 @@ void MuseData::processTie(int eindex, int rindex, int lastindex) { // track, so search for the same pitch in any track at the event time: nextrindex = searchForPitch(nexteindex, base40, -1); } - + if (nextrindex < 0) { // Failed to find a note at the target event time which could be // tied to the current note (no pitches at that time which match @@ -27486,10 +27760,61 @@ void MuseData::analyzeType(void) { int h = 0; MuseData& thing = *this; int foundattributes = 0; - int foundend = 0; - for (int i=0; i=0; i--) { + if (thing[i].getLength() > 0) { + if (thing[i][0] == '@') { + thing[i].setType(E_muserec_comment_line); + continue; + } + if (thing[i][0] == '&') { + // start or end of multi-line comment; + commentQ = !commentQ; + if (!commentQ) { + thing[i].setType(E_muserec_comment_toggle); + continue; + } + } + if (commentQ) { + thing[i].setType(E_muserec_comment_toggle); + continue; + } + } + h--; + if (h==1) { thing[i].setType(E_muserec_header_1); continue; } + else if (h==2) { thing[i].setType(E_muserec_header_2); continue; } + else if (h==3) { thing[i].setType(E_muserec_header_3); continue; } + else if (h==4) { thing[i].setType(E_muserec_header_4); continue; } + else if (h==5) { thing[i].setType(E_muserec_header_5); continue; } + else if (h==6) { thing[i].setType(E_muserec_header_6); continue; } + else if (h==7) { thing[i].setType(E_muserec_header_7); continue; } + else if (h==8) { thing[i].setType(E_muserec_header_8); continue; } + else if (h==9) { thing[i].setType(E_muserec_header_9); continue; } + else if (h==10) { thing[i].setType(E_muserec_header_10); continue; } + } + + commentQ = 0; + h = 11; + for (int i=groupmemberships+1; i 0) { if (thing[i][0] == '@') { thing[i].setType(E_muserec_comment_line); @@ -27519,6 +27844,7 @@ void MuseData::analyzeType(void) { else if (h==8) { thing[i].setType(E_muserec_header_8); continue; } else if (h==9) { thing[i].setType(E_muserec_header_9); continue; } else if (h==10) { thing[i].setType(E_muserec_header_10); continue; } + else if (h==11) { thing[i].setType(E_muserec_header_11); continue; } else if (h==12) { thing[i].setType(E_muserec_header_12); continue; } @@ -27586,7 +27912,8 @@ void MuseData::analyzeRhythm(void) { for (int i=0; i<(int)m_data.size(); i++) { if (m_data[i]->isAttributes()) { - if (hre.search(m_data[i]->getLine(), "Q:(\\d+)", "")) { + string line = m_data[i]->getLine(); + if (hre.search(line, "Q:(\\d+)", "")) { tpq = hre.getMatchInt(1); } } @@ -27611,7 +27938,7 @@ void MuseData::analyzeRhythm(void) { } else if (m_data[i]->isFiguredHarmony()) { // Tick values on figured harmony lines do not advance the // cumulative timestamp; instead they temporarily advance - // the time placement of the next figure if it occurs + // the time placement of the next figure if it occurs // during the same note as the previous figure. m_data[i]->setAbsBeat(cumulative + figadj); HumNum tick = m_data[i]->getLineTickDuration(); @@ -27671,7 +27998,8 @@ int MuseData::getInitialTpq(void) { continue; } if ((*m_data[i])[0] == '$') { - if (hre.search(m_data[i]->getLine(), "Q:(\\d+)", "")) { + string line = m_data[i]->getLine(); + if (hre.search(line, "Q:(\\d+)", "")) { output = hre.getMatchInt(1); } break; @@ -27680,7 +28008,8 @@ int MuseData::getInitialTpq(void) { } else { for (int i=0; i<(int)m_data.size(); i++) { if (m_data[i]->getType() == E_muserec_musical_attributes) { - if (hre.search(m_data[i]->getLine(), "Q:(\\d+)", "")) { + string line = m_data[i]->getLine(); + if (hre.search(line, "Q:(\\d+)", "")) { output = hre.getMatchInt(1); } break; @@ -27743,14 +28072,9 @@ MuseEventSet& MuseData::getEvent(int eindex) { // - ////////////////////////////// // -// MuseData::insertEventBackwards -- insert an event into a time-sorted -// organization of the file. Searches for the correct time location to -// insert event starting at the end of the list (since MuseData files -// are mostly sorted in time. -// +// printSequenceTimes -- // void printSequenceTimes(vector& m_sequence) { @@ -27761,8 +28085,17 @@ void printSequenceTimes(vector& m_sequence) { } -void MuseData::insertEventBackwards(HumNum atime, MuseRecord* arecord) { +////////////////////////////// +// +// MuseData::insertEventBackwards -- insert an event into a time-sorted +// organization of the file. Searches for the correct time location to +// insert event starting at the end of the list (since MuseData files +// are mostly sorted in time. +// +// + +void MuseData::insertEventBackwards(HumNum atime, MuseRecord* arecord) { if (m_sequence.empty()) { MuseEventSet* anevent = new MuseEventSet; anevent->setTime(atime); @@ -27842,7 +28175,7 @@ HumNum MuseData::getTiedDuration(int index) { if (getRecord(index).getNextTiedNoteLineIndex() < 0) { return getRecord(index).getNoteDuration(); } - + // this is start of a group of tied notes. Start figuring out // how long the duration of the tied group is. output = getRecord(index).getNoteDuration(); @@ -28081,9 +28414,9 @@ int MuseData::getMembershipPartNumber(const string& mstring) { HumRegex hre; for (int i=0; i<(int)m_data.size(); i++) { if (m_data[i]->getType() == E_muserec_header_12) { - if (hre.search(m_data[i]->getLine(), searchstring, "")) { - if (hre.search(m_data[i]->getLine(), - "part\\s*(\\d+)\\s*of\\s*(\\d+)")) { + string line = m_data[i]->getLine(); + if (hre.search(line, searchstring)) { + if (hre.search(line, "part\\s*(\\d+)\\s*of\\s*(\\d+)")) { string partnum = hre.getMatch(1); return hre.getMatchInt(1); } @@ -28202,7 +28535,14 @@ HumNum MuseData::getFileDuration(void) { // string MuseData::getLine(int index) { - return getRecord(index).getLine(); + if (index < 0) { + return ""; + } + if (index >= getLineCount()) { + return ""; + } + string output = getRecord(index).getLine(); + return output; } @@ -28218,6 +28558,549 @@ bool MuseData::isPartName(int index) { +////////////////////////////// +// +// MuseData::isWorkInfo -- return true if a work info line. +// + +bool MuseData::isWorkInfo(int index) { + return getRecord(index).isWorkInfo(); +} + + + +////////////////////////////// +// +// MuseData::isWorkTitle -- return true if a work title line. +// + +bool MuseData::isWorkTitle(int index) { + return getRecord(index).isWorkTitle(); +} + + + +////////////////////////////// +// +// MuseData::isHeaderRecord -- return true if in the header. +// + +bool MuseData::isHeaderRecord(int index) { + return getRecord(index).isHeaderRecord(); +} + + + +////////////////////////////// +// +// MuseData::isBodyRecord -- return true if in the body. +// + +bool MuseData::isBodyRecord(int index) { + return getRecord(index).isBodyRecord(); +} + + + +////////////////////////////// +// +// MuseData::isCopyright -- return true if a work title line. +// + +bool MuseData::isCopyright(int index) { + return getRecord(index).isCopyright(); +} + + + +////////////////////////////// +// +// MuseData::isMovementTitle -- return true if a movement title line. +// + +bool MuseData::isMovementTitle(int index) { + return getRecord(index).isMovementTitle(); +} + + + +////////////////////////////// +// +// MuseData::isRegularNote -- return true if a regular note line. +// This is either a single note, or the first note in a chord. +// + +bool MuseData::isRegularNote(int index) { + return getRecord(index).isRegularNote(); +} + + + +////////////////////////////// +// +// MuseData::isAnyNote -- return true if note line of any time. +// + +bool MuseData::isAnyNote(int index) { + return getRecord(index).isAnyNote(); +} + + + +////////////////////////////// +// +// MuseData::isEncoder -- return true if note line. +// + +bool MuseData::isEncoder(int index) { + return getRecord(index).isEncoder(); +} + + + +////////////////////////////// +// +// MuseData::isId -- return true if Id line. +// + +bool MuseData::isId(int index) { + return getRecord(index).isId(); +} + + + +////////////////////////////// +// +// MuseData::isSource -- return true if note line. +// + +bool MuseData::isSource(int index) { + return getRecord(index).isSource(); +} + + + +////////////////////////////// +// +// MuseData::getWorkInfo -- +// + +std::string MuseData::getWorkInfo(void) { + for (int i=0; i=0; i--) { + if (isspace(output[i])) { + output.resize((int)output.size() - 1); + } else { + break; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseData::analyzeLayers -- When there is a backup command in the +// measure and no voice information, provide the voice information. +// Primarily use the stem directions to make this determination. +// Also, other features can be used: +// beaming (do not split beamed notes across layers) +// pitch (higher versus lower pitch). +// Mostly this is only useful for two-voiced measures. +// How to deal with voices on multiple staves will be more complex. +// + +void MuseData::analyzeLayers(void) { + int lcount = getLineCount(); + for (int i=0; i= lcount) { + return lcount+1; + } + + // Not necessarily a barline, but at least the first + // record for the measure (may be missing at start + // of music). + + while ((i < lcount) && isHeaderRecord(i)) { + i++; + } + if ((i < lcount) && getRecord(i).isBarline()) { + i++; + } + // Now should be at start of data for a measure. + + if (i >= lcount) { + return lcount+1; + } + + vector> segments(1); + while (i < lcount) { + MuseRecord *mr = &getRecord(i); + if (mr->isBarline()) { + break; + } + segments.back().push_back(mr); + if (mr->isBackup()) { + segments.resize(segments.size() + 1); + } + i++; + } + int position = i-1; + + if (segments.size() < 2) { + // no backup in measure, so single voice/layer + return position; + } + + // Assign each backup segment to a successive track + // if the layer does not have explicit track information. + int track; + + for (int i=0; i<(int)segments.size(); i++) { + for (int j=0; j<(int)segments[i].size(); j++) { + MuseRecord* mr = segments[i][j]; + int trackfield = mr->getTrack(); + if (trackfield == 0) { + track = i+1; + } else { + track = trackfield; + } + mr->setLayer(track); + } + } + + + return position; +} + + + +////////////////////////////// +// +// assignHeaderBodyState -- +// + +void MuseData::assignHeaderBodyState(void) { + int state = 1; + int foundend = 0; + for (int i=0; i<(int)m_data.size(); i++) { + if (m_data[i]->isAnyComment()) { + // Comments inherit state if previous non-comment line + m_data[i]->setHeaderState(state); + continue; + } + if (state == 0) { + // no longer in the header + m_data[i]->setHeaderState(state); + continue; + } + if ((!foundend) && m_data[i]->isGroup()) { + foundend = 1; + m_data[i]->setHeaderState(state); + continue; + } + if (foundend && !m_data[i]->isGroup()) { + state = 0; + m_data[i]->setHeaderState(state); + continue; + } + // still in header + m_data[i]->setHeaderState(state); + } +} + + + /////////////////////////////////////////////////////////////////////////// // // friendly functions @@ -28581,6 +29464,7 @@ bool MuseDataSet::hasError(void) { } + ////////////////////////////// // // MuseDataSet::setError -- @@ -28592,6 +29476,17 @@ void MuseDataSet::setError(const string& error) { +////////////////////////////// +// +// MuseDataSet::getError -- +// + +string MuseDataSet::getError(void) { + return m_error; +} + + + /////////////////////////////////////////////////////////////////////////// ////////////////////////////// @@ -28953,7 +29848,7 @@ string MuseRecord::getTickDurationField(void) { case E_muserec_note_cue: case E_muserec_note_grace: default: - return ""; + return " "; // cerr << "Error: cannot use getTickDurationField function on line: " // << getLine() << endl; // return ""; @@ -29031,11 +29926,29 @@ int MuseRecord::getLineTickDuration(void) { if (getType() == E_muserec_backspace) { return -value; } + return value; } + + +////////////////////////////// +// +// MuseRecord::getTicks -- similar to getLineTickDuration, but is non-zero +// for secondary chord notes. +// + int MuseRecord::getTicks(void) { - return getLineTickDuration(); + string recordInfo = getTickDurationString(); + if (recordInfo.empty()) { + return 0; + } + int value = std::stoi(recordInfo); + if (getType() == E_muserec_backspace) { + return -value; + } + + return value; } @@ -29062,7 +29975,7 @@ int MuseRecord::getNoteTickDuration(void) { ////////////////////////////// // -// MuseRecord::setDots -- Only one or two dots allowed +// MuseRecord::setDots -- // void MuseRecord::setDots(int value) { @@ -29078,6 +29991,25 @@ void MuseRecord::setDots(int value) { +////////////////////////////// +// +// MuseRecord::getDotCount -- +// + +int MuseRecord::getDotCount(void) { + char value = getColumn(18); + switch (value) { + case ' ': return 0; + case '.': return 1; + case ':': return 2; + case ';': return 3; + case '!': return 4; + } + return 0; +} + + + ////////////////////////////// // // MuseRecord::setNoteheadShape -- Duration with augmentation dot component @@ -29740,8 +30672,11 @@ int MuseRecord::levelQ(void) { // string MuseRecord::getTrackField(void) { - allowNotesOnly("getTrackField"); - return extract(15, 15); + if (!isAnyNoteOrRest()) { + return extract(15, 15); + } else { + return " "; + } } @@ -29763,14 +30698,15 @@ string MuseRecord::getTrackString(void) { ////////////////////////////// // -// MuseRecord::getTrack -- +// MuseRecord::getTrack -- Return 0 if no track information (implicitly track 1, +// or unlabelled higher track). // int MuseRecord::getTrack(void) { int output = 1; string recordInfo = getTrackField(); if (recordInfo[0] == ' ') { - output = 1; + output = 0; } else { output = std::strtol(recordInfo.c_str(), NULL, 36); } @@ -29829,6 +30765,30 @@ string MuseRecord::getGraphicNoteTypeString(void) { +////////////////////////////// +// +// MuseRecord::getGraphicRecip -- +// + +string MuseRecord::getGraphicRecip(void) { + int notetype = getGraphicNoteType(); + string output; + switch (notetype) { + case -3: output = "0000"; break; // double-maxima + case -2: output = "000"; break; // maxima + case -1: output = "00"; break; // long + default: + output = to_string(notetype); // regular **recip number + } + int dotcount = getDotCount(); + for (int i=0; i= 32) { + return -2; + } else if (value >= 16) { + return -1; + } else if (value >= 8) { + return 0; + } else if (value >= 4) { + return 1; + } else if (value >= 2) { + return 2; + } else if (value >= 1) { + return 4; + } else if (value.getFloat() >= 0.5) { + return 8; + } else if (value.getFloat() >= 0.25) { + return 16; + } else if (value.getFloat() >= 0.125) { + return 32; + } else if (value.getFloat() >= 0.0625) { + return 64; + } else if (value.getFloat() >= 1.0/128) { + return 128; + } else if (value.getFloat() >= 1.0/256) { + return 256; + } else if (value.getFloat() >= 1.0/512) { + return 512; + } else { + return 0; + } + } else { + cerr << "Error: no graphic note type specified: " << getLine() << endl; + return 0; + } } switch (recordInfo[0]) { - case 'M': // Maxima + case 'M': // Maxima output = -2; break; - case 'L': case 'B': // Longa + case 'L': case 'B': // Longa output = -1; break; - case 'b': case 'A': // Breve + case 'b': case 'A': // Breve output = 0; break; - case 'w': case '9': // Whole + case 'w': case '9': // Whole output = 1; break; - case 'h': case '8': // Half + case 'h': case '8': // Half output = 2; break; - case 'q': case '7': // Quarter + case 'q': case '7': // Quarter output = 4; break; - case 'e': case '6': // Eighth + case 'e': case '6': // Eighth output = 8; break; - case 's': case '5': // Sixteenth + case 's': case '5': // Sixteenth output = 16; break; - case 't': case '4': // 32nd note + case 't': case '4': // 32nd note output = 32; break; - case 'x': case '3': // 64th note + case 'x': case '3': // 64th note output = 64; break; - case 'y': case '2': // 128th note + case 'y': case '2': // 128th note output = 128; break; - case 'z': case '1': // 256th note + case 'z': case '1': // 256th note output = 256; break; default: cerr << "Error: unknown graphical note type in column 17: " @@ -30667,8 +31663,8 @@ int MuseRecord::beam256Q(void) { string MuseRecord::getKernBeamStyle(void) { string output; - string beams = getBeamField(); // 6 characters wide - for (int i=0; i<6; i++) { + string beams = getBeamField(); + for (int i=0; i<(int)beams.size(); i++) { switch (beams[i]) { case '[': // start beam output += "L"; @@ -30679,10 +31675,10 @@ string MuseRecord::getKernBeamStyle(void) { case ']': // end beam output += "J"; break; - case 'K': // forward hook + case '/': // forward hook output += "K"; break; - case 'k': // backward hook + case '\\': // backward hook output += "k"; break; default: @@ -31060,7 +32056,7 @@ string MuseRecord::getVerse(int index) { ////////////////////////////// // // MuseRecord::getKernNoteStyle -- -// default values: beams = 0, stems = 0 +// default values: beams = 0, stems = 0 // string MuseRecord::getKernNoteStyle(int beams, int stems) { @@ -31077,17 +32073,75 @@ string MuseRecord::getKernNoteStyle(int beams, int stems) { notetype = notetype * 2; } } - tempdur << notetype; - output = tempdur.str(); - // add any dots of prolongation to the output string - output += getStringProlongation(); + // logical duration of the note + HumNum logicalduration = getTicks(); + logicalduration /= getTpq(); + string durrecip = Convert::durationToRecip(logicalduration); + + // graphical duration of the note + string graphicrecip = getGraphicRecip(); + HumNum graphicdur = Convert::recipToDuration(graphicrecip); + + string displayrecip; + + if (graphicdur != logicalduration) { + // switch to the logical duration and store the graphic + // duration. The logical duration will be used on the + // main kern token, and the graphic duration will be stored + // as a layout parameter, such as !LO:N:vis=4. to display + // the note as a dotted quarter regardless of the logical + // duration. + + // Current test file has encoding bug related to triplets, so + // disable graphic notation dealing with tuplets for now. + + // for now just looking to see if one has a dot and the other does not + if ((durrecip.find(".") != string::npos) && + (graphicrecip.find(".") == string::npos)) { + m_graphicrecip = graphicrecip; + displayrecip = durrecip; + } else if ((durrecip.find(".") == string::npos) && + (graphicrecip.find(".") != string::npos)) { + m_graphicrecip = graphicrecip; + displayrecip = durrecip; + } + } + + if (displayrecip.size() > 0) { + output = displayrecip; + } else { + tempdur << notetype; + output = tempdur.str(); + // add any dots of prolongation to the output string + output += getStringProlongation(); + } // add the pitch to the output string string musepitch = getPitchString(); string kernpitch = Convert::musePitchToKernPitch(musepitch); output += kernpitch; + string logicalAccidental = getAccidentalString(); + string notatedAccidental = getNotatedAccidentalString(); + + if (notatedAccidental.empty() && !logicalAccidental.empty()) { + // Indicate that the logical accidental should not be + // displayed (because of key signature or previous + // note in the measure that alters the accidental + // state of the current note). + output += "y"; + } else if ((logicalAccidental == notatedAccidental) && !notatedAccidental.empty()) { + // Indicate that the accidental should be displayed + // and is not suppressed by the key signature or a + // previous note in the measure. + output += "X"; + } + // There can be cases where the logical accidental + // is natural but the notated accidetnal is sharp (but + // the notated accidental means play a natural accidetnal). + // Deal with this later. + // if there is a notated natural sign, then add it now: string temp = getNotatedAccidentalField(); if (temp == "n") { @@ -31268,6 +32322,10 @@ string MuseRecord::getKernRestStyle(int quarter) { // add the pitch to the output string output += "r"; + if (isInvisibleRest()) { + output += "yy"; + } + return output; } @@ -31327,6 +32385,9 @@ string MuseRecord::getMeasureNumberString(void) { int MuseRecord::getMeasureNumber(void) { string measureInfo = getMeasureNumberField(); + if (measureInfo.empty()) { + return 0; + } return std::stoi(measureInfo); } @@ -31352,6 +32413,21 @@ int MuseRecord::measureNumberQ(void) { +////////////////////////////// +// +// MuseRecord::getMeasureFlagsString -- Columns 17 to 80. +// + +string MuseRecord::getMeasureFlagsString(void) { + if (m_recordString.size() < 17) { + return ""; + } else { + return trimSpaces(m_recordString.substr(16)); + } +} + + + ////////////////////////////// // // MuseRecord::measureFermataQ -- returns true if there is a @@ -31680,8 +32756,13 @@ int MuseRecord::getAttributeInt(char attribute) { if (ending == 0 || ending == 1) { return output; } else { - output = std::stoi(&getColumn(column+1)); - return output; + string value = &getColumn(column+1); + if (value.empty()) { + output = std::stoi(value); + return output; + } else { + return 0; + } } } @@ -31986,6 +33067,29 @@ void MuseRecord::allowNotesOnly(const string& functionName) { +////////////////////////////// +// +// MuseRecord::allowNotesAndRestsOnly -- +// + +void MuseRecord::allowNotesAndRestsOnly(const string& functionName) { + switch (getType()) { + case E_muserec_note_regular: + case E_muserec_note_chord: + case E_muserec_note_grace: + case E_muserec_note_cue: + case E_muserec_rest: + case E_muserec_rest_invisible: + break; + default: + cerr << "Error: can only access " << functionName + << " on a note or rest records. Line is: " << getLine() << endl; + return; + } +} + + + ////////////////////////////// // // MuseRecord::getAddElementIndex -- get the first element pointed @@ -32199,6 +33303,8 @@ MuseRecordBasic::MuseRecordBasic(void) { m_nexttiednote = -1; m_lasttiednote = -1; m_roundBreve = 0; + m_header = -1; + m_layer = 0; } @@ -32216,6 +33322,8 @@ MuseRecordBasic::MuseRecordBasic(const string& aLine, int index) { m_nexttiednote = -1; m_lasttiednote = -1; m_roundBreve = 0; + m_header = -1; + m_layer = 0; } @@ -32240,6 +33348,7 @@ MuseRecordBasic::~MuseRecordBasic() { m_nexttiednote = -1; m_lasttiednote = -1; m_roundBreve = 0; + m_layer = 0; } @@ -32251,6 +33360,16 @@ MuseRecordBasic::~MuseRecordBasic() { void MuseRecordBasic::clear(void) { m_recordString.clear(); + m_lineindex = -1; + m_absbeat = 0; + m_lineduration = 0; + m_noteduration = 0; + m_b40pitch = -100; + m_nexttiednote = -1; + m_lasttiednote = -1; + m_roundBreve = 0; + m_header = -1; + m_layer = 0; } @@ -32942,6 +34061,40 @@ bool MuseRecordBasic::isAttributes(void) { } + +////////////////////////////// +// +// MuseRecordBasic::isSource -- +// + +bool MuseRecordBasic::isSource(void) { + return m_type == E_muserec_source; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isEncoder -- +// + +bool MuseRecordBasic::isEncoder(void) { + return m_type == E_muserec_encoder; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isId -- +// + +bool MuseRecordBasic::isId(void) { + return m_type == E_muserec_id; +} + + + ////////////////////////////// // // MuseRecordBasic::isBarline -- @@ -32953,6 +34106,50 @@ bool MuseRecordBasic::isBarline(void) { +////////////////////////////// +// +// MuseRecordBasic::isBackup -- +// + +bool MuseRecordBasic::isBackup(void) { + return m_type == E_muserec_back; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isAnyComment -- +// + +bool MuseRecordBasic::isAnyComment(void) { + return isLineComment() || isBlockComment(); +} + + + +////////////////////////////// +// +// MuseRecordBasic::isLineComment -- +// + +bool MuseRecordBasic::isLineComment(void) { + return m_type == E_muserec_comment_line; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isBlockComment -- +// + +bool MuseRecordBasic::isBlockComment(void) { + return m_type == E_muserec_comment_toggle; +} + + + ////////////////////////////// // // MuseRecordBasic::isChordNote -- Is a regular note that is a seoncdary @@ -33012,10 +34209,24 @@ bool MuseRecordBasic::isFiguredHarmony(void) { ////////////////////////////// // -// MuseRecordBasic::isNote -- +// MuseRecordBasic::isRegularNote -- // -bool MuseRecordBasic::isNote(void) { +bool MuseRecordBasic::isRegularNote(void) { + switch (m_type) { + case E_muserec_note_regular: + return true; + } + return false; +} + + +////////////////////////////// +// +// MuseRecordBasic::isAnyNote -- +// + +bool MuseRecordBasic::isAnyNote(void) { switch (m_type) { case E_muserec_note_regular: case E_muserec_note_chord: @@ -33031,11 +34242,16 @@ bool MuseRecordBasic::isNote(void) { ////////////////////////////// // -// MuseRecordBasic::isRest -- +// MuseRecordBasic::isAnyNoteOrRest -- // -bool MuseRecordBasic::isRest(void) { +bool MuseRecordBasic::isAnyNoteOrRest(void) { switch (m_type) { + case E_muserec_note_regular: + case E_muserec_note_chord: + case E_muserec_note_cue: + case E_muserec_note_grace: + case E_muserec_note_grace_chord: case E_muserec_rest_invisible: case E_muserec_rest: return true; @@ -33045,6 +34261,320 @@ bool MuseRecordBasic::isRest(void) { +////////////////////////////// +// +// MuseRecordBasic::isInvisibleRest -- +// + +bool MuseRecordBasic::isInvisibleRest(void) { + switch (m_type) { + case E_muserec_rest_invisible: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isRegularRest -- +// + +bool MuseRecordBasic::isRegularRest(void) { + switch (m_type) { + case E_muserec_rest: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isAnyRest -- Also cue-sized rests? +// + +bool MuseRecordBasic::isAnyRest(void) { + switch (m_type) { + case E_muserec_rest_invisible: + case E_muserec_rest: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isCopyright -- +// + +bool MuseRecordBasic::isCopyright(void) { + switch (m_type) { + case E_muserec_copyright: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isWorkInfo -- +// + +bool MuseRecordBasic::isWorkInfo(void) { + switch (m_type) { + case E_muserec_work_info: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isWorkTitle -- +// + +bool MuseRecordBasic::isWorkTitle(void) { + switch (m_type) { + case E_muserec_work_title: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isMovementTitle -- +// + +bool MuseRecordBasic::isMovementTitle(void) { + switch (m_type) { + case E_muserec_movement_title: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isGroup -- +// + +bool MuseRecordBasic::isGroup(void) { + switch (m_type) { + case E_muserec_group: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isGroupMembership -- +// + +bool MuseRecordBasic::isGroupMembership(void) { + switch (m_type) { + case E_muserec_group_memberships: + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isHeaderRecord -- True if a header, or a comment +// occurring before the first non-header record. +// + +bool MuseRecordBasic::isHeaderRecord(void) { + return m_header > 0; +} + + + +////////////////////////////// +// +// MuseRecordBasic::isBodyRecord -- True if not a header record. +// + +bool MuseRecordBasic::isBodyRecord(void) { + return m_header == 0; +} + + + +////////////////////////////// +// +// MuseRecordBasic::trimSpaces -- +// + +string MuseRecordBasic::trimSpaces(std::string input) { + string output; + int status = 0; + for (int i=0; i<(int)input.size(); i++) { + if (!status) { + if (isspace(input[i])) { + continue; + } + status = 1; + } + output += input[i]; + } + for (int i=(int)output.size()-1; i>=0; i--) { + if (isspace(output[i])) { + output.resize((int)output.size() - 1); + } else { + break; + } + } + return output; +} + + + +////////////////////////////// +// +// MuseRecordBasic::setHeaderState -- 1 = in header, 0 = in body, -1 = undefined. +// Access with isHeaderRecord() and isBodyRecord(). +// + +void MuseRecordBasic::setHeaderState(int state) { + if (state > 0) { + m_header = 1; + } else if (state < 0) { + m_header = -1; + } else { + m_header = 0; + } +} + + + +////////////////////////////// +// +// MuseRecordBasic::setLayer -- Set the layer for the record. +// This information is taken from the track parameter +// of records, but may be inferred from its position in +// relation to backup commands. Zero means implicit layer 1. +// + +void MuseRecordBasic::setLayer(int layer) { + if (layer < 0) { + m_layer = 0; + } else { + m_layer = layer; + } +} + + + +////////////////////////////// +// +// MuseRecordBasic::getLayer -- Get the layer for the record. +// This information is taken from the track parameter +// of records, but may be inferred from its position in +// relation to backup commands. Zero means implicit layer 1. +// + +int MuseRecordBasic::getLayer(void) { + return m_layer; +} + + + +////////////////////////////// +// +// MuseRecordBasic:hasTpq -- +// + +bool MuseRecordBasic::hasTpq(void) { + if (m_tpq) { + return true; + } else { + return false; + } +} + + + +////////////////////////////// +// +// MuseRecordBasic:getTpq -- +// + +int MuseRecordBasic::getTpq(void) { + return m_tpq; +} + + + +////////////////////////////// +// +// MuseRecordBasic:setTpq -- +// + +void MuseRecordBasic::setTpq(int value) { + if (value <= 0) { + m_tpq = 0; + } else { + m_tpq = value; + } +} + + + +////////////////////////////// +// +// MuseRecordBasic:setVoice -- +// + +void MuseRecordBasic::setVoice(GridVoice* voice) { + m_voice = voice; +} + + + +////////////////////////////// +// +// MuseRecordBasic:getVoice -- +// + +GridVoice* MuseRecordBasic::getVoice(void) { + return m_voice; +} + + + +////////////////////////////// +// +// MuseRecordBasic:LayoutVis -- Return the graphical display of the +// rhythm for the note/rest if it was different than the logical version. +// This is temporary storage for inserting a layout command into the +// MuseData-to-Humdrum converter (tool-musedata2hum.cpp). +// + +std::string MuseRecordBasic::getLayoutVis(void) { + return m_graphicrecip; +} + + /////////////////////////////////////////////////////////////////////////// @@ -33061,6 +34591,11 @@ ostream& operator<<(ostream& out, MuseRecordBasic& aRecord) { return out; } +ostream& operator<<(ostream& out, MuseRecordBasic* aRecord) { + out << *aRecord; + return out; +} + class MxmlMeasure; @@ -59589,7 +61124,6 @@ bool Tool_musedata2hum::convertFile(ostream& out, const string& filename) { cerr << "Error description:\t" << mds.getError() << "\n"; exit(1); } - return convert(out, mds); } @@ -59613,7 +61147,6 @@ bool Tool_musedata2hum::convertString(ostream& out, const string& input) { } - bool Tool_musedata2hum::convert(ostream& out, MuseDataSet& mds) { initialize(); @@ -59627,11 +61160,111 @@ bool Tool_musedata2hum::convert(ostream& out, MuseDataSet& mds) { HumdrumFile outfile; outdata.transferTokens(outfile); outfile.createLinesFromTokens(); + + // Convert comments in header of first part: + for (int i=0; i< mds[0].getLineCount(); i++) { + if (mds[0][i].isAnyNote()) { + break; + } + if (mds[0].getLine(i).compare(0, 2, "@@") == 0) { + string output = mds[0].getLine(i); + for (int j=0; j<(int)output.size(); j++) { + if (output[j] == '@') { + output[j] = '!'; + } else { + break; + } + } + out << output << endl; + } + } + + string composer = mds[0].getComposer(); + if (!composer.empty()) { + out << "!!!COM: " << composer << endl; + } + + string cdate = mds[0].getComposerDate(); + if (!cdate.empty()) { + out << "!!!CDT: " << cdate << endl; + } + + string worktitle = mds[0].getWorkTitle(); + if (!worktitle.empty()) { + out << "!!!OTL: " << worktitle << endl; + } + + string movementtitle = mds[0].getMovementTitle(); + if (!movementtitle.empty()) { + out << "!!!OMV: " << movementtitle << endl; + } + + string opus = mds[0].getOpus(); + if (!opus.empty()) { + out << "!!!OPS: " << opus << endl; + } + + string number = mds[0].getNumber(); + if (!number.empty()) { + out << "!!!ONM: " << number << endl; + } + if (!m_omd.empty()) { - out << "!!!OMD:\t" << m_omd << endl; + out << "!!!OMD: " << m_omd << endl; } + out << outfile; + string source = mds[0].getSource(); + if (!source.empty()) { + out << "!!!SMS: " << source << endl; + } + + string encoder = mds[0].getEncoderName(); + if (!encoder.empty()) { + out << "!!!ENC: " << encoder << endl; + } + + string edate = mds[0].getEncoderDate(); + if (!edate.empty()) { + out << "!!!END: " << edate << endl; + } + + stringstream ss; + auto nowtime = std::chrono::system_clock::now(); + time_t currenttime = std::chrono::system_clock::to_time_t(nowtime); + ss << std::ctime(¤ttime); + out << "!!!ONB: Converted from MuseData with musedata2hum on " << ss.str(); + + string copyright = mds[0].getCopyright(); + if (!copyright.empty()) { + out << "!!!YEM: " << copyright << endl; + } + + // Convert comments in footer of last part: + int lastone = mds.getPartCount() - 1; + vector outputs; + for (int i=mds[lastone].getLineCount() - 1; i>=0; i--) { + if (mds[lastone][i].isAnyNote()) { + break; + } + if (mds[lastone].getLine(i).compare(0, 2, "@@") == 0) { + string output = mds[lastone].getLine(i); + for (int j=0; j<(int)output.size(); j++) { + if (output[j] == '@') { + output[j] = '!'; + } else { + break; + } + } + outputs.push_back(output); + } + } + + for (int i=(int)outputs.size() - 1; i>=0; i--) { + out << outputs[i] << endl; + } + return status; } @@ -59645,7 +61278,8 @@ bool Tool_musedata2hum::convert(ostream& out, MuseDataSet& mds) { bool Tool_musedata2hum::convertPart(HumGrid& outdata, MuseDataSet& mds, int index) { MuseData& part = mds[index]; m_lastfigure = NULL; - + m_lastnote = NULL; + m_lastbarnum = -1; m_tpq = part.getInitialTpq(); m_part = index; m_maxstaff = (int)mds.getPartCount(); @@ -59653,7 +61287,7 @@ bool Tool_musedata2hum::convertPart(HumGrid& outdata, MuseDataSet& mds, int inde bool status = true; int i = 0; while (i < part.getLineCount()) { - i = convertMeasure(outdata, part, i); + i = convertMeasure(outdata, part, index, i); } storePartName(outdata, part, index); @@ -59682,7 +61316,7 @@ void Tool_musedata2hum::storePartName(HumGrid& outdata, MuseData& part, int inde // Tool_musedata2hum::convertMeasure -- // -int Tool_musedata2hum::convertMeasure(HumGrid& outdata, MuseData& part, int startindex) { +int Tool_musedata2hum::convertMeasure(HumGrid& outdata, MuseData& part, int partindex, int startindex) { if (part.getLineCount() == 0) { return 1; } @@ -59695,7 +61329,10 @@ int Tool_musedata2hum::convertMeasure(HumGrid& outdata, MuseData& part, int star } GridMeasure* gm = getMeasure(outdata, starttime); - gm->setBarStyle(MeasureStyle::Plain); + setMeasureNumber(outdata[(int)outdata.size() - 1], part[startindex]); + if (partindex == 0) { + gm->setBarStyle(MeasureStyle::Plain); + } int i = startindex; for (i=startindex; isetTimeSigDur(m_timesigdur); if ((i < part.getLineCount()) && part[i].isBarline()) { - setMeasureStyle(outdata.back(), part[i]); + if (partindex == 0) { + // For now setting the barline style from the + // lowest staff. This is mostly because + // MEI/verovio can handle only one style + // on a system barline. But also because + // GridMeasure objects only has a setting + // for a single barline style. + setMeasureStyle(outdata.back(), part[i]); + } } return i; } +////////////////////////////// +// +// Tool_musedata2hum::setMeasureNumber -- +// + +void Tool_musedata2hum::setMeasureNumber(GridMeasure* gm, MuseRecord& mr) { + int pos = -1; + string line = mr.getLine(); + bool space = false; + for (int i=0; i<(int)line.size(); i++) { + if (isspace(line[i])) { + space = true; + continue; + } + if (!space) { + continue; + } + if (isdigit(line[i])) { + pos = i; + break; + } + } + if (pos < 0) { + return; + } + int num = stoi(line.substr(pos)); + if (m_lastbarnum >= 0) { + int temp = num; + num = m_lastbarnum; + m_lastbarnum = temp; + } + gm->setMeasureNumber(num); +} + ////////////////////////////// @@ -59730,9 +61409,25 @@ int Tool_musedata2hum::convertMeasure(HumGrid& outdata, MuseData& part, int star // void Tool_musedata2hum::setMeasureStyle(GridMeasure* gm, MuseRecord& mr) { + // Add bar numbers as well. string line = mr.getLine(); + string barstyle = mr.getMeasureFlagsString(); if (line.compare(0, 7, "mheavy2") == 0) { - gm->setStyle(MeasureStyle::Final); + if (barstyle.find(":|") != string::npos) { + gm->setStyle(MeasureStyle::RepeatBackward); + } else { + gm->setStyle(MeasureStyle::Final); + } + } else if (line.compare(0, 7, "mheavy3") == 0) { + if (barstyle.find("|:") != string::npos) { + gm->setStyle(MeasureStyle::RepeatForward); + } + } else if (line.compare(0, 7, "mheavy4") == 0) { + if (barstyle.find(":|:") != string::npos) { + gm->setStyle(MeasureStyle::RepeatBoth); + } + } else if (line.compare(0, 7, "mdouble") == 0) { + gm->setStyle(MeasureStyle::Double); } } @@ -59747,9 +61442,15 @@ void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { int part = m_part; int staff = 0; int maxstaff = m_maxstaff; - int voice = 0; + int layer = mr.getLayer(); + if (layer > 0) { + // convert to an index: + layer = layer - 1; + } + HumNum timestamp = mr.getAbsBeat(); string tok; + GridSlice* slice; if (mr.isBarline()) { tok = mr.getKernMeasureStyle(); @@ -59757,7 +61458,7 @@ void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { map attributes; mr.getAttributeMap(attributes); - string mtempo = attributes["D"]; + string mtempo = trimSpaces(attributes["D"]); if (!mtempo.empty()) { if (timestamp != 0) { string value = "!!!OMD: " + mtempo; @@ -59770,40 +61471,67 @@ void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { string mclef = attributes["C"]; if (!mclef.empty()) { string kclef = Convert::museClefToKernClef(mclef); - gm->addClefToken(kclef, timestamp, part, staff, voice, maxstaff); + if (!kclef.empty()) { + gm->addClefToken(kclef, timestamp, part, staff, layer, maxstaff); + } } string mkeysig = attributes["K"]; if (!mkeysig.empty()) { string kkeysig = Convert::museKeySigToKernKeySig(mkeysig); - gm->addKeySigToken(kkeysig, timestamp, part, staff, voice, maxstaff); + gm->addKeySigToken(kkeysig, timestamp, part, staff, layer, maxstaff); } string mtimesig = attributes["T"]; if (!mtimesig.empty()) { string ktimesig = Convert::museTimeSigToKernTimeSig(mtimesig); - gm->addTimeSigToken(ktimesig, timestamp, part, staff, voice, maxstaff); + slice = gm->addTimeSigToken(ktimesig, timestamp, part, staff, layer, maxstaff); setTimeSigDurInfo(ktimesig); + string kmeter = Convert::museMeterSigToKernMeterSig(mtimesig); + if (!kmeter.empty()) { + slice = gm->addMeterSigToken(kmeter, timestamp, part, staff, layer, maxstaff); + } } - - } else if (mr.isNote()) { + } else if (mr.isRegularNote()) { tok = mr.getKernNoteStyle(1, 1); - GridSlice* slice; - slice = gm->addDataToken(tok, timestamp, part, staff, voice, maxstaff); + slice = gm->addDataToken(tok, timestamp, part, staff, layer, maxstaff); + if (slice) { + mr.setVoice(slice->at(part)->at(staff)->at(layer)); + string gr = mr.getLayoutVis(); + if (gr.size() > 0) { + cerr << "GRAPHIC VERSION OF NOTEA " << gr << endl; + } + } + m_lastnote = slice->at(part)->at(staff)->at(layer)->getToken(); addNoteDynamics(slice, part, mr); } else if (mr.isFiguredHarmony()) { addFiguredHarmony(mr, gm, timestamp, part, maxstaff); } else if (mr.isChordNote()) { - cerr << "PROCESS CHORD NOTE HERE: " << mr << endl; + tok = mr.getKernNoteStyle(1, 1); + if (m_lastnote) { + string text = m_lastnote->getText(); + text += " "; + text += tok; + m_lastnote->setText(text); + } else { + cerr << "Warning: found chord note with no regular note to attach to" << endl; + } } else if (mr.isCueNote()) { cerr << "PROCESS CUE NOTE HERE: " << mr << endl; } else if (mr.isGraceNote()) { cerr << "PROCESS GRACE NOTE HERE: " << mr << endl; } else if (mr.isChordGraceNote()) { cerr << "PROCESS GRACE CHORD NOTE HERE: " << mr << endl; - } else if (mr.isRest()) { + } else if (mr.isAnyRest()) { tok = mr.getKernRestStyle(tpq); - gm->addDataToken(tok, timestamp, part, staff, voice, maxstaff); + slice = gm->addDataToken(tok, timestamp, part, staff, layer, maxstaff); + if (slice) { + mr.setVoice(slice->at(part)->at(staff)->at(layer)); + string gr = mr.getLayoutVis(); + if (gr.size() > 0) { + cerr << "GRAPHIC VERSION OF NOTEB " << gr << endl; + } + } } } @@ -59818,17 +61546,18 @@ void Tool_musedata2hum::addFiguredHarmony(MuseRecord& mr, GridMeasure* gm, HumNum timestamp, int part, int maxstaff) { string fh = mr.getFigureString(); fh = Convert::museFiguredBassToKernFiguredBass(fh); + GridSlice* slice; if (fh.find(":") == string::npos) { HTp fhtok = new HumdrumToken(fh); m_lastfigure = fhtok; - gm->addFiguredBass(fhtok, timestamp, part, maxstaff); + slice = gm->addFiguredBass(fhtok, timestamp, part, maxstaff); return; } if (!m_lastfigure) { HTp fhtok = new HumdrumToken(fh); m_lastfigure = fhtok; - gm->addFiguredBass(fhtok, timestamp, part, maxstaff); + slice = gm->addFiguredBass(fhtok, timestamp, part, maxstaff); return; } @@ -59875,7 +61604,7 @@ void Tool_musedata2hum::addFiguredHarmony(MuseRecord& mr, GridMeasure* gm, if (pieces.empty() || (position >= (int)pieces.size())) { HTp fhtok = new HumdrumToken(fh); m_lastfigure = fhtok; - gm->addFiguredBass(fhtok, timestamp, part, maxstaff); + slice = gm->addFiguredBass(fhtok, timestamp, part, maxstaff); return; } @@ -59893,7 +61622,7 @@ void Tool_musedata2hum::addFiguredHarmony(MuseRecord& mr, GridMeasure* gm, fh.erase(colpos, 1); HTp newtok = new HumdrumToken(fh); m_lastfigure = newtok; - gm->addFiguredBass(newtok, timestamp, part, maxstaff); + slice = gm->addFiguredBass(newtok, timestamp, part, maxstaff); } @@ -59932,17 +61661,21 @@ void Tool_musedata2hum::addNoteDynamics(GridSlice* slice, int part, } } + bool setdynamics = false; for (int i=0; i<(int)dynamics.size(); i++) { if (dynamics[i].empty()) { continue; } slice->at(part)->setDynamics(dynamics[i]); + setdynamics = true; break; // only one dynamic allowed (at least for now) } - HumGrid* grid = slice->getOwner(); - if (grid) { - grid->setDynamicsPresent(part); + if (setdynamics) { + HumGrid* grid = slice->getOwner(); + if (grid) { + grid->setDynamicsPresent(part); + } } } @@ -60001,6 +61734,34 @@ void Tool_musedata2hum::setInitialOmd(const string& omd) { +////////////////////////////// +// +// Tool_musedata2hum::trimSpaces -- +// + +string Tool_musedata2hum::trimSpaces(string input) { + string output; + int status = 0; + for (int i=0; i<(int)input.size(); i++) { + if (!status) { + if (isspace(input[i])) { + continue; + } + status = 1; + } + output += input[i]; + } + for (int i=(int)output.size()-1; i>=0; i--) { + if (isspace(output[i])) { + output.resize((int)output.size() - 1); + } else { + break; + } + } + return output; +} + + ////////////////////////////// From 960f3ad74633db6d4c16864181486ca07ef28b05 Mon Sep 17 00:00:00 2001 From: Craig Sapp Date: Sun, 6 Oct 2019 22:32:57 -0700 Subject: [PATCH 004/140] Figured bass and tuplet enhancement for humdrum-to-mei converter. --- include/vrv/iohumdrum.h | 13 ++++++++ src/iohumdrum.cpp | 74 +++++++++++++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/include/vrv/iohumdrum.h b/include/vrv/iohumdrum.h index 490f3361683..f2f535cbbaa 100644 --- a/include/vrv/iohumdrum.h +++ b/include/vrv/iohumdrum.h @@ -545,6 +545,7 @@ class HumdrumInput : public vrv::FileInputStream { void initializeIgnoreVector(hum::HumdrumFile &infile); bool hasIndent(hum::HTp tok); void prepareNonStandardKeySignature(KeySig *vrvkeysig, const std::string &ks, hum::HTp keytok); + void fixLargeTuplets(std::vector& tg); // header related functions: /////////////////////////////////////////// void createHeader(); @@ -730,6 +731,17 @@ class HumdrumInput : public vrv::FileInputStream { // converted into element in the MEI conversion. bool m_fb = false; + // m_fbstaff == state variable for keeping track of whether or not + // a particular staff (index) has figured bass. This is used by + // the tuplet@place parameter to avoid collision between tuplet numbers + // and figured bass. + std::vector m_fbstaff; + + // m_fbstates == position of the figured bass in relation to + // each staff index. 0 = no figured bass, -1 = figured bass below staff, + // +1 = figured bass above staff. + std::vector m_fbstates; + // m_setrightstem == used for setting right-side stem of half notes bool m_setrightstem = false; @@ -780,6 +792,7 @@ class HumdrumInput : public vrv::FileInputStream { // -1 = *below marker encountered in spine. // 0 = *auto neither above or below explicitly given (leave up to renderer). // Up to 1000 spines can be processed (see constructor). + // Indexed by track number of the spine it revers to. std::vector m_placement; // m_reverse == placement reversed or not reversed. currently used for **fb, diff --git a/src/iohumdrum.cpp b/src/iohumdrum.cpp index 0940ad422a5..cca2f27134b 100644 --- a/src/iohumdrum.cpp +++ b/src/iohumdrum.cpp @@ -568,22 +568,36 @@ bool HumdrumInput::convertHumdrum() stafftypes.push_back("**mens"); infile.getSpineStartList(staffstarts, stafftypes); + m_fbstates.resize(staffstarts.size()); + std::fill(m_fbstates.begin(), m_fbstates.end(), 0); + + m_fbstaff.resize(staffstarts.size()); + std::fill(m_fbstaff.begin(), m_fbstaff.end(), false); + std::vector spinestarts; infile.getSpineStartList(spinestarts); + int spineindex = -1; for (auto it : spinestarts) { - if (it->isDataType("**mxhm")) { + if (it->isDataType("**kern")) { + spineindex++; + } + else if (it->isDataType("**mxhm")) { m_harm = true; } - if (it->isDataType("**fing")) { + else if (it->isDataType("**mxhm")) { + m_harm = true; + } + else if (it->isDataType("**fing")) { m_fing = true; } - if (it->isDataType("**string")) { + else if (it->isDataType("**string")) { m_string = true; } - if (it->isDataType("**mens")) { + else if (it->isDataType("**mens")) { + spineindex++; m_mens = true; } - if (it->isDataType("**harm")) { + else if (it->isDataType("**harm")) { m_harm = true; } else if (it->isDataType("**rhrm")) { // **recip + **harm @@ -592,15 +606,27 @@ bool HumdrumInput::convertHumdrum() else if (it->getDataType().compare(0, 7, "**cdata") == 0) { m_harm = true; } - if (it->isDataType("**fb")) { + else if (it->isDataType("**fb")) { m_fb = true; + if (spineindex >= 0) { + m_fbstates[spineindex] = -1; + m_fbstaff[spineindex] = true; + } } - if (it->isDataType("**fba")) { + else if (it->isDataType("**fba")) { m_fb = true; + if (spineindex >= 0) { + m_fbstates[spineindex] = +1; + m_fbstaff[spineindex] = true; + } } - if (it->isDataType("**Bnum")) { + else if (it->isDataType("**Bnum")) { // older name m_fb = true; + if (spineindex >= 0) { + m_fbstates[spineindex] = -1; + m_fbstaff[spineindex] = true; + } } } @@ -4159,6 +4185,7 @@ void HumdrumInput::addFiguredBassForMeasure(int startline, int endline) return; } hum::HumdrumFile &infile = m_infiles[0]; + for (int i = startline; i < endline; ++i) { if (infile[i].isInterpretation()) { for (int j = 0; j < infile[i].getFieldCount(); ++j) { @@ -4290,6 +4317,9 @@ void HumdrumInput::addFiguredBassForMeasure(int startline, int endline) m_measure->AddChild(harm); int staffindex = m_rkern[kerntrack]; + if (m_placement.at(spinetrack)) { + m_fbstates.at(staffindex) = m_placement.at(spinetrack); + } hum::HumNum tstamp = getMeasureTstamp(token, staffindex); harm->SetTstamp(tstamp.getFloat()); setStaff(harm, staffindex + 1); @@ -5393,6 +5423,22 @@ bool HumdrumInput::convertStaffLayer(int track, int startline, int endline, int return fillContentsOfLayer(track, startline, endline, layerindex); } +////////////////////////////// +// +// HumdrumInput::fixLargeTuplets -- fix triple-breve/triplet-wholenote cases. +// + +void HumdrumInput::fixLargeTuplets(std::vector &tg) +{ + for (int i = 1; i < (int)tg.size(); i++) { + if ((tg[i].tupletstart == 2) && (tg[i].tupletend == 1) && (tg[i - 1].tupletstart == 1) + && (tg[i - 1].tupletend == 1)) { + tg[i].tupletstart = 0; + tg[i - 1].tupletend = 0; + } + } +} + ////////////////////////////// // // HumdrumInput::printGroupInfo -- @@ -5897,6 +5943,8 @@ bool HumdrumInput::fillContentsOfLayer(int track, int startline, int endline, in printGroupInfo(tgs, layerdata); } + fixLargeTuplets(tgs); + m_tupletscaling = 1; Note *note = NULL; @@ -9822,8 +9870,12 @@ void HumdrumInput::insertTuplet(std::vector &elements, std::vector< elements.push_back("tuplet"); pointers.push_back((void *)tuplet); - int staff = m_rkern[token->getTrack()]; + int staffindex = m_rkern[token->getTrack()]; int placement = 0; + if (m_fbstaff[staffindex]) { + placement = -m_fbstates.at(staffindex); + } + if (hasAboveParameter(layerdata[layerindex], "TUP")) { placement = +1; } @@ -9837,7 +9889,7 @@ void HumdrumInput::insertTuplet(std::vector &elements, std::vector< case +1: tuplet->SetBracketPlace(STAFFREL_basic_above); break; } } - if (ss[staff].verse) { + if (ss[staffindex].verse) { // If the music contains lyrics, force the tuplet above the staff. tuplet->SetBracketPlace(STAFFREL_basic_above); } @@ -10371,6 +10423,7 @@ void HumdrumInput::prepareBeamAndTupletGroups( if ((tuptop[i - 1] == -1) && (tupbot[i - 1] == -1)) { continue; } + if ((tuptop[i] != tuptop[i - 1]) || (tupbot[i] != tupbot[i - 1])) { if (tupletgroups[i] == tupletgroups[i - 1]) { correction++; @@ -14750,6 +14803,7 @@ void HumdrumInput::clear() m_breaks = false; m_duradj.clear(); m_nulls.clear(); + m_fbstates.clear(); } ////////////////////////////// From ade6d65e8f3dc30a50e248caa37eb7b774fa8367 Mon Sep 17 00:00:00 2001 From: Craig Sapp Date: Mon, 7 Oct 2019 20:59:13 -0700 Subject: [PATCH 005/140] Add more triplet breve corner cases. --- include/vrv/iohumdrum.h | 12 ++++++------ src/iohumdrum.cpp | 33 +++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/include/vrv/iohumdrum.h b/include/vrv/iohumdrum.h index f2f535cbbaa..33a7c7f17eb 100644 --- a/include/vrv/iohumdrum.h +++ b/include/vrv/iohumdrum.h @@ -545,7 +545,7 @@ class HumdrumInput : public vrv::FileInputStream { void initializeIgnoreVector(hum::HumdrumFile &infile); bool hasIndent(hum::HTp tok); void prepareNonStandardKeySignature(KeySig *vrvkeysig, const std::string &ks, hum::HTp keytok); - void fixLargeTuplets(std::vector& tg); + void fixLargeTuplets(std::vector &tg); // header related functions: /////////////////////////////////////////// void createHeader(); @@ -733,13 +733,13 @@ class HumdrumInput : public vrv::FileInputStream { // m_fbstaff == state variable for keeping track of whether or not // a particular staff (index) has figured bass. This is used by - // the tuplet@place parameter to avoid collision between tuplet numbers - // and figured bass. + // the tuplet@place parameter to avoid collision between tuplet numbers + // and figured bass. std::vector m_fbstaff; // m_fbstates == position of the figured bass in relation to - // each staff index. 0 = no figured bass, -1 = figured bass below staff, - // +1 = figured bass above staff. + // each staff index. 0 = no figured bass, -1 = figured bass below staff, + // +1 = figured bass above staff. std::vector m_fbstates; // m_setrightstem == used for setting right-side stem of half notes @@ -792,7 +792,7 @@ class HumdrumInput : public vrv::FileInputStream { // -1 = *below marker encountered in spine. // 0 = *auto neither above or below explicitly given (leave up to renderer). // Up to 1000 spines can be processed (see constructor). - // Indexed by track number of the spine it revers to. + // Indexed by track number of the spine it revers to. std::vector m_placement; // m_reverse == placement reversed or not reversed. currently used for **fb, diff --git a/src/iohumdrum.cpp b/src/iohumdrum.cpp index cca2f27134b..abd6564c757 100644 --- a/src/iohumdrum.cpp +++ b/src/iohumdrum.cpp @@ -5430,6 +5430,7 @@ bool HumdrumInput::convertStaffLayer(int track, int startline, int endline, int void HumdrumInput::fixLargeTuplets(std::vector &tg) { + // triplet-whole + triplet-breve cases for (int i = 1; i < (int)tg.size(); i++) { if ((tg[i].tupletstart == 2) && (tg[i].tupletend == 1) && (tg[i - 1].tupletstart == 1) && (tg[i - 1].tupletend == 1)) { @@ -5437,6 +5438,31 @@ void HumdrumInput::fixLargeTuplets(std::vector &tg tg[i - 1].tupletend = 0; } } + + // two triplet-halfs + triplet-breve case + for (int i = 2; i < (int)tg.size(); i++) { + if ((tg[i].tupletstart == 2) && (tg[i].tupletend == 1) && (tg[i - 1].tupletstart == 0) + && (tg[i - 1].tupletend == 1) && (tg[i - 2].tupletstart == 1) && (tg[i - 2].tupletend == 0)) { + tg[i - 1].numscale = 1; + tg[i - 2].numscale = 1; + tg[i].tupletstart = 0; + tg[i - 1].tupletend = 0; + tg[i].numbase = 2; + } + } + + // two triplet-halfs + triplet-breve case + two triplet-halfs + for (int i = 2; i < (int)tg.size(); i++) { + if ((tg[i].tupletstart == 0) && (tg[i].tupletend == 2) && (tg[i - 1].tupletstart == 2) + && (tg[i - 1].tupletend == 0) && (tg[i - 2].tupletstart == 1) && (tg[i - 2].tupletend == 1)) { + tg[i].tupletend = 1; + tg[i - 1].tupletstart = 0; + tg[i - 2].tupletend = 0; + tg[i - 2].numbase = 2; + tg[i].numscale = 1; + tg[i - 1].numscale = 1; + } + } } ////////////////////////////// @@ -5466,8 +5492,8 @@ void HumdrumInput::printGroupInfo(std::vector &tg, cerr << tg[i].beamend << "\t"; cerr << tg[i].gbeamstart << "\t"; cerr << tg[i].gbeamend << "\t"; - cerr << tg[i].tupletstart << "\t"; - cerr << tg[i].tupletend << "\t"; + cerr << "TS:" << tg[i].tupletstart << "\t"; + cerr << "TE:" << tg[i].tupletend << "\t"; cerr << tg[i].priority; cerr << endl; } @@ -5938,13 +5964,12 @@ bool HumdrumInput::fillContentsOfLayer(int track, int startline, int endline, in std::vector tgs; prepareBeamAndTupletGroups(layerdata, tgs); + fixLargeTuplets(tgs); if (m_debug) { printGroupInfo(tgs, layerdata); } - fixLargeTuplets(tgs); - m_tupletscaling = 1; Note *note = NULL; From 97d35720b557c26fb73f0f6b5d4078d3a7175476 Mon Sep 17 00:00:00 2001 From: Craig Sapp Date: Wed, 9 Oct 2019 12:23:55 -0700 Subject: [PATCH 006/140] Humlib updates --- include/hum/humlib.h | 8 +- src/hum/humlib.cpp | 184 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 187 insertions(+), 5 deletions(-) diff --git a/include/hum/humlib.h b/include/hum/humlib.h index d41e2e58782..58f5db469de 100644 --- a/include/hum/humlib.h +++ b/include/hum/humlib.h @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Sat Oct 5 22:33:07 PDT 2019 +// Last Modified: Tue Oct 8 12:41:24 PDT 2019 // Filename: humlib.h // URL: https://github.com/craigsapp/humlib/blob/master/include/humlib.h // Syntax: C++11 @@ -2063,6 +2063,7 @@ class HumdrumFileContent : public HumdrumFileStructure { void analyzeCrossStaffStemDirections (void); void analyzeCrossStaffStemDirections (HTp kernstart); + int hasPickup (void); protected: bool analyzeKernSlurs (HTp spinestart, std::vector& slurstarts, @@ -3324,6 +3325,7 @@ class Convert { HumNum scale = HumNum(1,4)); static std::string durationFloatToRecip (double duration, HumNum scale = HumNum(1,4)); + static HumNum timeSigToDurationInQuarter(HTp token); // Pitch processing, defined in Convert-pitch.cpp static std::string base40ToKern (int b40); @@ -5478,6 +5480,9 @@ class Tool_extract : public HumTool { vector& model, HumdrumFile& infile, int negate); void fillFieldDataByNoEmpty (vector& field, vector& subfield, vector& model, HumdrumFile& infile, int negate); + void fillFieldDataByNoRest (vector& field, vector& subfield, + vector& model, const string& searchstring, + HumdrumFile& infile, int state); private: @@ -5517,6 +5522,7 @@ class Tool_extract : public HumTool { int noEmptyQ = 0; // used with --no-empty option int emptyQ = 0; // used with --empty option int spineListQ = 0; // used with --spine option + int removerestQ = 0; // used with --no-rest option }; diff --git a/src/hum/humlib.cpp b/src/hum/humlib.cpp index 37590276264..369d51f46e9 100644 --- a/src/hum/humlib.cpp +++ b/src/hum/humlib.cpp @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Sat Oct 5 22:33:07 PDT 2019 +// Last Modified: Tue Oct 8 12:41:24 PDT 2019 // Filename: /include/humlib.cpp // URL: https://github.com/craigsapp/humlib/blob/master/src/humlib.cpp // Syntax: C++11 @@ -3083,6 +3083,35 @@ string Convert::durationFloatToRecip(double input, HumNum timebase) { +////////////////////////////// +// +// Convert::timeSigToDurationInQuarters -- Convert a **kern time signature +// into the duration of the measure for that time signature. +// output units are in quarter notes. +// Example: 6/8 => 3 quarters +// Example: 3/4 => 3 quarters +// Example: 3/8 => 3/2 quarters +// + +HumNum Convert::timeSigToDurationInQuarter(HTp token) { + HumRegex hre; + if (!token->isTimeSignature()) { + return 0; + } + // Handle extended **recip for denominator later... + if (!hre.search(token, "^\\*M(\\d+)/(\\d+)")) { + return 0; + } + int top = hre.getMatchInt(1); + int bot = hre.getMatchInt(2); + HumNum output = 4; + output /= bot; + output *= top; + return output; +} + + + ////////////////////////////// @@ -19471,6 +19500,61 @@ bool HumdrumFileContent::analyzeRScale(void) { +////////////////////////////// +// +// HumdrumFileContent::hasPickup -- Return false if there is no pickup measure. +// Return the barline index number if there is a pickup measure. A pickup measure +// is identified when the duration from the start of the file to the first +// barline is not zero or equal to the duration of the starting time signature. +// if there is not starting time signature, then there cannot be an identified +// pickup measure. +// + +int HumdrumFileContent::hasPickup(void) { + HumdrumFileContent& infile = *this; + int barline = -1; + HTp tsig = NULL; + for (int i=0; i 0) { + // second barline found, so stop looking for time signature + break; + } + barline = i; + continue; + } + if (!infile[i].isInterpretation()) { + continue; + } + if (tsig != NULL) { + continue; + } + for (int j=0; jisTimeSignature()) { + tsig = token; + break; + } + } + } + if (tsig == NULL) { + // no time signature so return 0 + return 0; + } + if (barline < 0) { + // no barlines in music + return 0; + } + HumNum mdur = infile[barline].getDurationFromStart(); + HumNum tdur = Convert::timeSigToDurationInQuarter(tsig); + if (mdur == tdur) { + return 0; + } + return barline; +} + + + ////////////////////////////// // @@ -50655,6 +50739,7 @@ Tool_extract::Tool_extract(void) { define("no-empty|no-empties=b", "Suppress spines with only null data tokens"); define("empty|empties=b", "Only keep spines with only null data tokens"); define("spine-list=b", "Show spine list and then exit"); + define("no-rest|no-rests=b", "remove **kern spines containing only rests (and their co-spines)"); define("debug=b", "print debugging information"); define("author=b"); // author of program @@ -50732,6 +50817,9 @@ void Tool_extract::processFile(HumdrumFile& infile) { interpstate); } else if (reverseQ) { reverseSpines(field, subfield, model, infile, reverseInterp); + } else if (removerestQ) { + fillFieldDataByNoRest(field, subfield, model, grepString, infile, + interpstate); } else if (grepQ) { fillFieldDataByGrep(field, subfield, model, grepString, infile, interpstate); @@ -50773,7 +50861,7 @@ void Tool_extract::processFile(HumdrumFile& infile) { // infile.printNonemptySegmentLabel(m_humdrum_text); // analyze the input file according to command-line options - if (fieldQ || grepQ) { + if (fieldQ || grepQ || removerestQ) { extractFields(infile, field, subfield, model); } else if (excludeQ) { excludeFields(infile, field, subfield, model); @@ -50893,6 +50981,93 @@ void Tool_extract::fillFieldDataByNoEmpty(vector& field, vector& subfi +////////////////////////////// +// +// Tool_extract::fillFieldDataByNoRest -- Find the spines which +// contain only rests and remove them. Also remove cospines (non-kern spines +// to the right of the kern spine containing only rests). +// + +void Tool_extract::fillFieldDataByNoRest(vector& field, vector& subfield, + vector& model, const string& searchstring, HumdrumFile& infile, + int state) { + + field.reserve(infile.getMaxTrack()+1); + subfield.reserve(infile.getMaxTrack()+1); + model.reserve(infile.getMaxTrack()+1); + field.resize(0); + subfield.resize(0); + model.resize(0); + + vector tracks; + tracks.resize(infile.getMaxTrack()+1); + fill(tracks.begin(), tracks.end(), 0); + int track; + + int i, j; + for (i=0; iisKern()) { + continue; + } + if (token->isNull()) { + continue; + } + if (token->isRest()) { + continue; + } + track = token->getTrack(); + tracks[track] = 1; + } + } + + // deal with co-spines + vector sstarts; + infile.getSpineStartList(sstarts); + for (int i=0; i<(int)sstarts.size(); i++) { + if (!sstarts[i]->isKern()) { + track = sstarts[i]->getTrack(); + tracks[track] = 1; + } + } + + // remove co-spines attached to removed kern spines + for (int i=0; i<(int)sstarts.size(); i++) { + if (!sstarts[i]->isKern()) { + continue; + } + if (tracks[sstarts[i]->getTrack()] != 0) { + continue; + } + for (int j=i+1; j<(int)sstarts.size(); j++) { + if (sstarts[j]->isKern()) { + break; + } + track = sstarts[j]->getTrack(); + tracks[track] = 0; + } + } + + int zero = 0; + for (i=1; i<(int)tracks.size(); i++) { + if (state != 0) { + tracks[i] = !tracks[i]; + } + if (tracks[i]) { + field.push_back(i); + subfield.push_back(zero); + model.push_back(zero); + } + } + +} + + + ////////////////////////////// // // Tool_extract::fillFieldDataByGrep -- @@ -50917,11 +51092,11 @@ void Tool_extract::fillFieldDataByGrep(vector& field, vector& subfield int i, j; for (i=0; igetTrack(); tracks[track] = 1; } @@ -52514,6 +52689,7 @@ void Tool_extract::initialize(HumdrumFile& infile) { } } + removerestQ = getBoolean("no-rest"); noEmptyQ = getBoolean("no-empty"); emptyQ = getBoolean("empty"); fieldQ = getBoolean("f"); From 6e1c05959a95264039e29e647d33932a11ba7b24 Mon Sep 17 00:00:00 2001 From: Craig Sapp Date: Thu, 10 Oct 2019 10:39:49 -0700 Subject: [PATCH 007/140] Humlib update. --- include/hum/humlib.h | 3 ++- src/hum/humlib.cpp | 54 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/include/hum/humlib.h b/include/hum/humlib.h index 58f5db469de..6df40af5cad 100644 --- a/include/hum/humlib.h +++ b/include/hum/humlib.h @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Tue Oct 8 12:41:24 PDT 2019 +// Last Modified: Wed Oct 9 14:56:17 PDT 2019 // Filename: humlib.h // URL: https://github.com/craigsapp/humlib/blob/master/include/humlib.h // Syntax: C++11 @@ -5154,6 +5154,7 @@ class Tool_composite : public HumTool { protected: void processFile (HumdrumFile& infile); void initialize (void); + HumNum getLineDuration (HumdrumFile& infile, int index, vector& isNull); private: string m_pitch = "e"; diff --git a/src/hum/humlib.cpp b/src/hum/humlib.cpp index 369d51f46e9..029a9294790 100644 --- a/src/hum/humlib.cpp +++ b/src/hum/humlib.cpp @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Tue Oct 8 12:41:24 PDT 2019 +// Last Modified: Wed Oct 9 14:56:17 PDT 2019 // Filename: /include/humlib.cpp // URL: https://github.com/craigsapp/humlib/blob/master/src/humlib.cpp // Syntax: C++11 @@ -46285,29 +46285,36 @@ void Tool_composite::processFile(HumdrumFile& infile) { } vector isRest(infile.getLineCount(), false); + vector isNull(infile.getLineCount(), false); for (int i=0; iisNull()) { + continue; + } + allnull = false; if (!tok->isKern()) { continue; } if (tok->isNote()) { - // allnull = false; allrest = false; break; } if (tok->isRest()) { - // allnull = false; + allnull = false; } } if (allrest) { isRest[i] = true; } + if (allnull) { + isNull[i] = true; + } } string pstring = getString("pitch"); @@ -46390,7 +46397,13 @@ void Tool_composite::processFile(HumdrumFile& infile) { } continue; } - string recip = Convert::durationToRecip(infile[i].getDuration()); + HumNum duration = getLineDuration(infile, i, isNull); + string recip; + if (isNull[i]) { + recip = "."; + } else { + recip = Convert::durationToRecip(duration); + } if (appendQ) { token = infile.token(i, infile[i].getFieldCount() - 1); @@ -46398,7 +46411,9 @@ void Tool_composite::processFile(HumdrumFile& infile) { token = infile.token(i, 0); } if (isRest[i]) { - recip += "r"; + if (!isNull[i]) { + recip += "r"; + } } else { recip += pstring; } @@ -46430,6 +46445,33 @@ void Tool_composite::processFile(HumdrumFile& infile) { } +////////////////////////////// +// +// Tool_composite::getLineDuration -- Return the duration of the line, but return +// 0 if the line only contains nulls. Also add the duration of any subsequent +// lines that are null lines before any data content lines. + +HumNum Tool_composite::getLineDuration(HumdrumFile& infile, int index, vector& isNull) { + if (isNull[index]) { + return 0; + } + if (!infile[index].isData()) { + return 0; + } + HumNum output = infile[index].getDuration(); + for (int i=index+1; i Date: Wed, 16 Oct 2019 16:20:38 -0700 Subject: [PATCH 008/140] Finish automatic placement of tuplet numbers when figured bass is present. --- src/iohumdrum.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/iohumdrum.cpp b/src/iohumdrum.cpp index abd6564c757..2e306b17197 100644 --- a/src/iohumdrum.cpp +++ b/src/iohumdrum.cpp @@ -9897,8 +9897,10 @@ void HumdrumInput::insertTuplet(std::vector &elements, std::vector< int staffindex = m_rkern[token->getTrack()]; int placement = 0; - if (m_fbstaff[staffindex]) { - placement = -m_fbstates.at(staffindex); + + int rstaffindex = (int)m_fbstaff.size() - staffindex - 1; + if (m_fbstaff[rstaffindex]) { + placement = -m_fbstates.at(rstaffindex); } if (hasAboveParameter(layerdata[layerindex], "TUP")) { From 520a7c2aef61761950fe59597f454dca291fed77 Mon Sep 17 00:00:00 2001 From: Craig Sapp Date: Wed, 16 Oct 2019 17:02:25 -0700 Subject: [PATCH 009/140] Humlib update --- include/hum/humlib.h | 41 ++- src/hum/humlib.cpp | 621 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 638 insertions(+), 24 deletions(-) diff --git a/include/hum/humlib.h b/include/hum/humlib.h index 6df40af5cad..2b4956078ef 100644 --- a/include/hum/humlib.h +++ b/include/hum/humlib.h @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Wed Oct 9 14:56:17 PDT 2019 +// Last Modified: Wed Oct 16 13:40:45 PDT 2019 // Filename: humlib.h // URL: https://github.com/craigsapp/humlib/blob/master/include/humlib.h // Syntax: C++11 @@ -2639,6 +2639,7 @@ class MuseRecordBasic { bool hasTpq (void); int getTpq (void); void setTpq (int value); + static std::string musedataToUtf8 (std::string& input); protected: std::string m_recordString; // actual characters on line @@ -2868,6 +2869,7 @@ class MuseRecord : public MuseRecordBasic { int textUnderlayQ (void); int getVerseCount (void); std::string getVerse (int index); + std::string getVerseUtf8 (int index); // general functions for note records: std::string getKernNoteStyle (int beams = 0, int stems = 0); @@ -3842,6 +3844,7 @@ class GridSlice : public std::vector { void initializePartStaves (std::vector& partdata); void initializeBySlice (GridSlice* slice); void initializeByStaffCount(int staffcount); + void reportVerseCount (int partindex, int staffindex, int count); HumNum getDuration (void); void setDuration (HumNum duration); @@ -3943,6 +3946,7 @@ class HumGrid : public std::vector { void setFiguredBassPresent (int partindex); void setHarmonyPresent (int partindex); void setVerseCount (int partindex, int staffindex, int count); + void reportVerseCount (int partindex, int staffindex, int count); void setHarmonyCount (int partindex, int count); void removeRedundantClefChanges (void); void removeSibeliusIncipit (void); @@ -5748,6 +5752,40 @@ ostream& operator<<(ostream& out, NotePoint& np); +class Tool_humsar : public HumTool { + public: + Tool_humsar (void); + ~Tool_humsar () {}; + + bool run (HumdrumFileSet& infiles); + bool run (HumdrumFile& infile); + bool run (const string& indata, ostream& out); + bool run (HumdrumFile& infile, ostream& out); + + protected: + void processFile (HumdrumFile& infile); + void searchAndReplaceInterpretation(HumdrumFile& infile); + void searchAndReplaceData(HumdrumFile& infile); + void searchAndReplaceBarline(HumdrumFile& infile); + void initialize (void); + void initializeSegment (HumdrumFile& infile); + bool isValid (HTp token); + bool isValidDataType (HTp token); + bool isValidSpine (HTp token); + void fillInExInterpList (void); + + private: + std::string m_search; // search string + std::string m_replace; // replace string + bool m_interpretation = false; // process only interpretation records + bool m_modified = false; + std::vector m_exinterps; // list of exclusive interpretations to process + std::vector m_spines; // usar with -s option + std::string m_grepoptions; + +}; + + class Tool_humsort : public HumTool { public: Tool_humsort (void); @@ -6357,6 +6395,7 @@ class Tool_musedata2hum : public HumTool { void storePartName (HumGrid& outdata, MuseData& part, int index); void addNoteDynamics (GridSlice* slice, int part, MuseRecord& mr); + void addLyrics (GridSlice* slice, int part, int staff, MuseRecord& mr); void addFiguredHarmony (MuseRecord& mr, GridMeasure* gm, HumNum timestamp, int part, int maxstaff); std::string trimSpaces (std::string input); diff --git a/src/hum/humlib.cpp b/src/hum/humlib.cpp index 029a9294790..16531e1e844 100644 --- a/src/hum/humlib.cpp +++ b/src/hum/humlib.cpp @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Wed Oct 9 14:56:17 PDT 2019 +// Last Modified: Wed Oct 16 13:40:45 PDT 2019 // Filename: /include/humlib.cpp // URL: https://github.com/craigsapp/humlib/blob/master/src/humlib.cpp // Syntax: C++11 @@ -1522,14 +1522,16 @@ string Convert::museFiguredBassToKernFiguredBass(const string& mfb) { for (int i=0; i<(int)mfb.size(); i++) { if (mfb[i] == 'b') { // blank spot in figure stack output += 'X'; + } else if (mfb[i] == 'f') { // flat + output += '-'; } else if ((mfb[i] == '&') && (i < (int)mfb.size()-1) && (mfb[i+1] == '0')) { output += ":"; i++; - } else if ((mfb[i] == '/')) { // assuming means flat + } else if ((mfb[i] == '/')) { // assuming slash means flat output += "-/"; - } else if ((mfb[i] == '\\')) { // assuming means sharp + } else if ((mfb[i] == '\\')) { // assuming slash means sharp output += "#/"; - } else if ((mfb[i] == '+')) { // assuming means sharp + } else if ((mfb[i] == '+')) { // assuming slash means sharp output += "#|"; } else if (isdigit(mfb[i]) && (i < (int)mfb.size() - 1) && (mfb[i+1] == '#')) { output += mfb[i]; @@ -1538,7 +1540,7 @@ string Convert::museFiguredBassToKernFiguredBass(const string& mfb) { i++; } else if (isdigit(mfb[i]) && (i < (int)mfb.size() - 1) && (mfb[i+1] == 'f')) { output += mfb[i]; - output += mfb[i+1]; + output += '-'; output += 'r'; i++; } else if (isdigit(mfb[i]) && (i < (int)mfb.size() - 1) && (mfb[i+1] == 'n')) { @@ -4201,6 +4203,21 @@ GridSlice* GridMeasure::addFiguredBass(HTp token, HumNum timestamp, int part, in } } + + if ((!processed) && (!this->empty()) && (this->back()->getTimestamp() == timestamp)) { + // This case is related to putting figures on the first note in a measure + // but the note is not yet there, but the key signature/meter/clef etc. have already + // been added. + gs = new GridSlice(this, timestamp, SliceType::Notes, maxstaff); + int staff = 0; + int voice = 0; + string null = "."; + gs->addToken(null, part, staff, voice); + gs->at(part)->setFiguredBass(token); + this->push_back(gs); + processed = true; + } + if (!processed) { cerr << "Error: could not insert figured bass: " << token << endl; } else { @@ -4213,6 +4230,8 @@ GridSlice* GridMeasure::addFiguredBass(HTp token, HumNum timestamp, int part, in return gs; } + + ////////////////////////////// // // GridMeasure::addFiguredBass -- @@ -4222,6 +4241,7 @@ GridSlice* GridMeasure::addFiguredBass(const string& tok, HumNum timestamp, int bool processed = false; if (this->empty() || (this->back()->getTimestamp() < timestamp)) { + // add a new GridSlice to an empty list or at end of list if timestamp // is after last entry in list. gs = new GridSlice(this, timestamp, SliceType::Notes, maxstaff); @@ -4259,6 +4279,20 @@ GridSlice* GridMeasure::addFiguredBass(const string& tok, HumNum timestamp, int } } + if ((!processed) && (!this->empty()) && (this->back()->getTimestamp() == timestamp)) { + // This case is related to putting figures on the first note in a measure + // but the note is not yet there, but the key signature/meter/clef etc. have already + // been added. + gs = new GridSlice(this, timestamp, SliceType::Notes, maxstaff); + int staff = 0; + int voice = 0; + string null = "."; + gs->addToken(null, part, staff, voice); + gs->at(part)->setFiguredBass(tok); + this->push_back(gs); + processed = true; + } + if (!processed) { cerr << "Error: could not inser figured bass: " << tok << endl; } else { @@ -6137,6 +6171,21 @@ void GridSlice::invalidate(void) { +////////////////////////////// +// +// GridSlice::reportVerseCount -- +// + +void GridSlice::reportVerseCount(int partindex, int staffindex, int count) { + if (!m_owner) { + return; + } + m_owner->reportVerseCount(partindex, staffindex, count); +} + + + + ////////////////////////////// // // GridStaff::GridStaff -- Constructor. @@ -7192,6 +7241,34 @@ void HumGrid::setHarmonyCount(int partindex, int count) { +////////////////////////////// +// +// HumGrid::reportVerseCount -- +// + +void HumGrid::reportVerseCount(int partindex, int staffindex, int count) { + if (count <= 0) { + return; + } + int staffnumber = staffindex + 1; + int partsize = (int)m_verseCount.size(); + if (partindex >= partsize) { + m_verseCount.resize(partindex+1); + } + int staffcount = (int)m_verseCount.at(partindex).size(); + if (staffnumber >= staffcount) { + m_verseCount.at(partindex).resize(staffnumber+1); + for (int i=staffcount; i<=staffnumber; i++) { + m_verseCount.at(partindex).at(i) = 0; + } + } + if (count > m_verseCount.at(partindex).at(staffnumber)) { + m_verseCount.at(partindex).at(staffnumber) = count; + } +} + + + ////////////////////////////// // // HumGrid::setVerseCount -- @@ -32137,6 +32214,18 @@ string MuseRecord::getVerse(int index) { +////////////////////////////// +// +// MuseRecord::getVerseUtf8 -- +// + +string MuseRecord::getVerseUtf8(int index) { + string tverse = getVerse(index); + return MuseRecord::musedataToUtf8(tverse); +} + + + ////////////////////////////// // // MuseRecord::getKernNoteStyle -- @@ -33370,6 +33459,7 @@ void MuseRecord::zerase(string& inout, int num) { + ////////////////////////////// // // MuseRecordBasic::MuseRecordBasic -- @@ -34659,6 +34749,113 @@ std::string MuseRecordBasic::getLayoutVis(void) { } + +////////////////////////////// +// +// MuseRecordBasic::musedataToUtf8 -- +// + +string MuseRecordBasic::musedataToUtf8(string& input) { + string output; + int isize = (int)input.size(); + for (int i=0; i= isize) { + output += input[i]; + continue; + } + string st = input.substr(i+1, 2); + + // graves + if (st == "A8") { output += (char)0xc3; output += (char)0x80; i+=2; continue; } + if (st == "E8") { output += (char)0xc3; output += (char)0x88; i+=2; continue; } + if (st == "I8") { output += (char)0xc3; output += (char)0x8c; i+=2; continue; } + if (st == "O8") { output += (char)0xc3; output += (char)0x92; i+=2; continue; } + if (st == "U8") { output += (char)0xc3; output += (char)0x99; i+=2; continue; } + if (st == "a8") { output += (char)0xc3; output += (char)0xa0; i+=2; continue; } + if (st == "e8") { output += (char)0xc3; output += (char)0xa8; i+=2; continue; } + if (st == "i8") { output += (char)0xc3; output += (char)0xac; i+=2; continue; } + if (st == "o8") { output += (char)0xc3; output += (char)0xb2; i+=2; continue; } + if (st == "u8") { output += (char)0xc3; output += (char)0xb9; i+=2; continue; } + + // acutes + if (st == "A7") { output += (char)0xc3; output += (char)0x81; i+=2; continue; } + if (st == "E7") { output += (char)0xc3; output += (char)0x89; i+=2; continue; } + if (st == "I7") { output += (char)0xc3; output += (char)0x8d; i+=2; continue; } + if (st == "O7") { output += (char)0xc3; output += (char)0x93; i+=2; continue; } + if (st == "U7") { output += (char)0xc3; output += (char)0x9a; i+=2; continue; } + if (st == "a7") { output += (char)0xc3; output += (char)0xa1; i+=2; continue; } + if (st == "e7") { output += (char)0xc3; output += (char)0xa9; i+=2; continue; } + if (st == "i7") { output += (char)0xc3; output += (char)0xad; i+=2; continue; } + if (st == "o7") { output += (char)0xc3; output += (char)0xb3; i+=2; continue; } + if (st == "u7") { output += (char)0xc3; output += (char)0xba; i+=2; continue; } + + // umlauts + if (st == "A3") { output += (char)0xc3; output += (char)0x84; i+=2; continue; } + if (st == "E3") { output += (char)0xc3; output += (char)0x8b; i+=2; continue; } + if (st == "I3") { output += (char)0xc3; output += (char)0x8f; i+=2; continue; } + if (st == "O3") { output += (char)0xc3; output += (char)0x96; i+=2; continue; } + if (st == "U3") { output += (char)0xc3; output += (char)0x9c; i+=2; continue; } + if (st == "a3") { output += (char)0xc3; output += (char)0xa4; i+=2; continue; } + if (st == "e3") { output += (char)0xc3; output += (char)0xab; i+=2; continue; } + if (st == "i3") { output += (char)0xc3; output += (char)0xaf; i+=2; continue; } + if (st == "o3") { output += (char)0xc3; output += (char)0xb6; i+=2; continue; } + if (st == "u3") { output += (char)0xc3; output += (char)0xbc; i+=2; continue; } + + // other + if (st == "s2") { output += (char)0xc3; output += (char)0x9f; i+=2; continue; } // eszett + + // Older Musedata files reverse the number and letters: + + // graves + if (st == "8A") { output += (char)0xc3; output += (char)0x80; i+=2; continue; } + if (st == "8E") { output += (char)0xc3; output += (char)0x88; i+=2; continue; } + if (st == "8I") { output += (char)0xc3; output += (char)0x8c; i+=2; continue; } + if (st == "8O") { output += (char)0xc3; output += (char)0x92; i+=2; continue; } + if (st == "8U") { output += (char)0xc3; output += (char)0x99; i+=2; continue; } + if (st == "8a") { output += (char)0xc3; output += (char)0xa0; i+=2; continue; } + if (st == "8e") { output += (char)0xc3; output += (char)0xa8; i+=2; continue; } + if (st == "8i") { output += (char)0xc3; output += (char)0xac; i+=2; continue; } + if (st == "8o") { output += (char)0xc3; output += (char)0xb2; i+=2; continue; } + if (st == "8u") { output += (char)0xc3; output += (char)0xb9; i+=2; continue; } + + // acutes + if (st == "7A") { output += (char)0xc3; output += (char)0x81; i+=2; continue; } + if (st == "7E") { output += (char)0xc3; output += (char)0x89; i+=2; continue; } + if (st == "7I") { output += (char)0xc3; output += (char)0x8d; i+=2; continue; } + if (st == "7O") { output += (char)0xc3; output += (char)0x93; i+=2; continue; } + if (st == "7U") { output += (char)0xc3; output += (char)0x9a; i+=2; continue; } + if (st == "7a") { output += (char)0xc3; output += (char)0xa1; i+=2; continue; } + if (st == "7e") { output += (char)0xc3; output += (char)0xa9; i+=2; continue; } + if (st == "7i") { output += (char)0xc3; output += (char)0xad; i+=2; continue; } + if (st == "7o") { output += (char)0xc3; output += (char)0xb3; i+=2; continue; } + if (st == "7u") { output += (char)0xc3; output += (char)0xba; i+=2; continue; } + + // umlauts + if (st == "3A") { output += (char)0xc3; output += (char)0x84; i+=2; continue; } + if (st == "3E") { output += (char)0xc3; output += (char)0x8b; i+=2; continue; } + if (st == "3I") { output += (char)0xc3; output += (char)0x8f; i+=2; continue; } + if (st == "3O") { output += (char)0xc3; output += (char)0x96; i+=2; continue; } + if (st == "3U") { output += (char)0xc3; output += (char)0x9c; i+=2; continue; } + if (st == "3a") { output += (char)0xc3; output += (char)0xa4; i+=2; continue; } + if (st == "3e") { output += (char)0xc3; output += (char)0xab; i+=2; continue; } + if (st == "3i") { output += (char)0xc3; output += (char)0xaf; i+=2; continue; } + if (st == "3o") { output += (char)0xc3; output += (char)0xb6; i+=2; continue; } + if (st == "3u") { output += (char)0xc3; output += (char)0xbc; i+=2; continue; } + + // other + if (st == "2s") { output += (char)0xc3; output += (char)0x9f; i+=2; continue; } // eszett + + } + + return output; +} + + + /////////////////////////////////////////////////////////////////////////// @@ -52937,6 +53134,8 @@ bool Tool_filter::run(HumdrumFileSet& infiles) { RUNTOOL(homophonic2, infile, commands[i].second, status); } else if (commands[i].first == "hproof") { RUNTOOL(hproof, infile, commands[i].second, status); + } else if (commands[i].first == "humsar") { + RUNTOOL(humsar, infile, commands[i].second, status); } else if (commands[i].first == "imitation") { RUNTOOL(imitation, infile, commands[i].second, status); } else if (commands[i].first == "extract") { @@ -54486,6 +54685,341 @@ ostream& operator<<(ostream& out, NotePoint& np) { +///////////////////////////////// +// +// Tool_humsar::Tool_humsar -- Set the recognized options for the tool. +// + +Tool_humsar::Tool_humsar(void) { + define("q|query=s", "query string"); + define("r|replace=s", "replace string"); + define("k|kern=b", "process kern spines only"); + define("x|exinterps|exinerp|exclusive-interpretation|exclusive-interpretations=s", "process only specified spines"); + define("s|spine|spines=s", "list of spines to process"); + define("I|interpretation=b", "process interpretation tokens only"); + define("i|ignore-case=b", "Ignore case of letters"); +} + + + +///////////////////////////////// +// +// Tool_humsar::run -- Do the main work of the tool. +// + +bool Tool_humsar::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; iisNull()) { + // Don't mess with null interpretations + continue; + } + if (!isValid(token)) { + continue; + } + if (hre.search(token, isearch, m_grepoptions)) { + string text = token->getText(); + hre.replaceDestructive(text, m_replace, isearch, m_grepoptions); + hre.replaceDestructive(text, "", "^=+"); + text = "=" + text; + token->setText(text); + m_modified = true; + } + } + } +} + + +////////////////////////////// +// +// Tool_humsar::searchAndReplaceInterpretation -- +// + +void Tool_humsar::searchAndReplaceInterpretation(HumdrumFile& infile) { + string isearch; + if (m_search[0] == '^') { + isearch = "^\\*" + m_search.substr(1); + } else { + isearch = "^\\*.*" + m_search; + } + HumRegex hre; + for (int i=0; iisNull()) { + // Don't mess with null interpretations + continue; + } + if (!isValid(token)) { + continue; + } + if (hre.search(token, isearch, m_grepoptions)) { + string text = token->getText(); + hre.replaceDestructive(text, m_replace, isearch, m_grepoptions); + hre.replaceDestructive(text, "", "^\\*+"); + text = "*" + text; + token->setText(text); + m_modified = true; + } + } + } +} + + + +////////////////////////////// +// +// Tool_humsar::searchAndReplaceData -- +// + +void Tool_humsar::searchAndReplaceData(HumdrumFile& infile) { + string dsearch = m_search; + + HumRegex hre; + for (int i=0; iisNull()) { + // Don't mess with null interpretations + continue; + } + if (!isValid(token)) { + continue; + } + if (hre.search(token, dsearch, m_grepoptions)) { + string text = token->getText(); + hre.replaceDestructive(text, m_replace, dsearch, m_grepoptions); + if (text == "") { + text = "."; + } + token->setText(text); + m_modified = true; + } + } + } +} + + + +////////////////////////////// +// +// Tool_humsar::isValidDataType -- usar with -x and -k options. +// + +bool Tool_humsar::isValidDataType(HTp token) { + if (m_exinterps.empty()) { + return true; + } + string datatype = token->getDataType(); + for (int i=0; i<(int)m_exinterps.size(); i++) { + if (datatype == m_exinterps[i]) { + return true; + } + } + return false; +} + + + +////////////////////////////// +// +// Tool_humsar::isValidSpine -- usar with -s option. +// + +bool Tool_humsar::isValidSpine(HTp token) { + if (m_spines.empty()) { + return true; + } + int track = token->getTrack(); + return m_spines.at(track); +} + + + +////////////////////////////// +// +// Tool_humsar::isValid -- +// + +bool Tool_humsar::isValid(HTp token) { + if (isValidDataType(token) && isValidSpine(token)) { + return true; + } + return false; +} + + + + + ///////////////////////////////// // // Tool_humsort::Tool_humsort -- Set the recognized options for the tool. @@ -60785,10 +61319,18 @@ void Tool_msearch::fillWords(HumdrumFile& infile, vector& words) { void Tool_msearch::fillWordsForTrack(vector& words, HTp starttoken) { - HTp tok = starttoken->getNextNNDT(); + HTp tok = starttoken->getNextToken(); while (tok != NULL) { if (tok->empty()) { - tok = tok->getNextNNDT(); + tok = tok->getNextToken(); + continue; + } + if (tok->isNull()) { + tok = tok->getNextToken(); + continue; + } + if (!tok->isData()) { + tok = tok->getNextToken(); continue; } if (tok->at(0) == '-') { @@ -60799,7 +61341,7 @@ void Tool_msearch::fillWordsForTrack(vector& words, words.back()->fullword.pop_back(); } } - tok = tok->getNextNNDT(); + tok = tok->getNextToken(); continue; } else { // start a new word @@ -60816,7 +61358,7 @@ void Tool_msearch::fillWordsForTrack(vector& words, } temp->starttoken = tok; words.push_back(temp); - tok = tok->getNextNNDT(); + tok = tok->getNextToken(); continue; } } @@ -60840,7 +61382,7 @@ void Tool_msearch::doTextSearch(HumdrumFile& infile, NoteGrid& grid, HumRegex hre; for (int i=0; i<(int)query.size(); i++) { for (int j=0; j<(int)words.size(); j++) { - if (hre.search(words[j]->fullword, query[i].word, "i")) { + if (hre.search(words.at(j)->fullword, query.at(i).word, "i")) { tcount++; markTextMatch(infile, *words[j]); } @@ -60946,13 +61488,23 @@ void Tool_msearch::markMatch(HumdrumFile& infile, vector& match) { string text; while (tok && (tok != mend)) { if (!tok->isData()) { - return; + tok = tok->getNextToken(); + continue; + } + if (tok->isNull()) { + tok = tok->getNextToken(); + continue; + } + if (tok->empty()) { + // skip marking null tokens + tok = tok->getNextToken(); + continue; } text = tok->getText() + m_marker; tok->setText(text); - tok = tok->getNextNNDT(); + tok = tok->getNextToken(); if (tok && !tok->isKern()) { - cerr << "STRANGE LINKING WITH TEXT SPINE IN getNextNNDT()" << endl; + cerr << "STRANGE LINKING WITH TEXT SPINE" << endl; break; } } @@ -60962,11 +61514,10 @@ void Tool_msearch::markMatch(HumdrumFile& infile, vector& match) { ////////////////////////////// // -// Tool_msearch::markTextMatch -- assumes monophonic music. +// Tool_msearch::markTextMatch -- assumes monophonic voices. // void Tool_msearch::markTextMatch(HumdrumFile& infile, TextInfo& word) { -// ggg HTp mstart = word.starttoken; HTp mnext = word.nexttoken; // while (mstart && !mstart->isKern()) { @@ -60997,7 +61548,12 @@ void Tool_msearch::markTextMatch(HumdrumFile& infile, TextInfo& word) { string text; while (tok && (tok != mnext)) { if (!tok->isData()) { - return; + tok = tok->getNextToken(); + continue; + } + if (tok->isNull()) { + tok = tok->getNextToken(); + continue; } text = tok->getText(); if ((!text.empty()) && (text.back() == '-')) { @@ -61008,7 +61564,7 @@ void Tool_msearch::markTextMatch(HumdrumFile& infile, TextInfo& word) { text += m_marker; } tok->setText(text); - tok = tok->getNextNNDT(); + tok = tok->getNextToken(); } } @@ -61668,7 +62224,7 @@ void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { HumNum timestamp = mr.getAbsBeat(); string tok; - GridSlice* slice; + GridSlice* slice = NULL; if (mr.isBarline()) { tok = mr.getKernMeasureStyle(); @@ -61722,6 +62278,7 @@ void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { } m_lastnote = slice->at(part)->at(staff)->at(layer)->getToken(); addNoteDynamics(slice, part, mr); + addLyrics(slice, part, staff, mr); } else if (mr.isFiguredHarmony()) { addFiguredHarmony(mr, gm, timestamp, part, maxstaff); } else if (mr.isChordNote()) { @@ -61764,18 +62321,17 @@ void Tool_musedata2hum::addFiguredHarmony(MuseRecord& mr, GridMeasure* gm, HumNum timestamp, int part, int maxstaff) { string fh = mr.getFigureString(); fh = Convert::museFiguredBassToKernFiguredBass(fh); - GridSlice* slice; if (fh.find(":") == string::npos) { HTp fhtok = new HumdrumToken(fh); m_lastfigure = fhtok; - slice = gm->addFiguredBass(fhtok, timestamp, part, maxstaff); + gm->addFiguredBass(fhtok, timestamp, part, maxstaff); return; } if (!m_lastfigure) { HTp fhtok = new HumdrumToken(fh); m_lastfigure = fhtok; - slice = gm->addFiguredBass(fhtok, timestamp, part, maxstaff); + gm->addFiguredBass(fhtok, timestamp, part, maxstaff); return; } @@ -61822,7 +62378,7 @@ void Tool_musedata2hum::addFiguredHarmony(MuseRecord& mr, GridMeasure* gm, if (pieces.empty() || (position >= (int)pieces.size())) { HTp fhtok = new HumdrumToken(fh); m_lastfigure = fhtok; - slice = gm->addFiguredBass(fhtok, timestamp, part, maxstaff); + gm->addFiguredBass(fhtok, timestamp, part, maxstaff); return; } @@ -61840,7 +62396,26 @@ void Tool_musedata2hum::addFiguredHarmony(MuseRecord& mr, GridMeasure* gm, fh.erase(colpos, 1); HTp newtok = new HumdrumToken(fh); m_lastfigure = newtok; - slice = gm->addFiguredBass(newtok, timestamp, part, maxstaff); + gm->addFiguredBass(newtok, timestamp, part, maxstaff); +} + + + +////////////////////////////// +// +// Tool_musedata2hum::addLyrics -- +// + +void Tool_musedata2hum::addLyrics(GridSlice* slice, int part, int staff, MuseRecord& mr) { + int versecount = mr.getVerseCount(); + if (versecount == 0) { + return; + } + for (int i=0; iat(part)->at(staff)->setVerse(i, verse); + } + slice->reportVerseCount(part, staff, versecount); } From 90f3de796756b0df37cdc92e23434d8152c5063c Mon Sep 17 00:00:00 2001 From: Craig Sapp Date: Sat, 26 Oct 2019 10:08:09 -0700 Subject: [PATCH 010/140] Humlib update --- include/hum/humlib.h | 35 ++- src/hum/humlib.cpp | 648 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 647 insertions(+), 36 deletions(-) diff --git a/include/hum/humlib.h b/include/hum/humlib.h index 2b4956078ef..2aa3e91c347 100644 --- a/include/hum/humlib.h +++ b/include/hum/humlib.h @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Wed Oct 16 13:40:45 PDT 2019 +// Last Modified: Sat Oct 19 18:43:18 PDT 2019 // Filename: humlib.h // URL: https://github.com/craigsapp/humlib/blob/master/include/humlib.h // Syntax: C++11 @@ -2615,6 +2615,7 @@ class MuseRecordBasic { bool isBodyRecord (void); bool isChordGraceNote (void); bool isChordNote (void); + bool isDirection (void); bool isAnyComment (void); bool isLineComment (void); bool isBlockComment (void); @@ -2940,18 +2941,40 @@ class MuseRecord : public MuseRecordBasic { int getAttributeInt (char attribute); int getAttributeField (std::string& output, const std::string& attribute); + ////////////////////////////// + // functions which work with musical direction records ('$'): + + // columns 17-18: type of direction + std::string getDirectionTypeField (void); + std::string getDirectionTypeString (void); + bool isTextDirection (void); + bool isHairpin (void); + bool isHairpinStart (void); + bool isHairpinStop (void); + bool isDashStart (void); + bool isDashStop (void); + bool isPedalStart (void); + bool isPedalEnd (void); + bool isRehearsal (void); + bool isOctaveUpStart (void); + bool isOctaveDownStart (void); + bool isOctaveStop (void); + + std::string getDirectionText (void); + std::string getTextDirection (void) { return getDirectionText(); } // ////////////////////////////// - std::string getKernRestStyle (int quarter = 16); + std::string getKernRestStyle (void); protected: - void allowNotesOnly (const std::string& functioName); + void allowNotesOnly (const std::string& functionName); void allowNotesAndRestsOnly (const std::string& functionName); void allowMeasuresOnly (const std::string& functioName); void allowFigurationOnly (const std::string& functioName); void allowFigurationAndNotesOnly (const std::string& functioName); + void allowDirectionsOnly (const std::string& functioName); int getAddElementIndex (int& index, std::string& output, const std::string& input); void zerase (std::string& inout, int num); @@ -3776,6 +3799,7 @@ class GridMeasure : public std::list { bool isRepeatBoth(void) { return m_style == MeasureStyle::RepeatBoth; } void addLayoutParameter(GridSlice* slice, int partindex, const std::string& locomment); + void addLayoutParameter(HumNum timestamp, int partindex, int staffindex, const std::string& locomment); void addDynamicsLayoutParameters(GridSlice* slice, int partindex, const std::string& locomment); void addFiguredBassLayoutParameters(GridSlice* slice, int partindex, const std::string& locomment); GridSlice* addFiguredBass(HTp token, HumNum timestamp, int part, int maxstaff); @@ -6399,6 +6423,8 @@ class Tool_musedata2hum : public HumTool { void addFiguredHarmony (MuseRecord& mr, GridMeasure* gm, HumNum timestamp, int part, int maxstaff); std::string trimSpaces (std::string input); + void addTextDirection (GridMeasure* gm, int part, int staff, + MuseRecord& mr, HumNum timestamp); private: // options: @@ -6408,7 +6434,6 @@ class Tool_musedata2hum : public HumTool { std::string m_omd = ""; // initial tempo designation (store for later output) // state variables: - int m_tpq = 1; // Ticks per quarter note int m_part = 0; // staff index currently being processed int m_maxstaff = 0; // total number of staves (parts) HumNum m_timesigdur = 4; // duration of current time signature in quarter notes @@ -6812,6 +6837,7 @@ class Tool_pccount : public HumTool { HumdrumFile& infile); std::string getFinal (HumdrumFile& infile); double getPercent (const string& pitchclass); + int getCount (const string& pitchclass); void setFactorMaximum (void); void setFactorNormalize (void); @@ -6835,6 +6861,7 @@ class Tool_pccount : public HumTool { double m_ratio = 0.67; bool m_key = true; double m_factor = 1.0; + int m_maxpc = 0; std::string m_title = ""; std::string m_id = "id"; std::map m_vcolor; diff --git a/src/hum/humlib.cpp b/src/hum/humlib.cpp index 16531e1e844..cf7f1d7c73b 100644 --- a/src/hum/humlib.cpp +++ b/src/hum/humlib.cpp @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Wed Oct 16 13:40:45 PDT 2019 +// Last Modified: Sat Oct 19 18:43:18 PDT 2019 // Filename: /include/humlib.cpp // URL: https://github.com/craigsapp/humlib/blob/master/src/humlib.cpp // Syntax: C++11 @@ -4558,6 +4558,173 @@ void GridMeasure::setTimeSigDur(HumNum duration) { +////////////////////////////// +// +// GridMeasure::addLayoutParameter -- Add a layout line at a particular timestamp +// + +void GridMeasure::addLayoutParameter(HumNum timestamp, int partindex, + int staffindex, const string& locomment) { + auto iter = this->rbegin(); + if (iter == this->rend()) { + // no items in measure yet, so add + cerr << "DEAL WITH THIS LAYOUT COMMAND" << endl; + return; + } + GridPart* part; + GridStaff* staff; + GridVoice* voice; + + auto previous = iter; + previous++; + while (previous != this->rend()) { + if ((*previous)->isLayoutSlice()) { + part = (*previous)->at(partindex); + staff = part->at(0); + if (staff->size() == 0) { + GridVoice* v = new GridVoice; + staff->push_back(v); + } + voice = staff->at(0); + if (voice) { + if (voice->getToken() == NULL) { + // create a token with text + HTp newtoken = new HumdrumToken(locomment); + voice->setToken(newtoken); + return; + } else if (*voice->getToken() == "!") { + // replace token with text + HTp newtoken = new HumdrumToken(locomment); + voice->setToken(newtoken); + return; + } + } else { + previous++; + continue; + } + } else { + break; + } + previous++; + } + + auto insertpoint = previous.base(); + GridSlice* newslice = new GridSlice(this, (*iter)->getTimestamp(), SliceType::Layouts); + newslice->initializeBySlice(*iter); + this->insert(insertpoint, newslice); + HTp newtoken = new HumdrumToken(locomment); + if (newslice->at(partindex)->at(0)->size() == 0) { + GridVoice* v = new GridVoice; + newslice->at(partindex)->at(0)->push_back(v); + } + newslice->at(partindex)->at(0)->at(0)->setToken(newtoken); +} + +/* +GridSlice* GridMeasure::addLayoutParameter(const string& tok, HumNum timestamp, + int part, int staff, int voice, int maxstaff, int gracenumber) { + if (gracenumber < 1) { + cerr << "ERROR: gracenumber " << gracenumber << " has to be larger than 0" << endl; + return NULL; + } + + GridSlice* gs = NULL; + // GridSlice* datatarget = NULL; + auto iterator = this->begin(); + if (this->empty()) { + // add a new GridSlice to an empty list or at end of list if timestamp + // is after last entry in list. + gs = new GridSlice(this, timestamp, SliceType::GraceNotes, maxstaff); + gs->addToken(tok, part, staff, voice); + this->push_back(gs); + } else if (timestamp > this->back()->getTimestamp()) { + + // Grace note needs to be added at the end of a measure: + auto it2 = this->end(); + it2--; + int counter = 0; + while (it2 != this->end()) { + if ((*it2)->isGraceSlice()) { + counter++; + if (counter == gracenumber) { + // insert grace note into this slice + (*it2)->addToken(tok, part, staff, voice); + return *it2; + } + } else if ((*it2)->isLayoutSlice()) { + // skip over any layout paramter lines. + it2--; + continue; + } else if ((*it2)->isDataSlice()) { + // insert grace note after this note + gs = new GridSlice(this, timestamp, SliceType::GraceNotes, maxstaff); + gs->addToken(tok, part, staff, voice); + it2++; + this->insert(it2, gs); + return gs; + } + it2--; + } + return NULL; + + } else { + // search for existing line with same timestamp on a data slice: + + while (iterator != this->end()) { + if (timestamp < (*iterator)->getTimestamp()) { + cerr << "STRANGE CASE 2 IN GRIDMEASURE::ADDGRACETOKEN" << endl; + cerr << "\tGRACE TIMESTAMP: " << timestamp << endl; + cerr << "\tTEST TIMESTAMP: " << (*iterator)->getTimestamp() << endl; + return NULL; + } + if ((*iterator)->isDataSlice()) { + if ((*iterator)->getTimestamp() == timestamp) { + // found dataslice just before graceslice(s) + // datatarget = *iterator; + break; + } + } + iterator++; + } + + auto it2 = iterator; + it2--; + int counter = 0; + while (it2 != this->end()) { + if ((*it2)->isGraceSlice()) { + counter++; + if (counter == gracenumber) { + // insert grace note into this slice + (*it2)->addToken(tok, part, staff, voice); + return *it2; + } + } else if ((*it2)->isLayoutSlice()) { + // skip over any layout paramter lines. + it2--; + continue; + } else if ((*it2)->isDataSlice()) { + // insert grace note after this note + gs = new GridSlice(this, timestamp, SliceType::GraceNotes, maxstaff); + gs->addToken(tok, part, staff, voice); + it2++; + this->insert(it2, gs); + return gs; + } + it2--; + } + + // grace note should be added at start of measure + gs = new GridSlice(this, timestamp, SliceType::GraceNotes, maxstaff); + gs->addToken(tok, part, staff, voice); + this->insert(this->begin(), gs); + + } + + return NULL; +} +*/ + + ////////////////////////////// // // GridMeasure::addLayoutParameter -- @@ -15781,6 +15948,9 @@ void HumdrumFileBase::getTrackSequence(vector >& sequence, bool foundTrack; for (i=0; iisAttributes()) { mr->setTpq(ticks); + continue; } string line = getLine(i); @@ -32235,6 +32406,11 @@ string MuseRecord::getVerseUtf8(int index) { string MuseRecord::getKernNoteStyle(int beams, int stems) { string output; + if (!isAnyNote()) { + // not a note, so return nothing + return ""; + } + // place the rhythm stringstream tempdur; int notetype = getGraphicNoteType(); @@ -32352,10 +32528,10 @@ string MuseRecord::getKernNoteStyle(int beams, int stems) { int nexttie = getNextTiedNoteLineIndex(); int state = 0; if (lasttie >= 0) { - state = 2; + state |= 2; } if (nexttie >= 0) { - state = 1; + state |= 1; } switch (state) { case 1: @@ -32464,13 +32640,25 @@ string MuseRecord::getKernNoteAccents(void) { // MuseRecord::getKernRestStyle -- // -string MuseRecord::getKernRestStyle(int quarter) { +string MuseRecord::getKernRestStyle(void) { + string output; string rhythmstring; // place the rhythm stringstream tempdur; + if (!isAnyRest()) { + // not a rest, so return nothing + return ""; + } + + // logical duration of the note + HumNum logicalduration = getTicks(); + logicalduration /= getTpq(); + string durrecip = Convert::durationToRecip(logicalduration); + + /* int notetype; if (graphicNoteTypeQ()) { notetype = getGraphicNoteType(); @@ -32491,6 +32679,9 @@ string MuseRecord::getKernRestStyle(int quarter) { rhythmstring = Convert::durationToRecip(dnotetype); output += rhythmstring; } + */ + + output = durrecip; // add the pitch to the output string output += "r"; @@ -33218,6 +33409,23 @@ void MuseRecord::allowMeasuresOnly(const string& functionName) { } +////////////////////////////// +// +// MuseRecord::allDirectionsOnly -- +// + +void MuseRecord::allowDirectionsOnly(const std::string& functionName) { + switch (getType()) { + case E_muserec_musical_directions: + break; + default: + cerr << "Error: can only access " << functionName + << " on a musical direction record. Line is: " << getLine() << endl; + return; + } +} + + ////////////////////////////// // @@ -33457,6 +33665,260 @@ void MuseRecord::zerase(string& inout, int num) { +///////////////////////////////////////// +// +// MuseRecord::getDirectionTypeString -- columns 17 and 18. +// A = segno sign +// B = right-justified text +// C = center-justified text +// D = left-justified text +// E = dynamics hairpin start +// F = dynamics hairpin end +// G = letter dynamics (text given starting in column 25) +// H = begin dashes (after words) +// J = end dashes +// P = pedal start +// Q = pedal stop +// R = rehearsal number or letter +// U = octave up start +// V = octave down start +// W = octave stop +// + +std::string MuseRecord::getDirectionTypeField(void) { + allowDirectionsOnly("getDirectionType"); + return extract(17, 18); +} + + + +////////////////////////////// +// +// MuseRecord::getDirectionTypeString -- Same as the field version, but +// trailing spaces are removed (not leading ones, at least for now). +// + +std::string MuseRecord::getDirectionTypeString(void) { + string output = getDirectionTypeField(); + if (output.back() == ' ') { + output.resize(output.size() - 1); + } + if (output.back() == ' ') { + output.resize(output.size() - 1); + } + return output; +} + + + +////////////////////////////// +// +// MuseRecord::isTextDirection -- Text is stored starting at column 25. +// B = right justified +// C = center justified +// D = left justified +// + +bool MuseRecord::isTextDirection(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('B') != string::npos) { + return true; + } + if (typefield.find('C') != string::npos) { + return true; + } + if (typefield.find('D') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isHairpin -- +// + +bool MuseRecord::isHairpin(void) { + string typefield = getDirectionTypeField(); + if (isHairpinStart()) { + return true; + } + if (isHairpinStop()) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isHairpinStart -- +// + +bool MuseRecord::isHairpinStart(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('E') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isHairpinStop -- +// + +bool MuseRecord::isHairpinStop(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('F') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isDashStart -- +// + +bool MuseRecord::isDashStart(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('H') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isDashStop -- +// + +bool MuseRecord::isDashStop(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('J') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isPedalStart -- +// + +bool MuseRecord::isPedalStart(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('P') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isPedalEnd -- +// + +bool MuseRecord::isPedalEnd(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('Q') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isRehearsal -- +// + +bool MuseRecord::isRehearsal(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('R') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isOctiveUpStart -- +// + +bool MuseRecord::isOctaveUpStart(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('U') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isOctaveDownStart -- +// + +bool MuseRecord::isOctaveDownStart(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('V') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::isOctaveStop -- +// + +bool MuseRecord::isOctaveStop(void) { + string typefield = getDirectionTypeField(); + if (typefield.find('W') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// MuseRecord::getDirectionText -- Return the text starting in column 25. +// + +std::string MuseRecord::getDirectionText(void) { + int length = (int)m_recordString.size(); + if (length < 25) { + // no text + return ""; + } + return trimSpaces(m_recordString.substr(24)); +} + + + @@ -34336,6 +34798,18 @@ bool MuseRecordBasic::isChordNote(void) { +////////////////////////////// +// +// MuseRecordBasic::isDirection -- Is a musical direction (text) +// instruction. +// + +bool MuseRecordBasic::isDirection(void) { + return m_type == E_muserec_musical_directions; +} + + + ////////////////////////////// // // MuseRecordBasic::isGraceNote -- A grace note, either a single note or @@ -62054,7 +62528,6 @@ bool Tool_musedata2hum::convertPart(HumGrid& outdata, MuseDataSet& mds, int inde m_lastfigure = NULL; m_lastnote = NULL; m_lastbarnum = -1; - m_tpq = part.getInitialTpq(); m_part = index; m_maxstaff = (int)mds.getPartCount(); @@ -62212,7 +62685,6 @@ void Tool_musedata2hum::setMeasureStyle(GridMeasure* gm, MuseRecord& mr) { // void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { - int tpq = m_tpq; int part = m_part; int staff = 0; int maxstaff = m_maxstaff; @@ -62223,6 +62695,7 @@ void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { } HumNum timestamp = mr.getAbsBeat(); +cerr << "CONVERTING LINE " << timestamp << "\t" << mr << endl; string tok; GridSlice* slice = NULL; @@ -62298,7 +62771,7 @@ void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { } else if (mr.isChordGraceNote()) { cerr << "PROCESS GRACE CHORD NOTE HERE: " << mr << endl; } else if (mr.isAnyRest()) { - tok = mr.getKernRestStyle(tpq); + tok = mr.getKernRestStyle(); slice = gm->addDataToken(tok, timestamp, part, staff, layer, maxstaff); if (slice) { mr.setVoice(slice->at(part)->at(staff)->at(layer)); @@ -62307,11 +62780,48 @@ void Tool_musedata2hum::convertLine(GridMeasure* gm, MuseRecord& mr) { cerr << "GRAPHIC VERSION OF NOTEB " << gr << endl; } } + } else if (mr.isDirection()) { + + cerr << "PROCESS DIRECTION HERE: " << mr << endl; + if (mr.isTextDirection()) { + addTextDirection(gm, part, staff, mr, timestamp); + } } } +////////////////////////////// +// +// Tool_musedata2hum::addTextDirection -- +// + +void Tool_musedata2hum::addTextDirection(GridMeasure* gm, int part, int staff, + MuseRecord& mr, HumNum timestamp) { + + if (!mr.isTextDirection()) { + return; + } + string text = mr.getTextDirection(); + if (text == "") { + // no text direction to process + return; + } + HumRegex hre; + hre.replaceDestructive(text, ":", ":", "g"); + string output = "!LO:TX"; + output += ":b"; // text below (figure out above cases) + output += ":t="; + output += text; + cerr << "LAYOUT FOR TEXT IS " << output << endl; + + // add staff index later + gm->addLayoutParameter(NULL, part, output); + + +} + + ////////////////////////////// // // Tool_musedata2hum::addFiguredHarmony -- @@ -69071,6 +69581,7 @@ void Tool_pccount::initialize(HumdrumFile& infile) { m_vcolor["[Canto 1]"] = "#e49689"; m_vcolor["[Canto]"] = "#e49689"; m_vcolor["[Soprano o Tenore]"] = "#e49689"; + m_vcolor["Soprano"] = "#e49689"; m_vcolor["Canto 2."] = "#d67365"; m_vcolor["Canto II"] = "#d67365"; @@ -69105,6 +69616,7 @@ void Tool_pccount::initialize(HumdrumFile& infile) { m_vcolor["Nona parte [Nono]"] = "#a39ce5"; m_vcolor["Basso"] = "#d2aef7"; + m_vcolor["Bass"] = "#d2aef7"; m_vcolor["Basso II"] = "#c69af5"; m_vcolor["Basso II [Decimo]"] = "#c69af5"; @@ -69149,9 +69661,20 @@ string Tool_pccount::getFinal(HumdrumFile& infile) { void Tool_pccount::processFile(HumdrumFile& infile) { countPitches(infile); - string datavar = "data_" + m_id; - string target = "id_" + m_id; - string jsonvar = "vega_" + m_id; + string datavar; + string target; + string jsonvar; + + if (m_attack) { + datavar = "data_" + m_id + "_count"; + target = "id_" + m_id + "_count"; + jsonvar = "vega_" + m_id + "_count"; + } else { + datavar = "data_" + m_id + "_dur"; + target = "id_" + m_id + "_dur"; + jsonvar = "vega_" + m_id + "_dur"; + } + if (m_template) { printVegaLiteJsonTemplate(datavar, infile); } else if (m_data) { @@ -69213,7 +69736,7 @@ void Tool_pccount::printVegaLiteHtml(const string& jsonvar, const string& target, const string& datavar, HumdrumFile& infile) { stringstream& out = m_free_text; - out << "
\n"; + out << "
\n"; out << "\n"; out << "