diff --git a/CHANGELOG.md b/CHANGELOG.md index bde5a9c1af1..a40484c6bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog ## [unreleased] +* Support (initial) for Volpiano input +* Support for neumatic notation oriscus and quilisma +* Support for neume layout without facsimile +* Support for numeral harmonics in MusicXML importer (@eNote-GmBH) ## [4.2.1] - 2024-05-07 * Fix GitHub actions (Python release only) @@ -9,6 +13,7 @@ * Support for `fTrem@unitdur` (@eNote-GmbH) * Upgrade to C++20 * Update of the Midifile library +* Improved logging * Fix lyric position in MIDI output * Fix string formatting output with some locale configurations (@ammatwain) diff --git a/include/hum/humlib.h b/include/hum/humlib.h index 6dd3ce528cb..d9b39732d30 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 Jun 30 19:51:54 WEST 2024 +// Last Modified: Sun Sep 8 23:07:16 PDT 2024 // Filename: min/humlib.h // URL: https://github.com/craigsapp/humlib/blob/master/min/humlib.h // Syntax: C++11 @@ -964,9 +964,9 @@ class HumInstrument { int setGM (const std::string& Hname, int aValue); private: - int index; - static std::vector<_HumInstrument> data; - static int classcount; + int m_index; + static std::vector<_HumInstrument> m_data; + static int m_classcount; protected: void initialize (void); @@ -2043,6 +2043,7 @@ class HumdrumFileBase : public HumHash { bool isRhythmAnalyzed (void); bool areStrandsAnalyzed (void); bool areStrophesAnalyzed (void); + void setFilenameFromSegment (void); template void initializeArray (std::vector>& array, TYPE value); @@ -5431,6 +5432,7 @@ int main(int argc, char** argv) { \ infile.readNoRhythm(std::cin); \ } \ int status = interface.run(infile, std::cout); \ + interface.finally(); \ if (interface.hasWarning()) { \ interface.getWarning(std::cerr); \ return 0; \ @@ -5439,7 +5441,6 @@ int main(int argc, char** argv) { \ interface.getError(std::cerr); \ return -1; \ } \ - interface.finally(); \ return !status; \ } @@ -5463,24 +5464,24 @@ int main(int argc, char** argv) { \ bool status = true; \ while (instream.readSingleSegment(infiles)) { \ status &= interface.run(infiles); \ - if (interface.hasWarning()) { \ - interface.getWarning(std::cerr); \ - } \ - if (interface.hasAnyText()) { \ - interface.getAllText(std::cout); \ - } \ - if (interface.hasError()) { \ - interface.getError(std::cerr); \ - return -1; \ - } \ - if (!interface.hasAnyText()) { \ - for (int i=0; i(interface)); \ bool status = interface.run(instream); \ + interface.finally(); \ if (interface.hasWarning()) { \ interface.getWarning(std::cerr); \ } \ @@ -5512,7 +5514,6 @@ int main(int argc, char** argv) { \ interface.getError(std::cerr); \ return -1; \ } \ - interface.finally(); \ interface.clearOutput(); \ return !status; \ } @@ -5536,6 +5537,7 @@ int main(int argc, char** argv) { \ hum::HumdrumFileSet infiles; \ instream.read(infiles); \ bool status = interface.run(infiles); \ + interface.finally(); \ if (interface.hasWarning()) { \ interface.getWarning(std::cerr); \ } \ @@ -5551,7 +5553,6 @@ int main(int argc, char** argv) { \ std::cout << infiles[i]; \ } \ } \ - interface.finally(); \ interface.clearOutput(); \ return !status; \ } @@ -7358,6 +7359,201 @@ class Tool_double : public HumTool { }; +class Tool_esac2hum : public HumTool { + public: + + class Note { + public: + std::vector m_errors; + std::string esac; + int m_dots = 0; + int m_underscores = 0; + int m_octave = 0; + int m_degree = 0; // scale degree (wrt major key) + int m_b40degree = 0; // scale degree as b40 interval + int m_alter = 0; // chromatic alteration of degree (flats/sharp from major scale degrees) + double m_ticks = 0.0; + bool m_tieBegin = false; + bool m_tieEnd = false; + bool m_phraseBegin = false; + bool m_phraseEnd = false; + std::string m_humdrum; // **kern conversion of EsAC note + int m_b40 = 0; // absolute b40 pitch (-1000 = rest); + int m_b12 = 0; // MIDI note number (-1000 = rest); + HumNum m_factor = 1; // for triplet which is 2/3 duration + + void calculateRhythms(int minrhy); + void calculatePitches(int tonic); + bool parseNote(const std::string& note, HumNum factor); + void generateHumdrum(int minrhy, int b40tonic); + bool isPitch(void); + bool isRest(void); + std::string getScaleDegree(void); + }; + + class Measure : public std::vector { + public: + std::vector m_errors; + std::string esac; + int m_barnum = -1000; // -1000 == unassigned bar number for this measure + // m_barnum = -1 == invisible barline (between two partial measures) + // m_barnum = 0 == pickup measure (partial measure at start of music) + double m_ticks = 0.0; + double m_tsticks = 0.0; + // m_measureTimeSignature is a **kern time signature + // (change) to display in the converted score. + std::string m_measureTimeSignature = ""; + bool m_partialBegin = false; // start of an incomplete measure + bool m_partialEnd = false; // end of an incomplete measure (pickup) + bool m_complete = false; // a complste measure + void calculateRhythms(int minrhy); + void calculatePitches(int tonic); + bool parseMeasure(const std::string& measure); + bool isUnassigned(void); + void setComplete(void); + bool isComplete(void); + void setPartialBegin(void); + bool isPartialBegin(void); + void setPartialEnd(void); + bool isPartialEnd(void); + }; + + class Phrase : public std::vector { + public: + std::vector m_errors; + double m_ticks = 0.0; + std::string esac; + void calculateRhythms(int minrhy); + void calculatePitches(int tonic); + bool parsePhrase(const std::string& phrase); + std::string getLastScaleDegree(); + void getNoteList(std::vector& notelist); + std::string getNO_REP(void); + int getFullMeasureCount(void); + }; + + class Score : public std::vector { + public: + int m_b40tonic = 0; + int m_minrhy = 0; + std::string m_clef; + std::string m_keysignature; + std::string m_keydesignation; + std::string m_timesig; + std::map m_params; + std::vector m_errors; + bool m_finalBarline = false; + bool hasFinalBarline(void) { return m_finalBarline; } + void calculateRhythms(int minrhy); + void calculatePitches(int tonic); + bool parseMel(const std::string& mel); + void analyzeTies(void); + void analyzePhrases(void); + void getNoteList(std::vector& notelist); + void getMeasureList(std::vector& measurelist); + void getPhraseNoteList(std::vector& notelist, int index); + void generateHumdrumNotes(void); + void calculateClef(void); + void calculateKeyInformation(void); + void calculateTimeSignatures(void); + void setAllTimesigTicks(double ticks); + void assignFreeMeasureNumbers(void); + void assignSingleMeasureNumbers(void); + void prepareMultipleTimeSignatures(const std::string& ts); + + void doAnalyses(void); + void analyzeMEL_SEM(void); + void analyzeMEL_RAW(void); + void analyzeNO_REP(void); + void analyzeRTM(void); + void analyzeSCL_DEG(void); + void analyzeSCL_SEM(void); + void analyzePHR_NO(void); + void analyzePHR_BARS(void); + void analyzePHR_CAD(void); + void analyzeACC(void); + }; + + Tool_esac2hum (void); + ~Tool_esac2hum () {}; + + bool convertFile (std::ostream& out, const std::string& filename); + bool convert (std::ostream& out, const std::string& input); + bool convert (std::ostream& out, std::istream& input); + + + protected: + void initialize (void); + + void convertEsacToHumdrum(std::ostream& output, std::istream& infile); + bool getSong (std::vector& song, std::istream& infile); + void convertSong (std::ostream& output, std::vector& infile); + static std::string trimSpaces (const std::string& input); + void printHeader (std::ostream& output); + void printFooter (std::ostream& output, std::vector& infile); + void printConversionDate (std::ostream& output); + void printPdfLinks (std::ostream& output); + void printParameters (void); + void printPageNumbers (std::ostream& output); + void getParameters (std::vector& infile); + void cleanText (std::string& buffer); + std::string createFilename (void); + void printBemComment (std::ostream& output); + void processSong (void); + void printScoreContents (std::ostream& output); + void embedAnalyses (std::ostream& output); + void printPdfUrl (std::ostream& output); + std::string getKolbergUrl (int volume); + void printKolbergPdfUrl (std::ostream& output); + + private: + bool m_debugQ = false; // used with --debug option + bool m_verboseQ = false; // used with --verbose option + std::string m_verbose; // p = print EsAC phrases, m = print measures, n = print notes. + // t after p, m, or n means print tick info + bool m_embedEsacQ = true; // used with -E option + bool m_dwokQ = false; // true if source is Oskar Kolberg: Dzieła Wszystkie + // (Oskar Kolberg: Complete Works) + // determined automatically if header line or TRD source contains "DWOK" string. + bool m_analysisQ = false; // used with -a option + + int m_inputline = 0; // used to keep track if the EsAC input line. + + std::string m_filePrefix; + std::string m_filePostfix = ".krn"; + bool m_fileTitleQ = false; + + std::string m_prevline; + std::string m_cutline; + std::vector m_globalComments; + + bool m_initialized = false; + int m_minrhy = 0; + + Tool_esac2hum::Score m_score; + + class KolbergInfo { + public: + std::string titlePL; + std::string titleEN; + int firstPrintPage; + int firstScanPage; + std::vector plates; + + KolbergInfo(void) { firstPrintPage = 0; firstScanPage = 0; } + KolbergInfo( + const std::string& pl, const std::string& en, int fpp, int fsp, const std::vector& plts) + : titlePL(pl), titleEN(en), firstPrintPage(fpp), firstScanPage(fsp), plates(plts) {} + }; + std::map m_kinfo; + KolbergInfo getKolbergInfo(int volume); + std::string getKolbergUrl(int volume, int printPage); + int calculateScanPage(int inputPrintPage, int printPage, int scanPage, const std::vector& platePages); + + +}; + + #define ND_NOTE 0 /* notes or rests + text and phrase markings */ #define ND_BAR 1 /* explicit barlines */ @@ -7387,10 +7583,10 @@ class NoteData { -class Tool_esac2hum : public HumTool { +class Tool_esac2humold : public HumTool { public: - Tool_esac2hum (void); - ~Tool_esac2hum () {}; + Tool_esac2humold (void); + ~Tool_esac2humold () {}; bool convertFile (std::ostream& out, const std::string& filename); bool convert (std::ostream& out, const std::string& input); @@ -7438,16 +7634,18 @@ class Tool_esac2hum : public HumTool { void printHumdrumFooterInfo(std::ostream& out, std::vector& song); private: - int debugQ = 0; // used with --debug option - int verboseQ = 0; // used with -v option - int splitQ = 0; // used with -s option - int firstfilenum = 1; // used with -f option - std::vector header; // used with -h option - std::vector trailer; // used with -t option - std::string fileextension; // used with -x option - std::string namebase; // used with -s option - - std::vector chartable; // used printChars() & printSpecialChars() + int debugQ = 0; // used with --debug option + int verboseQ = 0; // used with -v option + int splitQ = 0; // used with -s option + int firstfilenum = 1; // used with -f option + std::vector header; // used with -h option + std::vector trailer; // used with -t option + std::string fileextension; // used with -x option + std::string namebase; // used with -s option + + // Modern ESaC files use UTF-8 characters, older ESaC files use + // ASCII encodings of non-UTF7 characters: + std::vector chartable; // used in printChars() & printSpecialChars() int inputline = 0; }; @@ -7536,27 +7734,28 @@ class Tool_extract : public HumTool { void fillFieldDataByNoRest (std::vector& field, std::vector& subfield, std::vector& model, const std::string& searchstring, HumdrumFile& infile, int state); + void printInterpretationForKernSpine(HumdrumFile& infile, int index); private: // global variables - int excludeQ = 0; // used with -x option - int expandQ = 0; // used with -e option - std::string expandInterp = ""; // used with -E option - int interpQ = 0; // used with -i option - std::string interps = ""; // used with -i option - int debugQ = 0; // used with --debug option - int kernQ = 0; // used with -k option - int rkernQ = 0; // used with -K option - int fieldQ = 0; // used with -f or -p option - std::string fieldstring = ""; // used with -f or -p option + bool excludeQ = false; // used with -x option + bool expandQ = false; // used with -e option + std::string expandInterp = ""; // used with -E option + bool interpQ = false; // used with -i option + std::string interps = ""; // used with -i option + bool debugQ = false; // used with --debug option + bool kernQ = false; // used with -k option + bool rkernQ = false; // used with -K option + bool fieldQ = false; // used with -f or -p option + std::string fieldstring = ""; // used with -f or -p option std::vector field; // used with -f or -p option std::vector subfield; // used with -f or -p option std::vector model; // used with -p, or -e options and similar - int countQ = 0; // used with -C option - int traceQ = 0; // used with -t option - std::string tracefile = ""; // used with -t option - int reverseQ = 0; // used with -r option + bool countQ = false; // used with -C option + bool traceQ = false; // used with -t option + std::string tracefile = ""; // used with -t option + bool reverseQ = false; // used with -r option std::string reverseInterp = "**kern"; // used with -r and -R options. // sub-spine "b" expansion model: how to generate data for a secondary // spine if the primary spine is not divided. Models are: @@ -7566,17 +7765,18 @@ class Tool_extract : public HumTool { // data. 'n' will be used for non-kern spines when 'r' is used. int submodel = 'd'; // used with -m option std::string editorialInterpretation = "yy"; - std::string cointerp = "**kern"; // used with -c option - int comodel = 0; // used with -M option + std::string cointerp = "**kern"; // used with -c option + int comodel = 0; // used with -M option std::string subtokenseparator = " "; // used with a future option - int interpstate = 0; // used -I or with -i - int grepQ = 0; // used with -g option - std::string grepString = ""; // used with -g option + int interpstate = 0; // used -I or with -i + bool grepQ = false; // used with -g option + std::string grepString = ""; // used with -g option std::string blankName = "**blank"; // used with -n option - 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 + bool addRestsQ = false; // used with -n option + bool noEmptyQ = false; // used with --no-empty option + bool emptyQ = false; // used with --empty option + bool spineListQ = false; // used with --spine option + bool removerestQ = false; // used with --no-rest option }; @@ -8384,9 +8584,12 @@ class Tool_kernify : public HumTool { void generateDummyKernSpine (HumdrumFile& infile); std::string makeNullLine (HumdrumLine& line); std::string makeReverseLine (HumdrumLine& line); + bool prepareDataSpines (HumdrumFile& infile); + bool prepareDataSpine (HTp spinestart); private: bool m_forceQ = false; // used with -f option + bool m_hasDataInterpretations = false; }; @@ -9869,6 +10072,32 @@ class Tool_ordergps : public HumTool { }; +class Tool_pbar : public HumTool { + public: + Tool_pbar (void); + ~Tool_pbar () {}; + + bool run (HumdrumFileSet& infiles); + bool run (HumdrumFile& infile); + bool run (const std::string& indata, std::ostream& out); + bool run (HumdrumFile& infile, std::ostream& out); + + protected: + void processFile (HumdrumFile& infile); + void initialize (void); + void processSpine (HTp spineStart); + void printDataLine (HumdrumFile& infile, int index); + void printLocalCommentLine (HumdrumFile& infile, int index); + void addBarLineToFollowingNoteOrRest (HTp token); + void printInvisibleBarlines (HumdrumFile& infile, int index); + void printBarLine (HumdrumFile& infile, int index); + + private: + bool m_invisibleQ = false; // used with -i option + +}; + + class Tool_pccount : public HumTool { public: @@ -10293,6 +10522,83 @@ class Tool_rid : public HumTool { }; +class Tool_rphrase : public HumTool { + public: + + class VoiceInfo { + public: + std::string name; + std::vector restsBefore; + std::vector phraseDurs; + std::vector barStarts; + std::vector phraseStartToks; + }; + + Tool_rphrase (void); + ~Tool_rphrase () {}; + + bool run (HumdrumFileSet& infiles); + bool run (HumdrumFile& infile); + bool run (const std::string& indata, std::ostream& out); + bool run (HumdrumFile& infile, std::ostream& out); + void finally (void); + + protected: + void initialize (void); + void processFile (HumdrumFile& infile); + void fillVoiceInfo (std::vector& voiceInfo, std::vector& kstarts, HumdrumFile& infile); + void fillVoiceInfo (Tool_rphrase::VoiceInfo& voiceInfo, HTp& kstart, HumdrumFile& infile); + void fillCompositeInfo (Tool_rphrase::VoiceInfo& collapseInfo, HumdrumFile& infile); + void printVoiceInfo (std::vector& voiceInfo); + void printVoiceInfo (Tool_rphrase::VoiceInfo& voiceInfo); + + void printEmbeddedVoiceInfo(std::vector& voiceInfo, Tool_rphrase::VoiceInfo& collapseInfo, HumdrumFile& infile); + void printEmbeddedIndividualVoiceInfo(Tool_rphrase::VoiceInfo& voiceInfo, HumdrumFile& infile); + void printEmbeddedCompositeInfo(Tool_rphrase::VoiceInfo& compositeInfo, HumdrumFile& infile); + + void getCompositeStates(std::vector& noteStates, HumdrumFile& infile); + std::string getCompositeLabel(HumdrumFile& infile); + void markPhraseStartsInScore(HumdrumFile& infile, Tool_rphrase::VoiceInfo& voiceInfo); + void markCompositePhraseStartsInScore(HumdrumFile& infile, Tool_rphrase::VoiceInfo& collapseInfo); + void outputMarkedFile (HumdrumFile& infile, std::vector& voiceInfo, + Tool_rphrase::VoiceInfo& compositeInfo); + void printDataLine (HumdrumFile& infile, int index); + void markLongaDurations(HumdrumFile& infile); + std::string getVoiceInfo(HumdrumFile& infile); + void printEmbeddedVoiceInfoSummary(std::vector& voiceInfo, HumdrumFile& infile); + double twoDigitRound(double input); + void printHyperlink(const std::string& urlType); + + private: + bool m_averageQ = false; // for -a option + bool m_allAverageQ = false; // for -A option + bool m_breathQ = true; // for -B option + bool m_barlineQ = false; // for -m option + bool m_compositeQ = false; // for -c option + bool m_longaQ = false; // for -l option + bool m_filenameQ = false; // for -f option + bool m_fullFilenameQ = false; // for -F option + std::string m_filename; // for -f or -F option + bool m_sortQ = false; // for -s option + bool m_reverseSortQ = false; // for -S option + int m_pcount = 0; // for -a option + double m_sum = 0.0; // for -a option + int m_pcountComposite = 0; // for -c option + double m_sumComposite = 0.0; // for -c option + bool m_markQ = false; // for --mark option + double m_durUnit = 2.0; // for -d option + bool m_infoQ = false; // for -i option (when --mark is active) + bool m_squeezeQ = false; // for -z option + bool m_closeQ = false; // for --close option + std::string m_urlType; // for -u option + + int m_line = 1; + std::string m_voiceLengthColor = "crimson"; + std::string m_compositeLengthColor = "limegreen"; + +}; + + class Tool_ruthfix : public HumTool { public: Tool_ruthfix (void); @@ -10831,6 +11137,94 @@ class Tool_tabber : public HumTool { +class Tool_tandeminfo : public HumTool { + public: + class Entry { + public: + HTp token = NULL; + std::string description; + int count = 0; + }; + + Tool_tandeminfo (void); + ~Tool_tandeminfo () {}; + + bool run (HumdrumFileSet& infiles); + bool run (HumdrumFile& infile); + bool run (const std::string& indata, std::ostream& out); + bool run (HumdrumFile& infile, std::ostream& out); + + + protected: + void initialize (void); + void processFile (HumdrumFile& infile); + void printEntries (HumdrumFile& infile); + void printEntriesHtml (HumdrumFile& infile); + void printEntriesText (HumdrumFile& infile); + void doCountAnalysis (void); + + std::string getDescription (HTp token); + std::string checkForKeySignature (const std::string& tok); + std::string checkForKeyDesignation (const std::string& tok); + std::string checkForInstrumentInfo (const std::string& tok); + std::string checkForLabelInfo (const std::string& tok); + std::string checkForTimeSignature (const std::string& tok); + std::string checkForMeter (const std::string& tok); + std::string checkForTempoMarking (const std::string& tok); + std::string checkForClef (const std::string& tok); + std::string checkForStaffPartGroup (const std::string& tok); + std::string checkForTuplet (const std::string& tok); + std::string checkForHands (const std::string& tok); + std::string checkForPosition (const std::string& tok); + std::string checkForCue (const std::string& tok); + std::string checkForFlip (const std::string& tok); + std::string checkForTremolo (const std::string& tok); + std::string checkForOttava (const std::string& tok); + std::string checkForPedal (const std::string& tok); + std::string checkForBracket (const std::string& tok); + std::string checkForRscale (const std::string& tok); + std::string checkForTimebase (const std::string& tok); + std::string checkForTransposition (const std::string& tok); + std::string checkForGrp (const std::string& tok); + std::string checkForStria (const std::string& tok); + std::string checkForFont (const std::string& tok); + std::string checkForVerseLabels (const std::string& tok); + std::string checkForLanguage (const std::string& tok); + std::string checkForStemInfo (const std::string& tok); + std::string checkForXywh (const std::string& tok); + std::string checkForCustos (const std::string& tok); + std::string checkForTextInterps (const std::string& tok); + std::string checkForRep (const std::string& tok); + std::string checkForPline (const std::string& tok); + std::string checkForTacet (const std::string& tok); + std::string checkForFb (const std::string& tok); + std::string checkForColor (const std::string& tok); + std::string checkForThru (const std::string& tok); + + private: + bool m_exclusiveQ = true; // used with -X option (don't print exclusive interpretation) + bool m_unknownQ = false; // used with -u option (print only unknown tandem interpretations) + bool m_filenameQ = false; // used with -f option (print only unknown tandem interpretations) + bool m_descriptionQ = false; // used with -m option (print description of interpretation) + bool m_locationQ = false; // used with -l option (print location of interpretation in file) + bool m_zeroQ = false; // used with -z option (location address by 0-index) + bool m_tableQ = false; // used with -t option (display results as HTML table) + bool m_closeQ = false; // used with --close option (HTML details closed by default) + bool m_sortQ = false; // used with -s (sort entries alphabetically by tandem interpretation) + bool m_headerOnlyQ = false; // used with -h option (process only header interpretations) + bool m_bodyOnlyQ = false; // used with -H option (process only body interpretations) + bool m_countQ = false; // used with -c option (only show unique list with counts); + bool m_sortByCountQ = false; // used with -c and -n options (sort from low to high count) + bool m_sortByReverseCountQ = false; // used with -c and -N options (sort from high to low count) + bool m_humdrumQ = false; // used with --humdrum option (output data formatted with Humdrum syntax) + + std::string m_unknown = "unknown"; + + std::vector m_entries; + std::map m_count; +}; + + class Tool_tassoize : public HumTool { public: Tool_tassoize (void); diff --git a/include/vrv/barline.h b/include/vrv/barline.h index 164c2214130..407c4ca4ac7 100644 --- a/include/vrv/barline.h +++ b/include/vrv/barline.h @@ -9,6 +9,7 @@ #define __VRV_BARLINE_H__ #include "atts_shared.h" +#include "atts_visual.h" #include "layerelement.h" namespace vrv { @@ -27,6 +28,7 @@ enum class BarLinePosition { None, Left, Right }; */ class BarLine : public LayerElement, public AttBarLineLog, + public AttBarLineVis, public AttColor, public AttNNumberLike, public AttVisibility { @@ -76,9 +78,9 @@ class BarLine : public LayerElement, * @return First entry is true if the attribute was found, second entry contains the value */ ///@{ - std::pair GetLength(const StaffDef *staffDef) const; - std::pair GetMethod(const StaffDef *staffDef) const; - std::pair GetPlace(const StaffDef *staffDef) const; + std::pair GetLengthFromContext(const StaffDef *staffDef) const; + std::pair GetMethodFromContext(const StaffDef *staffDef) const; + std::pair GetPlaceFromContext(const StaffDef *staffDef) const; ///@} //----------// diff --git a/include/vrv/divline.h b/include/vrv/divline.h index 98622e62d5f..9ad45fee68c 100644 --- a/include/vrv/divline.h +++ b/include/vrv/divline.h @@ -37,13 +37,13 @@ class DivLine : public LayerElement, ///@{ DivLine(); virtual ~DivLine(); - virtual Object *Clone() const { return new DivLine(*this); } - virtual void Reset(); - virtual std::string GetClassName() const { return "DivLine"; } + Object *Clone() const override { return new DivLine(*this); } + void Reset() override; + std::string GetClassName() const override { return "DivLine"; } ///@} /** Override the method since alignment is required */ - virtual bool HasToBeAligned() const { return true; } + bool HasToBeAligned() const override { return true; } /** * Use to set the alignment for the Measure BarLine members. @@ -84,8 +84,8 @@ class DivLineAttr : public DivLine { ///@{ DivLineAttr(); virtual ~DivLineAttr(); - virtual Object *Clone() const { return new DivLineAttr(*this); } - virtual std::string GetClassName() const { return "DivLineAttr"; } + Object *Clone() const override { return new DivLineAttr(*this); } + std::string GetClassName() const override { return "DivLineAttr"; } ///@} // void SetLeft() { m_isLeft = true; } diff --git a/include/vrv/elementpart.h b/include/vrv/elementpart.h index 4387902979c..3f5c98d4d2c 100644 --- a/include/vrv/elementpart.h +++ b/include/vrv/elementpart.h @@ -22,7 +22,7 @@ class TupletNum; //---------------------------------------------------------------------------- /** - * This class models a group of dots as a layer element part and has not direct MEI equivlatent. + * This class models a group of dots as a layer element part and has no direct MEI equivalent. */ class Dots : public LayerElement, public AttAugmentDots { public: @@ -92,7 +92,7 @@ class Dots : public LayerElement, public AttAugmentDots { //---------------------------------------------------------------------------- /** - * This class models a stem as a layer element part and has not direct MEI equivlatent. + * This class models a stem as a layer element part and has no direct MEI equivalent. */ class Flag : public LayerElement { public: @@ -144,7 +144,7 @@ class Flag : public LayerElement { //---------------------------------------------------------------------------- /** - * This class models a bracket as a layer element part and has not direct MEI equivlatent. + * This class models a bracket as a layer element part and has no direct MEI equivalent. * It is used to represent tuplet brackets. */ class TupletBracket : public LayerElement, public AttTupletVis { @@ -243,7 +243,7 @@ class TupletBracket : public LayerElement, public AttTupletVis { //---------------------------------------------------------------------------- /** - * This class models a tuplet num as a layer element part and has not direct MEI equivlatent. + * This class models a tuplet num as a layer element part and has no direct MEI equivalent. * It is used to represent tuplet number */ class TupletNum : public LayerElement, public AttNumberPlacement, public AttTupletVis { diff --git a/include/vrv/iohumdrum.h b/include/vrv/iohumdrum.h index 92795b20182..b36fee2b2e7 100644 --- a/include/vrv/iohumdrum.h +++ b/include/vrv/iohumdrum.h @@ -900,6 +900,8 @@ class HumdrumInput : public vrv::Input { bool checkIfReversedSpineOrder(std::vector &staffstarts); bool hasOmdText(int startline, int endline); void processMeiOptions(hum::HumdrumFile &infile); + std::string getInstrumentNumber(hum::HTp icode); + void insertTextWithNewlines(Label *label, const std::string &text); // header related functions: /////////////////////////////////////////// void createHeader(); diff --git a/include/vrv/layerelement.h b/include/vrv/layerelement.h index ea8307a9da1..4740e64373b 100644 --- a/include/vrv/layerelement.h +++ b/include/vrv/layerelement.h @@ -80,7 +80,7 @@ class LayerElement : public Object, /** * Return true if the element has to be aligned horizontally - * It typically set to false for mRest, mRpt, etc. + * It is typically set to false for mRest, mRpt, etc. */ virtual bool HasToBeAligned() const { return false; } diff --git a/include/vrv/liquescent.h b/include/vrv/liquescent.h index e64d63c15aa..6a753ddb0d4 100644 --- a/include/vrv/liquescent.h +++ b/include/vrv/liquescent.h @@ -29,20 +29,20 @@ class Liquescent : public LayerElement, public PitchInterface, public PositionIn ///@{ Liquescent(); virtual ~Liquescent(); - virtual Object *Clone() const { return new Liquescent(*this); } - virtual void Reset(); - virtual std::string GetClassName() const { return "Liquescent"; } + Object *Clone() const override { return new Liquescent(*this); } + void Reset() override; + std::string GetClassName() const override { return "Liquescent"; } ///@} /** * @name Getter to interfaces */ ///@{ - virtual PitchInterface *GetPitchInterface() { return dynamic_cast(this); } + PitchInterface *GetPitchInterface() override { return dynamic_cast(this); } ///@} /** Override the method since alignment is required */ - virtual bool HasToBeAligned() const { return true; } + bool HasToBeAligned() const override { return true; } private: // diff --git a/include/vrv/object.h b/include/vrv/object.h index 18b0b59b47b..7da3e2c1309 100644 --- a/include/vrv/object.h +++ b/include/vrv/object.h @@ -165,7 +165,7 @@ class Object : public BoundingBox { const Resources *GetDocResources() const; /** - * Reset the object, that is 1) removing all childs and 2) resetting all attributes. + * Reset the object, that is 1) removing all children and 2) resetting all attributes. * The method is virtual, so _always_ call the parent in the method overriding it. */ virtual void Reset(); @@ -820,8 +820,8 @@ class Object : public BoundingBox { class ObjectListInterface { public: // constructors and destructors - ObjectListInterface(){}; - virtual ~ObjectListInterface(){}; + ObjectListInterface() = default; + virtual ~ObjectListInterface() = default; ObjectListInterface(const ObjectListInterface &listInterface); // copy constructor; ObjectListInterface &operator=(const ObjectListInterface &listInterface); // copy assignment; @@ -919,8 +919,8 @@ class ObjectListInterface { class TextListInterface : public ObjectListInterface { public: // constructors and destructors - TextListInterface(){}; - virtual ~TextListInterface(){}; + TextListInterface() = default; + virtual ~TextListInterface() = default; /** * Returns a contatenated version of all the text children diff --git a/include/vrv/oriscus.h b/include/vrv/oriscus.h index 391784a6e27..775c8bb6c78 100644 --- a/include/vrv/oriscus.h +++ b/include/vrv/oriscus.h @@ -29,20 +29,20 @@ class Oriscus : public LayerElement, public PitchInterface, public PositionInter ///@{ Oriscus(); virtual ~Oriscus(); - virtual Object *Clone() const { return new Oriscus(*this); } - virtual void Reset(); - virtual std::string GetClassName() const { return "Oriscus"; } + Object *Clone() const override { return new Oriscus(*this); } + void Reset() override; + std::string GetClassName() const override { return "Oriscus"; } ///@} /** * @name Getter to interfaces */ ///@{ - virtual PitchInterface *GetPitchInterface() { return dynamic_cast(this); } + PitchInterface *GetPitchInterface() override { return dynamic_cast(this); } ///@} /** Override the method since alignment is required */ - virtual bool HasToBeAligned() const { return true; } + bool HasToBeAligned() const override { return true; } private: // diff --git a/include/vrv/quilisma.h b/include/vrv/quilisma.h index 2ffbf576f94..9933f505772 100644 --- a/include/vrv/quilisma.h +++ b/include/vrv/quilisma.h @@ -29,20 +29,20 @@ class Quilisma : public LayerElement, public PitchInterface, public PositionInte ///@{ Quilisma(); virtual ~Quilisma(); - virtual Object *Clone() const { return new Quilisma(*this); } - virtual void Reset(); - virtual std::string GetClassName() const { return "Quilisma"; } + Object *Clone() const override { return new Quilisma(*this); } + void Reset() override; + std::string GetClassName() const override { return "Quilisma"; } ///@} /** * @name Getter to interfaces */ ///@{ - virtual PitchInterface *GetPitchInterface() { return dynamic_cast(this); } + PitchInterface *GetPitchInterface() override { return dynamic_cast(this); } ///@} /** Override the method since alignment is required */ - virtual bool HasToBeAligned() const { return true; } + bool HasToBeAligned() const override { return true; } private: // diff --git a/include/vrv/staff.h b/include/vrv/staff.h index 92a699a43bf..d49d48f0c8c 100644 --- a/include/vrv/staff.h +++ b/include/vrv/staff.h @@ -36,13 +36,8 @@ class LedgerLine { public: /** * @name Constructors, destructors, reset methods - * Reset method reset all attribute classes */ - ///@{ - LedgerLine(); - virtual ~LedgerLine(); - virtual void Reset(); - ///@} + LedgerLine() = default; /** * Add a dash to the ledger line object. diff --git a/include/vrv/toolkit.h b/include/vrv/toolkit.h index 8ec0376e71d..61bea74b805 100644 --- a/include/vrv/toolkit.h +++ b/include/vrv/toolkit.h @@ -770,6 +770,22 @@ class Toolkit { */ void ResetLogBuffer(); + /** + * Start capturing std::cerr from an external codebase for redirection to vrv::logBuffer. + * Only one capture should be active at a given time. Finish by calling LogRedirectStop. + */ + void LogRedirectStart(); + + /** + * End capturing std::cerr from an external codebase for redirection to vrv::logBuffer. + */ + void LogRedirectStop(); + + /** + * Load a string data with or without resetting the log buffer + */ + bool LoadData(const std::string &data, bool resetLogBuffer); + private: bool SetFont(const std::string &fontName); bool IsUTF16(const std::string &filename); @@ -805,6 +821,18 @@ class Toolkit { */ char *m_cString; + /** + * Temporary capture buffer for redirecting std::cerr to vrv::LogWarning. + * Used to coordinate between LogRedirectStart()/LogRedirectStop(). + */ + std::stringstream m_cerrCaptured; + + /** + * Temporary storage of the std::cerr read buffer during LogCapture. NULL when not in use. + * Used to coordinate between LogRedirectStart()/LogRedirectStop(). + */ + std::streambuf *m_cerrOriginalBuf; + EditorToolkit *m_editorToolkit; #ifndef NO_RUNTIME diff --git a/include/vrv/vrv.h b/include/vrv/vrv.h index 866458b9e70..daec31198e2 100644 --- a/include/vrv/vrv.h +++ b/include/vrv/vrv.h @@ -145,7 +145,7 @@ extern bool loggingToBuffer; */ extern struct timeval start; void LogElapsedTimeStart(); -void LogElapsedTimeEnd(const char *msg = "unspecified operation"); +void LogElapsedTimeStop(const char *msg = "unspecified operation"); //---------------------------------------------------------------------------- // Notation type checks diff --git a/src/barline.cpp b/src/barline.cpp index 30952b11ed7..8a5a6c81c87 100644 --- a/src/barline.cpp +++ b/src/barline.cpp @@ -33,9 +33,11 @@ namespace vrv { static const ClassRegistrar s_factory("barLine", BARLINE); -BarLine::BarLine() : LayerElement(BARLINE, "bline-"), AttBarLineLog(), AttColor(), AttNNumberLike(), AttVisibility() +BarLine::BarLine() + : LayerElement(BARLINE, "bline-"), AttBarLineLog(), AttBarLineVis(), AttColor(), AttNNumberLike(), AttVisibility() { this->RegisterAttClass(ATT_BARLINELOG); + this->RegisterAttClass(ATT_BARLINEVIS); this->RegisterAttClass(ATT_COLOR); this->RegisterAttClass(ATT_VISIBILITY); @@ -43,9 +45,10 @@ BarLine::BarLine() : LayerElement(BARLINE, "bline-"), AttBarLineLog(), AttColor( } BarLine::BarLine(ClassId classId) - : LayerElement(classId, "bline-"), AttBarLineLog(), AttColor(), AttNNumberLike(), AttVisibility() + : LayerElement(classId, "bline-"), AttBarLineLog(), AttBarLineVis(), AttColor(), AttNNumberLike(), AttVisibility() { this->RegisterAttClass(ATT_BARLINELOG); + this->RegisterAttClass(ATT_BARLINEVIS); this->RegisterAttClass(ATT_COLOR); this->RegisterAttClass(ATT_VISIBILITY); @@ -59,6 +62,7 @@ void BarLine::Reset() LayerElement::Reset(); this->ResetBarLineLog(); + this->ResetBarLineVis(); this->ResetColor(); this->ResetVisibility(); @@ -91,7 +95,7 @@ bool BarLine::IsDrawnThrough(const StaffGrp *staffGrp) const return false; } -std::pair BarLine::GetLength(const StaffDef *staffDef) const +std::pair BarLine::GetLengthFromContext(const StaffDef *staffDef) const { // First check the parent measure const Measure *measure = dynamic_cast(this->GetParent()); @@ -116,7 +120,7 @@ std::pair BarLine::GetLength(const StaffDef *staffDef) const return { false, 0.0 }; } -std::pair BarLine::GetMethod(const StaffDef *staffDef) const +std::pair BarLine::GetMethodFromContext(const StaffDef *staffDef) const { // First check the parent measure const Measure *measure = dynamic_cast(this->GetParent()); @@ -141,7 +145,7 @@ std::pair BarLine::GetMethod(const StaffDef *staffDef) con return { false, BARMETHOD_NONE }; } -std::pair BarLine::GetPlace(const StaffDef *staffDef) const +std::pair BarLine::GetPlaceFromContext(const StaffDef *staffDef) const { // First check the parent measure const Measure *measure = dynamic_cast(this->GetParent()); diff --git a/src/calcligatureorneumeposfunctor.cpp b/src/calcligatureorneumeposfunctor.cpp index a13226e6cfb..efefc3dc3f5 100644 --- a/src/calcligatureorneumeposfunctor.cpp +++ b/src/calcligatureorneumeposfunctor.cpp @@ -347,16 +347,19 @@ FunctorCode CalcLigatureOrNeumePosFunctor::VisitNeume(Neume *neume) } } - // If the nc overlaps with the previous, move it back from a line width - if (overlapWithPrevious) { - xRel -= lineWidth; - } + // xRel remains unset with facsimile + if (!m_doc->HasFacsimile()) { + // If the nc overlaps with the previous, move it back from a line width + if (overlapWithPrevious) { + xRel -= lineWidth; + } - nc->SetDrawingXRel(xRel); - // The first glyph set the spacing - unless we are starting a ligature, in which case no spacing should be added - // between the two nc - if (!previousLig) { - xRel += m_doc->GetGlyphWidth(nc->m_drawingGlyphs.at(0).m_fontNo, staffSize, false); + nc->SetDrawingXRel(xRel); + // The first glyph set the spacing - unless we are starting a ligature, in which case no spacing should be + // added between the two nc + if (!previousLig) { + xRel += m_doc->GetGlyphWidth(nc->m_drawingGlyphs.at(0).m_fontNo, staffSize, false); + } } previousNc = nc; diff --git a/src/editortoolkit_neume.cpp b/src/editortoolkit_neume.cpp index 50cf062f61d..11b5ea9a0a6 100644 --- a/src/editortoolkit_neume.cpp +++ b/src/editortoolkit_neume.cpp @@ -679,7 +679,8 @@ bool EditorToolkitNeume::Drag(std::string elementId, int x, int y) SortStaves(); - m_doc->GetDrawingPage()->ResetAligners(); + m_doc->GetDrawingPage()->LayOutTranscription(true); + if (m_doc->IsTranscription() && m_doc->HasFacsimile()) m_doc->SyncFromFacsimileDoc(); return true; // Can't reorder by layer since staves contain layers @@ -1250,7 +1251,8 @@ bool EditorToolkitNeume::Insert(std::string elementType, std::string staffId, in } layer->ReorderByXPos(); - m_doc->GetDrawingPage()->LayOutPitchPos(); + m_doc->GetDrawingPage()->LayOutTranscription(true); + if (m_doc->IsTranscription() && m_doc->HasFacsimile()) m_doc->SyncFromFacsimileDoc(); m_editInfo.import("status", status); @@ -1860,6 +1862,7 @@ bool EditorToolkitNeume::Set(std::string elementId, std::string attrType, std::s else if (AttModule::SetVisual(element, attrType, attrValue)) success = true; + m_doc->GetDrawingPage()->LayOutTranscription(true); m_editInfo.import("status", success ? "OK" : "FAILURE"); m_editInfo.import("message", success ? "" : "Could not set attribute '" + attrType + "' to '" + attrValue + "'."); return success; @@ -3517,6 +3520,7 @@ bool EditorToolkitNeume::ToggleLigature(std::vector elementIds) return false; } + m_doc->GetDrawingPage()->LayOutTranscription(true); if (m_doc->IsTranscription() && m_doc->HasFacsimile()) m_doc->SyncFromFacsimileDoc(); m_editInfo.import("status", "OK"); @@ -4248,6 +4252,14 @@ bool EditorToolkitNeume::AdjustPitchFromPosition(Object *obj, Clef *clef) } pi->SetOct(3); + // The default octave = 3, but the actual octave is calculated by + // taking into account the displacement of the clef + int octave = 3; + if (clef->GetDis() && clef->GetDisPlace()) { + octave += (clef->GetDisPlace() == STAFFREL_basic_above ? 1 : -1) * (clef->GetDis() / 7); + } + pi->SetOct(octave); + const int staffSize = m_doc->GetDrawingUnit(staff->m_drawingStaffSize); const int pitchDifference diff --git a/src/hum/humlib.cpp b/src/hum/humlib.cpp index 227c5637403..ed7780c0419 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 Jun 30 19:51:54 WEST 2024 +// Last Modified: Sun Sep 8 23:07:16 PDT 2024 // Filename: min/humlib.cpp // URL: https://github.com/craigsapp/humlib/blob/master/min/humlib.cpp // Syntax: C++11 @@ -4255,721 +4255,721 @@ string Convert::getLanguageName(const string& abbreviation) { if (abbreviation[i] == '@') { continue; } - code.push_back(tolower(abbreviation[i])); + code.push_back(toupper(abbreviation[i])); } if (code.size() == 2) { // ISO 639-1 language codes - if (code == "aa") { return "Afar"; } - if (code == "ab") { return "Abkhazian"; } - if (code == "ae") { return "Avestan"; } - if (code == "af") { return "Afrikaans"; } - if (code == "ak") { return "Akan"; } - if (code == "am") { return "Amharic"; } - if (code == "an") { return "Aragonese"; } - if (code == "ar") { return "Arabic"; } - if (code == "as") { return "Assamese"; } - if (code == "av") { return "Avaric"; } - if (code == "ay") { return "Aymara"; } - if (code == "az") { return "Azerbaijani"; } - if (code == "ba") { return "Bashkir"; } - if (code == "be") { return "Belarusian"; } - if (code == "bg") { return "Bulgarian"; } - if (code == "bh") { return "Bihari languages"; } - if (code == "bi") { return "Bislama"; } - if (code == "bm") { return "Bambara"; } - if (code == "bn") { return "Bengali"; } - if (code == "bo") { return "Tibetan"; } - if (code == "br") { return "Breton"; } - if (code == "bs") { return "Bosnian"; } - if (code == "ca") { return "Catalan"; } - if (code == "ce") { return "Chechen"; } - if (code == "ch") { return "Chamorro"; } - if (code == "co") { return "Corsican"; } - if (code == "cr") { return "Cree"; } - if (code == "cs") { return "Czech"; } - if (code == "cs") { return "Czech"; } - if (code == "cu") { return "Church Slavic"; } - if (code == "cv") { return "Chuvash"; } - if (code == "cy") { return "Welsh"; } - if (code == "cy") { return "Welsh"; } - if (code == "da") { return "Danish"; } - if (code == "de") { return "German"; } - if (code == "dv") { return "Divehi"; } - if (code == "dz") { return "Dzongkha"; } - if (code == "ee") { return "Ewe"; } - if (code == "el") { return "Greek, Modern (1453-)"; } - if (code == "en") { return "English"; } - if (code == "eo") { return "Esperanto"; } - if (code == "es") { return "Spanish"; } - if (code == "et") { return "Estonian"; } - if (code == "eu") { return "Basque"; } - if (code == "eu") { return "Basque"; } - if (code == "fa") { return "Persian"; } - if (code == "ff") { return "Fulah"; } - if (code == "fi") { return "Finnish"; } - if (code == "fj") { return "Fijian"; } - if (code == "fo") { return "Faroese"; } - if (code == "fr") { return "French"; } - if (code == "fy") { return "Western Frisian"; } - if (code == "ga") { return "Irish"; } - if (code == "gd") { return "Gaelic"; } - if (code == "gl") { return "Galician"; } - if (code == "gn") { return "Guarani"; } - if (code == "gu") { return "Gujarati"; } - if (code == "gv") { return "Manx"; } - if (code == "ha") { return "Hausa"; } - if (code == "he") { return "Hebrew"; } - if (code == "hi") { return "Hindi"; } - if (code == "ho") { return "Hiri Motu"; } - if (code == "hr") { return "Croatian"; } - if (code == "ht") { return "Haitian"; } - if (code == "hu") { return "Hungarian"; } - if (code == "hy") { return "Armenian"; } - if (code == "hz") { return "Herero"; } - if (code == "ia") { return "Interlingua"; } - if (code == "id") { return "Indonesian"; } - if (code == "ie") { return "Interlingue"; } - if (code == "ig") { return "Igbo"; } - if (code == "ii") { return "Sichuan Yi"; } - if (code == "ik") { return "Inupiaq"; } - if (code == "io") { return "Ido"; } - if (code == "is") { return "Icelandic"; } - if (code == "it") { return "Italian"; } - if (code == "iu") { return "Inuktitut"; } - if (code == "ja") { return "Japanese"; } - if (code == "jv") { return "Javanese"; } - if (code == "ka") { return "Georgian"; } - if (code == "kg") { return "Kongo"; } - if (code == "ki") { return "Kikuyu"; } - if (code == "kj") { return "Kuanyama"; } - if (code == "kk") { return "Kazakh"; } - if (code == "kl") { return "Greenlandic"; } - if (code == "km") { return "Central Khmer"; } - if (code == "kn") { return "Kannada"; } - if (code == "ko") { return "Korean"; } - if (code == "kr") { return "Kanuri"; } - if (code == "ks") { return "Kashmiri"; } - if (code == "ku") { return "Kurdish"; } - if (code == "kv") { return "Komi"; } - if (code == "kw") { return "Cornish"; } - if (code == "ky") { return "Kirghiz"; } - if (code == "la") { return "Latin"; } - if (code == "lb") { return "Luxembourgish"; } - if (code == "lg") { return "Ganda"; } - if (code == "li") { return "Limburgan"; } - if (code == "ln") { return "Lingala"; } - if (code == "lo") { return "Lao"; } - if (code == "lt") { return "Lithuanian"; } - if (code == "lu") { return "Luba-Katanga"; } - if (code == "lv") { return "Latvian"; } - if (code == "mg") { return "Malagasy"; } - if (code == "mh") { return "Marshallese"; } - if (code == "mi") { return "Maori"; } - if (code == "mk") { return "Macedonian"; } - if (code == "mk") { return "Macedonian"; } - if (code == "ml") { return "Malayalam"; } - if (code == "mn") { return "Mongolian"; } - if (code == "mr") { return "Marathi"; } - if (code == "ms") { return "Malay"; } - if (code == "mt") { return "Maltese"; } - if (code == "my") { return "Burmese"; } - if (code == "my") { return "Burmese"; } - if (code == "na") { return "Nauru"; } - if (code == "nb") { return "Bokmål, Norwegian"; } - if (code == "nd") { return "Ndebele, North"; } - if (code == "ne") { return "Nepali"; } - if (code == "ng") { return "Ndonga"; } - if (code == "nl") { return "Dutch"; } - if (code == "nl") { return "Dutch"; } - if (code == "nn") { return "Norwegian Nynorsk"; } - if (code == "no") { return "Norwegian"; } - if (code == "nr") { return "Ndebele, South"; } - if (code == "nv") { return "Navajo"; } - if (code == "ny") { return "Chichewa"; } - if (code == "oc") { return "Occitan (post 1500)"; } - if (code == "oj") { return "Ojibwa"; } - if (code == "om") { return "Oromo"; } - if (code == "or") { return "Oriya"; } - if (code == "os") { return "Ossetian"; } - if (code == "pa") { return "Panjabi"; } - if (code == "pi") { return "Pali"; } - if (code == "pl") { return "Polish"; } - if (code == "ps") { return "Pushto"; } - if (code == "pt") { return "Portuguese"; } - if (code == "qu") { return "Quechua"; } - if (code == "rm") { return "Romansh"; } - if (code == "rn") { return "Rundi"; } - if (code == "ro") { return "Romanian"; } - if (code == "ru") { return "Russian"; } - if (code == "rw") { return "Kinyarwanda"; } - if (code == "sa") { return "Sanskrit"; } - if (code == "sc") { return "Sardinian"; } - if (code == "sd") { return "Sindhi"; } - if (code == "se") { return "Northern Sami"; } - if (code == "sg") { return "Sango"; } - if (code == "si") { return "Sinhala"; } - if (code == "sl") { return "Slovenian"; } - if (code == "sm") { return "Samoan"; } - if (code == "sn") { return "Shona"; } - if (code == "so") { return "Somali"; } - if (code == "sq") { return "Albanian"; } - if (code == "sr") { return "Serbian"; } - if (code == "ss") { return "Swati"; } - if (code == "st") { return "Sotho, Southern"; } - if (code == "su") { return "Sundanese"; } - if (code == "sv") { return "Swedish"; } - if (code == "sw") { return "Swahili"; } - if (code == "ta") { return "Tamil"; } - if (code == "te") { return "Telugu"; } - if (code == "tg") { return "Tajik"; } - if (code == "th") { return "Thai"; } - if (code == "ti") { return "Tigrinya"; } - if (code == "tk") { return "Turkmen"; } - if (code == "tl") { return "Tagalog"; } - if (code == "tn") { return "Tswana"; } - if (code == "to") { return "Tonga (Tonga Islands)"; } - if (code == "tr") { return "Turkish"; } - if (code == "ts") { return "Tsonga"; } - if (code == "tt") { return "Tatar"; } - if (code == "tw") { return "Twi"; } - if (code == "ty") { return "Tahitian"; } - if (code == "ug") { return "Uighur"; } - if (code == "uk") { return "Ukrainian"; } - if (code == "ur") { return "Urdu"; } - if (code == "uz") { return "Uzbek"; } - if (code == "ve") { return "Venda"; } - if (code == "vi") { return "Vietnamese"; } - if (code == "vo") { return "Volapük"; } - if (code == "wa") { return "Walloon"; } - if (code == "wo") { return "Wolof"; } - if (code == "xh") { return "Xhosa"; } - if (code == "yi") { return "Yiddish"; } - if (code == "yo") { return "Yoruba"; } - if (code == "za") { return "Zhuang"; } - if (code == "zh") { return "Chinese"; } - if (code == "zu") { return "Zulu"; } + if (code == "AA") { return "Afar"; } + if (code == "AB") { return "Abkhazian"; } + if (code == "AE") { return "Avestan"; } + if (code == "AF") { return "Afrikaans"; } + if (code == "AK") { return "Akan"; } + if (code == "AM") { return "Amharic"; } + if (code == "AN") { return "Aragonese"; } + if (code == "AR") { return "Arabic"; } + if (code == "AS") { return "Assamese"; } + if (code == "AV") { return "Avaric"; } + if (code == "AY") { return "Aymara"; } + if (code == "AZ") { return "Azerbaijani"; } + if (code == "BA") { return "Bashkir"; } + if (code == "BE") { return "Belarusian"; } + if (code == "BG") { return "Bulgarian"; } + if (code == "BH") { return "Bihari languages"; } + if (code == "BI") { return "Bislama"; } + if (code == "BM") { return "Bambara"; } + if (code == "BN") { return "Bengali"; } + if (code == "BO") { return "Tibetan"; } + if (code == "BR") { return "Breton"; } + if (code == "BS") { return "Bosnian"; } + if (code == "CA") { return "Catalan"; } + if (code == "CE") { return "Chechen"; } + if (code == "CH") { return "Chamorro"; } + if (code == "CO") { return "Corsican"; } + if (code == "CR") { return "Cree"; } + if (code == "CS") { return "Czech"; } + if (code == "CS") { return "Czech"; } + if (code == "CU") { return "Church Slavic"; } + if (code == "CV") { return "Chuvash"; } + if (code == "CY") { return "Welsh"; } + if (code == "CY") { return "Welsh"; } + if (code == "DA") { return "Danish"; } + if (code == "DE") { return "German"; } + if (code == "DV") { return "Divehi"; } + if (code == "DZ") { return "Dzongkha"; } + if (code == "EE") { return "Ewe"; } + if (code == "EL") { return "Greek, Modern (1453-)"; } + if (code == "EN") { return "English"; } + if (code == "EO") { return "Esperanto"; } + if (code == "ES") { return "Spanish"; } + if (code == "ET") { return "Estonian"; } + if (code == "EU") { return "Basque"; } + if (code == "EU") { return "Basque"; } + if (code == "FA") { return "Persian"; } + if (code == "FF") { return "Fulah"; } + if (code == "FI") { return "Finnish"; } + if (code == "FJ") { return "Fijian"; } + if (code == "FO") { return "Faroese"; } + if (code == "FR") { return "French"; } + if (code == "FY") { return "Western Frisian"; } + if (code == "GA") { return "Irish"; } + if (code == "GD") { return "Gaelic"; } + if (code == "GL") { return "Galician"; } + if (code == "GN") { return "Guarani"; } + if (code == "GU") { return "Gujarati"; } + if (code == "GV") { return "Manx"; } + if (code == "HA") { return "Hausa"; } + if (code == "HE") { return "Hebrew"; } + if (code == "HI") { return "Hindi"; } + if (code == "HO") { return "Hiri Motu"; } + if (code == "HR") { return "Croatian"; } + if (code == "HT") { return "Haitian"; } + if (code == "HU") { return "Hungarian"; } + if (code == "HY") { return "Armenian"; } + if (code == "HZ") { return "Herero"; } + if (code == "IA") { return "Interlingua"; } + if (code == "ID") { return "Indonesian"; } + if (code == "IE") { return "Interlingue"; } + if (code == "IG") { return "Igbo"; } + if (code == "II") { return "Sichuan Yi"; } + if (code == "IK") { return "Inupiaq"; } + if (code == "IO") { return "Ido"; } + if (code == "IS") { return "Icelandic"; } + if (code == "IT") { return "Italian"; } + if (code == "IU") { return "Inuktitut"; } + if (code == "JA") { return "Japanese"; } + if (code == "JV") { return "Javanese"; } + if (code == "KA") { return "Georgian"; } + if (code == "KG") { return "Kongo"; } + if (code == "KI") { return "Kikuyu"; } + if (code == "KJ") { return "Kuanyama"; } + if (code == "KK") { return "Kazakh"; } + if (code == "KL") { return "Greenlandic"; } + if (code == "KM") { return "Central Khmer"; } + if (code == "KN") { return "Kannada"; } + if (code == "KO") { return "Korean"; } + if (code == "KR") { return "Kanuri"; } + if (code == "KS") { return "Kashmiri"; } + if (code == "KU") { return "Kurdish"; } + if (code == "KV") { return "Komi"; } + if (code == "KW") { return "Cornish"; } + if (code == "KY") { return "Kirghiz"; } + if (code == "LA") { return "Latin"; } + if (code == "LB") { return "Luxembourgish"; } + if (code == "LG") { return "Ganda"; } + if (code == "LI") { return "Limburgan"; } + if (code == "LN") { return "Lingala"; } + if (code == "LO") { return "Lao"; } + if (code == "LT") { return "Lithuanian"; } + if (code == "LU") { return "Luba-Katanga"; } + if (code == "LV") { return "Latvian"; } + if (code == "MG") { return "Malagasy"; } + if (code == "MH") { return "Marshallese"; } + if (code == "MI") { return "Maori"; } + if (code == "MK") { return "Macedonian"; } + if (code == "MK") { return "Macedonian"; } + if (code == "ML") { return "Malayalam"; } + if (code == "MN") { return "Mongolian"; } + if (code == "MR") { return "Marathi"; } + if (code == "MS") { return "Malay"; } + if (code == "MT") { return "Maltese"; } + if (code == "MY") { return "Burmese"; } + if (code == "MY") { return "Burmese"; } + if (code == "NA") { return "Nauru"; } + if (code == "NB") { return "Bokmål, Norwegian"; } + if (code == "ND") { return "Ndebele, North"; } + if (code == "NE") { return "Nepali"; } + if (code == "NG") { return "Ndonga"; } + if (code == "NL") { return "Dutch"; } + if (code == "NL") { return "Dutch"; } + if (code == "NN") { return "Norwegian Nynorsk"; } + if (code == "NO") { return "Norwegian"; } + if (code == "NR") { return "Ndebele, South"; } + if (code == "NV") { return "Navajo"; } + if (code == "NY") { return "Chichewa"; } + if (code == "OC") { return "Occitan (post 1500)"; } + if (code == "OJ") { return "Ojibwa"; } + if (code == "OM") { return "Oromo"; } + if (code == "OR") { return "Oriya"; } + if (code == "OS") { return "Ossetian"; } + if (code == "PA") { return "Panjabi"; } + if (code == "PI") { return "Pali"; } + if (code == "PL") { return "Polish"; } + if (code == "PS") { return "Pushto"; } + if (code == "PT") { return "Portuguese"; } + if (code == "QU") { return "Quechua"; } + if (code == "RM") { return "Romansh"; } + if (code == "RN") { return "Rundi"; } + if (code == "RO") { return "Romanian"; } + if (code == "RU") { return "Russian"; } + if (code == "RW") { return "Kinyarwanda"; } + if (code == "SA") { return "Sanskrit"; } + if (code == "SC") { return "Sardinian"; } + if (code == "SD") { return "Sindhi"; } + if (code == "SE") { return "Northern Sami"; } + if (code == "SG") { return "Sango"; } + if (code == "SI") { return "Sinhala"; } + if (code == "SL") { return "Slovenian"; } + if (code == "SM") { return "Samoan"; } + if (code == "SN") { return "Shona"; } + if (code == "SO") { return "Somali"; } + if (code == "SQ") { return "Albanian"; } + if (code == "SR") { return "Serbian"; } + if (code == "SS") { return "Swati"; } + if (code == "ST") { return "Sotho, Southern"; } + if (code == "SU") { return "Sundanese"; } + if (code == "SV") { return "Swedish"; } + if (code == "SW") { return "Swahili"; } + if (code == "TA") { return "Tamil"; } + if (code == "TE") { return "Telugu"; } + if (code == "TG") { return "Tajik"; } + if (code == "TH") { return "Thai"; } + if (code == "TI") { return "Tigrinya"; } + if (code == "TK") { return "Turkmen"; } + if (code == "TL") { return "Tagalog"; } + if (code == "TN") { return "Tswana"; } + if (code == "TO") { return "Tonga (Tonga Islands)"; } + if (code == "TR") { return "Turkish"; } + if (code == "TS") { return "Tsonga"; } + if (code == "TT") { return "Tatar"; } + if (code == "TW") { return "Twi"; } + if (code == "TY") { return "Tahitian"; } + if (code == "UG") { return "Uighur"; } + if (code == "UK") { return "Ukrainian"; } + if (code == "UR") { return "Urdu"; } + if (code == "UZ") { return "Uzbek"; } + if (code == "VE") { return "Venda"; } + if (code == "VI") { return "Vietnamese"; } + if (code == "VO") { return "Volapük"; } + if (code == "WA") { return "Walloon"; } + if (code == "WO") { return "Wolof"; } + if (code == "XH") { return "Xhosa"; } + if (code == "YI") { return "Yiddish"; } + if (code == "YO") { return "Yoruba"; } + if (code == "ZA") { return "Zhuang"; } + if (code == "ZH") { return "Chinese"; } + if (code == "ZU") { return "Zulu"; } } else if (code.size() == 3) { // ISO 639-2 language codes - if (code == "aar") { return "Afar"; } - if (code == "abk") { return "Abkhazian"; } - if (code == "ace") { return "Achinese"; } - if (code == "ach") { return "Acoli"; } - if (code == "ada") { return "Adangme"; } - if (code == "ady") { return "Adyghe"; } - if (code == "afa") { return "Afro-Asiatic languages"; } - if (code == "afh") { return "Afrihili"; } - if (code == "afr") { return "Afrikaans"; } - if (code == "ain") { return "Ainu"; } - if (code == "aka") { return "Akan"; } - if (code == "akk") { return "Akkadian"; } - if (code == "alb") { return "Albanian"; } - if (code == "ale") { return "Aleut"; } - if (code == "alg") { return "Algonquian languages"; } - if (code == "alt") { return "Southern Altai"; } - if (code == "amh") { return "Amharic"; } - if (code == "ang") { return "English, Old (ca.450-1100)"; } - if (code == "anp") { return "Angika"; } - if (code == "apa") { return "Apache languages"; } - if (code == "ara") { return "Arabic"; } - if (code == "arc") { return "Aramaic (700-300 BCE)"; } - if (code == "arg") { return "Aragonese"; } - if (code == "arm") { return "Armenian"; } - if (code == "arn") { return "Mapudungun"; } - if (code == "arp") { return "Arapaho"; } - if (code == "art") { return "Artificial languages"; } - if (code == "arw") { return "Arawak"; } - if (code == "asm") { return "Assamese"; } - if (code == "ast") { return "Asturian"; } - if (code == "ath") { return "Athapascan languages"; } - if (code == "aus") { return "Australian languages"; } - if (code == "ava") { return "Avaric"; } - if (code == "ave") { return "Avestan"; } - if (code == "awa") { return "Awadhi"; } - if (code == "aym") { return "Aymara"; } - if (code == "aze") { return "Azerbaijani"; } - if (code == "bad") { return "Banda languages"; } - if (code == "bai") { return "Bamileke languages"; } - if (code == "bak") { return "Bashkir"; } - if (code == "bal") { return "Baluchi"; } - if (code == "bam") { return "Bambara"; } - if (code == "ban") { return "Balinese"; } - if (code == "baq") { return "Basque"; } - if (code == "baq") { return "Basque"; } - if (code == "bas") { return "Basa"; } - if (code == "bat") { return "Baltic languages"; } - if (code == "bej") { return "Beja"; } - if (code == "bel") { return "Belarusian"; } - if (code == "bem") { return "Bemba"; } - if (code == "ben") { return "Bengali"; } - if (code == "ber") { return "Berber languages"; } - if (code == "bho") { return "Bhojpuri"; } - if (code == "bih") { return "Bihari languages"; } - if (code == "bik") { return "Bikol"; } - if (code == "bin") { return "Bini"; } - if (code == "bis") { return "Bislama"; } - if (code == "bla") { return "Siksika"; } - if (code == "bnt") { return "Bantu languages"; } - if (code == "bod") { return "Tibetan"; } - if (code == "bos") { return "Bosnian"; } - if (code == "bra") { return "Braj"; } - if (code == "bre") { return "Breton"; } - if (code == "btk") { return "Batak languages"; } - if (code == "bua") { return "Buriat"; } - if (code == "bug") { return "Buginese"; } - if (code == "bul") { return "Bulgarian"; } - if (code == "bur") { return "Burmese"; } - if (code == "bur") { return "Burmese"; } - if (code == "byn") { return "Blin"; } - if (code == "cad") { return "Caddo"; } - if (code == "cai") { return "Central American Indian languages"; } - if (code == "car") { return "Galibi Carib"; } - if (code == "cat") { return "Catalan"; } - if (code == "cau") { return "Caucasian languages"; } - if (code == "ceb") { return "Cebuano"; } - if (code == "cel") { return "Celtic languages"; } - if (code == "ces") { return "Czech"; } - if (code == "ces") { return "Czech"; } - if (code == "cha") { return "Chamorro"; } - if (code == "chb") { return "Chibcha"; } - if (code == "che") { return "Chechen"; } - if (code == "chg") { return "Chagatai"; } - if (code == "chi") { return "Chinese"; } - if (code == "chk") { return "Chuukese"; } - if (code == "chm") { return "Mari"; } - if (code == "chn") { return "Chinook jargon"; } - if (code == "cho") { return "Choctaw"; } - if (code == "chp") { return "Chipewyan"; } - if (code == "chr") { return "Cherokee"; } - if (code == "chu") { return "Church Slavic"; } - if (code == "chv") { return "Chuvash"; } - if (code == "chy") { return "Cheyenne"; } - if (code == "cmc") { return "Chamic languages"; } - if (code == "cnr") { return "Montenegrin"; } - if (code == "cop") { return "Coptic"; } - if (code == "cor") { return "Cornish"; } - if (code == "cos") { return "Corsican"; } - if (code == "cpe") { return "Creoles and pidgins, English based"; } - if (code == "cpf") { return "Creoles and pidgins, French-based"; } - if (code == "cpp") { return "Creoles and pidgins, Portuguese-based"; } - if (code == "cre") { return "Cree"; } - if (code == "crh") { return "Crimean Tatar"; } - if (code == "crp") { return "Creoles and pidgins"; } - if (code == "csb") { return "Kashubian"; } - if (code == "cus") { return "Cushitic languages"; } - if (code == "cym") { return "Welsh"; } - if (code == "cym") { return "Welsh"; } - if (code == "cze") { return "Czech"; } - if (code == "cze") { return "Czech"; } - if (code == "dak") { return "Dakota"; } - if (code == "dan") { return "Danish"; } - if (code == "dar") { return "Dargwa"; } - if (code == "day") { return "Land Dayak languages"; } - if (code == "del") { return "Delaware"; } - if (code == "den") { return "Slave (Athapascan)"; } - if (code == "deu") { return "German"; } - if (code == "dgr") { return "Dogrib"; } - if (code == "din") { return "Dinka"; } - if (code == "div") { return "Divehi"; } - if (code == "doi") { return "Dogri"; } - if (code == "dra") { return "Dravidian languages"; } - if (code == "dsb") { return "Lower Sorbian"; } - if (code == "dua") { return "Duala"; } - if (code == "dum") { return "Dutch, Middle (ca.1050-1350)"; } - if (code == "dut") { return "Dutch"; } - if (code == "dut") { return "Dutch"; } - if (code == "dyu") { return "Dyula"; } - if (code == "dzo") { return "Dzongkha"; } - if (code == "efi") { return "Efik"; } - if (code == "egy") { return "Egyptian (Ancient)"; } - if (code == "eka") { return "Ekajuk"; } - if (code == "ell") { return "Greek, Modern (1453-)"; } - if (code == "elx") { return "Elamite"; } - if (code == "eng") { return "English"; } - if (code == "enm") { return "English, Middle (1100-1500)"; } - if (code == "epo") { return "Esperanto"; } - if (code == "est") { return "Estonian"; } - if (code == "eus") { return "Basque"; } - if (code == "eus") { return "Basque"; } - if (code == "ewe") { return "Ewe"; } - if (code == "ewo") { return "Ewondo"; } - if (code == "fan") { return "Fang"; } - if (code == "fao") { return "Faroese"; } - if (code == "fas") { return "Persian"; } - if (code == "fat") { return "Fanti"; } - if (code == "fij") { return "Fijian"; } - if (code == "fil") { return "Filipino"; } - if (code == "fin") { return "Finnish"; } - if (code == "fiu") { return "Finno-Ugrian languages"; } - if (code == "fon") { return "Fon"; } - if (code == "fra") { return "French"; } - if (code == "fre") { return "French"; } - if (code == "frm") { return "French, Middle (ca.1400-1600)"; } - if (code == "fro") { return "French, Old (842-ca.1400)"; } - if (code == "frr") { return "Northern Frisian"; } - if (code == "frs") { return "Eastern Frisian"; } - if (code == "fry") { return "Western Frisian"; } - if (code == "ful") { return "Fulah"; } - if (code == "fur") { return "Friulian"; } - if (code == "gaa") { return "Ga"; } - if (code == "gay") { return "Gayo"; } - if (code == "gba") { return "Gbaya"; } - if (code == "gem") { return "Germanic languages"; } - if (code == "geo") { return "Georgin"; } - if (code == "ger") { return "German"; } - if (code == "gez") { return "Geez"; } - if (code == "gil") { return "Gilbertese"; } - if (code == "gla") { return "Gaelic"; } - if (code == "gle") { return "Irish"; } - if (code == "glg") { return "Galician"; } - if (code == "glv") { return "Manx"; } - if (code == "gmh") { return "German, Middle High (ca.1050-1500)"; } - if (code == "goh") { return "German, Old High (ca.750-1050)"; } - if (code == "gon") { return "Gondi"; } - if (code == "gor") { return "Gorontalo"; } - if (code == "got") { return "Gothic"; } - if (code == "grb") { return "Grebo"; } - if (code == "grc") { return "Greek, Ancient (to 1453)"; } - if (code == "gre") { return "Greek"; } - if (code == "grn") { return "Guarani"; } - if (code == "gsw") { return "Swiss German"; } - if (code == "guj") { return "Gujarati"; } - if (code == "gwi") { return "Gwich'in"; } - if (code == "hai") { return "Haida"; } - if (code == "hat") { return "Haitian"; } - if (code == "hau") { return "Hausa"; } - if (code == "haw") { return "Hawaiian"; } - if (code == "heb") { return "Hebrew"; } - if (code == "her") { return "Herero"; } - if (code == "hil") { return "Hiligaynon"; } - if (code == "him") { return "Himachali languages"; } - if (code == "hin") { return "Hindi"; } - if (code == "hit") { return "Hittite"; } - if (code == "hmn") { return "Hmong"; } - if (code == "hmo") { return "Hiri Motu"; } - if (code == "hrv") { return "Croatian"; } - if (code == "hsb") { return "Upper Sorbian"; } - if (code == "hun") { return "Hungarian"; } - if (code == "hup") { return "Hupa"; } - if (code == "hye") { return "Armenian"; } - if (code == "iba") { return "Iban"; } - if (code == "ibo") { return "Igbo"; } - if (code == "ice") { return "Icelandic"; } - if (code == "ido") { return "Ido"; } - if (code == "iii") { return "Sichuan Yi"; } - if (code == "ijo") { return "Ijo languages"; } - if (code == "iku") { return "Inuktitut"; } - if (code == "ile") { return "Interlingue"; } - if (code == "ilo") { return "Iloko"; } - if (code == "ina") { return "Interlingua)"; } - if (code == "inc") { return "Indic languages"; } - if (code == "ind") { return "Indonesian"; } - if (code == "ine") { return "Indo-European languages"; } - if (code == "inh") { return "Ingush"; } - if (code == "ipk") { return "Inupiaq"; } - if (code == "ira") { return "Iranian languages"; } - if (code == "iro") { return "Iroquoian languages"; } - if (code == "isl") { return "Icelandic"; } - if (code == "ita") { return "Italian"; } - if (code == "jav") { return "Javanese"; } - if (code == "jbo") { return "Lojban"; } - if (code == "jpn") { return "Japanese"; } - if (code == "jpr") { return "Judeo-Persian"; } - if (code == "jrb") { return "Judeo-Arabic"; } - if (code == "kaa") { return "Kara-Kalpak"; } - if (code == "kab") { return "Kabyle"; } - if (code == "kac") { return "Kachin"; } - if (code == "kal") { return "Greenlandic"; } - if (code == "kam") { return "Kamba"; } - if (code == "kan") { return "Kannada"; } - if (code == "kar") { return "Karen languages"; } - if (code == "kas") { return "Kashmiri"; } - if (code == "kat") { return "Georgian"; } - if (code == "kau") { return "Kanuri"; } - if (code == "kaw") { return "Kawi"; } - if (code == "kaz") { return "Kazakh"; } - if (code == "kbd") { return "Kabardian"; } - if (code == "kha") { return "Khasi"; } - if (code == "khi") { return "Khoisan languages"; } - if (code == "khm") { return "Central Khmer"; } - if (code == "kho") { return "Khotanese"; } - if (code == "kik") { return "Kikuyu"; } - if (code == "kin") { return "Kinyarwanda"; } - if (code == "kir") { return "Kirghiz"; } - if (code == "kmb") { return "Kimbundu"; } - if (code == "kok") { return "Konkani"; } - if (code == "kom") { return "Komi"; } - if (code == "kon") { return "Kongo"; } - if (code == "kor") { return "Korean"; } - if (code == "kos") { return "Kosraean"; } - if (code == "kpe") { return "Kpelle"; } - if (code == "krc") { return "Karachay-Balkar"; } - if (code == "krl") { return "Karelian"; } - if (code == "kro") { return "Kru languages"; } - if (code == "kru") { return "Kurukh"; } - if (code == "kua") { return "Kuanyama"; } - if (code == "kum") { return "Kumyk"; } - if (code == "kur") { return "Kurdish"; } - if (code == "kut") { return "Kutenai"; } - if (code == "lad") { return "Ladino"; } - if (code == "lah") { return "Lahnda"; } - if (code == "lam") { return "Lamba"; } - if (code == "lao") { return "Lao"; } - if (code == "lat") { return "Latin"; } - if (code == "lav") { return "Latvian"; } - if (code == "lez") { return "Lezghian"; } - if (code == "lim") { return "Limburgan"; } - if (code == "lin") { return "Lingala"; } - if (code == "lit") { return "Lithuanian"; } - if (code == "lol") { return "Mongo"; } - if (code == "loz") { return "Lozi"; } - if (code == "ltz") { return "Luxembourgish"; } - if (code == "lua") { return "Luba-Lulua"; } - if (code == "lub") { return "Luba-Katanga"; } - if (code == "lug") { return "Ganda"; } - if (code == "lui") { return "Luiseno"; } - if (code == "lun") { return "Lunda"; } - if (code == "luo") { return "Luo (Kenya and Tanzania)"; } - if (code == "lus") { return "Lushai"; } - if (code == "mac") { return "Macedonian"; } - if (code == "mac") { return "Macedonian"; } - if (code == "mad") { return "Madurese"; } - if (code == "mag") { return "Magahi"; } - if (code == "mah") { return "Marshallese"; } - if (code == "mai") { return "Maithili"; } - if (code == "mak") { return "Makasar"; } - if (code == "mal") { return "Malayalam"; } - if (code == "man") { return "Mandingo"; } - if (code == "mao") { return "Maori"; } - if (code == "map") { return "Austronesian languages"; } - if (code == "mar") { return "Marathi"; } - if (code == "mas") { return "Masai"; } - if (code == "may") { return "Malay"; } - if (code == "mdf") { return "Moksha"; } - if (code == "mdr") { return "Mandar"; } - if (code == "men") { return "Mende"; } - if (code == "mga") { return "Irish, Middle (900-1200)"; } - if (code == "mic") { return "Mi'kmaq"; } - if (code == "min") { return "Minangkabau"; } - if (code == "mis") { return "Uncoded languages"; } - if (code == "mkd") { return "Macedonian"; } - if (code == "mkd") { return "Macedonian"; } - if (code == "mkh") { return "Mon-Khmer languages"; } - if (code == "mlg") { return "Malagasy"; } - if (code == "mlt") { return "Maltese"; } - if (code == "mnc") { return "Manchu"; } - if (code == "mni") { return "Manipuri"; } - if (code == "mno") { return "Manobo languages"; } - if (code == "moh") { return "Mohawk"; } - if (code == "mon") { return "Mongolian"; } - if (code == "mos") { return "Mossi"; } - if (code == "mri") { return "Maori"; } - if (code == "msa") { return "Malay"; } - if (code == "mul") { return "Multiple languages"; } - if (code == "mun") { return "Munda languages"; } - if (code == "mus") { return "Creek"; } - if (code == "mwl") { return "Mirandese"; } - if (code == "mwr") { return "Marwari"; } - if (code == "mya") { return "Burmese"; } - if (code == "mya") { return "Burmese"; } - if (code == "myn") { return "Mayan languages"; } - if (code == "myv") { return "Erzya"; } - if (code == "nah") { return "Nahuatl languages"; } - if (code == "nai") { return "North American Indian languages"; } - if (code == "nap") { return "Neapolitan"; } - if (code == "nau") { return "Nauru"; } - if (code == "nav") { return "Navajo"; } - if (code == "nbl") { return "Ndebele, South"; } - if (code == "nde") { return "Ndebele, North"; } - if (code == "ndo") { return "Ndonga"; } - if (code == "nds") { return "Low German"; } - if (code == "nep") { return "Nepali"; } - if (code == "new") { return "Nepal Bhasa"; } - if (code == "nia") { return "Nias"; } - if (code == "nic") { return "Niger-Kordofanian languages"; } - if (code == "niu") { return "Niuean"; } - if (code == "nld") { return "Dutch"; } - if (code == "nld") { return "Dutch"; } - if (code == "nno") { return "Norwegian Nynorsk"; } - if (code == "nob") { return "Bokmål, Norwegian"; } - if (code == "nog") { return "Nogai"; } - if (code == "non") { return "Norse, Old"; } - if (code == "nor") { return "Norwegian"; } - if (code == "nqo") { return "N'Ko"; } - if (code == "nso") { return "Pedi"; } - if (code == "nub") { return "Nubian languages"; } - if (code == "nwc") { return "Classical Newari"; } - if (code == "nya") { return "Chichewa"; } - if (code == "nym") { return "Nyamwezi"; } - if (code == "nyn") { return "Nyankole"; } - if (code == "nyo") { return "Nyoro"; } - if (code == "nzi") { return "Nzima"; } - if (code == "oci") { return "Occitan (post 1500)"; } - if (code == "oji") { return "Ojibwa"; } - if (code == "ori") { return "Oriya"; } - if (code == "orm") { return "Oromo"; } - if (code == "osa") { return "Osage"; } - if (code == "oss") { return "Ossetian"; } - if (code == "ota") { return "Turkish, Ottoman (1500-1928)"; } - if (code == "oto") { return "Otomian languages"; } - if (code == "paa") { return "Papuan languages"; } - if (code == "pag") { return "Pangasinan"; } - if (code == "pal") { return "Pahlavi"; } - if (code == "pam") { return "Pampanga"; } - if (code == "pan") { return "Panjabi"; } - if (code == "pap") { return "Papiamento"; } - if (code == "pau") { return "Palauan"; } - if (code == "peo") { return "Persian, Old (ca.600-400 B.C.)"; } - if (code == "per") { return "Persian"; } - if (code == "phi") { return "Philippine languages"; } - if (code == "phn") { return "Phoenician"; } - if (code == "pli") { return "Pali"; } - if (code == "pol") { return "Polish"; } - if (code == "pon") { return "Pohnpeian"; } - if (code == "por") { return "Portuguese"; } - if (code == "pra") { return "Prakrit languages"; } - if (code == "pro") { return "Provençal, Old (to 1500)"; } - if (code == "pus") { return "Pushto"; } - if (code == "que") { return "Quechua"; } - if (code == "raj") { return "Rajasthani"; } - if (code == "rap") { return "Rapanui"; } - if (code == "rar") { return "Rarotongan"; } - if (code == "roa") { return "Romance languages"; } - if (code == "roh") { return "Romansh"; } - if (code == "rom") { return "Romany"; } - if (code == "ron") { return "Romanian"; } - if (code == "rum") { return "Romanian"; } - if (code == "run") { return "Rundi"; } - if (code == "rup") { return "Aromanian"; } - if (code == "rus") { return "Russian"; } - if (code == "sad") { return "Sandawe"; } - if (code == "sag") { return "Sango"; } - if (code == "sah") { return "Yakut"; } - if (code == "sai") { return "South American Indian languages"; } - if (code == "sal") { return "Salishan languages"; } - if (code == "sam") { return "Samaritan Aramaic"; } - if (code == "san") { return "Sanskrit"; } - if (code == "sas") { return "Sasak"; } - if (code == "sat") { return "Santali"; } - if (code == "scn") { return "Sicilian"; } - if (code == "sco") { return "Scots"; } - if (code == "sel") { return "Selkup"; } - if (code == "sem") { return "Semitic languages"; } - if (code == "sga") { return "Irish, Old (to 900)"; } - if (code == "sgn") { return "Sign Languages"; } - if (code == "shn") { return "Shan"; } - if (code == "sid") { return "Sidamo"; } - if (code == "sin") { return "Sinhala"; } - if (code == "sio") { return "Siouan languages"; } - if (code == "sit") { return "Sino-Tibetan languages"; } - if (code == "sla") { return "Slavic languages"; } - if (code == "slo") { return "Slovak"; } - if (code == "slv") { return "Slovenian"; } - if (code == "sma") { return "Southern Sami"; } - if (code == "sme") { return "Northern Sami"; } - if (code == "smi") { return "Sami languages"; } - if (code == "smj") { return "Lule Sami"; } - if (code == "smn") { return "Inari Sami"; } - if (code == "smo") { return "Samoan"; } - if (code == "sms") { return "Skolt Sami"; } - if (code == "sna") { return "Shona"; } - if (code == "snd") { return "Sindhi"; } - if (code == "snk") { return "Soninke"; } - if (code == "sog") { return "Sogdian"; } - if (code == "som") { return "Somali"; } - if (code == "son") { return "Songhai languages"; } - if (code == "sot") { return "Sotho, Southern"; } - if (code == "spa") { return "Spanish"; } - if (code == "sqi") { return "Albanian"; } - if (code == "srd") { return "Sardinian"; } - if (code == "srn") { return "Sranan Tongo"; } - if (code == "srp") { return "Serbian"; } - if (code == "srr") { return "Serer"; } - if (code == "ssa") { return "Nilo-Saharan languages"; } - if (code == "ssw") { return "Swati"; } - if (code == "suk") { return "Sukuma"; } - if (code == "sun") { return "Sundanese"; } - if (code == "sus") { return "Susu"; } - if (code == "sux") { return "Sumerian"; } - if (code == "swa") { return "Swahili"; } - if (code == "swe") { return "Swedish"; } - if (code == "syc") { return "Classical Syriac"; } - if (code == "syr") { return "Syriac"; } - if (code == "tah") { return "Tahitian"; } - if (code == "tai") { return "Tai languages"; } - if (code == "tam") { return "Tamil"; } - if (code == "tat") { return "Tatar"; } - if (code == "tel") { return "Telugu"; } - if (code == "tem") { return "Timne"; } - if (code == "ter") { return "Tereno"; } - if (code == "tet") { return "Tetum"; } - if (code == "tgk") { return "Tajik"; } - if (code == "tgl") { return "Tagalog"; } - if (code == "tha") { return "Thai"; } - if (code == "tib") { return "Tibetian"; } - if (code == "tig") { return "Tigre"; } - if (code == "tir") { return "Tigrinya"; } - if (code == "tiv") { return "Tiv"; } - if (code == "tkl") { return "Tokelau"; } - if (code == "tlh") { return "Klingon"; } - if (code == "tli") { return "Tlingit"; } - if (code == "tmh") { return "Tamashek"; } - if (code == "tog") { return "Tonga (Nyasa)"; } - if (code == "ton") { return "Tonga (Tonga Islands)"; } - if (code == "tpi") { return "Tok Pisin"; } - if (code == "tsi") { return "Tsimshian"; } - if (code == "tsn") { return "Tswana"; } - if (code == "tso") { return "Tsonga"; } - if (code == "tuk") { return "Turkmen"; } - if (code == "tum") { return "Tumbuka"; } - if (code == "tup") { return "Tupi languages"; } - if (code == "tur") { return "Turkish"; } - if (code == "tut") { return "Altaic languages"; } - if (code == "tvl") { return "Tuvalu"; } - if (code == "twi") { return "Twi"; } - if (code == "tyv") { return "Tuvinian"; } - if (code == "udm") { return "Udmurt"; } - if (code == "uga") { return "Ugaritic"; } - if (code == "uig") { return "Uighur"; } - if (code == "ukr") { return "Ukrainian"; } - if (code == "umb") { return "Umbundu"; } - if (code == "und") { return "Undetermined"; } - if (code == "urd") { return "Urdu"; } - if (code == "uzb") { return "Uzbek"; } - if (code == "vai") { return "Vai"; } - if (code == "ven") { return "Venda"; } - if (code == "vie") { return "Vietnamese"; } - if (code == "vol") { return "Volapük"; } - if (code == "vot") { return "Votic"; } - if (code == "wak") { return "Wakashan languages"; } - if (code == "wal") { return "Wolaitta"; } - if (code == "war") { return "Waray"; } - if (code == "was") { return "Washo"; } - if (code == "wel") { return "Welsh"; } - if (code == "wel") { return "Welsh"; } - if (code == "wen") { return "Sorbian languages"; } - if (code == "wln") { return "Walloon"; } - if (code == "wol") { return "Wolof"; } - if (code == "xal") { return "Kalmyk"; } - if (code == "xho") { return "Xhosa"; } - if (code == "yao") { return "Yao"; } - if (code == "yap") { return "Yapese"; } - if (code == "yid") { return "Yiddish"; } - if (code == "yor") { return "Yoruba"; } - if (code == "ypk") { return "Yupik languages"; } - if (code == "zap") { return "Zapotec"; } - if (code == "zbl") { return "Blissymbols"; } - if (code == "zen") { return "Zenaga"; } - if (code == "zgh") { return "Moroccan"; } - if (code == "zha") { return "Zhuang"; } - if (code == "zho") { return "Chinese"; } - if (code == "znd") { return "Zande languages"; } - if (code == "zul") { return "Zulu"; } - if (code == "zun") { return "Zuni"; } - if (code == "zza") { return "Zaza"; } + if (code == "AAR") { return "Afar"; } + if (code == "ABK") { return "Abkhazian"; } + if (code == "ACE") { return "Achinese"; } + if (code == "ACH") { return "Acoli"; } + if (code == "ADA") { return "Adangme"; } + if (code == "ADY") { return "Adyghe"; } + if (code == "AFA") { return "Afro-Asiatic languages"; } + if (code == "AFH") { return "Afrihili"; } + if (code == "AFR") { return "Afrikaans"; } + if (code == "AIN") { return "Ainu"; } + if (code == "AKA") { return "Akan"; } + if (code == "AKK") { return "Akkadian"; } + if (code == "ALB") { return "Albanian"; } + if (code == "ALE") { return "Aleut"; } + if (code == "ALG") { return "Algonquian languages"; } + if (code == "ALT") { return "Southern Altai"; } + if (code == "AMH") { return "Amharic"; } + if (code == "ANG") { return "English, Old (ca.450-1100)"; } + if (code == "ANP") { return "Angika"; } + if (code == "APA") { return "Apache languages"; } + if (code == "ARA") { return "Arabic"; } + if (code == "ARC") { return "Aramaic (700-300 BCE)"; } + if (code == "ARG") { return "Aragonese"; } + if (code == "ARM") { return "Armenian"; } + if (code == "ARN") { return "Mapudungun"; } + if (code == "ARP") { return "Arapaho"; } + if (code == "ART") { return "Artificial languages"; } + if (code == "ARW") { return "Arawak"; } + if (code == "ASM") { return "Assamese"; } + if (code == "AST") { return "Asturian"; } + if (code == "ATH") { return "Athapascan languages"; } + if (code == "AUS") { return "Australian languages"; } + if (code == "AVA") { return "Avaric"; } + if (code == "AVE") { return "Avestan"; } + if (code == "AWA") { return "Awadhi"; } + if (code == "AYM") { return "Aymara"; } + if (code == "AZE") { return "Azerbaijani"; } + if (code == "BAD") { return "Banda languages"; } + if (code == "BAI") { return "Bamileke languages"; } + if (code == "BAK") { return "Bashkir"; } + if (code == "BAL") { return "Baluchi"; } + if (code == "BAM") { return "Bambara"; } + if (code == "BAN") { return "Balinese"; } + if (code == "BAQ") { return "Basque"; } + if (code == "BAQ") { return "Basque"; } + if (code == "BAS") { return "Basa"; } + if (code == "BAT") { return "Baltic languages"; } + if (code == "BEJ") { return "Beja"; } + if (code == "BEL") { return "Belarusian"; } + if (code == "BEM") { return "Bemba"; } + if (code == "BEN") { return "Bengali"; } + if (code == "BER") { return "Berber languages"; } + if (code == "BHO") { return "Bhojpuri"; } + if (code == "BIH") { return "Bihari languages"; } + if (code == "BIK") { return "Bikol"; } + if (code == "BIN") { return "Bini"; } + if (code == "BIS") { return "Bislama"; } + if (code == "BLA") { return "Siksika"; } + if (code == "BNT") { return "Bantu languages"; } + if (code == "BOD") { return "Tibetan"; } + if (code == "BOS") { return "Bosnian"; } + if (code == "BRA") { return "Braj"; } + if (code == "BRE") { return "Breton"; } + if (code == "BTK") { return "Batak languages"; } + if (code == "BUA") { return "Buriat"; } + if (code == "BUG") { return "Buginese"; } + if (code == "BUL") { return "Bulgarian"; } + if (code == "BUR") { return "Burmese"; } + if (code == "BUR") { return "Burmese"; } + if (code == "BYN") { return "Blin"; } + if (code == "CAD") { return "Caddo"; } + if (code == "CAI") { return "Central American Indian languages"; } + if (code == "CAR") { return "Galibi Carib"; } + if (code == "CAT") { return "Catalan"; } + if (code == "CAU") { return "Caucasian languages"; } + if (code == "CEB") { return "Cebuano"; } + if (code == "CEL") { return "Celtic languages"; } + if (code == "CES") { return "Czech"; } + if (code == "CES") { return "Czech"; } + if (code == "CHA") { return "Chamorro"; } + if (code == "CHB") { return "Chibcha"; } + if (code == "CHE") { return "Chechen"; } + if (code == "CHG") { return "Chagatai"; } + if (code == "CHI") { return "Chinese"; } + if (code == "CHK") { return "Chuukese"; } + if (code == "CHM") { return "Mari"; } + if (code == "CHN") { return "Chinook jargon"; } + if (code == "CHO") { return "Choctaw"; } + if (code == "CHP") { return "Chipewyan"; } + if (code == "CHR") { return "Cherokee"; } + if (code == "CHU") { return "Church Slavic"; } + if (code == "CHV") { return "Chuvash"; } + if (code == "CHY") { return "Cheyenne"; } + if (code == "CMC") { return "Chamic languages"; } + if (code == "CNR") { return "Montenegrin"; } + if (code == "COP") { return "Coptic"; } + if (code == "COR") { return "Cornish"; } + if (code == "COS") { return "Corsican"; } + if (code == "CPE") { return "Creoles and pidgins, English based"; } + if (code == "CPF") { return "Creoles and pidgins, French-based"; } + if (code == "CPP") { return "Creoles and pidgins, Portuguese-based"; } + if (code == "CRE") { return "Cree"; } + if (code == "CRH") { return "Crimean Tatar"; } + if (code == "CRP") { return "Creoles and pidgins"; } + if (code == "CSB") { return "Kashubian"; } + if (code == "CUS") { return "Cushitic languages"; } + if (code == "CYM") { return "Welsh"; } + if (code == "CYM") { return "Welsh"; } + if (code == "CZE") { return "Czech"; } + if (code == "CZE") { return "Czech"; } + if (code == "DAK") { return "Dakota"; } + if (code == "DAN") { return "Danish"; } + if (code == "DAR") { return "Dargwa"; } + if (code == "DAY") { return "Land Dayak languages"; } + if (code == "DEL") { return "Delaware"; } + if (code == "DEN") { return "Slave (Athapascan)"; } + if (code == "DEU") { return "German"; } + if (code == "DGR") { return "Dogrib"; } + if (code == "DIN") { return "Dinka"; } + if (code == "DIV") { return "Divehi"; } + if (code == "DOI") { return "Dogri"; } + if (code == "DRA") { return "Dravidian languages"; } + if (code == "DSB") { return "Lower Sorbian"; } + if (code == "DUA") { return "Duala"; } + if (code == "DUM") { return "Dutch, Middle (ca.1050-1350)"; } + if (code == "DUT") { return "Dutch"; } + if (code == "DUT") { return "Dutch"; } + if (code == "DYU") { return "Dyula"; } + if (code == "DZO") { return "Dzongkha"; } + if (code == "EFI") { return "Efik"; } + if (code == "EGY") { return "Egyptian (Ancient)"; } + if (code == "EKA") { return "Ekajuk"; } + if (code == "ELL") { return "Greek, Modern (1453-)"; } + if (code == "ELX") { return "Elamite"; } + if (code == "ENG") { return "English"; } + if (code == "ENM") { return "English, Middle (1100-1500)"; } + if (code == "EPO") { return "Esperanto"; } + if (code == "EST") { return "Estonian"; } + if (code == "EUS") { return "Basque"; } + if (code == "EUS") { return "Basque"; } + if (code == "EWE") { return "Ewe"; } + if (code == "EWO") { return "Ewondo"; } + if (code == "FAN") { return "Fang"; } + if (code == "FAO") { return "Faroese"; } + if (code == "FAS") { return "Persian"; } + if (code == "FAT") { return "Fanti"; } + if (code == "FIJ") { return "Fijian"; } + if (code == "FIL") { return "Filipino"; } + if (code == "FIN") { return "Finnish"; } + if (code == "FIU") { return "Finno-Ugrian languages"; } + if (code == "FON") { return "Fon"; } + if (code == "FRA") { return "French"; } + if (code == "FRE") { return "French"; } + if (code == "FRM") { return "French, Middle (ca.1400-1600)"; } + if (code == "FRO") { return "French, Old (842-ca.1400)"; } + if (code == "FRR") { return "Northern Frisian"; } + if (code == "FRS") { return "Eastern Frisian"; } + if (code == "FRY") { return "Western Frisian"; } + if (code == "FUL") { return "Fulah"; } + if (code == "FUR") { return "Friulian"; } + if (code == "GAA") { return "Ga"; } + if (code == "GAY") { return "Gayo"; } + if (code == "GBA") { return "Gbaya"; } + if (code == "GEM") { return "Germanic languages"; } + if (code == "GEO") { return "Georgin"; } + if (code == "GER") { return "German"; } + if (code == "GEZ") { return "Geez"; } + if (code == "GIL") { return "Gilbertese"; } + if (code == "GLA") { return "Gaelic"; } + if (code == "GLE") { return "Irish"; } + if (code == "GLG") { return "Galician"; } + if (code == "GLV") { return "Manx"; } + if (code == "GMH") { return "German, Middle High (ca.1050-1500)"; } + if (code == "GOH") { return "German, Old High (ca.750-1050)"; } + if (code == "GON") { return "Gondi"; } + if (code == "GOR") { return "Gorontalo"; } + if (code == "GOT") { return "Gothic"; } + if (code == "GRB") { return "Grebo"; } + if (code == "GRC") { return "Greek, Ancient (to 1453)"; } + if (code == "GRE") { return "Greek"; } + if (code == "GRN") { return "Guarani"; } + if (code == "GSW") { return "Swiss German"; } + if (code == "GUJ") { return "Gujarati"; } + if (code == "GWI") { return "Gwich'in"; } + if (code == "HAI") { return "Haida"; } + if (code == "HAT") { return "Haitian"; } + if (code == "HAU") { return "Hausa"; } + if (code == "HAW") { return "Hawaiian"; } + if (code == "HEB") { return "Hebrew"; } + if (code == "HER") { return "Herero"; } + if (code == "HIL") { return "Hiligaynon"; } + if (code == "HIM") { return "Himachali languages"; } + if (code == "HIN") { return "Hindi"; } + if (code == "HIT") { return "Hittite"; } + if (code == "HMN") { return "Hmong"; } + if (code == "HMO") { return "Hiri Motu"; } + if (code == "HRV") { return "Croatian"; } + if (code == "HSB") { return "Upper Sorbian"; } + if (code == "HUN") { return "Hungarian"; } + if (code == "HUP") { return "Hupa"; } + if (code == "HYE") { return "Armenian"; } + if (code == "IBA") { return "Iban"; } + if (code == "IBO") { return "Igbo"; } + if (code == "ICE") { return "Icelandic"; } + if (code == "IDO") { return "Ido"; } + if (code == "III") { return "Sichuan Yi"; } + if (code == "IJO") { return "Ijo languages"; } + if (code == "IKU") { return "Inuktitut"; } + if (code == "ILE") { return "Interlingue"; } + if (code == "ILO") { return "Iloko"; } + if (code == "INA") { return "Interlingua)"; } + if (code == "INC") { return "Indic languages"; } + if (code == "IND") { return "Indonesian"; } + if (code == "INE") { return "Indo-European languages"; } + if (code == "INH") { return "Ingush"; } + if (code == "IPK") { return "Inupiaq"; } + if (code == "IRA") { return "Iranian languages"; } + if (code == "IRO") { return "Iroquoian languages"; } + if (code == "ISL") { return "Icelandic"; } + if (code == "ITA") { return "Italian"; } + if (code == "JAV") { return "Javanese"; } + if (code == "JBO") { return "Lojban"; } + if (code == "JPN") { return "Japanese"; } + if (code == "JPR") { return "Judeo-Persian"; } + if (code == "JRB") { return "Judeo-Arabic"; } + if (code == "KAA") { return "Kara-Kalpak"; } + if (code == "KAB") { return "Kabyle"; } + if (code == "KAC") { return "Kachin"; } + if (code == "KAL") { return "Greenlandic"; } + if (code == "KAM") { return "Kamba"; } + if (code == "KAN") { return "Kannada"; } + if (code == "KAR") { return "Karen languages"; } + if (code == "KAS") { return "Kashmiri"; } + if (code == "KAT") { return "Georgian"; } + if (code == "KAU") { return "Kanuri"; } + if (code == "KAW") { return "Kawi"; } + if (code == "KAZ") { return "Kazakh"; } + if (code == "KBD") { return "Kabardian"; } + if (code == "KHA") { return "Khasi"; } + if (code == "KHI") { return "Khoisan languages"; } + if (code == "KHM") { return "Central Khmer"; } + if (code == "KHO") { return "Khotanese"; } + if (code == "KIK") { return "Kikuyu"; } + if (code == "KIN") { return "Kinyarwanda"; } + if (code == "KIR") { return "Kirghiz"; } + if (code == "KMB") { return "Kimbundu"; } + if (code == "KOK") { return "Konkani"; } + if (code == "KOM") { return "Komi"; } + if (code == "KON") { return "Kongo"; } + if (code == "KOR") { return "Korean"; } + if (code == "KOS") { return "Kosraean"; } + if (code == "KPE") { return "Kpelle"; } + if (code == "KRC") { return "Karachay-Balkar"; } + if (code == "KRL") { return "Karelian"; } + if (code == "KRO") { return "Kru languages"; } + if (code == "KRU") { return "Kurukh"; } + if (code == "KUA") { return "Kuanyama"; } + if (code == "KUM") { return "Kumyk"; } + if (code == "KUR") { return "Kurdish"; } + if (code == "KUT") { return "Kutenai"; } + if (code == "LAD") { return "Ladino"; } + if (code == "LAH") { return "Lahnda"; } + if (code == "LAM") { return "Lamba"; } + if (code == "LAO") { return "Lao"; } + if (code == "LAT") { return "Latin"; } + if (code == "LAV") { return "Latvian"; } + if (code == "LEZ") { return "Lezghian"; } + if (code == "LIM") { return "Limburgan"; } + if (code == "LIN") { return "Lingala"; } + if (code == "LIT") { return "Lithuanian"; } + if (code == "LOL") { return "Mongo"; } + if (code == "LOZ") { return "Lozi"; } + if (code == "LTZ") { return "Luxembourgish"; } + if (code == "LUA") { return "Luba-Lulua"; } + if (code == "LUB") { return "Luba-Katanga"; } + if (code == "LUG") { return "Ganda"; } + if (code == "LUI") { return "Luiseno"; } + if (code == "LUN") { return "Lunda"; } + if (code == "LUO") { return "Luo (Kenya and Tanzania)"; } + if (code == "LUS") { return "Lushai"; } + if (code == "MAC") { return "Macedonian"; } + if (code == "MAC") { return "Macedonian"; } + if (code == "MAD") { return "Madurese"; } + if (code == "MAG") { return "Magahi"; } + if (code == "MAH") { return "Marshallese"; } + if (code == "MAI") { return "Maithili"; } + if (code == "MAK") { return "Makasar"; } + if (code == "MAL") { return "Malayalam"; } + if (code == "MAN") { return "Mandingo"; } + if (code == "MAO") { return "Maori"; } + if (code == "MAP") { return "Austronesian languages"; } + if (code == "MAR") { return "Marathi"; } + if (code == "MAS") { return "Masai"; } + if (code == "MAY") { return "Malay"; } + if (code == "MDF") { return "Moksha"; } + if (code == "MDR") { return "Mandar"; } + if (code == "MEN") { return "Mende"; } + if (code == "MGA") { return "Irish, Middle (900-1200)"; } + if (code == "MIC") { return "Mi'kmaq"; } + if (code == "MIN") { return "Minangkabau"; } + if (code == "MIS") { return "Uncoded languages"; } + if (code == "MKD") { return "Macedonian"; } + if (code == "MKD") { return "Macedonian"; } + if (code == "MKH") { return "Mon-Khmer languages"; } + if (code == "MLG") { return "Malagasy"; } + if (code == "MLT") { return "Maltese"; } + if (code == "MNC") { return "Manchu"; } + if (code == "MNI") { return "Manipuri"; } + if (code == "MNO") { return "Manobo languages"; } + if (code == "MOH") { return "Mohawk"; } + if (code == "MON") { return "Mongolian"; } + if (code == "MOS") { return "Mossi"; } + if (code == "MRI") { return "Maori"; } + if (code == "MSA") { return "Malay"; } + if (code == "MUL") { return "Multiple languages"; } + if (code == "MUN") { return "Munda languages"; } + if (code == "MUS") { return "Creek"; } + if (code == "MWL") { return "Mirandese"; } + if (code == "MWR") { return "Marwari"; } + if (code == "MYA") { return "Burmese"; } + if (code == "MYA") { return "Burmese"; } + if (code == "MYN") { return "Mayan languages"; } + if (code == "MYV") { return "Erzya"; } + if (code == "NAH") { return "Nahuatl languages"; } + if (code == "NAI") { return "North American Indian languages"; } + if (code == "NAP") { return "Neapolitan"; } + if (code == "NAU") { return "Nauru"; } + if (code == "NAV") { return "Navajo"; } + if (code == "NBL") { return "Ndebele, South"; } + if (code == "NDE") { return "Ndebele, North"; } + if (code == "NDO") { return "Ndonga"; } + if (code == "NDS") { return "Low German"; } + if (code == "NEP") { return "Nepali"; } + if (code == "NEW") { return "Nepal Bhasa"; } + if (code == "NIA") { return "Nias"; } + if (code == "NIC") { return "Niger-Kordofanian languages"; } + if (code == "NIU") { return "Niuean"; } + if (code == "NLD") { return "Dutch"; } + if (code == "NLD") { return "Dutch"; } + if (code == "NNO") { return "Norwegian Nynorsk"; } + if (code == "NOB") { return "Bokmål, Norwegian"; } + if (code == "NOG") { return "Nogai"; } + if (code == "NON") { return "Norse, Old"; } + if (code == "NOR") { return "Norwegian"; } + if (code == "NQO") { return "N'Ko"; } + if (code == "NSO") { return "Pedi"; } + if (code == "NUB") { return "Nubian languages"; } + if (code == "NWC") { return "Classical Newari"; } + if (code == "NYA") { return "Chichewa"; } + if (code == "NYM") { return "Nyamwezi"; } + if (code == "NYN") { return "Nyankole"; } + if (code == "NYO") { return "Nyoro"; } + if (code == "NZI") { return "Nzima"; } + if (code == "OCI") { return "Occitan (post 1500)"; } + if (code == "OJI") { return "Ojibwa"; } + if (code == "ORI") { return "Oriya"; } + if (code == "ORM") { return "Oromo"; } + if (code == "OSA") { return "Osage"; } + if (code == "OSS") { return "Ossetian"; } + if (code == "OTA") { return "Turkish, Ottoman (1500-1928)"; } + if (code == "OTO") { return "Otomian languages"; } + if (code == "PAA") { return "Papuan languages"; } + if (code == "PAG") { return "Pangasinan"; } + if (code == "PAL") { return "Pahlavi"; } + if (code == "PAM") { return "Pampanga"; } + if (code == "PAN") { return "Panjabi"; } + if (code == "PAP") { return "Papiamento"; } + if (code == "PAU") { return "Palauan"; } + if (code == "PEO") { return "Persian, Old (ca.600-400 B.C.)"; } + if (code == "PER") { return "Persian"; } + if (code == "PHI") { return "Philippine languages"; } + if (code == "PHN") { return "Phoenician"; } + if (code == "PLI") { return "Pali"; } + if (code == "POL") { return "Polish"; } + if (code == "PON") { return "Pohnpeian"; } + if (code == "POR") { return "Portuguese"; } + if (code == "PRA") { return "Prakrit languages"; } + if (code == "PRO") { return "Provençal, Old (to 1500)"; } + if (code == "PUS") { return "Pushto"; } + if (code == "QUE") { return "Quechua"; } + if (code == "RAJ") { return "Rajasthani"; } + if (code == "RAP") { return "Rapanui"; } + if (code == "RAR") { return "Rarotongan"; } + if (code == "ROA") { return "Romance languages"; } + if (code == "ROH") { return "Romansh"; } + if (code == "ROM") { return "Romany"; } + if (code == "RON") { return "Romanian"; } + if (code == "RUM") { return "Romanian"; } + if (code == "RUN") { return "Rundi"; } + if (code == "RUP") { return "Aromanian"; } + if (code == "RUS") { return "Russian"; } + if (code == "SAD") { return "Sandawe"; } + if (code == "SAG") { return "Sango"; } + if (code == "SAH") { return "Yakut"; } + if (code == "SAI") { return "South American Indian languages"; } + if (code == "SAL") { return "Salishan languages"; } + if (code == "SAM") { return "Samaritan Aramaic"; } + if (code == "SAN") { return "Sanskrit"; } + if (code == "SAS") { return "Sasak"; } + if (code == "SAT") { return "Santali"; } + if (code == "SCN") { return "Sicilian"; } + if (code == "SCO") { return "Scots"; } + if (code == "SEL") { return "Selkup"; } + if (code == "SEM") { return "Semitic languages"; } + if (code == "SGA") { return "Irish, Old (to 900)"; } + if (code == "SGN") { return "Sign Languages"; } + if (code == "SHN") { return "Shan"; } + if (code == "SID") { return "Sidamo"; } + if (code == "SIN") { return "Sinhala"; } + if (code == "SIO") { return "Siouan languages"; } + if (code == "SIT") { return "Sino-Tibetan languages"; } + if (code == "SLA") { return "Slavic languages"; } + if (code == "SLO") { return "Slovak"; } + if (code == "SLV") { return "Slovenian"; } + if (code == "SMA") { return "Southern Sami"; } + if (code == "SME") { return "Northern Sami"; } + if (code == "SMI") { return "Sami languages"; } + if (code == "SMJ") { return "Lule Sami"; } + if (code == "SMN") { return "Inari Sami"; } + if (code == "SMO") { return "Samoan"; } + if (code == "SMS") { return "Skolt Sami"; } + if (code == "SNA") { return "Shona"; } + if (code == "SND") { return "Sindhi"; } + if (code == "SNK") { return "Soninke"; } + if (code == "SOG") { return "Sogdian"; } + if (code == "SOM") { return "Somali"; } + if (code == "SON") { return "Songhai languages"; } + if (code == "SOT") { return "Sotho, Southern"; } + if (code == "SPA") { return "Spanish"; } + if (code == "SQI") { return "Albanian"; } + if (code == "SRD") { return "Sardinian"; } + if (code == "SRN") { return "Sranan Tongo"; } + if (code == "SRP") { return "Serbian"; } + if (code == "SRR") { return "Serer"; } + if (code == "SSA") { return "Nilo-Saharan languages"; } + if (code == "SSW") { return "Swati"; } + if (code == "SUK") { return "Sukuma"; } + if (code == "SUN") { return "Sundanese"; } + if (code == "SUS") { return "Susu"; } + if (code == "SUX") { return "Sumerian"; } + if (code == "SWA") { return "Swahili"; } + if (code == "SWE") { return "Swedish"; } + if (code == "SYC") { return "Classical Syriac"; } + if (code == "SYR") { return "Syriac"; } + if (code == "TAH") { return "Tahitian"; } + if (code == "TAI") { return "Tai languages"; } + if (code == "TAM") { return "Tamil"; } + if (code == "TAT") { return "Tatar"; } + if (code == "TEL") { return "Telugu"; } + if (code == "TEM") { return "Timne"; } + if (code == "TER") { return "Tereno"; } + if (code == "TET") { return "Tetum"; } + if (code == "TGK") { return "Tajik"; } + if (code == "TGL") { return "Tagalog"; } + if (code == "THA") { return "Thai"; } + if (code == "TIB") { return "Tibetian"; } + if (code == "TIG") { return "Tigre"; } + if (code == "TIR") { return "Tigrinya"; } + if (code == "TIV") { return "Tiv"; } + if (code == "TKL") { return "Tokelau"; } + if (code == "TLH") { return "Klingon"; } + if (code == "TLI") { return "Tlingit"; } + if (code == "TMH") { return "Tamashek"; } + if (code == "TOG") { return "Tonga (Nyasa)"; } + if (code == "TON") { return "Tonga (Tonga Islands)"; } + if (code == "TPI") { return "Tok Pisin"; } + if (code == "TSI") { return "Tsimshian"; } + if (code == "TSN") { return "Tswana"; } + if (code == "TSO") { return "Tsonga"; } + if (code == "TUK") { return "Turkmen"; } + if (code == "TUM") { return "Tumbuka"; } + if (code == "TUP") { return "Tupi languages"; } + if (code == "TUR") { return "Turkish"; } + if (code == "TUT") { return "Altaic languages"; } + if (code == "TVL") { return "Tuvalu"; } + if (code == "TWI") { return "Twi"; } + if (code == "TYV") { return "Tuvinian"; } + if (code == "UDM") { return "Udmurt"; } + if (code == "UGA") { return "Ugaritic"; } + if (code == "UIG") { return "Uighur"; } + if (code == "UKR") { return "Ukrainian"; } + if (code == "UMB") { return "Umbundu"; } + if (code == "UND") { return "Undetermined"; } + if (code == "URD") { return "Urdu"; } + if (code == "UZB") { return "Uzbek"; } + if (code == "VAI") { return "Vai"; } + if (code == "VEN") { return "Venda"; } + if (code == "VIE") { return "Vietnamese"; } + if (code == "VOL") { return "Volapük"; } + if (code == "VOT") { return "Votic"; } + if (code == "WAK") { return "Wakashan languages"; } + if (code == "WAL") { return "Wolaitta"; } + if (code == "WAR") { return "Waray"; } + if (code == "WAS") { return "Washo"; } + if (code == "WEL") { return "Welsh"; } + if (code == "WEL") { return "Welsh"; } + if (code == "WEN") { return "Sorbian languages"; } + if (code == "WLN") { return "Walloon"; } + if (code == "WOL") { return "Wolof"; } + if (code == "XAL") { return "Kalmyk"; } + if (code == "XHO") { return "Xhosa"; } + if (code == "YAO") { return "Yao"; } + if (code == "YAP") { return "Yapese"; } + if (code == "YID") { return "Yiddish"; } + if (code == "YOR") { return "Yoruba"; } + if (code == "YPK") { return "Yupik languages"; } + if (code == "ZAP") { return "Zapotec"; } + if (code == "ZBL") { return "Blissymbols"; } + if (code == "ZEN") { return "Zenaga"; } + if (code == "ZGH") { return "Moroccan"; } + if (code == "ZHA") { return "Zhuang"; } + if (code == "ZHO") { return "Chinese"; } + if (code == "ZND") { return "Zande languages"; } + if (code == "ZUL") { return "Zulu"; } + if (code == "ZUN") { return "Zuni"; } + if (code == "ZZA") { return "Zaza"; } } return code; } @@ -14507,8 +14507,8 @@ ostream& operator<<(ostream& out, HumHash* hash) { typedef unsigned long long TEMP64BITFIX; // declare static variables -vector<_HumInstrument> HumInstrument::data; -int HumInstrument::classcount = 0; +vector<_HumInstrument> HumInstrument::m_data; +int HumInstrument::m_classcount = 0; ////////////////////////////// @@ -14517,11 +14517,11 @@ int HumInstrument::classcount = 0; // HumInstrument::HumInstrument(void) { - if (classcount == 0) { + if (m_classcount == 0) { initialize(); } - classcount++; - index = -1; + m_classcount++; + m_index = -1; } @@ -14532,11 +14532,11 @@ HumInstrument::HumInstrument(void) { // HumInstrument::HumInstrument(const string& Hname) { - if (classcount == 0) { + if (m_classcount == 0) { initialize(); } - index = find(Hname); + m_index = find(Hname); } @@ -14547,7 +14547,7 @@ HumInstrument::HumInstrument(const string& Hname) { // HumInstrument::~HumInstrument() { - index = -1; + m_index = -1; } @@ -14558,8 +14558,8 @@ HumInstrument::~HumInstrument() { // int HumInstrument::getGM(void) { - if (index > 0) { - return data[index].gm; + if (m_index > 0) { + return m_data[m_index].gm; } else { return -1; } @@ -14581,7 +14581,7 @@ int HumInstrument::getGM(const string& Hname) { } if (tindex > 0) { - return data[tindex].gm; + return m_data[tindex].gm; } else { return -1; } @@ -14595,8 +14595,8 @@ int HumInstrument::getGM(const string& Hname) { // string HumInstrument::getName(void) { - if (index > 0) { - return data[index].name; + if (m_index > 0) { + return m_data[m_index].name; } else { return ""; } @@ -14617,7 +14617,7 @@ string HumInstrument::getName(const string& Hname) { tindex = find(Hname); } if (tindex > 0) { - return data[tindex].name; + return m_data[tindex].name; } else { return ""; } @@ -14631,8 +14631,8 @@ string HumInstrument::getName(const string& Hname) { // string HumInstrument::getHumdrum(void) { - if (index > 0) { - return data[index].humdrum; + if (m_index > 0) { + return m_data[m_index].humdrum; } else { return ""; } @@ -14651,7 +14651,7 @@ int HumInstrument::setGM(const string& Hname, int aValue) { } int rindex = find(Hname); if (rindex > 0) { - data[rindex].gm = aValue; + m_data[rindex].gm = aValue; } else { afi(Hname.c_str(), aValue, Hname.c_str()); sortData(); @@ -14668,11 +14668,11 @@ int HumInstrument::setGM(const string& Hname, int aValue) { void HumInstrument::setHumdrum(const string& Hname) { if (Hname.compare(0, 2, "*I") == 0) { - index = find(Hname.substr(2)); + m_index = find(Hname.substr(2)); } else { - index = find(Hname); + m_index = find(Hname); } -} + } @@ -14686,171 +14686,223 @@ void HumInstrument::setHumdrum(const string& Hname) { // // HumInstrument::initialize -- // - void HumInstrument::initialize(void) { - data.reserve(500); - afi("accor", GM_ACCORDION, "accordion"); - afi("alto", GM_RECORDER, "alto"); - afi("archl", GM_ACOUSTIC_GUITAR_NYLON, "archlute"); - afi("armon", GM_HARMONICA, "harmonica"); - afi("arpa", GM_ORCHESTRAL_HARP, "harp"); - afi("bagpI", GM_BAGPIPE, "bagpipe (Irish)"); - afi("bagpS", GM_BAGPIPE, "bagpipe (Scottish)"); - afi("banjo", GM_BANJO, "banjo"); - afi("barit", GM_CHOIR_AAHS, "baritone"); - afi("baset", GM_CLARINET, "bassett horn"); - afi("bass", GM_CHOIR_AAHS, "bass"); - afi("bdrum", GM_TAIKO_DRUM, "bass drum (kit)"); - afi("bguit", GM_ELECTRIC_BASS_FINGER, "electric bass guitar"); - afi("biwa", GM_FLUTE, "biwa"); - afi("bscan", GM_CHOIR_AAHS, "basso cantante"); - afi("bspro", GM_CHOIR_AAHS, "basso profondo"); - afi("calam", GM_OBOE, "chalumeau"); - afi("calpe", GM_LEAD_CALLIOPE, "calliope"); - afi("calto", GM_CHOIR_AAHS, "contralto"); - afi("campn", GM_TUBULAR_BELLS, "bell"); - afi("cangl", GM_ENGLISH_HORN, "english horn"); - afi("caril", GM_TUBULAR_BELLS, "carillon"); - afi("castr", GM_CHOIR_AAHS, "castrato"); - afi("casts", GM_WOODBLOCKS, "castanets"); - afi("cbass", GM_CONTRABASS, "contrabass"); - afi("cello", GM_CELLO, "violoncello"); - afi("cemba", GM_HARPSICHORD, "harpsichord"); - afi("cetra", GM_VIOLIN, "cittern"); - afi("chime", GM_TUBULAR_BELLS, "chimes"); - afi("chlma", GM_BASSOON, "alto shawm"); - afi("chlms", GM_BASSOON, "soprano shawm"); - afi("chlmt", GM_BASSOON, "tenor shawm"); - afi("clara", GM_CLARINET, "alto clarinet (in E-flat)"); - afi("clarb", GM_CLARINET, "bass clarinet (in B-flat)"); - afi("clarp", GM_CLARINET, "piccolo clarinet"); - afi("clars", GM_CLARINET, "soprano clarinet"); - afi("clavi", GM_CLAVI, "clavichord"); - afi("clest", GM_CELESTA, "celesta"); - afi("colsp", GM_FLUTE, "coloratura soprano"); - afi("cor", GM_FRENCH_HORN, "horn"); - afi("cornm", GM_BAGPIPE, "French bagpipe"); - afi("corno", GM_TRUMPET, "cornett"); - afi("cornt", GM_TRUMPET, "cornet"); - afi("crshc", GM_REVERSE_CYMBAL, "crash cymbal (kit)"); - afi("ctenor", GM_CHOIR_AAHS, "counter-tenor"); - afi("ctina", GM_ACCORDION, "concertina"); - afi("drmsp", GM_FLUTE, "dramatic soprano"); - afi("dulc", GM_DULCIMER, "dulcimer"); - afi("eguit", GM_ELECTRIC_GUITAR_CLEAN, "electric guitar"); - afi("fag_c", GM_BASSOON, "contrabassoon"); - afi("fagot", GM_BASSOON, "bassoon"); - afi("false", GM_RECORDER, "falsetto"); - afi("feme", GM_CHOIR_AAHS, "female voice"); - afi("fife", GM_BLOWN_BOTTLE, "fife"); - afi("fingc", GM_REVERSE_CYMBAL, "finger cymbal"); - afi("flt", GM_FLUTE, "flute"); - afi("flt_a", GM_FLUTE, "alto flute"); - afi("flt_b", GM_FLUTE, "bass flute"); - afi("fltda", GM_RECORDER, "alto recorder"); - afi("fltdb", GM_RECORDER, "bass recorder"); - afi("fltdn", GM_RECORDER, "sopranino recorder"); - afi("fltds", GM_RECORDER, "soprano recorder"); - afi("fltdt", GM_RECORDER, "tenor recorder"); - afi("flugh", GM_FRENCH_HORN, "flugelhorn"); - afi("forte", GM_HONKYTONK_PIANO, "fortepiano"); - afi("glock", GM_GLOCKENSPIEL, "glockenspiel"); - afi("gong", GM_STEEL_DRUMS, "gong"); - afi("guitr", GM_ACOUSTIC_GUITAR_NYLON, "guitar"); - afi("hammd", GM_DRAWBAR_ORGAN, "Hammond electronic organ"); - afi("heltn", GM_CHOIR_AAHS, "Heldentenor"); - afi("hichi", GM_OBOE, "hichiriki"); - afi("hurdy", GM_LEAD_CALLIOPE, "hurdy-gurdy"); - afi("kit", GM_SYNTH_DRUM, "drum kit"); - afi("kokyu", GM_FIDDLE, "kokyu (Japanese spike fiddle)"); - afi("komun", GM_KOTO, "komun'go (Korean long zither)"); - afi("koto", GM_KOTO, "koto (Japanese long zither)"); - afi("kruma", GM_TRUMPET, "alto crumhorn"); - afi("krumb", GM_TRUMPET, "bass crumhorn"); - afi("krums", GM_TRUMPET, "soprano crumhorn"); - afi("krumt", GM_TRUMPET, "tenor crumhorn"); - afi("liuto", GM_ACOUSTIC_GUITAR_NYLON, "lute"); - afi("lyrsp", GM_FLUTE, "lyric soprano"); - afi("lyrtn", GM_FRENCH_HORN, "lyric tenor"); - afi("male", GM_CHOIR_AAHS, "male voice"); - afi("mando", GM_ACOUSTIC_GUITAR_NYLON, "mandolin"); - afi("marac", GM_AGOGO, "maracas"); - afi("marim", GM_MARIMBA, "marimba"); - afi("mezzo", GM_CHOIR_AAHS, "mezzo soprano"); - afi("nfant", GM_CHOIR_AAHS, "child's voice"); - afi("nokan", GM_SHAKUHACHI, "nokan (a Japanese flute)"); - afi("oboeD", GM_ENGLISH_HORN, "oboe d'amore"); - afi("oboe", GM_OBOE, "oboe"); - afi("ocari", GM_OCARINA, "ocarina"); - afi("organ", GM_CHURCH_ORGAN, "pipe organ"); - afi("panpi", GM_PAN_FLUTE, "panpipe"); - afi("piano", GM_ACOUSTIC_GRAND_PIANO, "pianoforte"); - afi("piatt", GM_REVERSE_CYMBAL, "cymbals"); - afi("picco", GM_PICCOLO, "piccolo"); - afi("pipa", GM_ACOUSTIC_GUITAR_NYLON, "Chinese lute"); - afi("porta", GM_TANGO_ACCORDION, "portative organ"); - afi("psalt", GM_CLAVI, "psaltery (box zither)"); - afi("qin", GM_CLAVI, "qin, ch'in (Chinese zither)"); - afi("quitr", GM_ACOUSTIC_GUITAR_NYLON, "gittern"); - afi("rackt", GM_TRUMPET, "racket"); - afi("rebec", GM_ACOUSTIC_GUITAR_NYLON, "rebec"); - afi("recit", GM_CHOIR_AAHS, "recitativo"); - afi("reedo", GM_REED_ORGAN, "reed organ"); - afi("rhode", GM_ELECTRIC_PIANO_1, "Fender-Rhodes electric piano"); - afi("ridec", GM_REVERSE_CYMBAL, "ride cymbal (kit)"); - afi("sarod", GM_SITAR, "sarod"); - afi("sarus", GM_TUBA, "sarrusophone"); - afi("saxA", GM_ALTO_SAX, "E-flat alto saxophone"); - afi("saxB", GM_BARITONE_SAX, "B-flat bass saxophone"); - afi("saxC", GM_BARITONE_SAX, "E-flat contrabass saxophone"); - afi("saxN", GM_SOPRANO_SAX, "E-flat sopranino saxophone"); - afi("saxR", GM_BARITONE_SAX, "E-flat baritone saxophone"); - afi("saxS", GM_SOPRANO_SAX, "B-flat soprano saxophone"); - afi("saxT", GM_TENOR_SAX, "B-flat tenor saxophone"); - afi("sdrum", GM_SYNTH_DRUM, "snare drum (kit)"); - afi("shaku", GM_SHAKUHACHI, "shakuhachi"); - afi("shami", GM_SHAMISEN, "shamisen (Japanese fretless lute)"); - afi("sheng", GM_SHANAI, "mouth organ (Chinese)"); - afi("sho", GM_SHANAI, "mouth organ (Japanese)"); - afi("sitar", GM_SITAR, "sitar"); - afi("soprn", GM_CHOIR_AAHS, "soprano"); - afi("spshc", GM_REVERSE_CYMBAL, "splash cymbal (kit)"); - afi("steel", GM_STEEL_DRUMS, "steel-drum"); - afi("sxhA", GM_ALTO_SAX, "E-flat alto saxhorn"); - afi("sxhB", GM_BARITONE_SAX, "B-flat bass saxhorn"); - afi("sxhC", GM_BARITONE_SAX, "E-flat contrabass saxhorn"); - afi("sxhR", GM_BARITONE_SAX, "E-flat baritone saxhorn"); - afi("sxhS", GM_SOPRANO_SAX, "B-flat soprano saxhorn"); - afi("sxhT", GM_TENOR_SAX, "B-flat tenor saxhorn"); - afi("synth", GM_ELECTRIC_PIANO_2, "keyboard synthesizer"); - afi("tabla", GM_MELODIC_DRUM, "tabla"); - afi("tambn", GM_TINKLE_BELL, "tambourine"); - afi("tambu", GM_MELODIC_DRUM, "tambura"); - afi("tanbr", GM_MELODIC_DRUM, "tanbur"); - afi("tenor", GM_CHOIR_AAHS, "tenor"); - afi("timpa", GM_MELODIC_DRUM, "timpani"); - afi("tiorb", GM_ACOUSTIC_GUITAR_NYLON, "theorbo"); - afi("tom", GM_TAIKO_DRUM, "tom-tom drum"); - afi("trngl", GM_TINKLE_BELL, "triangle"); - afi("tromb", GM_TROMBONE, "bass trombone"); - afi("tromp", GM_TRUMPET, "trumpet"); - afi("tromt", GM_TROMBONE, "tenor trombone"); - afi("tuba", GM_TUBA, "tuba"); - afi("ud", GM_ACOUSTIC_GUITAR_NYLON, "ud"); - afi("ukule", GM_ACOUSTIC_GUITAR_NYLON, "ukulele"); - afi("vibra", GM_VIBRAPHONE, "vibraphone"); - afi("vina", GM_SITAR, "vina"); - afi("viola", GM_VIOLA, "viola"); - afi("violb", GM_CONTRABASS, "bass viola da gamba"); - afi("viold", GM_VIOLA, "viola d'amore"); - afi("violn", GM_VIOLIN, "violin"); - afi("violp", GM_VIOLIN, "piccolo violin"); - afi("viols", GM_VIOLIN, "treble viola da gamba"); - afi("violt", GM_CELLO, "tenor viola da gamba"); - afi("vox", GM_CHOIR_AAHS, "generic voice"); - afi("xylo", GM_XYLOPHONE, "xylophone"); - afi("zithr", GM_CLAVI, "zither"); - afi("zurna", GM_ACOUSTIC_GUITAR_NYLON, "zurna"); + m_data.reserve(500); + + // List has to be sorted by first parameter. Maybe put in map. + afi("accor", GM_ACCORDION, "accordion"); + afi("alto", GM_RECORDER, "alto"); + afi("anvil", GM_TINKLE_BELL, "anvil"); + afi("archl", GM_ACOUSTIC_GUITAR_NYLON, "archlute"); + afi("armon", GM_HARMONICA, "harmonica"); + afi("arpa", GM_ORCHESTRAL_HARP, "harp"); + afi("bagpI", GM_BAGPIPE, "bagpipe (Irish)"); + afi("bagpS", GM_BAGPIPE, "bagpipe (Scottish)"); + afi("banjo", GM_BANJO, "banjo"); + afi("bansu", GM_FLUTE, "bansuri"); + afi("barit", GM_CHOIR_AAHS, "baritone"); + afi("baset", GM_CLARINET, "bassett horn"); + afi("bass", GM_CHOIR_AAHS, "bass"); + afi("bdrum", GM_TAIKO_DRUM, "bass drum"); + afi("bguit", GM_ELECTRIC_BASS_FINGER, "electric bass guitar"); + afi("biwa", GM_FLUTE, "biwa"); + afi("bongo", GM_TAIKO_DRUM, "bongo"); + afi("brush", GM_BREATH_NOISE, "brush"); + afi("bscan", GM_CHOIR_AAHS, "basso cantante"); + afi("bspro", GM_CHOIR_AAHS, "basso profondo"); + afi("bugle", GM_TRUMPET, "bugle"); + afi("calam", GM_OBOE, "chalumeau"); + afi("calpe", GM_LEAD_CALLIOPE, "calliope"); + afi("calto", GM_CHOIR_AAHS, "contralto"); + afi("campn", GM_TUBULAR_BELLS, "bell"); + afi("cangl", GM_ENGLISH_HORN, "english horn"); + afi("canto", GM_CHOIR_AAHS, "canto"); + afi("caril", GM_TUBULAR_BELLS, "carillon"); + afi("castr", GM_CHOIR_AAHS, "castrato"); + afi("casts", GM_WOODBLOCKS, "castanets"); + afi("cbass", GM_CONTRABASS, "contrabass"); + afi("cello", GM_CELLO, "violoncello"); + afi("cemba", GM_HARPSICHORD, "harpsichord"); + afi("cetra", GM_VIOLIN, "cittern"); + afi("chain", GM_TINKLE_BELL, "chains"); + afi("chcym", GM_REVERSE_CYMBAL, "China cymbal"); + afi("chime", GM_TUBULAR_BELLS, "chimes"); + afi("chlma", GM_BASSOON, "alto shawm"); + afi("chlms", GM_BASSOON, "soprano shawm"); + afi("chlmt", GM_BASSOON, "tenor shawm"); + afi("clap", GM_GUNSHOT, "hand clapping"); + afi("clara", GM_CLARINET, "alto clarinet"); + afi("clarb", GM_CLARINET, "bass clarinet"); + afi("clarp", GM_CLARINET, "piccolo clarinet"); + afi("clars", GM_CLARINET, "clarinet"); + afi("clave", GM_AGOGO, "claves"); + afi("clavi", GM_CLAVI, "clavichord"); + afi("clest", GM_CELESTA, "celesta"); + afi("clrno", GM_TRUMPET, "clarino"); + afi("colsp", GM_FLUTE, "coloratura soprano"); + afi("conga", GM_TAIKO_DRUM, "conga"); + afi("cor", GM_FRENCH_HORN, "horn"); + afi("cornm", GM_BAGPIPE, "French bagpipe"); + afi("corno", GM_TRUMPET, "cornett"); + afi("cornt", GM_TRUMPET, "cornet"); + afi("coro", GM_CHOIR_AAHS, "chorus"); + afi("crshc", GM_REVERSE_CYMBAL, "crash cymbal"); + afi("ctenor", GM_CHOIR_AAHS, "counter-tenor"); + afi("ctina", GM_ACCORDION, "concertina"); + afi("drmsp", GM_FLUTE, "dramatic soprano"); + afi("drum", GM_SYNTH_DRUM, "drum"); + afi("drumP", GM_SYNTH_DRUM, "small drum"); + afi("dulc", GM_DULCIMER, "dulcimer"); + afi("eguit", GM_ELECTRIC_GUITAR_CLEAN, "electric guitar"); + afi("fag_c", GM_BASSOON, "contrabassoon"); + afi("fagot", GM_BASSOON, "bassoon"); + afi("false", GM_RECORDER, "falsetto"); + afi("fdrum", GM_TAIKO_DRUM, "frame drum"); + afi("feme", GM_CHOIR_AAHS, "female voice"); + afi("fife", GM_BLOWN_BOTTLE, "fife"); + afi("fingc", GM_REVERSE_CYMBAL, "finger cymbal"); + afi("flt", GM_FLUTE, "flute"); + afi("flt_a", GM_FLUTE, "alto flute"); + afi("flt_b", GM_FLUTE, "bass flute"); + afi("fltda", GM_RECORDER, "alto recorder"); + afi("fltdb", GM_RECORDER, "bass recorder"); + afi("fltdn", GM_RECORDER, "sopranino recorder"); + afi("fltds", GM_RECORDER, "soprano recorder"); + afi("fltdt", GM_RECORDER, "tenor recorder"); + afi("flugh", GM_FRENCH_HORN, "flugelhorn"); + afi("forte", GM_HONKYTONK_PIANO, "fortepiano"); + afi("gen", GM_ACOUSTIC_GRAND_PIANO, "generic instrument"); + afi("genB", GM_ACOUSTIC_GRAND_PIANO, "generic bass instrument"); + afi("genT", GM_ACOUSTIC_GRAND_PIANO, "generic treble instrument"); + afi("glock", GM_GLOCKENSPIEL, "glockenspiel"); + afi("gong", GM_REVERSE_CYMBAL, "gong"); + afi("guitr", GM_ACOUSTIC_GUITAR_NYLON, "guitar"); + afi("hammd", GM_DRAWBAR_ORGAN, "Hammond electronic organ"); + afi("hbell", GM_TINKLE_BELL, "handbell"); + afi("hbell", GM_TINKLE_BELL, "handbell"); + afi("heck", GM_BASSOON, "heckelphone"); + afi("heltn", GM_CHOIR_AAHS, "Heldentenor"); + afi("hichi", GM_OBOE, "hichiriki"); + afi("hurdy", GM_LEAD_CALLIOPE, "hurdy-gurdy"); + afi("kitv", GM_VIOLIN, "kit violin"); + afi("klav", GM_ACOUSTIC_GRAND_PIANO, "keyboard"); + afi("kokyu", GM_FIDDLE, "kokyu"); + afi("komun", GM_KOTO, "komun'go"); + afi("koto", GM_KOTO, "koto"); + afi("kruma", GM_TRUMPET, "alto crumhorn"); + afi("krumb", GM_TRUMPET, "bass crumhorn"); + afi("krums", GM_TRUMPET, "soprano crumhorn"); + afi("krumt", GM_TRUMPET, "tenor crumhorn"); + afi("lion", GM_AGOGO, "lion's roar"); + afi("liuto", GM_ACOUSTIC_GUITAR_NYLON, "lute"); + afi("lyrsp", GM_FLUTE, "lyric soprano"); + afi("lyrtn", GM_FRENCH_HORN, "lyric tenor"); + afi("male", GM_CHOIR_AAHS, "male voice"); + afi("mando", GM_ACOUSTIC_GUITAR_NYLON, "mandolin"); + afi("marac", GM_AGOGO, "maracas"); + afi("marim", GM_MARIMBA, "marimba"); + afi("mbari", GM_CHOIR_AAHS, "high baritone"); + afi("mezzo", GM_CHOIR_AAHS, "mezzo soprano"); + afi("nfant", GM_CHOIR_AAHS, "child's voice"); + afi("nokan", GM_SHAKUHACHI, "nokan"); + afi("oboe", GM_OBOE, "oboe"); + afi("oboeD", GM_ENGLISH_HORN, "oboe d'amore"); + afi("ocari", GM_OCARINA, "ocarina"); + afi("ondes", GM_PAD_SWEEP, "ondes Martenot"); + afi("ophic", GM_TUBA, "ophicleide"); + afi("organ", GM_CHURCH_ORGAN, "pipe organ"); + afi("oud", GM_ACOUSTIC_GUITAR_NYLON, "oud"); + afi("paila", GM_AGOGO, "timbales"); + afi("panpi", GM_PAN_FLUTE, "panpipe"); + afi("pbell", GM_TUBULAR_BELLS, "bell plate"); + afi("pguit", GM_ACOUSTIC_GUITAR_NYLON, "Portuguese guitar"); + afi("physh", GM_REED_ORGAN, "physharmonica"); + afi("piano", GM_ACOUSTIC_GRAND_PIANO, "pianoforte"); + afi("piatt", GM_REVERSE_CYMBAL, "cymbals"); + afi("picco", GM_PICCOLO, "piccolo"); + afi("pipa", GM_ACOUSTIC_GUITAR_NYLON, "Chinese lute"); + afi("porta", GM_TANGO_ACCORDION, "portative organ"); + afi("psalt", GM_CLAVI, "psaltery"); + afi("qin", GM_CLAVI, "qin"); + afi("quinto", GM_CHOIR_AAHS, "quinto"); + afi("quitr", GM_ACOUSTIC_GUITAR_NYLON, "gittern"); + afi("rackt", GM_TRUMPET, "racket"); + afi("ratl", GM_WOODBLOCKS, "rattle"); + afi("rebec", GM_ACOUSTIC_GUITAR_NYLON, "rebec"); + afi("recit", GM_CHOIR_AAHS, "recitativo"); + afi("reedo", GM_REED_ORGAN, "reed organ"); + afi("rhode", GM_ELECTRIC_PIANO_1, "Fender-Rhodes electric piano"); + afi("ridec", GM_REVERSE_CYMBAL, "ride cymbal"); + afi("sarod", GM_SITAR, "sarod"); + afi("sarus", GM_TUBA, "sarrusophone"); + afi("saxA", GM_ALTO_SAX, "alto saxophone"); + afi("saxB", GM_BARITONE_SAX, "bass saxophone"); + afi("saxC", GM_BARITONE_SAX, "contrabass saxophone"); + afi("saxN", GM_SOPRANO_SAX, "sopranino saxophone"); + afi("saxR", GM_BARITONE_SAX, "baritone saxophone"); + afi("saxS", GM_SOPRANO_SAX, "soprano saxophone"); + afi("saxT", GM_TENOR_SAX, "tenor saxophone"); + afi("sbell", GM_TINKLE_BELL, "sleigh bells"); + afi("sdrum", GM_SYNTH_DRUM, "snare drum (kit)"); + afi("shaku", GM_SHAKUHACHI, "shakuhachi"); + afi("shami", GM_SHAMISEN, "shamisen"); + afi("sheng", GM_SHANAI, "sheng"); + afi("sho", GM_SHANAI, "sho"); + afi("siren", GM_FX_SCI_FI, "siren"); + afi("sitar", GM_SITAR, "sitar"); + afi("slap", GM_GUNSHOT, "slapstick"); + afi("soprn", GM_CHOIR_AAHS, "soprano"); + afi("spshc", GM_REVERSE_CYMBAL, "splash cymbal"); + afi("steel", GM_STEEL_DRUMS, "steel-drum"); + afi("stim", GM_SEASHORE, "Sprechstimme"); + afi("stimA", GM_SEASHORE, "Sprechstimme, alto"); + afi("stimB", GM_SEASHORE, "Sprechstimme, bass"); + afi("stimC", GM_SEASHORE, "Sprechstimme, contralto"); + afi("stimR", GM_SEASHORE, "Sprechstimme, baritone"); + afi("stimS", GM_SEASHORE, "Sprechstimme, soprano"); + afi("strdr", GM_AGOGO, "string drum"); + afi("sxhA", GM_ALTO_SAX, "alto saxhorn"); + afi("sxhB", GM_BARITONE_SAX, "bass saxhorn"); + afi("sxhC", GM_BARITONE_SAX, "contrabass saxhorn"); + afi("sxhR", GM_BARITONE_SAX, "baritone saxhorn"); + afi("sxhS", GM_SOPRANO_SAX, "soprano saxhorn"); + afi("sxhT", GM_TENOR_SAX, "tenor saxhorn"); + afi("synth", GM_ELECTRIC_PIANO_2, "keyboard synthesizer"); + afi("tabla", GM_MELODIC_DRUM, "tabla"); + afi("tambn", GM_TINKLE_BELL, "tambourine"); + afi("tambu", GM_MELODIC_DRUM, "tambura"); + afi("tanbr", GM_MELODIC_DRUM, "tanbur"); + afi("tblok", GM_WOODBLOCKS, "temple blocks"); + afi("tdrum", GM_SYNTH_DRUM, "tenor drum"); + afi("tenor", GM_CHOIR_AAHS, "tenor"); + afi("timpa", GM_MELODIC_DRUM, "timpani"); + afi("tiorb", GM_ACOUSTIC_GUITAR_NYLON, "theorbo"); + afi("tom", GM_TAIKO_DRUM, "tom-tom drum"); + afi("trngl", GM_TINKLE_BELL, "triangle"); + afi("tromb", GM_TROMBONE, "bass trombone"); + afi("tromp", GM_TRUMPET, "trumpet"); + afi("tromt", GM_TROMBONE, "tenor trombone"); + afi("tuba", GM_TUBA, "tuba"); + afi("tubaB", GM_TUBA, "bass tuba"); + afi("tubaC", GM_TUBA, "contrabass tuba"); + afi("tubaT", GM_TUBA, "tenor tuba"); + afi("tubaU", GM_TUBA, "subcontra tuba"); + afi("ukule", GM_ACOUSTIC_GUITAR_NYLON, "ukulele"); + afi("vibra", GM_VIBRAPHONE, "vibraphone"); + afi("vina", GM_SITAR, "vina"); + afi("viola", GM_VIOLA, "viola"); + afi("violb", GM_CONTRABASS, "bass viola da gamba"); + afi("viold", GM_VIOLA, "viola d'amore"); + afi("violn", GM_VIOLIN, "violin"); + afi("violp", GM_VIOLIN, "piccolo violin"); + afi("viols", GM_VIOLIN, "treble viola da gamba"); + afi("violt", GM_CELLO, "tenor viola da gamba"); + afi("vox", GM_CHOIR_AAHS, "generic voice"); + afi("wblok", GM_WOODBLOCKS, "woodblock"); + afi("xylo", GM_XYLOPHONE, "xylophone"); + afi("zithr", GM_CLAVI, "zither"); + afi("zurna", GM_ACOUSTIC_GUITAR_NYLON, "zurna"); + } @@ -14867,7 +14919,7 @@ void HumInstrument::afi(const char* humdrum_name, int midinum, x.humdrum = humdrum_name; x.gm = midinum; - data.push_back(x); + m_data.push_back(x); } @@ -14884,14 +14936,14 @@ int HumInstrument::find(const string& Hname) { key.name = ""; key.gm = 0; - searchResult = bsearch(&key, data.data(), - data.size(), sizeof(_HumInstrument), + searchResult = bsearch(&key, m_data.data(), + m_data.size(), sizeof(_HumInstrument), &data_compare_by_humdrum_name); if (searchResult == NULL) { return -1; } else { - return (int)(((TEMP64BITFIX)(searchResult)) - ((TEMP64BITFIX)(data.data())))/ + return (int)(((TEMP64BITFIX)(searchResult)) - ((TEMP64BITFIX)(m_data.data())))/ sizeof(_HumInstrument); } } @@ -14917,7 +14969,7 @@ int HumInstrument::data_compare_by_humdrum_name(const void* a, // void HumInstrument::sortData(void) { - qsort(data.data(), data.size(), sizeof(_HumInstrument), + qsort(m_data.data(), m_data.size(), sizeof(_HumInstrument), &HumInstrument::data_compare_by_humdrum_name); } @@ -20488,24 +20540,16 @@ bool HumdrumFileBase::read(const char* filename) { bool HumdrumFileBase::read(istream& contents) { - clear(); - m_displayError = true; - char buffer[123123] = {0}; - HLp s; - while (contents.getline(buffer, sizeof(buffer), '\n')) { - s = new HumdrumLine(buffer); - s->setOwner(this); - m_lines.push_back(s); - } - return analyzeBaseFromLines(); -/* - if (!analyzeTokens()) { return isValid(); } - if (!analyzeLines() ) { return isValid(); } - if (!analyzeSpines()) { return isValid(); } - if (!analyzeLinks() ) { return isValid(); } - if (!analyzeTracks()) { return isValid(); } - return isValid(); -*/ + clear(); + m_displayError = true; + std::string buffer; + HLp s; + while (std::getline(contents, buffer)) { + s = new HumdrumLine(buffer); + s->setOwner(this); + m_lines.push_back(s); + } + return analyzeBaseFromLines(); } @@ -20569,6 +20613,39 @@ bool HumdrumFileBase::analyzeBaseFromLines(void) { +////////////////////////////// +// +// HumdrumFileBase::setFilenameFromSegment -- Update filename based on any +// !!!!SEGMENT: line at the top of the file. +// + +void HumdrumFileBase::setFilenameFromSegment(void) { + HumdrumFileBase& infile = *this; + for (int i=0; i kstarts = infile.getKernSpineStartList(); - bool status = 0; + bool status = 0; for (int i=0; i<(int)kstarts.size(); i++) { status |= doHandAnalysis(kstarts[i], attacksOnlyQ); } @@ -24426,7 +24503,7 @@ void HumdrumFileContent::fillMidiInfo(vector>>>& tr // // HumdrumFileContent::processStrandNotesForMidi -- store strand tokens/subtokens by MIDI note // in the midi track entry. -// +// // First index if track info is the MIDI note number, second is a list // of tokens for that note number, with the second value of the pair // giving the subtoken index of the note in the token. @@ -27598,6 +27675,13 @@ int HumdrumFileStream::getFile(HumdrumFile& infile) { restarting: + stringstream buffer; + string templine; + if (!m_newfilebuffer.empty()) { + buffer << m_newfilebuffer << endl; + m_newfilebuffer = ""; + } + newinput = NULL; if (m_urlbuffer.eof()) { @@ -27630,21 +27714,21 @@ int HumdrumFileStream::getFile(HumdrumFile& infile) { // (3) If ifstream is closed but there is a file to be processed, // load it into the ifstream and start processing it immediately. else if (((int)m_filelist.size() > 0) && - (m_curfile < (int)m_filelist.size()-1)) { + (m_curfile < (int)m_filelist.size()-1)) { m_curfile++; if (m_instream.is_open()) { m_instream.close(); } - if (strstr(m_filelist[m_curfile].c_str(), "://") != NULL) { + if (m_filelist[m_curfile].find("://") != string::npos) { // The next file to read is a URL/URI, so buffer the // data from the internet and start reading that instead // of reading from a file on the hard disk. - fillUrlBuffer(m_urlbuffer, m_filelist[m_curfile].c_str()); - infile.setFilename(m_filelist[m_curfile].c_str()); + fillUrlBuffer(m_urlbuffer, m_filelist[m_curfile]); + infile.setFilename(m_filelist[m_curfile]); goto restarting; } - m_instream.open(m_filelist[m_curfile].c_str()); - infile.setFilename(m_filelist[m_curfile].c_str()); + m_instream.open(m_filelist[m_curfile]); + infile.setFilename(m_filelist[m_curfile]); if (!m_instream.is_open()) { // file does not exist or cannot be opened close // the file and try luck with next file in the list @@ -27671,17 +27755,16 @@ int HumdrumFileStream::getFile(HumdrumFile& infile) { if (m_newfilebuffer.size() > 0) { // store the filename for the current HumdrumFile being read: HumRegex hre; - if (hre.search(m_newfilebuffer, - R"(^!!!!SEGMENT\s*([+-]?\d+)?\s*:\s*(.*)\s*$)")) { + if (hre.search(m_newfilebuffer, R"(^!!!!SEGMENT\s*([+-]?\d+)?\s*:\s*(.*)\s*$)")) { if (hre.getMatchLength(1) > 0) { - infile.setSegmentLevel(atoi(hre.getMatch(1).c_str())); + infile.setSegmentLevel(hre.getMatchInt(1)); } else { infile.setSegmentLevel(0); } infile.setFilename(hre.getMatch(2)); } else if ((m_curfile >=0) && (m_curfile < (int)m_filelist.size()) - && (m_filelist.size() > 0)) { - infile.setFilename(m_filelist[m_curfile].c_str()); + && (m_filelist.size() > 0)) { + infile.setFilename(m_filelist[m_curfile]); } else { // reading from standard input, but no name. } @@ -27692,7 +27775,6 @@ int HumdrumFileStream::getFile(HumdrumFile& infile) { return 0; } - stringstream buffer; int foundUniversalQ = 0; // Start reading the input stream. If !!!!SEGMENT: universal comment @@ -27700,15 +27782,13 @@ int HumdrumFileStream::getFile(HumdrumFile& infile) { // newly read HumdrumFile. If other universal comments are found, then // overwrite the old universal comments here. - int addedFilename = 0; - //int searchName = 0; + // int addedFilename = 0; int dataFoundQ = 0; int starstarFoundQ = 0; int starminusFoundQ = 0; if (m_newfilebuffer.size() < 4) { //searchName = 1; } - char templine[123123] = {0}; if (newinput->eof()) { if (m_curfile < (int)m_filelist.size()-1) { @@ -27723,85 +27803,49 @@ int HumdrumFileStream::getFile(HumdrumFile& infile) { // if the previous line from the last read starts with "**" // then treat it as part of the current file. - if ((m_newfilebuffer.size() > 1) && - (strncmp(m_newfilebuffer.c_str(), "**", 2)) == 0) { + if ((m_newfilebuffer.size() > 1) && (m_newfilebuffer.compare(0, 2, "**") == 0)) { buffer << m_newfilebuffer << "\n"; m_newfilebuffer = ""; starstarFoundQ = 1; } while (!input.eof()) { - input.getline(templine, 123123, '\n'); - if ((!dataFoundQ) && - (strncmp(templine, "!!!!SEGMENT", strlen("!!!!SEGMENT")) == 0)) { - string tempstring; - tempstring = templine; - HumRegex hre; - if (hre.search(tempstring, - "^!!!!SEGMENT\\s*([+-]?\\d+)?\\s*:\\s*(.*)\\s*$")) { - if (hre.getMatchLength(1) > 0) { - infile.setSegmentLevel(atoi(hre.getMatch(1).c_str())); - } else { - infile.setSegmentLevel(0); - } - infile.setFilename(hre.getMatch(2)); + getline(input, templine); + if (templine.compare(0, strlen("!!!!SEGMENT"), "!!!!SEGMENT") == 0) { + // Store the current segment line in the buffer before breaking. + if (!buffer.str().empty()) { + m_newfilebuffer = templine; + break; } + m_newfilebuffer = templine; } - if (strncmp(templine, "**", 2) == 0) { + if (templine.compare(0, 2, "**") == 0) { if (starstarFoundQ == 1) { m_newfilebuffer = templine; // already found a **, so this one is defined as a file // segment. Exit from the loop and process the previous - // content, waiting until the next read for to start with + // content, waiting until the next read to start with // this line. break; } starstarFoundQ = 1; } - if (input.eof() && (strcmp(templine, "") == 0)) { + if (input.eof() && templine.empty()) { // No more data coming from current stream, so this is // the end of the HumdrumFile. Break from the while loop // and then store the read contents of the stream in the // HumdrumFile. break; } - // (1) Does the line start with "!!!!SEGMENT"? If so, then - // this is either the name of the current or next file to process. - // (1a) this is the name of the current file to process if no - // data has yet been found, - // (1b) or a name is being actively searched for. - if (strncmp(templine, "!!!!SEGMENT", strlen("!!!!SEGMENT")) == 0) { - m_newfilebuffer = templine; - if (dataFoundQ) { - // this new filename is for the next chunk to process in the - // current file stream, not this one, so stop reading the - // HumdrumFile content and send what has already been read back - // out with new contents. - } else { - // !!!!SEGMENT: came before any real data was read, so - // it is most likely the name of the current file - // (i.e., it comes at the start of the file stream and - // is the name of the first HumdrumFile in the stream). - HumRegex hre; - if (hre.search(m_newfilebuffer, - R"(^!!!!SEGMENT\s*([+-]?\d+)?\s:\s*(.*)\s*$)")) { - if (hre.getMatchLength(1) > 0) { - infile.setSegmentLevel(atoi(hre.getMatch(1).c_str())); - } else { - infile.setSegmentLevel(0); - } - infile.setFilename(hre.getMatch(2)); - } - } - } - int len = (int)strlen(templine); - if ((len > 4) && (strncmp(templine, "!!!!", 4) == 0) && - (templine[4] != '!') && - (dataFoundQ == 0) && - (strncmp(templine, "!!!!filter:", strlen("!!!!filter:")) != 0) && - (strncmp(templine, "!!!!SEGMENT:", strlen("!!!!SEGMENT:")) != 0)) { + + int len = templine.length(); + if ((len > 4) && (templine.compare(0, 4, "!!!!") == 0) && + (templine[4] != '!') && + (dataFoundQ == 0) && + (templine.compare(0, strlen("!!!!filter:"), "!!!!filter:") != 0) && + (templine.compare(0, strlen("!!!!SEGMENT:"), "!!!!SEGMENT:") != 0)) { // This is a universal comment. Should it be appended // to the list or should the current list be erased and // this record placed into the first entry? @@ -27819,39 +27863,33 @@ int HumdrumFileStream::getFile(HumdrumFile& infile) { continue; } - if (strncmp(templine, "*-", 2) == 0) { + if (templine.compare(0, 2, "*-") == 0) { starminusFoundQ = 1; } - // if before first ** in a data file or after *-, and the line - // does not start with '!' or '*', then assume that it is a file - // name which should be added to the file list to read. - if (((starminusFoundQ == 1) || (starstarFoundQ == 0)) - && (templine[0] != '*') && (templine[0] != '!')) { - if ((templine[0] != '\0') && (templine[0] != ' ')) { - // The file can only be added once in this manner - // so that infinite loops are prevented. + if (((starminusFoundQ == 1) || (starstarFoundQ == 0)) && (templine[0] != '*') && (templine[0] != '!')) { + if ((!templine.empty()) && (templine[0] != ' ')) { int found = 0; - for (int mm=0; mm<(int)m_filelist.size(); mm++) { - if (strcmp(m_filelist[mm].c_str(), templine) == 0) { + for (int mm = 0; mm < (int)m_filelist.size(); mm++) { + if (m_filelist[mm] == templine) { found = 1; } } if (!found) { m_filelist.push_back(templine); - addedFilename = 1; + // addedFilename = 1; } continue; } } dataFoundQ = 1; // found something other than universal comments - // should empty lines be treated somewhat as universal comments? // store the data line for later parsing into HumdrumFile record: buffer << templine << "\n"; } +/* if (dataFoundQ == 0) { // never found anything for some strange reason. if (addedFilename) { @@ -27859,6 +27897,7 @@ int HumdrumFileStream::getFile(HumdrumFile& infile) { } return 0; } +*/ // Arriving here means that reading of the data stream is complete. // The string stream variable "buffer" contains the HumdrumFile @@ -27870,29 +27909,33 @@ int HumdrumFileStream::getFile(HumdrumFile& infile) { contents.str(""); // empty any contents in buffer contents.clear(); // reset error flags in buffer - for (int i=0; i<(int)m_universals.size(); i++) { - // Convert universals reference records to globals, but do not demote !!!!filter: + for (int i=0; i < (int)m_universals.size(); i++) { if (m_universals[i].compare(0, 11, "!!!!filter:") == 0) { continue; } contents << &(m_universals[i][1]) << "\n"; } + contents << buffer.str(); - string filename = infile.getFilename(); + string oldfilename = infile.getFilename(); infile.readNoRhythm(contents); - if (!filename.empty()) { - infile.setFilename(filename); + string newfilename = infile.getFilename(); + if (newfilename.empty() && !oldfilename.empty()) { + infile.setFilename(oldfilename); } + infile.setFilenameFromSegment(); + return 1; } + + ////////////////////////////// // // HumdrumFileStream::fillUrlBuffer -- // - void HumdrumFileStream::fillUrlBuffer(stringstream& uribuffer, const string& uriname) { #ifdef USING_URI @@ -31327,7 +31370,7 @@ int HumdrumLine::createTokensFromLine(void) { token = new HumdrumToken(); token->setOwner(this); m_tokens.push_back(token); - m_tokens.push_back(0); + m_tabs.push_back(0); } else if (this->compare(0, 2, "!!") == 0) { token = new HumdrumToken(this->c_str()); token->setOwner(this); @@ -39785,7 +39828,7 @@ void MuseData::linkMusicDirections(void) { // // MuseData::getMeasureNumber -- If index == 0, return the next barnumber // minus 1. If on a measure record, return the number on that line. -// If neither, then search backwards for the last measure line (or 0 +// If neither, then search backwards for the last measure line (or 0 // index) and return the measure number for that barline (or 0 index). // @@ -39819,7 +39862,7 @@ string MuseData::getMeasureNumber(int index) { string number = md[i].getMeasureNumber(); return number; } else { - // first measure is not numbered, so return next + // first measure is not numbered, so return next // measure number minus 1. for (int j=index; j terminate) { m_error << "Error: missing option argument" << endl; + m_error << "ARGV count: " << m_argv.size() << endl; + m_error << "terminate: " << terminate << endl; + m_error << "tcount: " << tcount << endl; break; } if (isOption(m_argv[i], i)) { @@ -55536,6 +55582,7 @@ bool Tool_autobeam::run(HumdrumFile& infile) { } // Re-load the text for each line from their tokens. infile.createLinesFromTokens(); + m_humdrum_text << infile; return true; } @@ -56364,9 +56411,9 @@ void Tool_autobeam::processMeasure(vector& measure) { if ((current.first % 3 == 0) && (current.first != 3)) { // compound meter, so shift the beat to 3x the demoniator beatdur *= 3; - } else if (current.first == 3 && (current.second > 4)) { + } else if (current.first == 3 && (current.second < 4)) { // time signatures such as 3/8 and 3/16 which should - // beam together at the measure level (3/4 not included). + // beam together at the measure level (3/4 and 3/2 not included). beatdur *= 3; } } @@ -66280,13 +66327,14 @@ void Tool_composite::addCoincidenceMarks(HumdrumFile& infile) { if (token->isNull()) { continue; } - if (token->isRest()) { - continue; - } - if (token->isNoteAttack()) { - string text = *token; - text += m_coinMark; - token->setText(text); + for (int i=0; igetSubtokenCount(); i++) { + string subtok = token->getSubtoken(i); + if (subtok.find("r") != string::npos) { + continue; + } + subtok += m_coinMark; + token->replaceSubtoken(i, subtok); + // Maybe highlight only if note attack? } } } @@ -66686,6 +66734,11 @@ void Tool_composite::getAnalysisOutputLine(ostream& output, HumdrumFile& infile, tempout << "/"; } } + if (m_coinMarkQ) { + if (value.find("R") != string::npos) { + tempout << m_coinMark; + } + } if (processedQ) { tempout << "\t"; } @@ -68962,7 +69015,7 @@ void Tool_composite::addMeterSignatureChanges(HumdrumFile& output, HumdrumFile& ////////////////////////////// // -// adjustBadCoincidenceRests -- Sometimes coincidence rests are not so great, particularly +// adjustBadCoincidenceRests -- Sometimes coincidence rests are not so great, particularly // when they are long and there is a small note that will add to it to fill in a measure // (such as a 5 eighth-note rest in 6/8). Try to simplify such case in this function // (more can be added on a case-by-case basis). @@ -72906,7 +72959,7 @@ Tool_deg::Tool_deg(void) { define("kern=b", "prefix composite rhythm **kern spine with -I option"); define("k|kern-tracks=s", "process only the specified kern spines"); define("kd|dk|key-default|default-key=s", "default (initial) key if none specified in data"); - define("kf|fk|key-force|force-key=s", "use the given key for analysing deg data (ignore modulations)"); + define("kf|fk|key-force|force-key|forced-key=s", "use the given key for analysing deg data (ignore modulations)"); define("o|octave|octaves|degree=b", "encode octave information int **degree spines"); define("r|recip=b", "prefix output data with **recip spine with -I option"); define("t|ties=b", "include scale degrees for tied notes"); @@ -78189,17 +78242,10 @@ void Tool_double::doubleRhythms(HumdrumFile& infile) { // Tool_esac2hum::Tool_esac2hum(void) { - define("debug=b", "print debug information"); - define("v|verbose=b", "verbose output"); - define("h|header=s:", "header filename for placement in output"); - define("t|trailer=s:", "trailer filename for placement in output"); - define("s|split=s:file", "split song info into separate files"); - define("x|extension=s:.krn", "split filename extension"); - define("f|first=i:1", "number of first split filename"); - define("author=b", "author of program"); - define("version=b", "compilation info"); - define("example=b", "example usages"); - define("help=b", "short description"); + define("debug=b", "Print debugging statements"); + define("v|verbose=s", "Print verbose messages"); + define("e|embed-esac=b", "Eembed EsAC data in output"); + define("a|analyses|analysis=b", "Generate EsAC analysis fields"); } @@ -78211,13 +78257,12 @@ Tool_esac2hum::Tool_esac2hum(void) { // bool Tool_esac2hum::convertFile(ostream& out, const string& filename) { - ifstream file(filename); - stringstream s; - if (file) { - s << file.rdbuf(); - file.close(); - } - return convert(out, s.str()); + initialize(); + ifstream file(filename); + if (file) { + return convert(out, file); + } + return false; } @@ -78235,87 +78280,57 @@ bool Tool_esac2hum::convert(ostream& out, const string& input) { } - - ////////////////////////////// // // Tool_esac2hum::initialize -- // -bool Tool_esac2hum::initialize(void) { - // handle basic options: - if (getBoolean("author")) { - cerr << "Written by Craig Stuart Sapp, " - << "craig@ccrma.stanford.edu, March 2002" << endl; - return false; - } else if (getBoolean("version")) { - cerr << getCommand() << ", version: 6 June 2017" << endl; - cerr << "compiled: " << __DATE__ << endl; - return false; - } else if (getBoolean("help")) { - usage(getCommand()); - return false; - } else if (getBoolean("example")) { - example(); - return false; - } - - debugQ = getBoolean("debug"); - verboseQ = getBoolean("verbose"); - - if (getBoolean("header")) { - if (!getFileContents(header, getString("header"))) { - return false; - } - } else { - header.resize(0); - } - if (getBoolean("trailer")) { - if (!getFileContents(trailer, getString("trailer"))) { - return false; - } - } else { - trailer.resize(0); - } - - if (getBoolean("split")) { - splitQ = 1; +void Tool_esac2hum::initialize(void) { + m_debugQ = getBoolean("debug"); // print debugging information + m_verboseQ = getBoolean("verbose"); // print input EsAC MEL[] data when true + m_verbose = getString("verbose"); // p = phrase, m=measure, n=note + m_embedEsacQ = getBoolean("embed-esac"); // don't print input EsAC data + m_analysisQ = getBoolean("analyses"); // embed analysis in EsAC data + if (m_analysisQ) { + m_embedEsacQ = true; } - namebase = getString("split"); - fileextension = getString("extension"); - firstfilenum = getInteger("first"); - return true; } -////////////////////////////////////////////////////////////////////////// - - ////////////////////////////// // // Tool_esac2hum::convertEsacToHumdrum -- // void Tool_esac2hum::convertEsacToHumdrum(ostream& output, istream& infile) { - initialize(); - vector song; - song.reserve(400); - int init = 0; - // int filecounter = firstfilenum; - string outfilename; - string numberstring; - // ofstream outfile; + m_inputline = 0; + m_prevline = ""; + + vector song; // contents of one EsAC song, extracted from input stream + song.reserve(1000); + while (!infile.eof()) { - if (debugQ) { + if (m_debugQ) { cerr << "Getting a song..." << endl; } - getSong(song, infile, init); - if (debugQ) { + bool status = getSong(song, infile); + if (!status) { + cerr << "Error getting a song" << endl; + continue; + } + if (m_debugQ) { cerr << "Got a song ..." << endl; } - init = 1; - convertSong(song, output); + if (song.empty()) { + cerr << "Song is empty" << endl; + continue; + } + if (song.size() < 4) { + cerr << "Song is too short" << endl; + continue; + } + convertSong(output, song); } } @@ -78323,49 +78338,127 @@ void Tool_esac2hum::convertEsacToHumdrum(ostream& output, istream& infile) { ////////////////////////////// // -// Tool_esac2hum::getSong -- get a song from the EsAC file +// Tool_esac2hum::getSong -- get a song from a multiple-song EsAC file. +// Search for a CUT[] line which indicates the first line of the data. +// There will/can be some text above the CUT[] line. The CUT[] field +// may contain newlnes, so searching only for CUT[ to also handle these +// cases. // -bool Tool_esac2hum::getSong(vector& song, istream& infile, int init) { - string holdbuffer; +bool Tool_esac2hum::getSong(vector& song, istream& infile) { song.resize(0); - if (init) { - // do nothing holdbuffer has the CUT[] information - } else { - while (!infile.eof() && holdbuffer.compare(0, 4, "CUT[") != 0) { - getline(infile, holdbuffer); - if (verboseQ) { - cerr << "Contents: " << holdbuffer << endl; + m_globalComments.clear(); + + HumRegex hre; + string buffer; + + // First find the next CUT[] line in the input which indcates + // the start of a song. There typically is a non-empty line just above CUT[] + // containing information about the collection. + if (m_cutline.empty()) { + while (!infile.eof()) { + getline(infile, buffer); + + if (hre.search(buffer, "^[!#]{2,}")) { + hre.search(buffer, "^([!#]{2,})(.*)$"); + string prefix = hre.getMatch(1); + string postfix = hre.getMatch(2); + hre.replaceDestructive(prefix, "!", "#", "g"); + string comment = prefix + postfix; + m_globalComments.push_back(comment); + continue; } - if (holdbuffer.compare(0, 2, "!!") == 0) { - song.push_back(holdbuffer); + + cleanText(buffer); + m_inputline++; + if (buffer.compare(0, 4, "CUT[") == 0) { + m_cutline = buffer; + break; + } else { + m_prevline = buffer; + continue; } } - if (infile.eof()) { - return false; - } } - if (!infile.eof()) { - song.push_back(holdbuffer); - } else { + if (m_cutline.empty()) { return false; } - getline(infile, holdbuffer); - chopExtraInfo(holdbuffer); - inputline++; - if (verboseQ) { - cerr << "READ LINE: " << holdbuffer << endl; + if (infile.eof()) { + return false; } - while (!infile.eof() && (holdbuffer.compare(0, 4, "CUT[", 4) != 0)) { - song.push_back(holdbuffer); - getline(infile, holdbuffer); - chopExtraInfo(holdbuffer); - inputline++; - if (verboseQ) { - cerr << "READ ANOTHER LINE: " << holdbuffer << endl; + + if (!hre.search(m_prevline, "^\\s*$")) { + song.push_back(m_prevline); + } + song.push_back(m_cutline); + + m_prevline.clear(); + m_cutline.clear(); + + bool expectingCloseQ = false; + + while (!infile.eof()) { + getline(infile, buffer); + + if (hre.search(buffer, "^#{2,}")) { + hre.search(buffer, "^(#{2,})(.*)$"); + string prefix = hre.getMatch(1); + string postfix = hre.getMatch(2); + hre.replaceDestructive(prefix, "!", "#", "g"); + string comment = prefix + postfix; + m_globalComments.push_back(comment); + continue; + } + + cleanText(buffer); + m_inputline++; + if (m_debugQ) { + cerr << "READ LINE: " << buffer << endl; + } + if (expectingCloseQ) { + if (buffer.find("[") != string::npos) { + cerr << "Strange error on line " << m_inputline << ": " << buffer << endl; + continue; + } else if (!hre.search(buffer, "[\\[\\]]")) { + // intermediate parameter line (not starting or ending) + song.push_back(buffer); + continue; + } + + if (hre.search(buffer, "^[^\\]]*\\]\\s*$")) { + // closing bracket + expectingCloseQ = 0; + song.push_back(buffer); + continue; + } else { + cerr << "STRANGE CASE HERE " << buffer << endl; + } + continue; } + + if (hre.search(buffer, "^\\s*$")) { + continue; + } + + if (hre.search(buffer, "^[A-Za-z][^\\[\\]]*$")) { + // collection line + m_prevline = buffer; + continue; + } + + if (hre.search(buffer, "^[A-Za-z]+\\s*\\[[^\\]]*\\s*$")) { + // parameter with opening [ + expectingCloseQ = true; + } else { + } + + song.push_back(buffer); + } + + if (expectingCloseQ) { + cerr << "Strange case: expecting closing of a song parameter around line " << m_inputline++ << endl; } return true; @@ -78375,945 +78468,1247 @@ bool Tool_esac2hum::getSong(vector& song, istream& infile, int init) { ////////////////////////////// // -// Tool_esac2hum::chopExtraInfo -- remove phrase number information from Luxembourg data. +// Tool_esac2hum::cleanText -- remove \x88 and \x98 bytes from string (should not affect UTF-8 encodings) +// since those bytes do not seem to be involved with any UTF-8 characters. // -void Tool_esac2hum::chopExtraInfo(string& buffer) { +void Tool_esac2hum::cleanText(std::string& buffer) { HumRegex hre; - hre.replaceDestructive(buffer, "", "^\\s+"); - hre.replaceDestructive(buffer, "", "\\s+$"); + + // Fix UTF-8 double encodings (related to editing with Windows-1252 or ISO-8859-2 programs): + + // Ą: c3 84 c2 84 - c4 84 + hre.replaceDestructive(buffer, "\xc4\x84", "\xc3\x84\xc2\x84", "g"); + + // ą: c3 84 c2 85 - c4 85 + hre.replaceDestructive(buffer, "\xc4\x85", "\xc3\x84\xc2\x85", "g"); + + // Ć: c3 84 c2 86 -> c4 86 + hre.replaceDestructive(buffer, "\xc4\x86", "\xc3\x84\xc2\x86", "g"); + + // ć: c3 84 c2 87 -> c4 87 + hre.replaceDestructive(buffer, "\xc4\x87", "\xc3\x84\xc2\x87", "g"); + + // Ę: c3 84 c2 98 -> c4 98 + hre.replaceDestructive(buffer, "\xc4\x98", "\xc3\x84\xc2\x98", "g"); + + // ę: c3 84 c2 99 -> c4 99 + hre.replaceDestructive(buffer, "\xc4\x99", "\xc3\x84\xc2\x99", "g"); + + // Ł: c4 b9 c2 81 -> c5 81 + hre.replaceDestructive(buffer, "\xc5\x81", "\xc4\xb9\xc2\x81", "g"); + + // ł: c4 b9 c2 82 -> c5 82 + hre.replaceDestructive(buffer, "\xc5\x82", "\xc4\xb9\xc2\x82", "g"); + + // Ń: c4 b9 c2 83 -> c5 83 + hre.replaceDestructive(buffer, "\xc5\x83", "\xc4\xb9\xc2\x83", "g"); + + // ń: c4 b9 c2 84 -> c5 84 + hre.replaceDestructive(buffer, "\xc5\x84", "\xc4\xb9\xc2\x84", "g"); + + // Ó: c4 82 c5 93 -> c3 93 (note: not sequential with ó) + hre.replaceDestructive(buffer, "\xc3\x93", "\xc4\x82\xc5\x93", "g"); + + // ó: c4 82 c5 82 -> c3 b3 (note: not sequential with Ó) + hre.replaceDestructive(buffer, "\xc3\xb3", "\xc4\x82\xc5\x82", "g"); + + // Ś: c4 b9 c2 9a -> c5 9a + hre.replaceDestructive(buffer, "\xc5\x9a", "\xc4\xb9\xc2\x9b", "g"); + + // ś: c4 b9 c2 9b -> c5 9b + hre.replaceDestructive(buffer, "\xc5\x9b", "\xc4\xb9\xc2\x9b", "g"); + + // Ź: c4 b9 c5 9a -> c5 b9 + hre.replaceDestructive(buffer, "\xc5\xb9", "\xc4\xb9\xc5\x9a", "g"); + + // ź: c4 b9 c5 9f -> c5 ba + hre.replaceDestructive(buffer, "\xc5\xba", "\xc4\xb9\xc5\x9f", "g"); + + // Ż: c4 b9 c5 a5 -> c5 bb + hre.replaceDestructive(buffer, "\xc5\xbb", "\xc4\xb9\xc5\xa5", "g"); + + // ż: c4 b9 c5 ba -> c5 bc + hre.replaceDestructive(buffer, "\xc5\xbc", "\xc4\xb9\xc5\xba", "g"); + + + // Random leftover characters from some character conversion: + hre.replaceDestructive(buffer, "", "[\x88\x98]", "g"); + + // Remove MS-DOS newline character at ends of lines: + if (!buffer.empty()) { + if (buffer.back() == 0x0d) { + // windows newline piece + buffer.resize(buffer.size() - 1); + } + } + // In VHV, when saving content to the local computer in EsAC mode, the 0x0d character should be added back. } ////////////////////////////// // -// Tool_esac2hum::printHumdrumHeaderInfo -- +// Tool_esac2hum::trimSpaces -- remove any trailing or leading spaces. // -void Tool_esac2hum::printHumdrumHeaderInfo(ostream& out, vector& song) { - for (int i=0; i<(int)song.size(); i++) { - if (song[i].size() == 0) { - continue; - } - if (song[i].compare(0, 2, "!!") == 0) { - out << song[i] << "\n"; - continue; - } - if ((song[i][0] == ' ') || (song[i][0] == '\t')) { - continue; - } - break; - } +string Tool_esac2hum::trimSpaces(const string& input) { + string output = input; + HumRegex hre; + hre.replaceDestructive(output, "", "^\\s+"); + hre.replaceDestructive(output, "", "\\s+$"); + return output; } ////////////////////////////// // -// Tool_esac2hum::printHumdrumFooterInfo -- +// Tool_esac2hum::convertSong -- // -void Tool_esac2hum::printHumdrumFooterInfo(ostream& out, vector& song) { - int i = 0; - for (i=0; i<(int)song.size(); i++) { - if (song[i].size() == 0) { - continue; - } - if (song[i].compare(0, 2, "!!") == 0) { - continue; - } - if ((song[i][0] == ' ') || (song[i][0] == '\t')) { - continue; - } - break; - } - int j = i; - for (j=i; j<(int)song.size(); j++) { - if (song[j].compare(0, 2, "!!") == 0) { - out << song[j] << "\n"; - } - } +void Tool_esac2hum::convertSong(ostream& output, vector& infile) { + getParameters(infile); + processSong(); + // printParameters(); + printHeader(output); + printScoreContents(output); + printFooter(output, infile); } ////////////////////////////// // -// Tool_esac2hum::convertSong -- +// Tool_esac2hum::processSong -- parse and preliminary conversion to Humdrum. // -void Tool_esac2hum::convertSong(vector& song, ostream& out) { +void Tool_esac2hum::processSong(void) { + string mel = m_score.m_params["MEL"]; + m_score.parseMel(mel); +} - int i; - if (verboseQ) { - for (i=0; i<(int)song.size(); i++) { - out << song[i] << "\n"; + + +////////////////////////////// +// +// Tool_esac2hum::printScoreContents -- +// + +void Tool_esac2hum::printScoreContents(ostream& output) { + + vector& errors = m_score.m_errors; + if (!errors.empty()) { + for (int z=0; z<(int)errors.size(); z++) { + output << "!!" << errors.at(z) << endl; } } - printHumdrumHeaderInfo(out, song); + if (!m_score.m_clef.empty()) { + output << m_score.m_clef << endl; + } + if (!m_score.m_keysignature.empty()) { + output << m_score.m_keysignature << endl; + } + if (!m_score.m_keydesignation.empty()) { + output << m_score.m_keydesignation << endl; + } + if (!m_score.m_timesig.empty()) { + output << m_score.m_timesig << endl; + } - string key; - double mindur = 1.0; - string meter; - int tonic = 0; - getKeyInfo(song, key, mindur, tonic, meter, out); + for (int i=0; i<(int)m_score.size(); i++) { + Tool_esac2hum::Phrase& phrase = m_score.at(i); + if (m_verbose.find("p") != string::npos) { + output << "!!esac-phrase: " << phrase.esac; + if (m_verbose.find("pi") != string::npos) { + output << " ["; + output << "ticks:" << phrase.m_ticks; + output << "]"; + } + vector& errors = phrase.m_errors; + if (!errors.empty()) { + for (int z=0; z<(int)errors.size(); z++) { + output << "!!" << errors.at(z) << endl; + } + } + output << endl; + } - vector songdata; - songdata.resize(0); - songdata.reserve(1000); - getNoteList(song, songdata, mindur, tonic); - placeLyrics(song, songdata); + for (int j=0; j<(int)phrase.size(); j++) { - vector numerator; - vector denominator; - getMeterInfo(meter, numerator, denominator); + Tool_esac2hum::Measure& measure = phrase.at(j); + if ((j == 0) && (i > 0)) { + output << "!!LO:LB:g=esac" << endl; + } + if (measure.m_barnum != 0) { // don't print barline if first is pickup + output << "="; + if (measure.m_barnum > 0) { + output << measure.m_barnum; + } else if (measure.m_barnum == -1) { + output << "-"; // "non-controlling" barline. + } else { + // visible barline, but not assigned a measure + // number (probably need more analysis to assign + // a measure number to this barline). + } + output << endl; + } + if (m_verbose.find("m") != string::npos) { + output << "!!esac-measure: " << measure.esac; + if (m_verbose.find("mi") != string::npos) { + output << " ["; + output << "ticks:" << measure.m_ticks; + if (measure.isComplete()) { + output << "; CM"; + } + if (measure.isPartialBegin()) { + output << "; PB"; + } + if (measure.isPartialEnd()) { + output << "; PE"; + } + if (measure.isUnassigned()) { + output << "; UN"; + } + output << "]"; + } + output << endl; + } + vector& errors = measure.m_errors; + if (!errors.empty()) { + for (int z=0; z<(int)errors.size(); z++) { + output << "!!" << errors.at(z) << endl; + } + } - postProcessSongData(songdata, numerator, denominator); + // print time signature change + if (!measure.m_measureTimeSignature.empty()) { + output << measure.m_measureTimeSignature << endl; + } - printTitleInfo(song, out); - out << "!!!id: " << key << "\n"; + for (int k=0; k<(int)measure.size(); k++) { - // check for presence of lyrics - int textQ = 0; - for (i=0; i<(int)songdata.size(); i++) { - if (songdata[i].text != "") { - textQ = 1; - break; + Tool_esac2hum::Note& note = measure.at(k); + if (m_verbose.find("n") != string::npos) { + output << "!!esac-note: " << note.esac; + if (m_verbose.find("ni") != string::npos) { + output << " ["; + output << "ticks:" << note.m_ticks; + output << ", deg:" << note.m_degree; + output << ", alt:" << note.m_alter; + output << ", oct:" << note.m_octave; + output << "]"; + } + vector& errors = note.m_errors; + if (!errors.empty()) { + for (int z=0; z<(int)errors.size(); z++) { + output << "!!" << errors.at(z) << endl; + } + } + output << endl; + } + output << note.m_humdrum << endl; + + } } } - for (i=0; i<(int)header.size(); i++) { - out << header[i] << "\n"; + if (m_score.hasFinalBarline()) { + output << "==" << endl; + } else { + output << "=" << endl; } +} - out << "**kern"; - if (textQ) { - out << "\t**text"; - } - out << "\n"; - printKeyInfo(songdata, tonic, textQ, out); - for (i=0; i<(int)songdata.size(); i++) { - printNoteData(songdata[i], textQ, out); - } - out << "*-"; - if (textQ) { - out << "\t*-"; + +////////////////////////////// +// +// Tool_esac2hum::Score::parseMel -- +// + +bool Tool_esac2hum::Score::parseMel(const string& mel) { + clear(); + reserve(100); + + HumRegex hre; + if (hre.search(mel, "^\\s*$")) { + // no data; + cerr << "ERROR: MEL parameter is empty or non-existent" << endl; + return false; } - out << "\n"; - out << "!!!minrhy: "; - out << Convert::durationFloatToRecip(mindur)<<"\n"; - out << "!!!meter"; - if (numerator.size() > 1) { - out << "s"; + vector lines; + string line; + + stringstream linestream; + linestream << mel; + + int lineNumber = 0; + while (std::getline(linestream, line)) { + lineNumber++; + if (hre.search(line, "^\\s*$")) { + // Skip blank lines + continue; + } + string unknown = line; + hre.replaceDestructive(unknown, "", "[\\^0-9b\\s/._#()+-]+", "g"); + if (!unknown.empty()) { + cerr << "Unknown characters " << ">>" << unknown << "<< " << " on mel line " << lineNumber << ": " << line << endl; + } + line = Tool_esac2hum::trimSpaces(line); + lines.push_back(line); } - out << ": " << meter; - if ((meter == "frei") || (meter == "Frei")) { - out << " [unmetered]"; - } else if (meter.find('/') == string::npos) { - out << " interpreted as ["; - for (i=0; i<(int)numerator.size(); i++) { - out << numerator[i] << "/" << denominator[i]; - if (i < (int)numerator.size()-1) { - out << ", "; + + m_finalBarline = false; + for (int i=0; i<(int)lines.size(); i++) { + string line = lines[i]; + if (i == (int)lines.size() - 1) { + if (hre.search(line, "^(.*)\\s*//\\s*$")) { + m_finalBarline = true; + lines.back() = hre.getMatch(1); } } - out << "]"; } - out << "\n"; - - printBibInfo(song, out); - printSpecialChars(out); - - for (i=0; i<(int)songdata.size(); i++) { - if (songdata[i].lyricerr) { - out << "!!!RWG: Lyric placement mismatch " - << "in phrase (too many syllables) " << songdata[i].phnum << " [" - << key << "]\n"; - break; + // remove the last line if it is only "//": + if (!lines.empty()) { + if (hre.search(lines.back(), "^\\s*$")) { + lines.resize(lines.size() - 1); } } + if (lines.empty()) { + cerr << "ERROR: No notes in MEL data" << endl; + return false; + } - for (i=0; i<(int)trailer.size(); i++) { - out << trailer[i] << "\n"; + for (int i=0; i<(int)lines.size(); i++) { + resize(size() + 1); + back().parsePhrase(lines[i]); } - printHumdrumFooterInfo(out, song); + analyzeTies(); + analyzePhrases(); + generateHumdrumNotes(); + calculateClef(); + calculateKeyInformation(); + calculateTimeSignatures(); -/* - if (!splitQ) { - out << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl; + return true; +} + + + +////////////////////////////// +// +// Tool_esac2hum::Score::assignFreeMeasureNumbers -- The time signature +// is "FREI", so assign a measure number to eavery barline, not checking +// for pickup or partial measures. +// + +void Tool_esac2hum::Score::assignFreeMeasureNumbers(void) { + vector measurelist; + getMeasureList(measurelist); + + int barnum = 1; + for (int i=0; i<(int)measurelist.size(); i++) { + measurelist[i]->m_barnum = barnum++; + measurelist[i]->m_partialBegin = false; + measurelist[i]->m_partialEnd = false; + measurelist[i]->m_complete = true; } -*/ } ////////////////////////////// // -// Tool_esac2hum::placeLyrics -- extract lyrics (if any) and place on correct notes +// Tool_esac2hum::Score::assignSingleMeasureNumbers -- There is a +// single time signature for the entire melody, so identify full +// and unfull measures, marking full that match the time signature +// duration as complete, and then try to pair measures and look +// for a pickup measure at the start of the music. +// The Measure::tsticks is the expected duration of the measure +// according to the time signature. // -bool Tool_esac2hum::placeLyrics(vector& song, vector& songdata) { - int start = -1; - int stop = -1; - getLineRange(song, "TXT", start, stop); - if (start < 0) { - // no TXT[] field, so don't do anything - return true; +void Tool_esac2hum::Score::assignSingleMeasureNumbers(void) { + vector measurelist; + getMeasureList(measurelist); + + if (measurelist.empty()) { + // strange error: no measures; + return; } - int line = 0; - vector lyrics; - string buffer; - for (line=0; line<=stop-start; line++) { - if (song[line+start].size() <= 4) { - cerr << "Error: lyric line is too short!: " - << song[line+start] << endl; - return false; + + // first identify complete measures: + for (int i=0; i<(int)measurelist.size(); i++) { + if (measurelist[i]->m_tsticks == measurelist[i]->m_ticks) { + measurelist[i]->setComplete(); } - buffer = song[line+start].substr(4); - if (line == stop - start) { - auto loc = buffer.rfind(']'); - if (loc != string::npos) { - buffer.resize(loc); - } + } + + // check for pickup measure at beginning of music + if (measurelist[0]->m_ticks < measurelist[0]->m_tsticks) { + measurelist[0]->setPartialEnd(); + // check for partial measure at end that matches end measure + if (measurelist.back()->m_ticks < measurelist.back()->m_tsticks) { + measurelist.back()->setPartialBegin(); } - if (buffer == "") { + } + + // search for pairs of partial measures + for (int i=1; i<(int)measurelist.size(); i++) { + if (!measurelist[i]->isUnassigned()) { continue; } - getLyrics(lyrics, buffer); - cleanupLyrics(lyrics); - placeLyricPhrase(songdata, lyrics, line); + if (!measurelist[i-1]->isUnassigned()) { + continue; + } + double ticks1 = measurelist[i-1]->m_ticks; + double ticks2 = measurelist[i]->m_ticks; + double tsticks1 = measurelist[i-1]->m_tsticks; + double tsticks2 = measurelist[i]->m_tsticks; + if (tsticks1 != tsticks2) { + // strange error; + continue; + } + if (ticks1 + ticks2 == tsticks2) { + measurelist[i-1]->setPartialBegin(); + measurelist[i]->setPartialEnd(); + } } - return true; + // Now assign barlines to measures. that are complete or + // partial starts. + int barnum = 1; + for (int i=0; i<(int)measurelist.size(); i++) { + if (measurelist[i]->isComplete()) { + measurelist[i]->m_barnum = barnum++; + } else if (measurelist[i]->isPartialBegin()) { + measurelist[i]->m_barnum = barnum++; + } else if (measurelist[i]->isPartialEnd()) { + measurelist[i]->m_barnum = -1; + } + } + if (measurelist[0]->isPartialEnd()) { + measurelist[0]->m_barnum = 0; // pickup: don't add barline on first measure + } } ////////////////////////////// // -// Tool_esac2hum::cleanupLyrics -- add preceeding dashes, avoid starting *'s if any, -// and convert _'s to spaces. +// Tool_esac2hum::Measure::isUnassigned -- // -void Tool_esac2hum::cleanupLyrics(vector& lyrics) { - int length; - int length2; - int i, j, m; - int lastsyl = 0; - for (i=0; i<(int)lyrics.size(); i++) { - length = (int)lyrics[i].size(); - for (j=0; j 0) { - if ((lyrics[i] != ".") && - (lyrics[i] != "") && - (lyrics[i] != "%") && - (lyrics[i] != "^") && - (lyrics[i] != "|") && - (lyrics[i] != " ")) { - lastsyl = -1; - for (m=i-1; m>=0; m--) { - if ((lyrics[m] != ".") && - (lyrics[m] != "") && - (lyrics[m] != "%") && - (lyrics[i] != "^") && - (lyrics[m] != "|") && - (lyrics[m] != " ")) { - lastsyl = m; - break; - } - } - if (lastsyl >= 0) { - length2 = (int)lyrics[lastsyl].size(); - if (lyrics[lastsyl][length2-1] == '-') { - for (j=0; j<=length; j++) { - lyrics[i][length - j + 1] = lyrics[i][length - j]; - } - lyrics[i][0] = '-'; - } - } - } - } - // avoid *'s on the start of lyrics by placing a space before - // them if they exist. - if (lyrics[i][0] == '*') { - length = (int)lyrics[i].size(); - for (j=0; j<=length; j++) { - lyrics[i][length - j + 1] = lyrics[i][length - j]; - } - lyrics[i][0] = ' '; - } +////////////////////////////// +// +// Tool_esac2hum::Measure::setComplete -- +// - // avoid !'s on the start of lyrics by placing a space before - // them if they exist. - if (lyrics[i][0] == '!') { - length = (int)lyrics[i].size(); - for (j=0; j<=length; j++) { - lyrics[i][length - j + 1] = lyrics[i][length - j]; - } - lyrics[i][0] = ' '; - } +void Tool_esac2hum::Measure::setComplete(void) { + m_complete = true; + m_partialBegin = false; + m_partialEnd = false; +} - } + +////////////////////////////// +// +// Tool_esac2hum::Measure::isComplete -- +// + +bool Tool_esac2hum::Measure::isComplete(void) { + return m_complete; } -/////////////////////////////// +////////////////////////////// // -// Tool_esac2hum::getLyrics -- extract the lyrics from the text string. +// Tool_esac2hum::Measure::setPartialBegin -- // -void Tool_esac2hum::getLyrics(vector& lyrics, const string& buffer) { - lyrics.resize(0); - int zero1 = 0; - string current; - int zero2 = 0; - zero2 = zero1 + zero2; +void Tool_esac2hum::Measure::setPartialBegin(void) { + m_complete = false; + m_partialBegin = true; + m_partialEnd = false; +} - int length = (int)buffer.size(); - int i; - i = 0; - while (i& songdata, vector& lyrics, int line) { - int i = 0; - int start = 0; - int found = 0; +void Tool_esac2hum::Measure::setPartialEnd(void) { + m_complete = false; + m_partialBegin = false; + m_partialEnd = true; +} - if (lyrics.empty()) { - return true; + + +////////////////////////////// +// +// Tool_esac2hum::Measure::isPartialEnd -- +// + +bool Tool_esac2hum::Measure::isPartialEnd(void) { + return m_partialEnd; +} + + + +////////////////////////////// +// +// Tool_esac2hum::Score::calculateTimeSignatures -- +// + +void Tool_esac2hum::Score::calculateTimeSignatures(void) { + string ts = m_params["_time"]; + ts = trimSpaces(ts); + if (ts.find("FREI") != string::npos) { + m_timesig = "*MX"; + setAllTimesigTicks(0.0); + assignFreeMeasureNumbers(); + return; } - // find the phrase to which the lyrics belongs - for (i=0; i<(int)songdata.size(); i++) { - if (songdata[i].phnum == line) { - found = 1; - break; + + HumRegex hre; + if (hre.search(ts, "^(\\d+)/(\\d+)$")) { + m_timesig = "*M" + ts; + int top = hre.getMatchInt(1); + int bot = hre.getMatchInt(2); + // check if bot is a power of two? + double tsticks = top * m_minrhy / bot; + setAllTimesigTicks(tsticks); + assignSingleMeasureNumbers(); + return; + } else if (hre.search(ts, "^(\\d+/\\d+(?:\\s+|$)){2,}$")) { + prepareMultipleTimeSignatures(ts); + } + + // Complicated case where the time signature changes + vector timesigs; + hre.split(timesigs, ts, "\\s+"); + if (timesigs.size() < 2) { + string error = "ERROR: Cannot find time signature(s) in KEY[] field: "; + error += m_params["KEY"]; + m_errors.push_back(error); + return; + } + +/* ggg + vector bticks(timesigs.size(), 0); + for (int i=0; i<(int)bticks +*/ + + +} + + +////////////////////////////// +// +// Tool_esac2hum::Score::prepareMultipleTimeSignatures -- +// N.B.: Will have problems when the duration of time siganture +// in a list are the same such as "4/4 2/2". +// + +void Tool_esac2hum::Score::prepareMultipleTimeSignatures(const string& ts) { + vector tss; + HumRegex hre; + string timesigs = ts; + hre.split(tss, timesigs, "\\s+"); + if (tss.size() < 2) { + cerr << "Time sigs: " << ts << " needs to have at least two time signatures" << endl; + } + + // Calculate tick duration of time signature in list: + vector tsticks(tss.size(), 0); + for (int i=0; i<(int)tss.size(); i++) { + if (!hre.search(tss[i], "^(\\d+)/(\\d+)$")) { + continue; } + int top = hre.getMatchInt(1); + int bot = hre.getMatchInt(2); + double ticks = top * m_minrhy / bot; + tsticks[i] = ticks; } - start = i; - if (!found) { - cerr << "Error: cannot find music for lyrics line " << line << endl; - cerr << "Error near input data line: " << inputline << endl; - return false; + //cerr << "\nMultiple time signatures in melody: " << endl; + //for (int i=0; i<(int)tss.size(); i++) { + // cerr << "(" << i+1 << "): " << tss[i] << "\tticks:" << tsticks[i] << endl; + //} + //cerr << endl; + + // First assign a time signature to every inner measure in a phrase, which + // is presumed to be a complete measure: + for (int i=0; i<(int)size(); i++) { + Tool_esac2hum::Phrase& phrase = at(i); + for (int j=1; j<(int)phrase.size()-1; j++) { + Tool_esac2hum::Measure& measure = phrase.at(j); + for (int k=0; k<(int)tss.size(); k++) { + if (tsticks[k] == measure.m_ticks) { + measure.m_measureTimeSignature = "*M" + tss[k]; + measure.setComplete(); + } + } + } } - for (i=0; i<(int)lyrics.size() && i+start < (int)songdata.size(); i++) { - if ((lyrics[i] == " ") || (lyrics[i] == ".") || (lyrics[i] == "")) { - if (songdata[i+start].pitch < 0) { - lyrics[i] = "%"; - } else { - lyrics[i] = "|"; + // Now check if the measure at the end and beginning + // of the next phrase are both complete. If not then + // calculate partial measure pairs. + for (int i=0; i<(int)size()-1; i++) { + Tool_esac2hum::Phrase& phrase = at(i); + Tool_esac2hum::Phrase& nextphrase = at(i+1); + if (phrase.size() < 2) { + // deal with phrases with a single measure later + continue; + } + if (nextphrase.size() < 2) { + // deal with phrases with a single measure later + continue; + } + Tool_esac2hum::Measure& measure = phrase.back(); + Tool_esac2hum::Measure& nextmeasure = nextphrase.at(0); + + int mticks = measure.m_ticks; + int nmticks = nextmeasure.m_ticks; + + int found1 = -1; + int found2 = -1; + + for (int j=(int)tss.size() - 1; j>=0; j--) { + if (tsticks.at(j) == mticks) { + found1 = j; + } + if (tsticks.at(j) == nmticks) { + found2 = j; } - // lyrics[i] = "."; } - songdata[i+start].text = lyrics[i]; - songdata[i+start].lyricnum = line; - if (line != songdata[i+start].phnum) { - songdata[i+start].lyricerr = 1; // lyric does not line up with music + if ((found1 >= 0) && (found2 >= 0)) { + // The two measures are complete + measure.m_measureTimeSignature = "*M" + tss[found1]; + nextmeasure.m_measureTimeSignature = "*M" + tss[found2]; + measure.setComplete(); + nextmeasure.setComplete(); + } else { + // See if the sum of the two measures match + // a listed time signature. if so, then they + // form two partial measures. + int ticksum = mticks + nmticks; + for (int z=0; z<(int)tsticks.size(); z++) { + if (tsticks.at(z) == ticksum) { + nextmeasure.m_barnum = -1; + measure.m_measureTimeSignature = "*M" + tss.at(z); + nextmeasure.m_measureTimeSignature = "*M" + tss.at(z); + measure.setPartialBegin(); + nextmeasure.setPartialEnd(); + } + } } } - return true; -} + // Check if the first measure is a complete time signature in duration. + // If not then mark as pickup measure. If incomplete and last measure + // is incomplete, then merge into a single measure (partial start for + // last measure and partial end for first measure. + if (empty()) { + // no data + } else if ((size() == 1) && (at(0).size() <= 1)) { + // single measure in melody + } else { + Tool_esac2hum::Measure& firstmeasure = at(0).at(0); + Tool_esac2hum::Measure& lastmeasure = back().back(); + double firstticks = firstmeasure.m_ticks; + double lastticks = lastmeasure.m_ticks; + int foundfirst = -1; + int foundlast = -1; -////////////////////////////// -// -// Tool_esac2hum::printSpecialChars -- print high ASCII character table -// + for (int i=(int)tss.size() - 1; i>=0; i--) { + if (tsticks.at(i) == firstticks) { + foundfirst = i; + } + if (tsticks.at(i) == lastticks) { + foundlast = i; + } + } -void Tool_esac2hum::printSpecialChars(ostream& out) { - int i; - for (i=0; i<(int)chartable.size(); i++) { - if (chartable[i]) { - switch (i) { - case 129: out << "!!!RNB" << ": symbol: ü = u umlaut (UTF-8: " - << (char)0xc3 << (char)0xb3 << ")\n"; break; - case 130: out << "!!!RNB" << ": symbol: é= e acute (UTF-8: " - << (char)0xc3 << (char)0xa9 << ")\n"; break; - case 132: out << "!!!RNB" << ": symbol: ä = a umlaut (UTF-8: " - << (char)0xc3 << (char)0xa4 << ")\n"; break; - case 134: out << "!!!RNB" << ": symbol: $c = c acute (UTF-8: " - << (char)0xc4 << (char)0x87 << ")\n"; break; - case 136: out << "!!!RNB" << ": symbol: $l = l slash (UTF-8: " - << (char)0xc5 << (char)0x82 << ")\n"; break; - case 140: out << "!!!RNB" << ": symbol: î = i circumflex (UTF-8: " - << (char)0xc3 << (char)0xaf << ")\n"; break; - case 141: out << "!!!RNB" << ": symbol: $X = Z acute (UTF-8: " - << (char)0xc5 << (char)0xb9 << ")\n"; break; - case 142: out << "!!!RNB" << ": symbol: ä = a umlaut (UTF-8: " - << (char)0xc3 << (char)0xa4 << ")\n"; break; - case 143: out << "!!!RNB" << ": symbol: $C = C acute (UTF-8: " - << (char)0xc4 << (char)0x86 << ")\n"; break; - case 148: out << "!!!RNB" << ": symbol: ö = o umlaut (UTF-8: " - << (char)0xc3 << (char)0xb6 << ")\n"; break; - case 151: out << "!!!RNB" << ": symbol: $S = S acute (UTF-8: " - << (char)0xc5 << (char)0x9a << ")\n"; break; - case 152: out << "!!!RNB" << ": symbol: $s = s acute (UTF-8: " - << (char)0xc5 << (char)0x9b << ")\n"; break; - case 156: out << "!!!RNB" << ": symbol: $s = s acute (UTF-8: " - << (char)0xc5 << (char)0x9b << ")\n"; break; - case 157: out << "!!!RNB" << ": symbol: $L = L slash (UTF-8: " - << (char)0xc5 << (char)0x81 << ")\n"; break; - case 159: out << "!!!RNB" << ": symbol: $vc = c hachek (UTF-8: " - << (char)0xc4 << (char)0x8d << ")\n"; break; - case 162: out << "!!!RNB" << ": symbol: ó= o acute (UTF-8: " - << (char)0xc3 << (char)0xb3 << ")\n"; break; - case 163: out << "!!!RNB" << ": symbol: ú= u acute (UTF-8: " - << (char)0xc3 << (char)0xba << ")\n"; break; - case 165: out << "!!!RNB" << ": symbol: $a = a hook (UTF-8: " - << (char)0xc4 << (char)0x85 << ")\n"; break; - case 169: out << "!!!RNB" << ": symbol: $e = e hook (UTF-8: " - << (char)0xc4 << (char)0x99 << ")\n"; break; - case 171: out << "!!!RNB" << ": symbol: $y = z acute (UTF-8: " - << (char)0xc5 << (char)0xba << ")\n"; break; - case 175: out << "!!!RNB" << ": symbol: $Z = Z dot (UTF-8: " - << (char)0xc5 << (char)0xbb << ")\n"; break; - case 179: out << "!!!RNB" << ": symbol: $l = l slash (UTF-8: " - << (char)0xc5 << (char)0x82 << ")\n"; break; - case 185: out << "!!!RNB" << ": symbol: $a = a hook (UTF-8: " - << (char)0xc4 << (char)0x85 << ")\n"; break; - case 189: out << "!!!RNB" << ": symbol: $Z = Z dot (UTF-8: " - << (char)0xc5 << (char)0xbb << ")\n"; break; - case 190: out << "!!!RNB" << ": symbol: $z = z dot (UTF-8: " - << (char)0xc5 << (char)0xbc << ")\n"; break; - case 191: out << "!!!RNB" << ": symbol: $z = z dot (UTF-8: " - << (char)0xc5 << (char)0xbc << ")\n"; break; - case 224: out << "!!!RNB" << ": symbol: Ó= O acute (UTF-8: " - << (char)0xc3 << (char)0x93 << ")\n"; break; - case 225: out << "!!!RNB" << ": symbol: ß = sz ligature (UTF-8: " - << (char)0xc3 << (char)0x9f << ")\n"; break; - case 0xdf: out << "!!!RNB" << ": symbol: ß = sz ligature (UTF-8: " - << (char)0xc3 << (char)0x9f << ")\n"; break; -// Polish version: -// case 228: out << "!!!RNB" << ": symbol: $n = n acute (UTF-8: " -// << (char)0xc5 << (char)0x84 << ")\n"; break; -// Luxembourg version for some reason...: - case 228: out << "!!!RNB" << ": symbol: ä = a umlaut (UTF-8: " - << (char)0xc5 << (char)0x84 << ")\n"; break; - case 230: out << "!!!RNB" << ": symbol: c = c\n"; break; - case 231: out << "!!!RNB" << ": symbol: $vs = s hachek (UTF-8: " - << (char)0xc5 << (char)0xa1 << ")\n"; break; - case 234: out << "!!!RNB" << ": symbol: $e = e hook (UTF-8: " - << (char)0xc4 << (char)0x99 << ")\n"; break; - case 241: out << "!!!RNB" << ": symbol: $n = n acute (UTF-8: " - << (char)0xc5 << (char)0x84 << ")\n"; break; - case 243: out << "!!!RNB" << ": symbol: ó= o acute (UTF-8: " - << (char)0xc3 << (char)0xb3 << ")\n"; break; - case 252: out << "!!!RNB" << ": symbol: ü = u umlaut (UTF-8: " - << (char)0xc3 << (char)0xbc << ")\n"; break; -// default: + if ((foundfirst >= 0) && (foundlast >= 0)) { + // first and last measures are both complete + firstmeasure.m_measureTimeSignature = "*M" + tss.at(foundfirst); + lastmeasure.m_measureTimeSignature = "*M" + tss.at(foundlast); + firstmeasure.setComplete(); + lastmeasure.setComplete(); + } else { + // if both sum to a time signature than assigned that time signature to both + double sumticks = firstticks + lastticks; + int sumfound = -1; + for (int i=0; i<(int)tsticks.size(); i++) { + if (tsticks[i] == sumticks) { + sumfound = i; + break; + } + } + if (sumfound >= 0) { + // First and last meatures match a time signture, so + // use that time signture for both, mark firt measure + // last pickup (barnum -> 0), and mark last as partial + // measure start + firstmeasure.m_measureTimeSignature = "*M" + tss.at(sumfound); + lastmeasure.m_measureTimeSignature = "*M" + tss.at(sumfound); + firstmeasure.m_barnum = 0; + firstmeasure.setPartialEnd(); + lastmeasure.setPartialBegin(); + } else if ((foundfirst >= 0) && (foundlast < 0)) { + firstmeasure.setComplete(); + lastmeasure.setPartialBegin(); + } else if ((foundfirst < 0) && (foundlast >= 0)) { + firstmeasure.setPartialEnd(); + lastmeasure.setComplete(); + } + } + } + + + // Now assign bar numbers + // First probalby check for pairs of uncategorized measure durations (deal with that later). + vector measurelist; + getMeasureList(measurelist); + int barnum = 1; + for (int i=0; i<(int)measurelist.size(); i++) { + if ((i == 0) && measurelist.at(i)->isPartialEnd()) { + measurelist.at(i)->m_barnum = 0; + continue; } + if (measurelist.at(i)->isComplete()) { + measurelist.at(i)->m_barnum = barnum++; + } else if (measurelist.at(i)->isPartialBegin()) { + measurelist.at(i)->m_barnum = barnum++; + } else if (measurelist.at(i)->isPartialEnd()) { + measurelist.at(i)->m_barnum = -1; + } else { + measurelist.at(i)->m_errors.push_back("UNCATEGORIZED MEASURE"); } - chartable[i] = 0; } + + // Now remove duplicate time signatures + string current = ""; + for (int i=0; i<(int)measurelist.size(); i++) { + if (measurelist.at(i)->m_measureTimeSignature == current) { + measurelist.at(i)->m_measureTimeSignature = ""; + } else { + current = measurelist.at(i)->m_measureTimeSignature; + } + } + } ////////////////////////////// // -// Tool_esac2hum::printTitleInfo -- print the first line of the CUT[] field. +// Tool_esac2hum::Score::setAllTimeSigTicks -- Used for calculating bar numbers; // -bool Tool_esac2hum::printTitleInfo(vector& song, ostream& out) { - int start = -1; - int stop = -1; - getLineRange(song, "CUT", start, stop); - if (start == -1) { - cerr << "Error: cannot find CUT[] field in song: " << song[0] << endl; - return false; - } - - string buffer; - buffer = song[start].substr(4); - if (buffer.back() == ']') { - buffer.resize((int)buffer.size() - 1); - } +void Tool_esac2hum::Score::setAllTimesigTicks(double ticks) { + vector measurelist; + getMeasureList(measurelist); - out << "!!!OTL: "; - for (int i=0; i<(int)buffer.size(); i++) { - printChar(buffer[i], out); + for (int i=0; i<(int)measurelist.size(); i++) { + measurelist[i]->m_tsticks = ticks; } - out << "\n"; - - return true; } ////////////////////////////// // -// Tool_esac2hum::printChar -- print text characters, translating high-bit data -// if required. +// Tool_esac2hum::Score::calculateKeyInformation -- // -void Tool_esac2hum::printChar(unsigned char c, ostream& out) { - out << c; -/* - if (c < 128) { - out << c; - } else { - chartable[c]++; - switch (c) { - case 129: out << "ü"; break; - case 130: out << "é"; break; - case 132: out << "ä"; break; - case 134: out << "$c"; break; - case 136: out << "$l"; break; - case 140: out << "î"; break; - case 141: out << "$X"; break; // Z acute - case 142: out << "ä"; break; // ? - case 143: out << "$C"; break; - case 148: out << "ö"; break; - case 151: out << "$S"; break; - case 152: out << "$s"; break; - case 156: out << "$s"; break; // 1250 encoding - case 157: out << "$L"; break; - case 159: out << "$vc"; break; // Cech c with v accent - case 162: out << "ó"; break; - case 163: out << "ú"; break; - case 165: out << "$a"; break; - case 169: out << "$e"; break; - case 171: out << "$y"; break; - case 175: out << "$Z"; break; // 1250 encoding - case 179: out << "$l"; break; // 1250 encoding - case 185: out << "$a"; break; // 1250 encoding - case 189: out << "$Z"; break; // Z dot - case 190: out << "$z"; break; // z dot - case 191: out << "$z"; break; // 1250 encoding - case 224: out << "Ó"; break; - case 225: out << "ß"; break; - case 0xdf: out << "ß"; break; - // Polish version: - // case 228: out << "$n"; break; - // Luxembourg version (for some reason...) - case 228: out << "ä"; break; - case 230: out << "c"; break; // ? - case 231: out << "$vs"; break; // Cech s with v accent - case 234: out << "$e"; break; // 1250 encoding - case 241: out << "$n"; break; // 1250 encoding - case 243: out << "ó"; break; // 1250 encoding - case 252: out << "ü"; break; - default: out << c; +void Tool_esac2hum::Score::calculateKeyInformation(void) { + vector notelist; + getNoteList(notelist); + + vector b40pcs(40, 0); + for (int i=0; i<(int)notelist.size(); i++) { + int pc = notelist[i]->m_b40degree; + if ((pc >= 0) && (pc < 40)) { + b40pcs.at(pc)++; + } + } + + string tonic = m_params["_tonic"]; + if (tonic.empty()) { + // no tonic for some strange reason + // error will be reported when calculating Humdrum pitches. + return; + } + char letter = std::toupper(tonic[0]); + + // Compare counts of third and sixth pitch classes: + int majorsum = b40pcs.at(12) + b40pcs.at(29); + int minorsum = b40pcs.at(11) + b40pcs.at(28); + if (minorsum > majorsum) { + letter = std::tolower(letter); + } + string flats; + string sharps; + for (int i=1; i<(int)tonic.size(); i++) { + if (tonic[i] == 'b') { + flats += "-"; + } else if (tonic[i] == '#') { + sharps += "#"; + } + } + + m_keydesignation = "*"; + m_keydesignation += letter; + + if (!flats.empty() && !sharps.empty()) { + m_errors.push_back("ERROR: tonic note cannot include both sharps and flats."); + } + if (!flats.empty()) { + m_keydesignation += flats; + } else { + m_keydesignation += sharps; + } + m_keydesignation += ":"; + + if (std::isupper(letter)) { + + // major key signature + if (m_keydesignation == "*C:") { + m_keysignature = "*k[]"; + } else if (m_keydesignation == "*G:") { + m_keysignature = "*k[f#]"; + } else if (m_keydesignation == "*D:") { + m_keysignature = "*k[f#c#]"; + } else if (m_keydesignation == "*A:") { + m_keysignature = "*k[f#c#g#]"; + } else if (m_keydesignation == "*E:") { + m_keysignature = "*k[f#c#g#d#]"; + } else if (m_keydesignation == "*B:") { + m_keysignature = "*k[f#c#g#d#a#]"; + } else if (m_keydesignation == "*F#:") { + m_keysignature = "*k[f#c#g#d#a#e#]"; + } else if (m_keydesignation == "*C#:") { + m_keysignature = "*k[f#c#g#d#a#e#b#]"; + } else if (m_keydesignation == "*F:") { + m_keysignature = "*k[b-]"; + } else if (m_keydesignation == "*B-:") { + m_keysignature = "*k[b-e-]"; + } else if (m_keydesignation == "*E-:") { + m_keysignature = "*k[b-e-a-]"; + } else if (m_keydesignation == "*A-:") { + m_keysignature = "*k[b-e-a-d-]"; + } else if (m_keydesignation == "*D-:") { + m_keysignature = "*k[b-e-a-d-g-]"; + } else if (m_keydesignation == "*G-:") { + m_keysignature = "*k[b-e-a-d-g-c-]"; + } else if (m_keydesignation == "*C-:") { + m_keysignature = "*k[b-e-a-d-g-f-]"; + } else { + m_errors.push_back("ERROR: invalid/exotic key signature required."); + } + + } else { + + // minor key signature + if (m_keydesignation == "*a:") { + m_keysignature = "*k[]"; + } else if (m_keydesignation == "*e:") { + m_keysignature = "*k[f#]"; + } else if (m_keydesignation == "*b:") { + m_keysignature = "*k[f#c#]"; + } else if (m_keydesignation == "*f#:") { + m_keysignature = "*k[f#c#g#]"; + } else if (m_keydesignation == "*c#:") { + m_keysignature = "*k[f#c#g#d#]"; + } else if (m_keydesignation == "*g#:") { + m_keysignature = "*k[f#c#g#d#a#]"; + } else if (m_keydesignation == "*d#:") { + m_keysignature = "*k[f#c#g#d#a#e#]"; + } else if (m_keydesignation == "*a#:") { + m_keysignature = "*k[f#c#g#d#a#e#b#]"; + } else if (m_keydesignation == "*d:") { + m_keysignature = "*k[b-]"; + } else if (m_keydesignation == "*g:") { + m_keysignature = "*k[b-e-]"; + } else if (m_keydesignation == "*c:") { + m_keysignature = "*k[b-e-a-]"; + } else if (m_keydesignation == "*f:") { + m_keysignature = "*k[b-e-a-d-]"; + } else if (m_keydesignation == "*b-:") { + m_keysignature = "*k[b-e-a-d-g-]"; + } else if (m_keydesignation == "*e-:") { + m_keysignature = "*k[b-e-a-d-g-c-]"; + } else if (m_keydesignation == "*a-:") { + m_keysignature = "*k[b-e-a-d-g-c-f-]"; + } else { + m_errors.push_back("ERROR: invalid/exotic key signature required."); } } -*/ + } ////////////////////////////// // -// Tool_esac2hum::printKeyInfo -- +// Tool_esac2hum::Score::calculateClef -- // -void Tool_esac2hum::printKeyInfo(vector& songdata, int tonic, int textQ, - ostream& out) { - vector pitches(40, 0); - int pitchsum = 0; - int pitchcount = 0; - int i; - for (i=0; i<(int)songdata.size(); i++) { - if (songdata[i].pitch >= 0) { - pitches[songdata[i].pitch % 40]++; - pitchsum += Convert::base40ToMidiNoteNumber(songdata[i].pitch); - pitchcount++; +void Tool_esac2hum::Score::calculateClef(void) { + vector notelist; + getNoteList(notelist); + + double sum = 0; + double count = 0; + int min12 = 1000; + int max12 = -1000; + + for (int i=0; i<(int)notelist.size(); i++) { + int b12 = notelist[i]->m_b12; + if (b12 > 0) { + sum += b12; + count++; + if (b12 < min12) { + min12 = b12; + } + if (b12 > max12) { + max12 = b12; + } } } + double average = sum / count; - // generate a clef, choosing either treble or bass clef depending - // on the average pitch. - double averagepitch = pitchsum * 1.0 / pitchcount; - if (averagepitch > 60.0) { - out << "*clefG2"; - if (textQ) { - out << "\t*clefG2"; - } - out << "\n"; + + if ((min12 > 54) && (average >= 60.0)) { + m_clef = "*clefG2"; + } else if ((max12 < 67) && (average < 60.0)) { + m_clef = "*clefF4"; + } else if ((min12 > 47) && (min12 <= 57) && (max12 < 77) && (max12 >= 65)) { + m_clef = "*clefGv2"; + } else if (average < 60.0) { + m_clef = "*clefF2"; } else { - out << "*clefF4"; - if (textQ) { - out << "\t*clefF4"; - } - out << "\n"; + m_clef = "*clefG2"; } +} - // generate a key signature - vector diatonic(7, 0); - diatonic[0] = getAccidentalMax(pitches[1], pitches[2], pitches[3]); - diatonic[1] = getAccidentalMax(pitches[7], pitches[8], pitches[9]); - diatonic[2] = getAccidentalMax(pitches[13], pitches[14], pitches[15]); - diatonic[3] = getAccidentalMax(pitches[18], pitches[19], pitches[20]); - diatonic[4] = getAccidentalMax(pitches[24], pitches[25], pitches[26]); - diatonic[5] = getAccidentalMax(pitches[30], pitches[31], pitches[32]); - diatonic[6] = getAccidentalMax(pitches[36], pitches[37], pitches[38]); - int flatcount = 0; - int sharpcount = 0; - int naturalcount = 0; - for (i=0; i<7; i++) { - switch (diatonic[i]) { - case -1: flatcount++; break; - case 0: naturalcount++; break; - case +1: sharpcount++; break; + +////////////////////////////// +// +// Tool_esac2hum::generateHumdrumNotes -- +// + +void Tool_esac2hum::Score::generateHumdrumNotes(void) { + vector notelist; + getNoteList(notelist); + + string tonic = m_params["_tonic"]; + if (tonic.empty()) { + string error = "Error: cannot find tonic pitch in KEY[] field: "; + error += m_params["KEY"]; + m_errors.push_back(error); + return; + } + char letter = std::tolower(tonic[0]); + m_b40tonic = 40 * 4 + 2; // start with middle C + switch (letter) { + case 'd': m_b40tonic += 6; break; + case 'e': m_b40tonic += 12; break; + case 'f': m_b40tonic += 17; break; + case 'g': m_b40tonic += 23; break; + case 'a': m_b40tonic += 29; break; + case 'b': m_b40tonic += 35; break; + } + int flats = 0; + int sharps = 0; + for (int i=1; i<(int)tonic.size(); i++) { + if (tonic[i] == 'b') { + flats++; + } else if (tonic[i] == '#') { + sharps++; } } + if (flats > 0) { + m_b40tonic -= flats; + } else if (sharps > 0) { + m_b40tonic += sharps; + } - char kbuf[32] = {0}; - if (naturalcount == 7) { - // do nothing - } else if (flatcount > sharpcount) { - // print a flat key signature - if (diatonic[6] == -1) strcat(kbuf, "b-"); else goto keysigend; - if (diatonic[2] == -1) strcat(kbuf, "e-"); else goto keysigend; - if (diatonic[5] == -1) strcat(kbuf, "a-"); else goto keysigend; - if (diatonic[1] == -1) strcat(kbuf, "d-"); else goto keysigend; - if (diatonic[4] == -1) strcat(kbuf, "g-"); else goto keysigend; - if (diatonic[0] == -1) strcat(kbuf, "c-"); else goto keysigend; - if (diatonic[3] == -1) strcat(kbuf, "f-"); else goto keysigend; - } else { - // print a sharp key signature - if (diatonic[3] == +1) strcat(kbuf, "f#"); else goto keysigend; - if (diatonic[0] == +1) strcat(kbuf, "c#"); else goto keysigend; - if (diatonic[4] == +1) strcat(kbuf, "g#"); else goto keysigend; - if (diatonic[1] == +1) strcat(kbuf, "d#"); else goto keysigend; - if (diatonic[5] == +1) strcat(kbuf, "a#"); else goto keysigend; - if (diatonic[2] == +1) strcat(kbuf, "e#"); else goto keysigend; - if (diatonic[6] == +1) strcat(kbuf, "b#"); else goto keysigend; + string minrhy = m_params["_minrhy"]; + if (minrhy.empty()) { + m_errors.push_back("Error: cannot find KEY[] minrhy"); + return; } -keysigend: - out << "*k[" << kbuf << "]"; - if (textQ) { - out << "\t*k[" << kbuf << "]"; + m_minrhy = std::stoi(minrhy); + // maybe check of power of two? + + for (int i=0; i<(int)notelist.size(); i++) { + notelist.at(i)->generateHumdrum(m_minrhy, m_b40tonic); } - out << "\n"; - // look at the third scale degree above the tonic pitch - int minor = pitches[(tonic + 40 + 11) % 40]; - int major = pitches[(tonic + 40 + 12) % 40]; +} - if (minor > major) { - // minor key (or related mode) - out << "*" << Convert::base40ToKern(40 * 4 + tonic) << ":"; - if (textQ) { - out << "\t*" << Convert::base40ToKern(40 * 4 + tonic) << ":"; + + +////////////////////////////// +// +// Tool_esac2hum::Note::generateHumdrum -- convert EsAC note to Humdrum note token. +// + +void Tool_esac2hum::Note::generateHumdrum(int minrhy, int b40tonic) { + string pitch; + if (m_degree != 0) { + m_b40degree = 0; + switch (abs(m_degree)) { + case 2: m_b40degree += 6; break; + case 3: m_b40degree += 12; break; + case 4: m_b40degree += 17; break; + case 5: m_b40degree += 23; break; + case 6: m_b40degree += 29; break; + case 7: m_b40degree += 35; break; + } + if ((m_alter >= -2) && (m_alter <= 2)) { + m_b40degree += m_alter; + } else { + m_errors.push_back("Error: chromatic alteration on note too large"); } - out << "\n"; + m_b40 = 40 * m_octave + m_b40degree + b40tonic; + pitch = Convert::base40ToKern(m_b40); + // m_b12 is used for calculating clef later on. + m_b12 = Convert::base40ToMidiNoteNumber(m_b40); } else { - // major key (or related mode) - out << "*" << Convert::base40ToKern(40 * 3 + tonic) << ":"; - if (textQ) { - out << "\t*" << Convert::base40ToKern(40 * 3 + tonic) << ":"; - } - out << "\n"; + pitch = "r"; + m_b40 = -1000; + m_b40degree = -1000; + } + + HumNum duration(1, minrhy); + int multiplier = (1 << m_underscores); + duration *= multiplier; + duration *= 4; // convert from whole notes to quarter notes + duration *= m_factor; + string recip = Convert::durationToRecip(duration); + for (int i=0; i b && a > c) { - return -1; - } else if (c > a && c > b) { - return +1; - } else { - return 0; +void Tool_esac2hum::Score::analyzeTies(void) { + vector notelist; + getNoteList(notelist); + + for (int i=1; i<(int)notelist.size(); i++) { + // negative m_degree indicates a tied note to previous note + if (notelist.at(i)->m_degree < 0) { + // Tied note, so link to previous note. + notelist.at(i)->m_tieEnd = true; + notelist.at(i-1)->m_tieBegin = true; + if (notelist.at(i-1)->m_degree >= 0) { + notelist.at(i)->m_degree = -notelist.at(i-1)->m_degree; + // Copy chromatic alteration and octave: + notelist[i]->m_alter = notelist.at(i-1)->m_alter; + notelist[i]->m_octave = notelist.at(i-1)->m_octave; + } + } } } + ////////////////////////////// // -// Tool_esac2hum::postProcessSongData -- clean up data and do some interpreting. +// Tool_esac2hum::Score::getNoteList -- Return a list of all notes +// in the score. // -void Tool_esac2hum::postProcessSongData(vector& songdata, vector& numerator, - vector& denominator) { - int i, j; - // move phrase start markers off of rests and onto the - // first note that it finds - for (i=0; i<(int)songdata.size()-1; i++) { - if (songdata[i].pitch < 0 && songdata[i].phstart) { - songdata[i+1].phstart = songdata[i].phstart; - songdata[i].phstart = 0; +void Tool_esac2hum::Score::getNoteList(vector& notelist) { + notelist.clear(); + for (int i=0; i<(int)size(); i++) { + Tool_esac2hum::Phrase& phrase = at(i); + for (int j=0; j<(int)phrase.size(); j++) { + Tool_esac2hum::Measure& measure = phrase[j]; + for (int k=0; k<(int)measure.size(); k++) { + notelist.push_back(&measure.at(k)); + } } } +} - // move phrase ending markers off of rests and onto the - // previous note that it finds - for (i=(int)songdata.size()-1; i>0; i--) { - if (songdata[i].pitch < 0 && songdata[i].phend) { - songdata[i-1].phend = songdata[i].phend; - songdata[i].phend = 0; + + +////////////////////////////// +// +// Tool_esac2hum::Score::getMeasureList -- +// + +void Tool_esac2hum::Score::getMeasureList(vector& measurelist) { + measurelist.clear(); + for (int i=0; i<(int)size(); i++) { + Tool_esac2hum::Phrase& phrase = at(i); + for (int j=0; j<(int)phrase.size(); j++) { + Tool_esac2hum::Measure& measure = phrase[j]; + measurelist.push_back(&measure); } } +} - // examine barline information - double dur = 0.0; - for (i=(int)songdata.size()-1; i>=0; i--) { - if (songdata[i].bar == 1) { - songdata[i].bardur = dur; - dur = songdata[i].duration; - } else { - dur += songdata[i].duration; - } - } - - int barnum = 0; - double firstdur = 0.0; - if (numerator.size() == 1 && numerator[0] > 0) { - // handle single non-frei meter - songdata[0].num = numerator[0]; - songdata[0].denom = denominator[0]; - dur = 0; - double meterdur = 4.0 / denominator[0] * numerator[0]; - for (i=0; i<(int)songdata.size(); i++) { - if (songdata[i].bar) { - dur = 0.0; - } else { - dur += songdata[i].duration; - if (fabs(dur - meterdur) < 0.001) { - songdata[i].bar = 1; - songdata[i].barinterp = 1; - dur = 0.0; - } - } - } - - // readjust measure beat counts - dur = 0.0; - for (i=(int)songdata.size()-1; i>=0; i--) { - if (songdata[i].bar == 1) { - songdata[i].bardur = dur; - dur = songdata[i].duration; - } else { - dur += songdata[i].duration; - } - } - firstdur = dur; - - // number the barlines - barnum = 0; - if (fabs(firstdur - meterdur) < 0.001) { - // music for first bar, next bar will be bar 2 - barnum = 2; - } else { - barnum = 1; - // pickup-measure - } - for (i=0; i<(int)songdata.size(); i++) { - if (songdata[i].bar == 1) { - songdata[i].barnum = barnum++; - } - } - - } else if (numerator.size() == 1 && numerator[0] == -1) { - // handle free meter - // number the barline - firstdur = dur; - barnum = 1; - for (i=0; i<(int)songdata.size(); i++) { - if (songdata[i].bar == 1) { - songdata[i].barnum = barnum++; - } - } - } else { - // handle multiple time signatures +////////////////////////////// +// +// Tool_esac2hum::Score::analyzePhrases -- Create a list of notes in the score +// and then search for ^ (-1 degrees) which mean a tied continuation +// of the previous note. +// - // get the duration of each type of meter: - vector meterdurs; - meterdurs.resize(numerator.size()); - for (i=0; i<(int)meterdurs.size(); i++) { - meterdurs[i] = 4.0 / denominator[i] * numerator[i]; - } +void Tool_esac2hum::Score::analyzePhrases(void) { + // first create a list of the notes in the score + vector notelist; + for (int i=0; i<(int)size(); i++) { + getPhraseNoteList(notelist, i); - // measure beat counts: - dur = 0.0; - for (i=(int)songdata.size()-1; i>=0; i--) { - if (songdata[i].bar == 1) { - songdata[i].bardur = dur; - dur = songdata[i].duration; - } else { - dur += songdata[i].duration; - } + if (notelist.empty()) { + at(i).m_errors.push_back("ERROR: no notes in phrase."); + return; } - firstdur = dur; - // interpret missing barlines - int currentmeter = 0; - // find first meter - for (i=0; i<(int)numerator.size(); i++) { - if (fabs(firstdur - meterdurs[i]) < 0.001) { - songdata[0].num = numerator[i]; - songdata[0].denom = denominator[i]; - currentmeter = i; + // Find the first non-rest note and mark with phrase start: + bool foundNote = false; + for (int j=0; j<(int)notelist.size(); j++) { + if (notelist.at(j)->m_degree <= 0) { + continue; } + foundNote = true; + notelist.at(j)->m_phraseBegin = true; + break; } - // now handle the meters in the rest of the music... - int fnd = 0; - dur = 0; - for (i=0; i<(int)songdata.size()-1; i++) { - if (songdata[i].bar) { - if (songdata[i].bardur != meterdurs[currentmeter]) { - // try to find the correct new meter - fnd = 0; - for (j=0; j<(int)numerator.size(); j++) { - if (j == currentmeter) { - continue; - } - if (fabs(songdata[i].bardur - meterdurs[j]) < 0.001) { - songdata[i+1].num = numerator[j]; - songdata[i+1].denom = denominator[j]; - currentmeter = j; - fnd = 1; - } - } - if (!fnd) { - for (j=0; j<(int)numerator.size(); j++) { - if (j == currentmeter) { - continue; - } - if (fabs(songdata[i].bardur/2.0 - meterdurs[j]) < 0.001) { - songdata[i+1].num = numerator[j]; - songdata[i+1].denom = denominator[j]; - currentmeter = j; - fnd = 1; - } - } - } - } - dur = 0.0; - } else { - dur += songdata[i].duration; - if (fabs(dur - meterdurs[currentmeter]) < 0.001) { - songdata[i].bar = 1; - songdata[i].barinterp = 1; - dur = 0.0; - } - } + if (!foundNote) { + at(i).m_errors.push_back("Error: cannot find any notes in phrase."); + continue; } - // perhaps sum duration of measures again and search for error here? - - // finally, number the barlines: - barnum = 1; - for (i=0; i<(int)numerator.size(); i++) { - if (fabs(firstdur - meterdurs[i]) < 0.001) { - barnum = 2; - break; - } - } - for (i=0; i<(int)songdata.size(); i++) { - if (songdata[i].bar == 1) { - songdata[i].barnum = barnum++; + // Find the last non-rest note and mark with phrase end: + for (int j=(int)notelist.size()-1; j>=0; j--) { + if (notelist.at(j)->m_degree <= 0) { + continue; } + notelist.at(j)->m_phraseEnd = true; + break; } - - } - } - ////////////////////////////// // -// Tool_esac2hum::getMeterInfo -- +// Tool_esac2hum::Score::getPhraseNoteList -- Return a list of all notes +// in the 0-indexed phrase // -void Tool_esac2hum::getMeterInfo(string& meter, vector& numerator, - vector& denominator) { - numerator.clear(); - denominator.clear(); - HumRegex hre; - hre.replaceDestructive(meter, "", "^\\s+"); - hre.replaceDestructive(meter, "", "\\s+$"); - if (hre.search(meter, "^(\\d+)/(\\d+)$")) { - numerator.push_back(hre.getMatchInt(1)); - denominator.push_back(hre.getMatchInt(2)); +void Tool_esac2hum::Score::getPhraseNoteList(vector& notelist, int index) { + notelist.clear(); + if (index < 0) { + m_errors.push_back("ERROR: trying to access a negative phrase index"); return; } - if (hre.search(meter, "^frei$", "i")) { - numerator.push_back(-1); - denominator.push_back(-1); + if (index >= (int)size()) { + m_errors.push_back("ERROR: trying to access a phrase index that is too large"); return; } - cerr << "NEED TO DEAL WITH METER: " << meter << endl; + Tool_esac2hum::Phrase& phrase = at(index); + + for (int i=0; i<(int)phrase.size(); i++) { + Tool_esac2hum::Measure& measure = phrase[i]; + for (int j=0; j<(int)measure.size(); j++) { + Tool_esac2hum::Note& note = measure.at(j); + notelist.push_back(¬e); + } + } } ////////////////////////////// // -// Tool_esac2hum::getLineRange -- get the staring line and ending line of a data -// field. Returns -1 if the data field was not found. +// Tool_esac2hum::Phrase::getNoteList -- Return a list of all notes +// in the phrase. // -void Tool_esac2hum::getLineRange(vector& song, const string& field, - int& start, int& stop) { - string searchstring = field;; - searchstring += "["; - start = stop = -1; - for (int i=0; i<(int)song.size(); i++) { - auto loc = song[i].find(']'); - if (song[i].compare(0, searchstring.size(), searchstring) == 0) { - start = i; - if (loc != string::npos) { - stop = i; - break; - } - } else if ((start >= 0) && (loc != string::npos)) { - stop = i; - break; +void Tool_esac2hum::Phrase::getNoteList(vector& notelist) { + notelist.clear(); + Tool_esac2hum::Phrase& phrase = *this; + + for (int i=0; i<(int)phrase.size(); i++) { + Tool_esac2hum::Measure& measure = phrase[i]; + for (int j=0; j<(int)measure.size(); j++) { + Tool_esac2hum::Note& note = measure.at(j); + notelist.push_back(¬e); } } } @@ -79322,172 +79717,117 @@ void Tool_esac2hum::getLineRange(vector& song, const string& field, ////////////////////////////// // -// Tool_esac2hum::getNoteList -- get a list of the notes and rests and barlines in -// the MEL field. +// Tool_esac2hum::Phrase::parsePhrase -- // -bool Tool_esac2hum::getNoteList(vector& song, vector& songdata, double mindur, - int tonic) { - songdata.resize(0); - NoteData tempnote; - int melstart = -1; - int melstop = -1; - int i, j; - int octave = 0; - int degree = 0; - int accidental = 0; - double duration = mindur; - int bar = 0; - // int tuplet = 0; - int major[8] = {-1, 0, 6, 12, 17, 23, 29, 35}; - // int oldstate = -1; - int state = -1; - int nextstate = -1; - int phend = 0; - int phnum = 0; - int phstart = 0; - int slend = 0; - int slstart = 0; - int tie = 0; +bool Tool_esac2hum::Phrase::parsePhrase(const string& phrase) { + esac = phrase; - getLineRange(song, "MEL", melstart, melstop); + vector bars; - for (i=melstart; i<=melstop; i++) { - if (song[i].size() < 4) { - cerr << "Error: invalid line in MEL[]: " << song[i] << endl; - return false; - } - j = 4; - phstart = 1; - phend = 0; - // Note Format: (+|-)*[0..7]_*\.*( )? - // ONADB - // Order of data: Octave, Note, Accidental, Duration, Barline + HumRegex hre; + string newphrase = phrase; + newphrase = trimSpaces(newphrase); + hre.split(bars, newphrase, "\\s+"); + if (bars.empty()) { + cerr << "Funny error with no measures" << endl; + return false; + } + int length = (int)bars.size(); + for (int i=0; i tokens; + vector factors; + HumNum factor = 1; + int length = (int)measure.size(); + for (int i=0; i': break; // unknown marker -// case '<': break; // - case '^': tie = 1; state = STATE_NOTE; break; - default : cerr << "Error: unknown character " << song[i][j] - << " on the line: " << song[i] << endl; - return false; - } - j++; - switch (song[i][j]) { - case '-': case '+': nextstate = STATE_OCTAVE; break; - case 'O': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': nextstate = STATE_NOTE; break; - case 'b': case '#': nextstate = STATE_ACC; break; - case '_': case '.': nextstate = STATE_DUR; break; - case '{': nextstate = STATE_SLSTART; break; - case '}': nextstate = STATE_SLEND; break; - case '^': nextstate = STATE_NOTE; break; - case ' ': - if (song[i][j+1] == ' ') nextstate = STATE_BAR; - else if (song[i][j+1] == '/') nextstate = -2; - break; - case '\0': - phend = 1; - break; - default: nextstate = -1; - } + bool marker = false; + if (std::isdigit(measure[i])) { + marker = true; + } else if (measure[i] == '^') { // tie placeholder for degree digit + marker = true; + } else if (measure[i] == '(') { // tuplet start + marker = true; + } else if (measure[i] == '-') { // octave lower + marker = true; + } else if (measure[i] == '+') { // octave higher + marker = true; + } - if (nextstate < state || - ((nextstate == STATE_NOTE) && (state == nextstate))) { - tempnote.clear(); - if (degree < 0) { // rest - tempnote.pitch = -999; - } else { - tempnote.pitch = degree + 40*(octave + 4) + accidental + tonic; - } - if (tie) { - tempnote.pitch = songdata[(int)songdata.size()-1].pitch; - if (songdata[(int)songdata.size()-1].tieend) { - songdata[(int)songdata.size()-1].tiecont = 1; - songdata[(int)songdata.size()-1].tieend = 0; - } else { - songdata[(int)songdata.size()-1].tiestart = 1; - } - tempnote.tieend = 1; - } - tempnote.duration = duration; - tempnote.phend = phend; - tempnote.bar = bar; - tempnote.phstart = phstart; - tempnote.slstart = slstart; - tempnote.slend = slend; - if (nextstate == -2) { - tempnote.bar = 2; - tempnote.phend = 1; - } - tempnote.phnum = phnum; + if (marker && !tokens.empty() && !tokens.back().empty()) { + char checkChar = tokens.back().back(); + if (checkChar == '(') { + marker = false; + } else if (checkChar == '-') { + marker = false; + } else if (checkChar == '+') { + marker = false; + } + } - songdata.push_back(tempnote); - duration = mindur; - degree = 0; - bar = 0; - tie = 0; - phend = 0; - phstart = 0; - slend = 0; - slstart = 0; - octave = 0; - accidental = 0; - if (nextstate == -2) { - return true; - } + if (marker) { + tokens.resize(tokens.size() + 1); + tokens.back() += measure[i]; + factors.resize(factors.size() + 1); + factors.back() = factor; + } else { + if (!tokens.empty()) { + tokens.back() += measure[i]; + } else { + cerr << "!!ERROR: unknown character at start of measure: " << measure << endl; } } - phnum++; + + if (measure[i] == ')') { + factor = 1; + } + } + + if (tokens.empty()) { + cerr << "!!ERROR: In measure: " << measure << ": no notes to parts." << endl; + return false; + } + + for (int i=0; i<(int)tokens.size(); i++) { + resize(size() + 1); + back().parseNote(tokens[i], factors[i]); + } + + // Calculate ticks for measure: + m_ticks = 0; + for (int i=0; i<(int)size(); i++) { + m_ticks += at(i).m_ticks; } return true; @@ -79497,1068 +79837,1420 @@ bool Tool_esac2hum::getNoteList(vector& song, vector& songdata ////////////////////////////// // -// Tool_esac2hum::printNoteData -- +// Tool_esac2hum::Note::parseNote -- // -void Tool_esac2hum::printNoteData(NoteData& data, int textQ, ostream& out) { +bool Tool_esac2hum::Note::parseNote(const string& note, HumNum factor) { + esac = note; - if (data.num > 0) { - out << "*M" << data.num << "/" << data.denom; - if (textQ) { - out << "\t*M" << data.num << "/" << data.denom; + int minus = 0; + int plus = 0; + int b = 0; + int s = 0; + m_degree = 0; + m_dots = 0; + + for (int i=0; i<(int)note.size(); i++) { + if (note[i] == '.') { // augmentation dot + m_dots++; + } else if (note[i] == '_') { // duration modifier + m_underscores++; + } else if (note[i] == '-') { // lower octave + minus++; + } else if (note[i] == '+') { // upper octave + plus++; + } else if (note[i] == 'b') { // flat + b++; + } else if (note[i] == '#') { // sharp + s++; + } else if (isdigit(note[i])) { + m_degree = note[i] - '0'; + } else if (note[i] == '^') { // tied to previous note + m_degree = -1000; } - out << "\n"; } - if (data.phstart == 1) { - out << "{"; + + m_ticks = 1 << m_underscores; + if (m_dots > 0) { + m_ticks = m_ticks * (2.0 - 1.0/(1 << m_dots)); } - if (data.slstart == 1) { - out << "("; + + if (b > 2) { + cerr << "!! ERROR: more than double flat not parseable, note: " << esac << endl; } - if (data.tiestart == 1) { - out << "["; + if (s > 2) { + cerr << "!! ERROR: more than double sharp not parseable, note: " << esac << endl; } - out << Convert::durationFloatToRecip(data.duration); - if (data.pitch < 0) { - out << "r"; - } else { - out << Convert::base40ToKern(data.pitch); + + m_alter = s - b; + m_octave = plus - minus; + + m_factor = factor; + + return true; +} + + + +////////////////////////////// +// +// Tool_esac2hum::printHeader -- +// + +void Tool_esac2hum::printHeader(ostream& output) { + string filename = createFilename(); + output << "!!!!SEGMENT: " << filename << endl; + + string title = m_score.m_params["_title"]; + output << "!!!OTL:"; + if (!title.empty()) { + output << " " << title; } - if (data.tiecont == 1) { - out << "_"; + output << endl; + // sometimes CUT[] has two lines, and the sescond is the text incipit: + string incipit = m_score.m_params["_incipit"]; + if (!incipit.empty()) { + output << "!!!TIN: " << incipit << endl; } - if (data.tieend == 1) { - out << "]"; + + string source = m_score.m_params["_source"]; + output << "!!!source:"; + if (!source.empty()) { + output << " " << source; } - if (data.slend == 1) { - out << ")"; + output << endl; + + string id = m_score.m_params["_id"]; + output << "!!!id:"; + if (!id.empty()) { + output << " " << id; } - if (data.phend == 1) { - out << "}"; + output << endl; + + string signature = m_score.m_params["SIG"]; + output << "!!!signature:"; + if (!signature.empty()) { + output << " " << signature; } + output << endl; - if (textQ) { - out << "\t"; - if (data.phstart == 1) { - out << "{"; - } - if (data.text == "") { - if (data.pitch < 0) { - data.text = "%"; - } else { - data.text = "|"; - } - } - if (data.pitch < 0 && (data.text.find('%') == string::npos)) { - out << "%"; - } - if (data.text == " *") { - if (data.pitch < 0) { - data.text = "%*"; + output << "**kern" << endl; +} + + + +////////////////////////////// +// +// Tool_esac2hum::createFilename -- from SIG[] and CUT[], with spaces in CUT[] turned into +// underscores and accents removed from characters. +// +// Also need to deal with decomposed accents, if necessary: +// 0x0301: Combining acute accent +// 0x0300: Combining grave accent +// 0x0302: Combining circumflex accent +// 0x0303: Combining tilde +// 0x0308: Combining diaeresis (umlaut) +// 0x0327: Combining cedilla +// 0x0328: Combining ogonek +// 0x0304: Combining macron +// 0x0306: Combining breve +// 0x0307: Combining dot above +// 0x0323: Combining dot below +// 0x030A: Combining ring above +// 0x030B: Combining double acute accent +// 0x030C: Combining caron +// +// +// std::unordered_map m_accent_map = { +// {'á', 'a'}, {'à', 'a'}, {'ä', 'a'}, {'â', 'a'}, {'ã', 'a'}, {'å', 'a'}, +// {'é', 'e'}, {'è', 'e'}, {'ë', 'e'}, {'ê', 'e'}, +// {'í', 'i'}, {'ì', 'i'}, {'ï', 'i'}, {'î', 'i'}, +// {'ó', 'o'}, {'ò', 'o'}, {'ö', 'o'}, {'ô', 'o'}, {'õ', 'o'}, {'ø', 'o'}, +// {'ú', 'u'}, {'ù', 'u'}, {'ü', 'u'}, {'û', 'u'}, +// {'ý', 'y'}, {'ÿ', 'y'}, +// {'ñ', 'n'}, {'ç', 'c'}, +// {'ą', 'a'}, {'ć', 'c'}, {'ę', 'e'}, {'ł', 'l'}, {'ń', 'n'}, +// {'ś', 's'}, {'ź', 'z'}, {'ż', 'z'} +// }; + +string Tool_esac2hum::createFilename(void) { + string source = m_score.m_params["_source"]; + string prefix; + string sig = m_score.m_params["SIG"]; + string title = m_score.m_params["_title"]; + string id = m_score.m_params["_id"]; + if (sig.empty()) { + sig = id; + } + + HumRegex hre; + // Should not be spaces, but just in case; + hre.replaceDestructive(sig, "", "\\s+", "g"); + hre.replaceDestructive(source, "", "\\s+", "g"); + + if (!m_filePrefix.empty()) { + prefix = m_filePrefix; + source = ""; + } + + // Convert spaces to underscores: + hre.replaceDestructive(title, "_", "\\s+", "g"); + // Remove accents: + hre.replaceDestructive(title, "a", "á", "g"); + hre.replaceDestructive(title, "a", "à", "g"); + hre.replaceDestructive(title, "a", "ä", "g"); + hre.replaceDestructive(title, "a", "â", "g"); + hre.replaceDestructive(title, "a", "ã", "g"); + hre.replaceDestructive(title, "a", "å", "g"); + hre.replaceDestructive(title, "e", "é", "g"); + hre.replaceDestructive(title, "e", "è", "g"); + hre.replaceDestructive(title, "e", "ë", "g"); + hre.replaceDestructive(title, "e", "ê", "g"); + hre.replaceDestructive(title, "i", "í", "g"); + hre.replaceDestructive(title, "i", "ì", "g"); + hre.replaceDestructive(title, "i", "ï", "g"); + hre.replaceDestructive(title, "i", "î", "g"); + hre.replaceDestructive(title, "o", "ó", "g"); + hre.replaceDestructive(title, "o", "ò", "g"); + hre.replaceDestructive(title, "o", "ö", "g"); + hre.replaceDestructive(title, "o", "ô", "g"); + hre.replaceDestructive(title, "o", "õ", "g"); + hre.replaceDestructive(title, "o", "ø", "g"); + hre.replaceDestructive(title, "u", "ú", "g"); + hre.replaceDestructive(title, "u", "ù", "g"); + hre.replaceDestructive(title, "u", "ü", "g"); + hre.replaceDestructive(title, "u", "û", "g"); + hre.replaceDestructive(title, "y", "ý", "g"); + hre.replaceDestructive(title, "y", "ÿ", "g"); + hre.replaceDestructive(title, "n", "ñ", "g"); + hre.replaceDestructive(title, "c", "ç", "g"); + hre.replaceDestructive(title, "a", "ą", "g"); + hre.replaceDestructive(title, "c", "ć", "g"); + hre.replaceDestructive(title, "e", "ę", "g"); + hre.replaceDestructive(title, "l", "ł", "g"); + hre.replaceDestructive(title, "n", "ń", "g"); + hre.replaceDestructive(title, "s", "ś", "g"); + hre.replaceDestructive(title, "z", "ź", "g"); + hre.replaceDestructive(title, "z", "ż", "g"); + hre.replaceDestructive(title, "", "[^a-zA-Z0-9-_.]", "g"); + + std::transform(title.begin(), title.end(), title.begin(), + [](unsigned char c) { return std::tolower(c); }); + + string output; + if (!prefix.empty()) { + output += prefix + "-"; + } else if (!source.empty()) { + if (hre.search(source, "^DWOK(\\d+)$")) { + string volume = hre.getMatch(1); + if (volume.size() == 1) { + volume = "0" + volume; + } + if (!sig.empty()) { + if (hre.search(sig, "^(\\d\\d)")) { + string volume2 = hre.getMatch(1); + if (volume == volume2) { + source = "DWOK"; + output += source; + } + } else { + output += source + "-"; + } } else { - data.text = "|*"; + output += source + "-"; } + } else { + output += source + "-"; } - if (data.text == "^") { - data.text = "|^"; - } - printString(data.text, out); - if (data.phend == 1) { - out << "}"; - } } + output += sig; + if (!(sig.empty() || title.empty())) { + output += "-"; + } + output += title; + if (output.empty()) { + output = "file"; + } + output += m_filePostfix; - out << "\n"; + return output; +} - // print barline information - if (data.bar == 1) { - out << "="; - if (data.barnum > 0) { - out << data.barnum; + +////////////////////////////// +// +// Tool_esac2hum::getParameters -- +// + +void Tool_esac2hum::getParameters(vector& infile) { + m_score.m_params.clear(); + HumRegex hre; + bool expectingCloseQ = false; + string lastKey = ""; + for (int i=0; i<(int)infile.size(); i++) { + if (hre.search(infile[i], "^\\s*$")) { + continue; } - if (data.barinterp) { - // out << "yy"; + if ((i == 0) && hre.search(infile[i], "^([A-Z_a-z][^\\]\\[]*)\\s*$")) { + m_score.m_params["_source"] = hre.getMatch(1); + continue; } - if (debugQ) { - if (data.bardur > 0.0) { - out << "[" << data.bardur << "]"; + if (expectingCloseQ) { + if (infile[i].find("[") != string::npos) { + cerr << "Strange case searching for close: " << infile[i] << endl; + } else if (infile[i].find("]") == string::npos) { + // continuing a parameter: + if (lastKey == "") { + cerr << "Strange case of no last key when closing parameter: " << infile[i] << endl; + } else { + m_score.m_params[lastKey] += "\n" + infile[i]; + } + } else if (hre.search(infile[i], "^([^\\]]+)\\]\\s*$")) { + // closing a parameter: + if (lastKey == "") { + cerr << "Strange case B of no last key when closing parameter: " << infile[i] << endl; + } else { + string value = hre.getMatch(1); + m_score.m_params[lastKey] += "\n" + value; + expectingCloseQ = false; + continue; + } + } else { + cerr << "Problem closing parameter: " << infile[i] << endl; } + continue; + } else if (hre.search(infile[i], "^\\s*([A-Z_a-z]+)\\s*\\[([^\\]]*)\\]\\s*$")) { + // single line parameter + string key = hre.getMatch(1); + string value = hre.getMatch(2); + + // Rare cases where the key has lower case letters that should not be there: + std::transform(key.begin(), key.end(), key.begin(), + [](unsigned char c) { return std::toupper(c); }); + + m_score.m_params[key] = value; + continue; + } else if (hre.search(infile[i], "^\\s*([A-Z_a-z]+)\\s*\\[([^\\]]*)\\s*$")) { + // opening of a parameter + string key = hre.getMatch(1); + string value = hre.getMatch(2); + + // Rare cases where the key has lower case letters that should not be there: + std::transform(key.begin(), key.end(), key.begin(), + [](unsigned char c) { return std::toupper(c); }); + + m_score.m_params[key] = value; + lastKey = key; + expectingCloseQ = true; + continue; + } else if (hre.search(infile[i], "^#")) { + // Do nothing: an external comment, or embedded filter processed + // when filter loading the file. + } else { + cerr << "UNKNOWN CASE: " << infile[i] << endl; } - if (textQ) { - out << "\t"; - out << "="; - if (data.barnum > 0) { - out << data.barnum; - } - if (data.barinterp) { - // out << "yy"; - } - if (debugQ) { - if (data.bardur > 0.0) { - out << "[" << data.bardur << "]"; + } + + // The CUT[] line can be multiple lines, the first being the title and + // the second being the text incipit. Split them into _title and _incipit + // fields (not checking if more than two lines): + string cut = m_score.m_params["CUT"]; + if (hre.search(cut, "^\\s*(.*?)\\n(.*?)\\s*$", "s")) { + m_score.m_params["_title"] = trimSpaces(hre.getMatch(1)); + m_score.m_params["_incipit"] = trimSpaces(hre.getMatch(2)); + } else { + // Don't know if CUT[] is title or incipit, but assign to title. + m_score.m_params["_title"] = trimSpaces(cut); + m_score.m_params["_incipit"] = ""; + } + + string key = m_score.m_params["KEY"]; + if (hre.search(key, "^\\s*([^\\s]+)\\s+(\\d+)\\s+([A-Gacdefg][b#]*)\\s+(.*?)\\s*$")) { + string id = hre.getMatch(1); + string minrhy = hre.getMatch(2); + string tonic = hre.getMatch(3); + if (tonic.size() >= 1) { + if (tonic[0] == 'b') { + cerr << "Error: key signature cannot be 'b'." << endl; + } else { + if (std::islower(tonic[0])) { + cerr << "Warning: Tonic note should be upper case." << endl; + tonic[0] = std::toupper(tonic[0]); } } } + string time = hre.getMatch(4); + m_score.m_params["_id"] = id; + m_score.m_params["_minrhy"] = minrhy; + m_score.m_params["_tonic"] = tonic; + m_score.m_params["_time"] = time; + m_minrhy = stoi(minrhy); + } else { + cerr << "Problem parsing KEY parameter: " << key << endl; + } - out << "\n"; - } else if (data.bar == 2) { - out << "=="; - if (textQ) { - out << "\t=="; - } - out << "\n"; + string trd = m_score.m_params["TRD"]; + if (hre.search(trd, "^\\s*(.*)\\ss\\.")) { + m_score.m_params["_source_trd"] = hre.getMatch(1); + } + if (hre.search(trd, "\\bs\\.\\s*(\\d+)\\s*-\\s*(\\d+)?")) { + m_score.m_params["_page"] = hre.getMatch(1) + "-" + hre.getMatch(2); + } else if (hre.search(trd, "\\bs\\.\\s*(\\d+)")) { + m_score.m_params["_page"] = hre.getMatch(1); + } else { + cerr << "CANNOT FIND PAGE NUMBER IN " << trd << endl; + } + + if (m_debugQ) { + printParameters(); + } + + if (hre.search(m_score.m_params["_source_trd"], "^\\s*(DWOK\\d+)")) { + m_dwokQ = true; + } else if (hre.search(m_score.m_params["_source"], "^\\s*(DWOK\\d+)")) { + m_dwokQ = true; } + } ////////////////////////////// // -// Tool_esac2hum::getKeyInfo -- look for a KEY[] entry and extract the data. +// Tool_esac2hum::printParameters -- // -// ggg fix this function + +void Tool_esac2hum::printParameters(void) { + cerr << endl; + cerr << "========================================" << endl; + for (const auto& [key, value] : m_score.m_params) { + cerr << "Key: " << key << ", Value: " << value << endl; + } + cerr << "========================================" << endl; + cerr << endl; +} + + + +////////////////////////////// +// +// Tool_esac2hum::printBemComment -- // -bool Tool_esac2hum::getKeyInfo(vector& song, string& key, double& mindur, - int& tonic, string& meter, ostream& out) { - int i; - for (i=0; i<(int)song.size(); i++) { - if (song[i].compare(0, 4, "KEY[") == 0) { - key = song[i][4]; // letter - key += song[i][5]; // number - key += song[i][6]; // number - key += song[i][7]; // number - key += song[i][8]; // number - if (!isspace(song[i][9])) { - key += song[i][9]; // optional letter (sometimes ' or ") - } - if (!isspace(song[i][10])) { - key += song[i][10]; // illegal but possible extra letter - } - if (song[i][10] != ' ') { - out << "!! Warning key field is not complete" << endl; - out << "!!Key field: " << song[i] << endl; - } +void Tool_esac2hum::printBemComment(ostream& output) { + string bem = m_score.m_params["BEM"]; + if (bem.empty()) { + return; + } + output << "!!!ONB: " << bem << endl; +} - mindur = (song[i][11] - '0') * 10 + (song[i][12] - '0'); - mindur = 4.0 / mindur; - string tonicstr; - if (song[i][14] != ' ') { - tonicstr[0] = song[i][14]; - if (tolower(song[i][15]) == 'b') { - tonicstr[1] = '-'; - } else { - tonicstr[1] = song[i][15]; - } - tonicstr[2] = '\0'; - } else { - tonicstr = song[i][15]; - } - // convert German notation to English for note names - // Hopefully all references to B will mean English B-flat. - if (tonicstr == "B") { - tonicstr = "B-"; - } - if (tonicstr == "H") { - tonicstr = "B"; - } +////////////////////////////// +// +// Tool_esac2hum::printFooter -- +// - tonic = Convert::kernToBase40(tonicstr); - if (tonic <= 0) { - cerr << "Error: invalid tonic on line: " << song[i] << endl; - return false; - } - tonic = tonic % 40; - meter = song[i].substr(17); - if (meter.back() != ']') { - cerr << "Error with meter on line: " << song[i] << endl; - cerr << "Meter area: " << meter << endl; - cerr << "Expected ] as last character but found " << meter.back() << endl; - return false; - } else { - meter.resize((int)meter.size() - 1); - } - return true; +void Tool_esac2hum::printFooter(ostream& output, vector& infile) { + output << "*-" << endl; + + printBemComment(output); + printPdfLinks(output); + printPageNumbers(output); + printConversionDate(output); + + + if (m_embedEsacQ) { + output << "!!@@BEGIN: ESAC" << endl; + output << "!!@CONTENTS:" << endl;; + for (int i=0; i<(int)infile.size(); i++) { + output << "!!" << infile[i] << endl; + } + if (m_analysisQ) { + embedAnalyses(output); + } + output << "!!@@END: ESAC" << endl; + } + + if (!m_globalComments.empty()) { + for (int i=0; i<(int)m_globalComments.size(); i++) { + output << m_globalComments.at(i) << endl; } } - cerr << "Error: did not find a KEY field" << endl; - return false; } /////////////////////////////// // -// Tool_esac2hum::getFileContents -- read a file into the array. +// Tool_esac2hum::printPageNumbers -- // -bool Tool_esac2hum::getFileContents(vector& array, const string& filename) { - ifstream infile(filename.c_str()); - array.reserve(100); - array.resize(0); - - if (!infile.is_open()) { - cerr << "Error: cannot open file: " << filename << endl; - return false; +void Tool_esac2hum::printPageNumbers(ostream& output) { + HumRegex hre; + string trd = m_score.m_params["TRD"]; + if (hre.search(trd, "\\bs\\.\\s*(\\d+)\\s*-\\s*(\\d+)", "im")) { + output << "!!!page: " << hre.getMatch(1) << "-" << hre.getMatch(2) << endl; + } else if (hre.search(trd, "\\bs\\.\\s*(\\d+)", "im")) { + output << "!!!page: " << hre.getMatch(1) << endl; } +} - char holdbuffer[1024] = {0}; - infile.getline(holdbuffer, 256, '\n'); - while (!infile.eof()) { - array.push_back(holdbuffer); - infile.getline(holdbuffer, 256, '\n'); - } - infile.close(); - return true; -} +/////////////////////////////// +// +// Tool_esac::embedAnalyses -- +// + +void Tool_esac2hum::embedAnalyses(ostream& output) { + m_score.doAnalyses(); + string MEL_SEM = m_score.m_params["MEL_SEM"]; + string MEL_RAW = m_score.m_params["MEL_RAW"]; + string NO_REP = m_score.m_params["NO_REP"]; + string RTM = m_score.m_params["RTM"]; + string SCL_DEG = m_score.m_params["SCL_DEG"]; + string SCL_SEM = m_score.m_params["SCL_SEM"]; + string PHR_NO = m_score.m_params["PHR_NO"]; + string PHR_BARS = m_score.m_params["PHR_BARS"]; + string PHR_CAD = m_score.m_params["PHR_CAD"]; + string ACC = m_score.m_params["ACC"]; + + bool allEmptyQ = true; + if (!MEL_SEM.empty() ) { allEmptyQ = false; } + else if (!MEL_RAW.empty() ) { allEmptyQ = false; } + else if (!NO_REP.empty() ) { allEmptyQ = false; } + else if (!RTM.empty() ) { allEmptyQ = false; } + else if (!SCL_DEG.empty() ) { allEmptyQ = false; } + else if (!SCL_SEM.empty() ) { allEmptyQ = false; } + else if (!PHR_NO.empty() ) { allEmptyQ = false; } + else if (!PHR_BARS.empty()) { allEmptyQ = false; } + else if (!PHR_CAD.empty() ) { allEmptyQ = false; } + else if (!ACC.empty() ) { allEmptyQ = false; } + + if (allEmptyQ) { + // no analyses for some strange reason. + return; + } + output << "!!@ANALYSES:" << endl; + if (!MEL_SEM.empty() ) { output << "!!MEL_SEM[" << MEL_SEM << "]" << endl; } + if (!MEL_RAW.empty() ) { output << "!!MEL_RAW[" << MEL_RAW << "]" << endl; } + if (!NO_REP.empty() ) { output << "!!NO_REP[" << NO_REP << "]" << endl; } + if (!RTM.empty() ) { output << "!!RTM[" << RTM << "]" << endl; } + if (!SCL_DEG.empty() ) { output << "!!SCL_DEG[" << SCL_DEG << "]" << endl; } + if (!SCL_SEM.empty() ) { output << "!!SCL_SEM[" << SCL_SEM << "]" << endl; } + if (!PHR_NO.empty() ) { output << "!!PHR_NO[" << PHR_NO << "]" << endl; } + if (!PHR_BARS.empty()) { output << "!!PHR_BARS[" << PHR_BARS << "]" << endl; } + if (!PHR_CAD.empty() ) { output << "!!PHR_CAD[" << PHR_CAD << "]" << endl; } + if (!ACC.empty() ) { output << "!!ACC[" << ACC << "]" << endl; } +} -////////////////////////////// +/////////////////////////////// // -// Tool_esac2hum::example -- +// Tool_esac2hum::printPdfLinks -- // -void Tool_esac2hum::example(void) { +void Tool_esac2hum::printPdfLinks(ostream& output) { + output << "!!!URL: http://webesac.pcss.pl WebEsAC" << endl; + + if (!m_dwokQ) { + return; + } + + output << "!!!URL: https::kolberg.ispan.pl/dwok/tomy Oskar Kolberg: Complete Works digital edition" << endl; + printKolbergPdfUrl(output); } -////////////////////////////// +/////////////////////////////// // -// Tool_esac2hum::usage -- +// Tool_esac2hum::printCoversionDate -- // -void Tool_esac2hum::usage(const string& command) { +void Tool_esac2hum::printConversionDate(ostream& output) { + std::time_t t = std::time(nullptr); + std::tm* now = std::localtime(&t); + output << "!!!ONB: Converted on "; + output << std::put_time(now, "%Y/%m/%d"); + output << " with esac2hum" << endl; +} + + + +////////////////////////////// +// +// Tool_esac2hum::Score::doAnalyses -- +// +void Tool_esac2hum::Score::doAnalyses(void) { + analyzeMEL_SEM(); + analyzeMEL_RAW(); + analyzeNO_REP(); + analyzeRTM(); + analyzeSCL_DEG(); + analyzeSCL_SEM(); + analyzePHR_NO(); + analyzePHR_BARS(); + analyzePHR_CAD(); + analyzeACC(); } ////////////////////////////// // -// Tool_esac2hum::printBibInfo -- +// Tool_esac2hum::Score::analyzeMEL_SEM -- Current +// algorithm: calculate intervals across rest, ignore tied notes. +// values are differences between m_b12 of notes. // -void Tool_esac2hum::printBibInfo(vector& song, ostream& out) { - int i, j; - char buffer[32] = {0}; - int start = -1; - int stop = -1; - int count = 0; - string templine; +void Tool_esac2hum::Score::analyzeMEL_SEM(void) { + vector notelist; + getNoteList(notelist); - for (i=0; i<(int)song.size(); i++) { - if (song[i] == "") { + vector b12s; // list of notes to calculate intervals between + + for (int i=0; i<(int)notelist.size(); i++) { + if (notelist[i]->isRest()) { continue; } - if (song[i][0] != ' ') { - if (song[i].size() < 4 || song[i][3] != '[') { - if (song[i].compare(0, 2, "!!") != 0) { - out << "!! " << song[i] << "\n"; - } - continue; - } - strncpy(buffer, song[i].c_str(), 3); - buffer[3] = '\0'; - if (strcmp(buffer, "MEL") == 0) continue; - if (strcmp(buffer, "TXT") == 0) continue; - // if (strcmp(buffer, "KEY") == 0) continue; - getLineRange(song, buffer, start, stop); + if (notelist[i]->m_tieEnd) { + continue; + } + b12s.push_back(notelist[i]->m_b12); + } - // don't print CUT field if only one line. !!!OTL: will contain CUT[] - // if (strcmp(buffer, "CUT") == 0 && start == stop) continue; + string output; + for (int i=1; i<(int)b12s.size(); i++) { + int difference = b12s[i] - b12s[i-1]; + output += to_string(difference); + if (i < (int)b12s.size() - 1) { + output += " "; + } + } - buffer[0] = tolower(buffer[0]); - buffer[1] = tolower(buffer[1]); - buffer[2] = tolower(buffer[2]); + m_params["MEL_SEM"] = output; +} - count = 1; - templine = ""; - for (j=start; j<=stop; j++) { - if (song[j].size() < 4) { - continue; - } - if (stop - start == 0) { - templine = song[j].substr(4); - auto loc = templine.find(']'); - if (loc != string::npos) { - templine.resize(loc); - } - if (templine != "") { - out << "!!!" << buffer << ": "; - printString(templine, out); - out << "\n"; - } - } else if (j==start) { - out << "!!!" << buffer << count++ << ": "; - printString(song[j].substr(4), out); - out << "\n"; - } else if (j==stop) { - templine = song[j].substr(4); - auto loc = templine.find(']'); - if (loc != string::npos) { - templine.resize(loc); - } - if (templine != "") { - out << "!!!" << buffer << count++ << ": "; - printString(templine, out); - out << "\n"; - } - } else { - out << "!!!" << buffer << count++ << ": "; - printString(&(song[j][4]), out); - out << "\n"; - } - } + +////////////////////////////// +// +// Tool_esac2hum::Score::analyzeMEL_RAW -- Remove rhythms from MEL[] data. +// Preserve spaces as in original MEL[]; +// What to do with parentheses? Currently removed. +// What to do with tied notes? Currently removed. +// + +void Tool_esac2hum::Score::analyzeMEL_RAW(void) { + string output = m_params["MEL"]; + HumRegex hre; + hre.replaceDestructive(output, "", "[^\\d+\\sb#-]+", "g"); + hre.replaceDestructive(output, "", "\\s*//\\s*$"); + hre.replaceDestructive(output, "\n!!", "\n", "g"); + m_params["MEL_RAW"] = output; +} + + + +////////////////////////////// +// +// Tool_esac2hum::Score::analyzeNO_REP -- Return +// the non-repeated notes/rests without rhythms +// in each phrase with a newlines between phrases +// and no spaces between notes or measures. +// + +void Tool_esac2hum::Score::analyzeNO_REP(void) { + string output; + for (int i=0; i<(int)size(); i++) { + Tool_esac2hum::Phrase& phrase = at(i); + string line = phrase.getNO_REP(); + if (i > 0) { + output += "\n "; } + output += line; } + + HumRegex hre; + hre.replaceDestructive(output, "\n!!", "\n", "g"); + + m_params["NO_REP"] = output; } ////////////////////////////// // -// Tool_esac2hum::printString -- print characters in string. +// Tool_esac2hum::Phrase::getNO_REP -- Return +// the non-repeated notes/rests without rhythms +// with no spaces between notes or measures. +// What to do if line starts with an ending tied note? +// Currently ignoring leading tied end notes. // -void Tool_esac2hum::printString(const string& string, ostream& out) { - for (int i=0; i<(int)string.size(); i++) { - printChar(string[i], out); +string Tool_esac2hum::Phrase::getNO_REP(void) { + vector notelist; + getNoteList(notelist); + string output; + int foundNonTie = false; + string lastitem = ""; + for (int i=0; i<(int)notelist.size(); i++) { + if (!foundNonTie && notelist[i]->m_tieEnd) { + continue; + } + foundNonTie = true; + string curitem = notelist[i]->getScaleDegree(); + if (curitem != lastitem) { + output += curitem; + lastitem = curitem; + } } + return output; } +////////////////////////////// +// +// Tool_esac2hum::Score::analyzeRTM -- Convert pitches/rests to "x". +// What to do with tied notes? Leaving ^ in for now. +// What to do with ()? Removing for now. +// + +void Tool_esac2hum::Score::analyzeRTM(void) { + string output = m_params["MEL"]; + HumRegex hre; + hre.replaceDestructive(output, "", "[()]+", "g"); + hre.replaceDestructive(output, "x", "[+-]*(\\d|\\^)[b#]*", "g"); + hre.replaceDestructive(output, "", "\\s*//\\s*$"); + hre.replaceDestructive(output, "\n!!", "\n", "g"); + m_params["RTM"] = output; +} + -///////////////////////////////// +////////////////////////////// // -// Tool_extract::Tool_extract -- Set the recognized options for the tool. +// Tool_esac2hum::Score::analyzeSCL_DEG -- List of scale degrees +// present in melody from lowest to highest with no spaces between +// the scale degrees. // -Tool_extract::Tool_extract(void) { - define("P|F|S|x|exclude=s:", "remove listed spines from output"); - define("i=s:", "exclusive interpretation list to extract from input"); - define("I=s:", "exclusive interpretation exclusion list"); - define("f|p|s|field|path|spine=s:", "for extraction of particular spines"); - define("C|count=b", "print a count of the number of spines in file"); - define("c|cointerp=s:**kern", "exclusive interpretation for cospines"); - define("g|grep=s:", "extract spines which match a given regex."); - define("r|reverse=b", "reverse order of spines by **kern group"); - define("R=s:**kern", "reverse order of spine by exinterp group"); - define("t|trace=s:", "use a trace file to extract data"); - define("e|expand=b", "expand spines with subspines"); - define("k|kern=s", "extract by kern spine group"); - define("K|reverse-kern=s", "extract by kern spine group top to bottom numbering"); - define("E|expand-interp=s:", "expand subspines limited to exinterp"); - define("m|model|method=s:d", "method for extracting secondary spines"); - define("M|cospine-model=s:d", "method for extracting cospines"); - define("Y|no-editoral-rests=b", "do not display yy marks on interpreted rests"); - define("n|name|b|blank=s:**blank", "name if exinterp added with 0"); - 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)"); +void Tool_esac2hum::Score::analyzeSCL_DEG(void) { + vector notelist; + getNoteList(notelist); + map list; + for (int i=0; i<(int)notelist.size(); i++) { + if (notelist[i]->isRest()) { + continue; + } + if (notelist[i]->m_tieEnd) { + continue; + } + int b40 = notelist[i]->m_b40; + list[b40] = notelist[i]; + } - define("debug=b", "print debugging information"); - define("author=b", "author of the program"); - define("version=b", "compilation info"); - define("example=b", "example usages"); - define("h|help=b", "short description"); + string output; + for (const auto& pair : list) { + output += pair.second->getScaleDegree(); + } + m_params["SCL_DEG"] = output; } -///////////////////////////////// +////////////////////////////// // -// Tool_extract::run -- Primary interfaces to the tool. +// Tool_esac2hum::Score::analyzeSCL_SEM -- Get the semitone +// between scale degrees in SCL_DEG analysis. // -bool Tool_extract::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i notelist; + getNoteList(notelist); + map list; + for (int i=0; i<(int)notelist.size(); i++) { + if (notelist[i]->isRest()) { + continue; + } + if (notelist[i]->m_tieEnd) { + continue; + } + int b40 = notelist[i]->m_b40; + list[b40] = notelist[i]; } - return status; -} - -bool Tool_extract::run(const string& indata, ostream& out) { - HumdrumFile infile(indata); - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; + string output; + Tool_esac2hum::Note* lastnote = nullptr; + for (const auto& pair : list) { + if (lastnote == nullptr) { + lastnote = pair.second; + continue; + } + int second = pair.second->m_b12; + int first = lastnote->m_b12; + int difference = second -first; + if (!output.empty()) { + output += " "; + } + output += to_string(difference); + lastnote = pair.second; } - return status; + m_params["SCL_SEM"] = output; } -bool Tool_extract::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; - } - return status; + +////////////////////////////// +// +// Tool_esac2hum::Score::analyzePHR_NO -- +// + +void Tool_esac2hum::Score::analyzePHR_NO(void) { + int phraseCount = (int)size(); + m_params["PHR_NO"] = to_string(phraseCount); } + + +////////////////////////////// // -// In-place processing of file: +// Tool_esac2hum::Score::analyzePHR_BARS -- Return the number +// of measures in each phrase. // -bool Tool_extract::run(HumdrumFile& infile) { - initialize(infile); - processFile(infile); - // Re-load the text for each line from their tokens. - // infile.createLinesFromTokens(); - return true; +void Tool_esac2hum::Score::analyzePHR_BARS(void) { + string output; + for (int i=0; i<(int)size(); i++) { + Tool_esac2hum::Phrase& phrase = at(i); + int barCount = phrase.getFullMeasureCount(); + output += to_string(barCount); + if (i < (int)size() - 1) { + output += " "; + } + } + m_params["PHR_BARS"] = output; } ////////////////////////////// // -// Tool_extract::processFile -- +// Tool_esac2hum:::Phrase::getFullMeasureCount -- Return the number +// of measures, but subtrack one if the first measure is a +// partialEnd and the last is a partialBegin. // -void Tool_extract::processFile(HumdrumFile& infile) { - if (countQ) { - m_free_text << infile.getMaxTrack() << endl; - return; +int Tool_esac2hum::Phrase::getFullMeasureCount(void) { + int measureCount = (int)size(); + if (measureCount < 2) { + return measureCount; } - if (expandQ) { - expandSpines(field, subfield, model, infile, expandInterp); - } else if (interpQ) { - getInterpretationFields(field, subfield, model, infile, interps, - 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); - } else if (emptyQ) { - fillFieldDataByEmpty(field, subfield, model, infile, interpstate); - } else if (noEmptyQ) { - fillFieldDataByNoEmpty(field, subfield, model, infile, interpstate); - } else if (fieldQ || excludeQ) { - fillFieldData(field, subfield, model, fieldstring, infile); + if (at(0).isPartialEnd() && back().isPartialBegin()) { + measureCount--; } - if (spineListQ) { - m_free_text << "-s "; - for (int i=0; i<(int)field.size(); i++) { - m_free_text << field[i]; - if (i < (int)field.size() - 1) { - m_free_text << ","; - } - } - m_free_text << endl; - return; + // if the fist is partial and the last is not, also -1 + if (at(0).isPartialEnd() && back().isComplete()) { + measureCount--; } - if (debugQ && !traceQ) { - m_free_text << "!! Field Expansion List:"; - for (int j=0; j<(int)field.size(); j++) { - m_free_text << " " << field[j]; - if (subfield[j]) { - m_free_text << (char)subfield[j]; - } - if (model[j]) { - m_free_text << (char)model[j]; - } - } - m_free_text << endl; + // if the fist is complete and the last is incomplete, also -1 + if (at(0).isComplete() && back().isPartialBegin()) { + measureCount--; } - // preserve SEGMENT filename if present (now printed in main()) - // infile.printNonemptySegmentLabel(m_humdrum_text); - // analyze the input file according to command-line options - if (fieldQ || grepQ || removerestQ) { - extractFields(infile, field, subfield, model); - } else if (excludeQ) { - excludeFields(infile, field, subfield, model); - } else if (traceQ) { - extractTrace(infile, tracefile); - } else { - m_humdrum_text << infile; - } + // what to do if first measure is pickup (and maybe last measure)? + return measureCount; } ////////////////////////////// // -// Tool_extract::getNullDataTracks -- +// Tool_esac2hum::Score::analyzePHR_CAD -- Give a space-delimited +// list of the last scale degree of each phrase. // -vector Tool_extract::getNullDataTracks(HumdrumFile& infile) { - vector output(infile.getMaxTrack() + 1, 1); - for (int i=0; igetTrack(); - if (!output[track]) { - continue; - } - if (!token->isNull()) { - output[track] = 0; - } +void Tool_esac2hum::Score::analyzePHR_CAD(void) { + string output; + for (int i=0; i<(int)size(); i++) { + Tool_esac2hum::Phrase& phrase = at(i); + output += phrase.getLastScaleDegree(); + if (i < (int)size() - 1) { + output += " "; } - // maybe exit here if all tracks are non-null } - - return output; + m_params["PHR_CAD"] = output; } ////////////////////////////// // -// Tool_extract::fillFieldDataByEmpty -- Only keep the spines which contain only -// null data tokens. +// Tool_esac2hum::Phrase::getLastScaleDegree -- // -void Tool_extract::fillFieldDataByEmpty(vector& field, vector& subfield, - vector& model, HumdrumFile& infile, int negate) { - - 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 nullTrack = getNullDataTracks(infile); +string Tool_esac2hum::Phrase::getLastScaleDegree(void) { + vector notelist; + getNoteList(notelist); - int zero = 0; - for (int i=1; i<(int)nullTrack.size(); i++) { - if (negate) { - if (!nullTrack[i]) { - field.push_back(i); - subfield.push_back(zero); - model.push_back(zero); - } - } else { - if (nullTrack[i]) { - field.push_back(i); - subfield.push_back(zero); - model.push_back(zero); - } + for (int i=(int)notelist.size() - 1; i>=0; i--) { + if (notelist[i]->isPitch()) { + return notelist[i]->getScaleDegree(); } } + return "?"; } - ////////////////////////////// // -// Tool_extract::fillFieldDataByNoEmpty -- Only keep spines which are not all -// null data tokens. +// Tool_esac2hum::Note::getScaleDegree -- return the scale degree +// string for the note, such as: 6, -6, +7b, 5#. // -void Tool_extract::fillFieldDataByNoEmpty(vector& field, vector& subfield, - vector& model, HumdrumFile& infile, int negate) { - - 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 nullTrack = getNullDataTracks(infile); - for (int i=1; i<(int)nullTrack.size(); i++) { - nullTrack[i] = !nullTrack[i]; +string Tool_esac2hum::Note::getScaleDegree(void) { + string output; + if (m_octave < 0) { + for (int i=0; i<-m_octave; i++) { + output += "-"; + } + } else if (m_octave > 0) { + for (int i=0; i 0) { + for (int i=0; i& field, vector& subfield, - vector& model, const string& searchstring, HumdrumFile& infile, - int state) { +bool Tool_esac2hum::Note::isPitch(void) { + return (m_degree > 0); +} - field.clear(); - subfield.clear(); - model.clear(); - // Check every **kern spine for any notes. If there is a note - // then the tracks variable for that spine will be marked - // as non-zero. - vector tracks(infile.getMaxTrack() + 1, 0); - int track; - int partline = 0; - bool dataQ = false; - for (int i=0; iisKern()) { - continue; - } - if (token->isNull()) { - continue; - } - if (token->isRest()) { - continue; - } - track = token->getTrack(); - tracks[track] = 1; - } - } +bool Tool_esac2hum::Note::isRest(void) { + return (m_degree <= 0); +} - // Go back and mark any empty spines as non-empty if they - // are in a part that contains multiple staves. I.e., only - // delete a staff if all staves for the part are empty. - // There should be a single *part# line at the start of the - // score. - if (partline > 0) { - vector kerns; - for (int i=0; iisKern()) { - continue; - } - kerns.push_back(token); - } - for (int i=0; i<(int)kerns.size(); i++) { - for (int j=i+1; j<(int)kerns.size(); j++) { - if (*kerns[i] != *kerns[j]) { - continue; - } - if (kerns[i]->find("*part") == string::npos) { - continue; - } - int track1 = kerns[i]->getTrack(); - int track2 = kerns[j]->getTrack(); - int state1 = tracks[track1]; - int state2 = tracks[track2]; - if ((state1 && !state2) || (state2 && !state1)) { - // Prevent empty staff from being removed - // from a multi-staff part: - tracks[track1] = 1; - tracks[track2] = 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; - } - } +////////////////////////////// +// +// Tool_esac2hum::Score::analyzeACC -- The first scale degree +// of each (complete) meausre, or partial measure start. +// the scale degress for each phrase are placed into a word +// without spaces, and then a space between each phrase. +// +// Todo: Deal with tied notes at starts of measures. +// - // 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; +void Tool_esac2hum::Score::analyzeACC(void) { + string output; + for (int i=0; i<(int)size(); i++) { + Tool_esac2hum::Phrase& phrase = at(i); + for (int j=0; j<(int)phrase.size(); j++) { + Tool_esac2hum::Measure& measure = phrase.at(j); + if (measure.isComplete()) { + output += measure.at(0).getScaleDegree(); } - track = sstarts[j]->getTrack(); - tracks[track] = 0; } - } - - int zero = 0; - for (int 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); + if (i < (int)size() -1) { + output += " "; } } - + m_params["ACC"] = output; +} + + + +////////////////////////////// +// +// Tool_esac2hum::getKolbergInfo -- +// + +Tool_esac2hum::KolbergInfo Tool_esac2hum::getKolbergInfo(int volume) { + + if (!m_initialized) { + m_initialized = true; + // Parameters: Polish volume title, English translation, print start page, Equivalent start scan (pdf page), Plate scan page vector + m_kinfo.emplace( 1, KolbergInfo("Pieśni ludu polskego", "Polish folk songs", 3, 99, {149, 150, 167, 168, 233, 234, 251, 252, 317, 318, 335, 336, 401, 402, 419, 420, 485, 486, 503, 504})); + m_kinfo.emplace( 2, KolbergInfo("Sandomierskie", "Sandomierz", 23, 34, {})); + m_kinfo.emplace( 3, KolbergInfo("Kujawy I", "Kuyavia I (north central Poland)", 209, 221, {})); + m_kinfo.emplace( 4, KolbergInfo("Kujawy II", "Kuyavia II (north central Poland)", 69, 83, {})); + m_kinfo.emplace( 5, KolbergInfo("Krakowskie I", "Crakow I", 194, 222, {})); + m_kinfo.emplace( 6, KolbergInfo("Krakowskie II", "Crakow II", 5, 29, {49, 50})); + // 7: Krakowskie III/Crakow III: no music + m_kinfo.emplace( 8, KolbergInfo("Krakowskie IV", "Crakow IV", 162, 182, {})); + m_kinfo.emplace( 9, KolbergInfo("W. Ks. Poznańskie I", "Grand Duchy of Poznań I", 117, 141, {})); + m_kinfo.emplace(10, KolbergInfo("W. Ks. Poznańskie II", "Grand Duchy of Poznań II", 60, 76, {})); + m_kinfo.emplace(11, KolbergInfo("W. Ks. Poznańskie III", "Grand Duchy of Poznań III", 39, 57, {})); + m_kinfo.emplace(12, KolbergInfo("W. Ks. Poznańskie IV", "Grand Duchy of Poznań IV", 3, 19, {})); + m_kinfo.emplace(13, KolbergInfo("W. Ks. Poznańskie V", "Grand Duchy of Poznań V", 3, 27, {})); + m_kinfo.emplace(14, KolbergInfo("W. Ks. Poznańskie VI", "Grand Duchy of Poznań VI", 157, 165, {})); + m_kinfo.emplace(15, KolbergInfo("W. Ks. Poznańskie VII", "Grand Duchy of Poznań VII", 317, 327, {})); + m_kinfo.emplace(16, KolbergInfo("Lubelskie I", "Lublin Voivodeship I", 105, 125, {})); + m_kinfo.emplace(17, KolbergInfo("Lubelskie II", "Lublin Voivodeship II", 1, 23, {})); + m_kinfo.emplace(18, KolbergInfo("Kieleckie I", "Kielce Voivodeship I", 49, 65, {})); + m_kinfo.emplace(19, KolbergInfo("Kieleckie II", "Kielce Voivodeship II", 1, 15, {})); + m_kinfo.emplace(20, KolbergInfo("Radomskie I", "Radom Voivodeship I", 75, 95, {})); + m_kinfo.emplace(21, KolbergInfo("Radomskie II", "Radom Voivodeship II", 1, 19, {})); + m_kinfo.emplace(22, KolbergInfo("Łęczyckie", "Łęczyca Voivodeship", 18, 36, {})); + m_kinfo.emplace(23, KolbergInfo("Kaliskie", "Kalisz Region", 54, 68, {})); + m_kinfo.emplace(24, KolbergInfo("Mazowsze I", "Mazovia I", 79, 103, {})); + m_kinfo.emplace(25, KolbergInfo("Mazowsze II", "Mazovia II", 1, 26, {})); + // 26: Mazowsze III/Mazovia III: no music + m_kinfo.emplace(27, KolbergInfo("Mazowsze IV", "Mazovia IV", 115, 134, {})); + m_kinfo.emplace(28, KolbergInfo("Mazowsze V", "Mazovia V", 64, 83, {})); + m_kinfo.emplace(29, KolbergInfo("Pokucie I", "Pokuttia I", 90, 122, {})); + m_kinfo.emplace(30, KolbergInfo("Pokucie II", "Pokuttia II", 1, 14, {})); + m_kinfo.emplace(31, KolbergInfo("Pokucie III", "Pokuttia III", 10, 31, {})); + // 32: Pokucie IV/Pokuttia IV: no music + m_kinfo.emplace(33, KolbergInfo("Chełmskie I", "Chełm Voivodeship I", 114, 150, {163, 164, 175, 176, 177, 178})); + m_kinfo.emplace(34, KolbergInfo("Chełmskie II", "Chełm Voivodeship II", 4, 21, {})); + m_kinfo.emplace(35, KolbergInfo("Przemyskie", "Przemyśl Voivodeship", 11, 47, {93, 94, 115, 116, 167, 168})); + // 36: Wołyń/Volhynia: complications + // 37: Miscellanea I/Miscellanea I: no music + // 38, Miscellanea II/Miscellanea II: no music + m_kinfo.emplace(39, KolbergInfo("Pomorze", "Pomerania", 67, 115, {129, 130, 147, 148})); + m_kinfo.emplace(40, KolbergInfo("Mazury Pruskie", "Prussian Masuria", 96, 155, {356, 357, 358, 359, 464, 465, 482, 483})); + + m_kinfo.emplace(41, KolbergInfo("Mazowsze VI", "Mazovia VI", 20, 95, {108, 109, 126, 127, 288, 289, 306, 307, 388, 389, 406, 407})); + m_kinfo.emplace(42, KolbergInfo("Mazowsze VII", "Mazovia VII", 6, 15, {})); + m_kinfo.emplace(43, KolbergInfo("Śląsk", "Silesia", 21, 62, {74, 75})); + m_kinfo.emplace(44, KolbergInfo("Góry i Podgórze I", "Mountains and Foothills I", 64, 110, {111, 112, 129, 130, 195, 196, 213, 214, 343, 344, 361, 362})); + m_kinfo.emplace(45, KolbergInfo("Góry i Podgórze II", "Mountains and Foothills II", 1, 11, {91, 92, 109, 110, 335, 336, 353, 354, 499, 500})); + m_kinfo.emplace(46, KolbergInfo("Kaliskie i Sieradzkie", "Kalisz Region and Sieradz Voivodeship", 3, 29, {43, 44, 61, 62, 175, 176, 193, 194})); + m_kinfo.emplace(47, KolbergInfo("Podole", "Podolia", 59, 105, {151, 152, 153, 154, 155, 156, 157, 158})); + m_kinfo.emplace(48, KolbergInfo("Tarnowskie-Rzeszowskie", "Tarnów-Rzeszów Voivodeship", 65, 103, {119, 120, 233, 234, 251, 252})); + m_kinfo.emplace(49, KolbergInfo("Sanockie-Krośnieńskie I", "Sanok-Krosno Voivodeship I", 109, 185, {189, 190, 239, 240, 257, 258, 387, 388, 405, 406, 455, 456, 473, 474})); + m_kinfo.emplace(50, KolbergInfo("Sanockie-Krośnieńskie II", "Sanok-Krosno Voivodeship II", 1, 14, {30, 31, 48, 49, 114, 115, 132, 133, 198, 199, 216 ,217, 282, 283, 300, 301, 366, 367, 384, 385})); + // 51: Sanockie-Krośnieńskie III/Sanok-Krosno Voivodeship III: no music + m_kinfo.emplace(52, KolbergInfo("Białoruś-Polesie", "Belarus-Polesia", 116, 169, {182, 183, 200, 201, 266, 267, 284, 285, 382, 383, 400, 401, 530, 531, 548, 549})); + m_kinfo.emplace(53, KolbergInfo("Litwa", "Lithuania", 142, 176, {195, 196, 325, 326, 359, 360, 441, 442, 459, 460})); + m_kinfo.emplace(54, KolbergInfo("Ruś karpacka I", "Carpathian Ruthenia I", 267, 365, {371, 372})); + m_kinfo.emplace(55, KolbergInfo("Ruś karpacka II", "Carpathian Ruthenia II", 22, 37, {48, 49, 146, 147, 164, 165, 214, 215, 232, 233, 426, 427, 444, 445})); + m_kinfo.emplace(56, KolbergInfo("Ruś czerwona I", "Red Ruthenia I", 61, 157, {173, 174, 191, 192, 209, 210, 259, 260, 27, 278, 311, 312, 329, 330, 443, 444, 461, 462})); + // 57: Ruś czerwona II/Red Ruthenia II, 14, 19, {70, 71, 88, 89}: complications + // 58: Materiały do etnografii Słowian wschodnich/Materials for the ethnography of the Eastern Slavs + // 59/I, Materiały do etnografii Słowian zachodnich i południowych. Cz. I Łużyce/Materials for the ethnography of western and southern Slavs. Part I Lusatia + // 59/II, Materiały do etnografii Słowian zachodnich i południowych. Cz. II Czechy, Słowacja/Materials for the ethnography of western and southern Slavs. Part II Czech Republic, Slovakia + // 59/III, Materiały do etnografii Słowian zachodnich i południowych. Cz. III Słowiańszczyzna południowa/Materials for the ethnography of western and southern Slavs. Part III Southern Slavs + // 60: Przysłowia/Proverbs: no music + // 61, Pisma muzyczne, cz. I/Musical writings, part I: no music + // 62, Pisma muzyczne, cz. II/"Musical writings, part II, 25, 33: not in EsAC + // 63, Studia, rozprawy i artykuły/Studies, dissertations and articles", 55, 113: not in EsAC + m_kinfo.emplace(64, KolbergInfo("Korespondencja Oskara Kolberga, cz. I (1837-1876)", "Correspondence of Oskar Kolberg, Part I (1837-1876)", 216, 261, {})); + // 65: Korespondencja Oskara Kolberga, cz. II (1877-1882)/Correspondence of Oskar Kolberg, Part II (1877-1882): no music + // 66: Korespondencja Oskara Kolberga, cz. III (1883-1890)/Correspondence of Oskar Kolberg, Part III (1883-1890): no music + /// 67: Pieśni i melodie ludowe w opracowaniu fortepianowym, cz. I-II/Folk songs and melodies in piano arrangement, part I-II, 3, 22, not in EsAC + // 68: Kompozycje wokalno-instrumentalne/Vocal and instrumental compositions, 3, 49, not in EsAC + // 69: Kompozycje fortepianowe/Piano compositions: 3, 39, not in EsAC + // 70: Pieśni ludu polskiego. Supl. do t.1/Polish folk songs: Supplement to Volume 1: no music + m_kinfo.emplace(71, KolbergInfo("Sandomierskie. Suplement do t. 2 Dzieła Wszystkie", "Sandomierz Voivodeship. Supplement to vol. 2 of The Complete Works", 3, 40, {116, 117, 134, 135})); + m_kinfo.emplace(72.1, KolbergInfo("Kujawy. Suplement do t. 3 i 4, cz. I, Kuyavia", "Supplement to vol. 3 and 4, part I", 3, 30, {292, 293, 310, 311})); + // 72.2, Kujawy. Suplement do t. 3 i 4, cz. II, Kuyavia/Supplement to vol. 3 and 4, part II,: missing information about pages? + m_kinfo.emplace(73, KolbergInfo("Krakowsike. Suplement do T. 5-8", "Cracow: Supplement to Volumes 5-8", 39, 163, {297, 298, 407, 408})); + // 74: Wielkie Księstwo Poznańskie. Suplement do t. 9-15/The Grand Duchy of Poznan. Supplement to vol. 9-15: no music + m_kinfo.emplace(75, KolbergInfo("Lubelskie. Supplement do tomów 16-17", "Lublin: Supplement to volumes 16-17", 4, 60, {281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324})); + m_kinfo.emplace(76, KolbergInfo("Kieleckie. Supplement do T. 18-19", "Kielce: Supplement to Volumes 18-19", 5, 41, {})); + // 77: Radomskie. Suplement do t. 20 i 21. I/Radomskie: Supplement to Volumes 20-21. I: complications + m_kinfo.emplace(78, KolbergInfo("Łęczyckie. Suplement do t. 22", "Łęczyca Voivodeship: Supplement to Volume 22", 3, 1, {})); + m_kinfo.emplace(79, KolbergInfo("Kaliskie. Suplement do t. 23", "Kalisz Region. Supplement to vol. 23", 3, 38, {})); + m_kinfo.emplace(80, KolbergInfo("Mazowsze. Suplement do t. 24-28, cz. I", "Mazovia. Supplement to vol. 24-28, part I", 7, 89, {})); + m_kinfo.emplace(81, KolbergInfo("Pokucie. Suplement do tomów 29-32", "Corrections: Supplement to Volumes 29-32", 3, 73, {121, 122, 139, 140})); + m_kinfo.emplace(82, KolbergInfo("Chełmskie. Suplement do T. 33 i 34", "Chełm supplement to Volumes 33 and 34", 7, 105, {})); + m_kinfo.emplace(83, KolbergInfo("Przemyskie. Suplement do tomu 35 DWOK", "Przemyśl Voivodeship: Supplement to Volume 35 DWOK", 9, 112, {380, 381, 382, 383, 384, 385, 386, 387})); + m_kinfo.emplace(84, KolbergInfo("Wołyń. Suplement do t. 36., Volhynia", "Supplement to Volume 36", 35, 97, {313, 314, 315, 316, 317, 318, 319, 320})); + + + } + + auto it = m_kinfo.find(volume); + if (it != m_kinfo.end()) { + return it->second; + } else { + return KolbergInfo(); + } } ////////////////////////////// // -// Tool_extract::fillFieldDataByGrep -- +// Tool_esac::getKolbergUrl -- // -void Tool_extract::fillFieldDataByGrep(vector& field, vector& subfield, - vector& model, const string& searchstring, HumdrumFile& infile, - int state) { +string Tool_esac2hum::getKolbergUrl(int volume) { + if ((volume < 1) || (volume > 84)) { + // Such a volume does not exist, return empty string. + return ""; + } - field.reserve(infile.getMaxTrack()+1); - subfield.reserve(infile.getMaxTrack()+1); - model.reserve(infile.getMaxTrack()+1); - field.resize(0); - subfield.resize(0); - model.resize(0); + stringstream ss; + ss << std::setw(3) << std::setfill('0') << volume; + // not https:// + string url = "http://www.oskarkolberg.pl/MediaFiles/"; + url += ss.str(); + url += "dwok.pdf"; - vector tracks; - tracks.resize(infile.getMaxTrack()+1); - fill(tracks.begin(), tracks.end(), 0); - HumRegex hre; - int track; + KolbergInfo kinfo = getKolbergInfo(volume); + if (kinfo.titlePL.empty()) { + // Do not have the page number info for volume, so just give URL for the volume. + return url; + } - int i, j; - for (i=0; igetTrack(); - tracks[track] = 1; - } + string pageinfo = m_score.m_params["_page"]; + int printPage = 0; + if (!pageinfo.empty()) { + HumRegex hre; + if (hre.search(pageinfo, "(\\d+)")) { + printPage = hre.getMatchInt(1); + } else { + cerr << "XX PRINT PAGE: " << printPage << "\t_page: " << pageinfo << endl; } + } else { + cerr << "YY PRINT PAGE: " << printPage << "\t_page: IS EMPTY: " << pageinfo << endl; } - 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); + // Calculate the scan page that matches with the print page: + int startPrintPage = kinfo.firstPrintPage; + int startScanPage = kinfo.firstScanPage; + int scanPage = calculateScanPage(printPage, startPrintPage, startScanPage, kinfo.plates); + + url += "#page=" + to_string(scanPage); + + if (!kinfo.titlePL.empty()) { + url += " @PL{Oskar Kolberg Dzieła Wszystkie " + to_string(volume) + ": " + kinfo.titlePL; + url += ", s. " + pageinfo; + url += "}"; + } + + if (!kinfo.titleEN.empty()) { + url += " @EN{Oskar Kolberg Complete Works " + to_string(volume) + ": " + kinfo.titleEN; + url += ", p"; + if (pageinfo.find("-") != string::npos) { + url += "p"; } + url += "." + pageinfo; + url += "}"; + } + + if (kinfo.titlePL.empty() && kinfo.titleEN.empty()) { + url += " @PL{Oskar Kolberg Dzieła Wszystike " + to_string(volume); + url += " @PL{Oskar Kolberg Complete Works " + to_string(volume); } + + return url; } ////////////////////////////// // -// Tool_extract::getInterpretationFields -- +// Tool_esac2hum::printKolbergPdfUrl -- // -void Tool_extract::getInterpretationFields(vector& field, vector& subfield, - vector& model, HumdrumFile& infile, string& interps, int state) { - vector sstrings; // search strings - sstrings.reserve(100); - sstrings.resize(0); - - int i, j, k; - string buffer; - buffer = interps; - +void Tool_esac2hum::printKolbergPdfUrl(ostream& output) { + string source = m_score.m_params["_source"]; HumRegex hre; - hre.replaceDestructive(buffer, "", "\\s+", "g"); - - int start = 0; - while (hre.search(buffer, start, "^([^,]+)")) { - sstrings.push_back(hre.getMatch(1)); - start = hre.getMatchEndIndex(1); + if (!hre.search(source, "^DWOK(\\d+)")) { + return; } - - if (debugQ) { - m_humdrum_text << "!! Interpretation strings to search for: " << endl; - for (i=0; i<(int)sstrings.size(); i++) { - m_humdrum_text << "!!\t" << sstrings[i] << endl; - } + int volume = hre.getMatchInt(1); + string url = getKolbergUrl(volume); + if (!url.empty()) { + output << "!!!URL-pdf: " << url << endl; } +} - vector tracks; - tracks.resize(infile.getMaxTrack()+1); - fill(tracks.begin(), tracks.end(), 0); - // Algorithm below could be made more efficient by - // not searching the entire file... - for (i=0; igetTrack()] = 1; - } - } - } - } - field.reserve(tracks.size()); - subfield.reserve(tracks.size()); - model.reserve(tracks.size()); +////////////////////////////// +// +// Tool_esac2hum::calculateScanPage -- +// - field.resize(0); - subfield.resize(0); - model.resize(0); +int Tool_esac2hum::calculateScanPage(int inputPrintPage, int printPage, int scanPage, const std::vector& platePages) { + int currentPrintPage = printPage; + int currentScanPage = scanPage; + size_t plateIndex = 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); + // Iterate until we reach the input print page + while (currentPrintPage < inputPrintPage) { + ++currentScanPage; // Increment the scan page + + // Check if the current scan page matches the current plate page in the vector + if (plateIndex < platePages.size() && currentScanPage == platePages[plateIndex]) { + // Skip the plate page (increment scanPage but not printPage) + ++plateIndex; + } else { + // If not a plate page, increment the print page + ++currentPrintPage; } } + return currentScanPage; } -////////////////////////////// + +///////////////////////////////// // -// Tool_extract::expandSpines -- +// Tool_esac2humold::Tool_esac2humold -- Set the recognized options for the tool. // -void Tool_extract::expandSpines(vector& field, vector& subfield, vector& model, - HumdrumFile& infile, string& interp) { +Tool_esac2humold::Tool_esac2humold(void) { + define("debug=b", "print debug information"); + define("v|verbose=b", "verbose output"); + define("h|header=s:", "header filename for placement in output"); + define("t|trailer=s:", "trailer filename for placement in output"); + define("s|split=s:file", "split song info into separate files"); + define("x|extension=s:.krn", "split filename extension"); + define("f|first=i:1", "number of first split filename"); + define("author=b", "author of program"); + define("version=b", "compilation info"); + define("example=b", "example usages"); + define("help=b", "short description"); +} - vector splits; - splits.resize(infile.getMaxTrack()+1); - fill(splits.begin(), splits.end(), 0); - int i, j; - for (i=0; igetSpineInfo().c_str(), '(') != NULL) { - splits[infile[i].token(j)->getTrack()] = 1; - } - } +////////////////////////////// +// +// Tool_esac2humold::convert -- Convert a MusicXML file into +// Humdrum content. +// + +bool Tool_esac2humold::convertFile(ostream& out, const string& filename) { + ifstream file(filename); + stringstream s; + if (file) { + s << file.rdbuf(); + file.close(); } + return convert(out, s.str()); +} - field.reserve(infile.getMaxTrack()*2); - field.resize(0); - subfield.reserve(infile.getMaxTrack()*2); - subfield.resize(0); +bool Tool_esac2humold::convert(ostream& out, istream& input) { + convertEsacToHumdrum(out, input); + return true; +} - model.reserve(infile.getMaxTrack()*2); - model.resize(0); - int allQ = 0; - if (interp.size() == 0) { - allQ = 1; - } +bool Tool_esac2humold::convert(ostream& out, const string& input) { + stringstream ss; + ss << input; + convertEsacToHumdrum(out, ss); + return true; +} - // ggg - vector dummyfield; - vector dummysubfield; - vector dummymodel; - getInterpretationFields(dummyfield, dummysubfield, model, infile, interp, 1); - vector interptracks; - interptracks.resize(infile.getMaxTrack()+1); - fill(interptracks.begin(), interptracks.end(), 0); - for (i=0; i<(int)dummyfield.size(); i++) { - interptracks[dummyfield[i]] = 1; +////////////////////////////// +// +// Tool_esac2humold::initialize -- +// + +bool Tool_esac2humold::initialize(void) { + // handle basic options: + if (getBoolean("author")) { + cerr << "Written by Craig Stuart Sapp, " + << "craig@ccrma.stanford.edu, March 2002" << endl; + return false; + } else if (getBoolean("version")) { + cerr << getCommand() << ", version: 6 June 2017" << endl; + cerr << "compiled: " << __DATE__ << endl; + return false; + } else if (getBoolean("help")) { + usage(getCommand()); + return false; + } else if (getBoolean("example")) { + example(); + return false; } - int aval = 'a'; - int bval = 'b'; - int zero = 0; - for (i=1; i<(int)splits.size(); i++) { - if (splits[i] && (allQ || interptracks[i])) { - field.push_back(i); - subfield.push_back(aval); - model.push_back(zero); - field.push_back(i); - subfield.push_back(bval); - model.push_back(zero); - } else { - field.push_back(i); - subfield.push_back(zero); - model.push_back(zero); + debugQ = getBoolean("debug"); + verboseQ = getBoolean("verbose"); + + if (getBoolean("header")) { + if (!getFileContents(header, getString("header"))) { + return false; } + } else { + header.resize(0); } - - if (debugQ) { - m_humdrum_text << "!!expand: "; - for (i=0; i<(int)field.size(); i++) { - m_humdrum_text << field[i]; - if (subfield[i]) { - m_humdrum_text << (char)subfield[i]; - } - if (i < (int)field.size()-1) { - m_humdrum_text << ","; - } + if (getBoolean("trailer")) { + if (!getFileContents(trailer, getString("trailer"))) { + return false; } - m_humdrum_text << endl; + } else { + trailer.resize(0); + } + + if (getBoolean("split")) { + splitQ = 1; } + namebase = getString("split"); + fileextension = getString("extension"); + firstfilenum = getInteger("first"); + return true; } +////////////////////////////////////////////////////////////////////////// + + ////////////////////////////// // -// Tool_extract::reverseSpines -- reverse the order of spines, grouped by the -// given exclusive interpretation. +// Tool_esac2humold::convertEsacToHumdrum -- // -void Tool_extract::reverseSpines(vector& field, vector& subfield, - vector& model, HumdrumFile& infile, const string& exinterp) { - - vector target; - target.resize(infile.getMaxTrack()+1); - fill(target.begin(), target.end(), 0); - - vector trackstarts; - infile.getSpineStartList(trackstarts); - - for (int t=0; t<(int)trackstarts.size(); t++) { - if (trackstarts[t]->isDataType(exinterp)) { - target.at(t + 1) = 1; +void Tool_esac2humold::convertEsacToHumdrum(ostream& output, istream& infile) { + initialize(); + vector song; + song.reserve(400); + int init = 0; + // int filecounter = firstfilenum; + string outfilename; + string numberstring; + // ofstream outfile; + while (!infile.eof()) { + if (debugQ) { + cerr << "Getting a song..." << endl; + } + getSong(song, infile, init); + if (debugQ) { + cerr << "Got a song ..." << endl; } + init = 1; + convertSong(song, output); } +} - field.reserve(infile.getMaxTrack()*2); - field.resize(0); - int lasti = (int)target.size(); - for (int i=(int)target.size()-1; i>0; i--) { - if (target[i]) { - lasti = i; - field.push_back(i); - for (int j=i+1; j<(int)target.size(); j++) { - if (!target.at(j)) { - field.push_back(j); - } else { - break; - } + +////////////////////////////// +// +// Tool_esac2humold::getSong -- get a song from the EsAC file +// + +bool Tool_esac2humold::getSong(vector& song, istream& infile, int init) { + string holdbuffer; + song.resize(0); + if (init) { + // do nothing holdbuffer has the CUT[] information + } else { + while (!infile.eof() && holdbuffer.compare(0, 4, "CUT[") != 0) { + getline(infile, holdbuffer); + if (verboseQ) { + cerr << "Contents: " << holdbuffer << endl; + } + if (holdbuffer.compare(0, 2, "!!") == 0) { + song.push_back(holdbuffer); } } + if (infile.eof()) { + return false; + } } - // if the grouping spine is not first, then preserve the - // locations of the pre-spines. - int extras = 0; - if (lasti != 1) { - extras = lasti - 1; - field.resize(field.size()+extras); - for (int i=0; i<(int)field.size()-extras; i++) { - field[(int)field.size()-1-i] = field[(int)field.size()-1-extras-i]; - } - for (int i=0; i& field, vector& subfield, - vector& model, string& fieldstring, HumdrumFile& infile) { - - int maxtrack = infile.getMaxTrack(); +void Tool_esac2humold::chopExtraInfo(string& buffer) { + HumRegex hre; + hre.replaceDestructive(buffer, "", "^\\s+"); + hre.replaceDestructive(buffer, "", "\\s+$"); +} - field.reserve(maxtrack); - field.resize(0); - subfield.reserve(maxtrack); - subfield.resize(0); - model.reserve(maxtrack); - model.resize(0); +////////////////////////////// +// +// Tool_esac2humold::printHumdrumHeaderInfo -- +// - HumRegex hre; - string buffer = fieldstring; - hre.replaceDestructive(buffer, "", "\\s", "gs"); - int start = 0; - string tempstr; - vector tempfield; - vector tempsubfield; - vector tempmodel; - while (hre.search(buffer, start, "^([^,]+,?)")) { - tempfield.clear(); - tempsubfield.clear(); - tempmodel.clear(); - processFieldEntry(tempfield, tempsubfield, tempmodel, hre.getMatch(1), infile); - start += hre.getMatchEndIndex(1); - field.insert(field.end(), tempfield.begin(), tempfield.end()); - subfield.insert(subfield.end(), tempsubfield.begin(), tempsubfield.end()); - model.insert(model.end(), tempmodel.begin(), tempmodel.end()); +void Tool_esac2humold::printHumdrumHeaderInfo(ostream& out, vector& song) { + for (int i=0; i<(int)song.size(); i++) { + if (song[i].size() == 0) { + continue; + } + if (song[i].compare(0, 2, "!!") == 0) { + out << song[i] << "\n"; + continue; + } + if ((song[i][0] == ' ') || (song[i][0] == '\t')) { + continue; + } + break; } } @@ -80566,1225 +81258,1203 @@ void Tool_extract::fillFieldData(vector& field, vector& subfield, ////////////////////////////// // -// Tool_extract::processFieldEntry -- -// 3-6 expands to 3 4 5 6 -// $ expands to maximum spine track -// $-1 expands to maximum spine track minus 1, etc. +// Tool_esac2humold::printHumdrumFooterInfo -- // -void Tool_extract::processFieldEntry(vector& field, - vector& subfield, vector& model, const string& astring, - HumdrumFile& infile) { - - int finitsize = (int)field.size(); - int maxtrack = infile.getMaxTrack(); +void Tool_esac2humold::printHumdrumFooterInfo(ostream& out, vector& song) { + int i = 0; + for (i=0; i<(int)song.size(); i++) { + if (song[i].size() == 0) { + continue; + } + if (song[i].compare(0, 2, "!!") == 0) { + continue; + } + if ((song[i][0] == ' ') || (song[i][0] == '\t')) { + continue; + } + break; + } + int j = i; + for (j=i; j<(int)song.size(); j++) { + if (song[j].compare(0, 2, "!!") == 0) { + out << song[j] << "\n"; + } + } +} - vector ktracks; - infile.getKernSpineStartList(ktracks); - int maxkerntrack = (int)ktracks.size(); - int modletter; - int subletter; - HumRegex hre; - string buffer = astring; +////////////////////////////// +// +// Tool_esac2humold::convertSong -- +// - // remove any comma left at end of input astring (or anywhere else) - hre.replaceDestructive(buffer, "", ",", "g"); +void Tool_esac2humold::convertSong(vector& song, ostream& out) { - // first remove $ symbols and replace with the correct values - if (kernQ) { - removeDollarsFromString(buffer, maxkerntrack); - } else { - removeDollarsFromString(buffer, maxtrack); + int i; + if (verboseQ) { + for (i=0; i<(int)song.size(); i++) { + out << song[i] << "\n"; + } } - int zero = 0; - if (hre.search(buffer, "^(\\d+)-(\\d+)$")) { - int firstone = hre.getMatchInt(1); - int lastone = hre.getMatchInt(2); + printHumdrumHeaderInfo(out, song); - if ((firstone < 1) && (firstone != 0)) { - m_error_text << "Error: range token: \"" << astring << "\"" - << " contains too small a number at start: " << firstone << endl; - m_error_text << "Minimum number allowed is " << 1 << endl; - return; - } - if ((lastone < 1) && (lastone != 0)) { - m_error_text << "Error: range token: \"" << astring << "\"" - << " contains too small a number at end: " << lastone << endl; - m_error_text << "Minimum number allowed is " << 1 << endl; - return; - } - if (firstone > maxtrack) { - m_error_text << "Error: range token: \"" << astring << "\"" - << " contains number too large at start: " << firstone << endl; - m_error_text << "Maximum number allowed is " << maxtrack << endl; - return; - } - if (lastone > maxtrack) { - m_error_text << "Error: range token: \"" << astring << "\"" - << " contains number too large at end: " << lastone << endl; - m_error_text << "Maximum number allowed is " << maxtrack << endl; - return; - } + string key; + double mindur = 1.0; + string meter; + int tonic = 0; + getKeyInfo(song, key, mindur, tonic, meter, out); - if (firstone > lastone) { - for (int i=firstone; i>=lastone; i--) { - field.push_back(i); - subfield.push_back(zero); - model.push_back(zero); - } - } else { - for (int i=firstone; i<=lastone; i++) { - field.push_back(i); - subfield.push_back(zero); - model.push_back(zero); - } - } - } else if (hre.search(buffer, "^(\\d+)([a-z]*)")) { - int value = hre.getMatchInt(1); - modletter = 0; - subletter = 0; - if (hre.getMatch(2) == "a") { - subletter = 'a'; - } - if (hre.getMatch(2) == "b") { - subletter = 'b'; - } - if (hre.getMatch(2) == "c") { - subletter = 'c'; - } - if (hre.getMatch(2) == "d") { - modletter = 'd'; - } - if (hre.getMatch(2) == "n") { - modletter = 'n'; - } - if (hre.getMatch(2) == "r") { - modletter = 'r'; - } + vector songdata; + songdata.resize(0); + songdata.reserve(1000); + getNoteList(song, songdata, mindur, tonic); + placeLyrics(song, songdata); - if ((value < 1) && (value != 0)) { - m_error_text << "Error: range token: \"" << astring << "\"" - << " contains too small a number at end: " << value << endl; - m_error_text << "Minimum number allowed is " << 1 << endl; - return; - } - if (value > maxtrack) { - m_error_text << "Error: range token: \"" << astring << "\"" - << " contains number too large at start: " << value << endl; - m_error_text << "Maximum number allowed is " << maxtrack << endl; - return; - } - field.push_back(value); - if (value == 0) { - subfield.push_back(zero); - model.push_back(zero); - } else { - subfield.push_back(subletter); - model.push_back(modletter); + vector numerator; + vector denominator; + getMeterInfo(meter, numerator, denominator); + + postProcessSongData(songdata, numerator, denominator); + + printTitleInfo(song, out); + out << "!!!id: " << key << "\n"; + + // check for presence of lyrics + int textQ = 0; + for (i=0; i<(int)songdata.size(); i++) { + if (songdata[i].text != "") { + textQ = 1; + break; } } - if (!kernQ) { - return; + for (i=0; i<(int)header.size(); i++) { + out << header[i] << "\n"; } - // Insert fields to next **kern spine. - vector newfield; - vector newsubfield; - vector newmodel; + out << "**kern"; + if (textQ) { + out << "\t**text"; + } + out << "\n"; - vector trackstarts; - infile.getTrackStartList(trackstarts); - int i, j; - int spine; + printKeyInfo(songdata, tonic, textQ, out); + for (i=0; i<(int)songdata.size(); i++) { + printNoteData(songdata[i], textQ, out); + } + out << "*-"; + if (textQ) { + out << "\t*-"; + } + out << "\n"; - // convert kern tracks into spine tracks: - for (i=finitsize; i<(int)field.size(); i++) { - if (field[i] > 0) { - spine = ktracks[field[i]-1]->getTrack(); - field[i] = spine; + out << "!!!minrhy: "; + out << Convert::durationFloatToRecip(mindur)<<"\n"; + out << "!!!meter"; + if (numerator.size() > 1) { + out << "s"; + } + out << ": " << meter; + if ((meter == "frei") || (meter == "Frei")) { + out << " [unmetered]"; + } else if (meter.find('/') == string::npos) { + out << " interpreted as ["; + for (i=0; i<(int)numerator.size(); i++) { + out << numerator[i] << "/" << denominator[i]; + if (i < (int)numerator.size()-1) { + out << ", "; + } } + out << "]"; } + out << "\n"; - int startspineindex, stopspineindex; - for (i=0; i<(int)field.size(); i++) { - newfield.push_back(field[i]); // copy **kern spine index into new list - newsubfield.push_back(subfield[i]); - newmodel.push_back(model[i]); + printBibInfo(song, out); + printSpecialChars(out); - // search for non **kern spines after specified **kern spine: - startspineindex = field[i] + 1 - 1; - stopspineindex = maxtrack; - for (j=startspineindex; jisKern()) { - break; - } - newfield.push_back(j+1); - newsubfield.push_back(zero); - newmodel.push_back(zero); + for (i=0; i<(int)songdata.size(); i++) { + if (songdata[i].lyricerr) { + out << "!!!RWG: Lyric placement mismatch " + << "in phrase (too many syllables) " << songdata[i].phnum << " [" + << key << "]\n"; + break; } } - field = newfield; - subfield = newsubfield; - model = newmodel; + for (i=0; i<(int)trailer.size(); i++) { + out << trailer[i] << "\n"; + } + + printHumdrumFooterInfo(out, song); + +/* + if (!splitQ) { + out << "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl; + } +*/ } ////////////////////////////// // -// Tool_extract::removeDollarsFromString -- substitute $ sign for maximum track count. +// Tool_esac2humold::placeLyrics -- extract lyrics (if any) and place on correct notes // -void Tool_extract::removeDollarsFromString(string& buffer, int maxtrack) { - HumRegex hre; - char buf2[128] = {0}; - int value2; - - if (hre.search(buffer, "\\$$")) { - snprintf(buf2, 128, "%d", maxtrack); - hre.replaceDestructive(buffer, buf2, "\\$$"); - } - - if (hre.search(buffer, "\\$(?![\\d-])")) { - // don't know how this case could happen, however... - snprintf(buf2, 128, "%d", maxtrack); - hre.replaceDestructive(buffer, buf2, "\\$(?![\\d-])", "g"); +bool Tool_esac2humold::placeLyrics(vector& song, vector& songdata) { + int start = -1; + int stop = -1; + getLineRange(song, "TXT", start, stop); + if (start < 0) { + // no TXT[] field, so don't do anything + return true; } - - if (hre.search(buffer, "\\$0")) { - // replace $0 with maxtrack (used for reverse orderings) - snprintf(buf2, 128, "%d", maxtrack); - hre.replaceDestructive(buffer, buf2, "\\$0", "g"); + int line = 0; + vector lyrics; + string buffer; + for (line=0; line<=stop-start; line++) { + if (song[line+start].size() <= 4) { + cerr << "Error: lyric line is too short!: " + << song[line+start] << endl; + return false; + } + buffer = song[line+start].substr(4); + if (line == stop - start) { + auto loc = buffer.rfind(']'); + if (loc != string::npos) { + buffer.resize(loc); + } + } + if (buffer == "") { + continue; + } + getLyrics(lyrics, buffer); + cleanupLyrics(lyrics); + placeLyricPhrase(songdata, lyrics, line); } - while (hre.search(buffer, "\\$(-?\\d+)")) { - value2 = maxtrack - abs(hre.getMatchInt(1)); - snprintf(buf2, 128, "%d", value2); - hre.replaceDestructive(buffer, buf2, "\\$-?\\d+"); - } + return true; } ////////////////////////////// // -// Tool_extract::excludeFields -- print all spines except the ones in the list of fields. +// Tool_esac2humold::cleanupLyrics -- add preceeding dashes, avoid starting *'s if any, +// and convert _'s to spaces. // -void Tool_extract::excludeFields(HumdrumFile& infile, vector& field, - vector& subfield, vector& model) { - int start = 0; - for (int i=0; igetTrack(), field)) { - continue; +void Tool_esac2humold::cleanupLyrics(vector& lyrics) { + int length; + int length2; + int i, j, m; + int lastsyl = 0; + for (i=0; i<(int)lyrics.size(); i++) { + length = (int)lyrics[i].size(); + for (j=0; j 0) { + if ((lyrics[i] != ".") && + (lyrics[i] != "") && + (lyrics[i] != "%") && + (lyrics[i] != "^") && + (lyrics[i] != "|") && + (lyrics[i] != " ")) { + lastsyl = -1; + for (m=i-1; m>=0; m--) { + if ((lyrics[m] != ".") && + (lyrics[m] != "") && + (lyrics[m] != "%") && + (lyrics[i] != "^") && + (lyrics[m] != "|") && + (lyrics[m] != " ")) { + lastsyl = m; + break; + } } - if (start != 0) { - m_humdrum_text << '\t'; + if (lastsyl >= 0) { + length2 = (int)lyrics[lastsyl].size(); + if (lyrics[lastsyl][length2-1] == '-') { + for (j=0; j<=length; j++) { + lyrics[i][length - j + 1] = lyrics[i][length - j]; + } + lyrics[i][0] = '-'; + } } - start = 1; - m_humdrum_text << infile.token(i, j); } - if (start != 0) { - m_humdrum_text << endl; + } + + // avoid *'s on the start of lyrics by placing a space before + // them if they exist. + if (lyrics[i][0] == '*') { + length = (int)lyrics[i].size(); + for (j=0; j<=length; j++) { + lyrics[i][length - j + 1] = lyrics[i][length - j]; + } + lyrics[i][0] = ' '; + } + + // avoid !'s on the start of lyrics by placing a space before + // them if they exist. + if (lyrics[i][0] == '!') { + length = (int)lyrics[i].size(); + for (j=0; j<=length; j++) { + lyrics[i][length - j + 1] = lyrics[i][length - j]; } + lyrics[i][0] = ' '; } + } + } -////////////////////////////// +/////////////////////////////// // -// Tool_extract::extractFields -- print all spines in the list of fields. +// Tool_esac2humold::getLyrics -- extract the lyrics from the text string. // -void Tool_extract::extractFields(HumdrumFile& infile, vector& field, - vector& subfield, vector& model) { - - HumRegex hre; - int start = 0; - int target; - int subtarget; - int modeltarget; - string spat; +void Tool_esac2humold::getLyrics(vector& lyrics, const string& buffer) { + lyrics.resize(0); + int zero1 = 0; + string current; + int zero2 = 0; + zero2 = zero1 + zero2; - for (int i=0; igetTrack() != target) { - continue; - } - switch (subtarget) { - case 'a': - getSearchPat(spat, target, "a"); - if (hre.search(infile.token(i,j)->getSpineInfo(), spat) || - !hre.search(infile.token(i, j)->getSpineInfo(), "\\(")) { - if (start != 0) { - m_humdrum_text << '\t'; - } - start = 1; - m_humdrum_text << infile.token(i, j); - } - break; - case 'b': - getSearchPat(spat, target, "b"); - if (hre.search(infile.token(i, j)->getSpineInfo(), spat)) { - if (start != 0) { - m_humdrum_text << '\t'; - } - start = 1; - m_humdrum_text << infile.token(i, j); - } else if (!hre.search(infile.token(i, j)->getSpineInfo(), - "\\(")) { - if (start != 0) { - m_humdrum_text << '\t'; - } - start = 1; - dealWithSecondarySubspine(field, subfield, model, t, - infile, i, j, modeltarget); - } - break; - case 'c': - if (start != 0) { - m_humdrum_text << '\t'; - } - start = 1; - dealWithCospine(field, subfield, model, t, infile, i, j, - modeltarget, modeltarget, cointerp); - break; - default: - if (start != 0) { - m_humdrum_text << '\t'; - } - start = 1; - m_humdrum_text << infile.token(i, j); - } - } - } - } - if (start != 0) { - m_humdrum_text << endl; + while (i < length && buffer[i] != ' ') { + current += buffer[i++]; } + lyrics.push_back(current); + i++; } + } ////////////////////////////// // -// Tool_extract::dealWithCospine -- extract the required token(s) from a co-spine. +// Tool_esac2humold::placeLyricPhrase -- match lyrics from a phrase to the songdata. // -void Tool_extract::dealWithCospine(vector& field, vector& subfield, vector& model, - int targetindex, HumdrumFile& infile, int line, int cospine, - int comodel, int submodel, const string& cointerp) { - - vector cotokens; - cotokens.reserve(50); - - string buffer; - int i, j, k; - int index; +bool Tool_esac2humold::placeLyricPhrase(vector& songdata, vector& lyrics, int line) { + int i = 0; + int start = 0; + int found = 0; - if (infile[line].isInterpretation()) { - m_humdrum_text << infile.token(line, cospine); - return; + if (lyrics.empty()) { + return true; } - if (infile[line].isBarline()) { - m_humdrum_text << infile.token(line, cospine); - return; + // find the phrase to which the lyrics belongs + for (i=0; i<(int)songdata.size(); i++) { + if (songdata[i].phnum == line) { + found = 1; + break; + } } + start = i; - if (infile[line].isLocalComment()) { - m_humdrum_text << infile.token(line, cospine); - return; + if (!found) { + cerr << "Error: cannot find music for lyrics line " << line << endl; + cerr << "Error near input data line: " << inputline << endl; + return false; } - int count = infile[line].token(cospine)->getSubtokenCount(); - for (k=0; kgetSubtoken(k); - cotokens.resize(cotokens.size()+1); - index = (int)cotokens.size()-1; - cotokens[index] = buffer; + for (i=0; i<(int)lyrics.size() && i+start < (int)songdata.size(); i++) { + if ((lyrics[i] == " ") || (lyrics[i] == ".") || (lyrics[i] == "")) { + if (songdata[i+start].pitch < 0) { + lyrics[i] = "%"; + } else { + lyrics[i] = "|"; + } + // lyrics[i] = "."; + } + songdata[i+start].text = lyrics[i]; + songdata[i+start].lyricnum = line; + if (line != songdata[i+start].phnum) { + songdata[i+start].lyricerr = 1; // lyric does not line up with music + } } - vector spineindex; - vector subspineindex; + return true; +} - spineindex.reserve(infile.getMaxTrack()*2); - spineindex.resize(0); - subspineindex.reserve(infile.getMaxTrack()*2); - subspineindex.resize(0); - for (j=0; jisDataType(cointerp)) { - continue; - } - if (*infile.token(line, j) == ".") { - continue; +////////////////////////////// +// +// Tool_esac2humold::printSpecialChars -- print high ASCII character table +// + +void Tool_esac2humold::printSpecialChars(ostream& out) { + int i; + for (i=0; i<(int)chartable.size(); i++) { + if (chartable[i]) { + switch (i) { + case 129: out << "!!!RNB" << ": symbol: ü = u umlaut (UTF-8: " + << (char)0xc3 << (char)0xb3 << ")\n"; break; + case 130: out << "!!!RNB" << ": symbol: é= e acute (UTF-8: " + << (char)0xc3 << (char)0xa9 << ")\n"; break; + case 132: out << "!!!RNB" << ": symbol: ä = a umlaut (UTF-8: " + << (char)0xc3 << (char)0xa4 << ")\n"; break; + case 134: out << "!!!RNB" << ": symbol: $c = c acute (UTF-8: " + << (char)0xc4 << (char)0x87 << ")\n"; break; + case 136: out << "!!!RNB" << ": symbol: $l = l slash (UTF-8: " + << (char)0xc5 << (char)0x82 << ")\n"; break; + case 140: out << "!!!RNB" << ": symbol: î = i circumflex (UTF-8: " + << (char)0xc3 << (char)0xaf << ")\n"; break; + case 141: out << "!!!RNB" << ": symbol: $X = Z acute (UTF-8: " + << (char)0xc5 << (char)0xb9 << ")\n"; break; + case 142: out << "!!!RNB" << ": symbol: ä = a umlaut (UTF-8: " + << (char)0xc3 << (char)0xa4 << ")\n"; break; + case 143: out << "!!!RNB" << ": symbol: $C = C acute (UTF-8: " + << (char)0xc4 << (char)0x86 << ")\n"; break; + case 148: out << "!!!RNB" << ": symbol: ö = o umlaut (UTF-8: " + << (char)0xc3 << (char)0xb6 << ")\n"; break; + case 151: out << "!!!RNB" << ": symbol: $S = S acute (UTF-8: " + << (char)0xc5 << (char)0x9a << ")\n"; break; + case 152: out << "!!!RNB" << ": symbol: $s = s acute (UTF-8: " + << (char)0xc5 << (char)0x9b << ")\n"; break; + case 156: out << "!!!RNB" << ": symbol: $s = s acute (UTF-8: " + << (char)0xc5 << (char)0x9b << ")\n"; break; + case 157: out << "!!!RNB" << ": symbol: $L = L slash (UTF-8: " + << (char)0xc5 << (char)0x81 << ")\n"; break; + case 159: out << "!!!RNB" << ": symbol: $vc = c hachek (UTF-8: " + << (char)0xc4 << (char)0x8d << ")\n"; break; + case 162: out << "!!!RNB" << ": symbol: ó= o acute (UTF-8: " + << (char)0xc3 << (char)0xb3 << ")\n"; break; + case 163: out << "!!!RNB" << ": symbol: ú= u acute (UTF-8: " + << (char)0xc3 << (char)0xba << ")\n"; break; + case 165: out << "!!!RNB" << ": symbol: $a = a hook (UTF-8: " + << (char)0xc4 << (char)0x85 << ")\n"; break; + case 169: out << "!!!RNB" << ": symbol: $e = e hook (UTF-8: " + << (char)0xc4 << (char)0x99 << ")\n"; break; + case 171: out << "!!!RNB" << ": symbol: $y = z acute (UTF-8: " + << (char)0xc5 << (char)0xba << ")\n"; break; + case 175: out << "!!!RNB" << ": symbol: $Z = Z dot (UTF-8: " + << (char)0xc5 << (char)0xbb << ")\n"; break; + case 179: out << "!!!RNB" << ": symbol: $l = l slash (UTF-8: " + << (char)0xc5 << (char)0x82 << ")\n"; break; + case 185: out << "!!!RNB" << ": symbol: $a = a hook (UTF-8: " + << (char)0xc4 << (char)0x85 << ")\n"; break; + case 189: out << "!!!RNB" << ": symbol: $Z = Z dot (UTF-8: " + << (char)0xc5 << (char)0xbb << ")\n"; break; + case 190: out << "!!!RNB" << ": symbol: $z = z dot (UTF-8: " + << (char)0xc5 << (char)0xbc << ")\n"; break; + case 191: out << "!!!RNB" << ": symbol: $z = z dot (UTF-8: " + << (char)0xc5 << (char)0xbc << ")\n"; break; + case 224: out << "!!!RNB" << ": symbol: Ó= O acute (UTF-8: " + << (char)0xc3 << (char)0x93 << ")\n"; break; + case 225: out << "!!!RNB" << ": symbol: ß = sz ligature (UTF-8: " + << (char)0xc3 << (char)0x9f << ")\n"; break; + case 0xdf: out << "!!!RNB" << ": symbol: ß = sz ligature (UTF-8: " + << (char)0xc3 << (char)0x9f << ")\n"; break; +// Polish version: +// case 228: out << "!!!RNB" << ": symbol: $n = n acute (UTF-8: " +// << (char)0xc5 << (char)0x84 << ")\n"; break; +// Luxembourg version for some reason...: + case 228: out << "!!!RNB" << ": symbol: ä = a umlaut (UTF-8: " + << (char)0xc5 << (char)0x84 << ")\n"; break; + case 230: out << "!!!RNB" << ": symbol: c = c\n"; break; + case 231: out << "!!!RNB" << ": symbol: $vs = s hachek (UTF-8: " + << (char)0xc5 << (char)0xa1 << ")\n"; break; + case 234: out << "!!!RNB" << ": symbol: $e = e hook (UTF-8: " + << (char)0xc4 << (char)0x99 << ")\n"; break; + case 241: out << "!!!RNB" << ": symbol: $n = n acute (UTF-8: " + << (char)0xc5 << (char)0x84 << ")\n"; break; + case 243: out << "!!!RNB" << ": symbol: ó= o acute (UTF-8: " + << (char)0xc3 << (char)0xb3 << ")\n"; break; + case 252: out << "!!!RNB" << ": symbol: ü = u umlaut (UTF-8: " + << (char)0xc3 << (char)0xbc << ")\n"; break; +// default: } - count = infile[line].token(j)->getSubtokenCount(); - for (k=0; kgetSubtoken(k); - if (comodel == 'r') { - if (buffer == "r") { - continue; - } - } - spineindex.push_back(j); - subspineindex.push_back(k); } + chartable[i] = 0; } +} - if (debugQ) { - m_humdrum_text << "\n!!codata:\n"; - for (i=0; i<(int)cotokens.size(); i++) { - m_humdrum_text << "!!\t" << i << "\t" << cotokens[i]; - if (i < (int)spineindex.size()) { - m_humdrum_text << "\tspine=" << spineindex[i]; - m_humdrum_text << "\tsubspine=" << subspineindex[i]; - } else { - m_humdrum_text << "\tspine=."; - m_humdrum_text << "\tsubspine=."; - } - m_humdrum_text << endl; - } - } - string buff; - int start = 0; - for (i=0; i<(int)field.size(); i++) { - if (infile.token(line, field[i])->isDataType(cointerp)) { - continue; - } +////////////////////////////// +// +// Tool_esac2humold::printTitleInfo -- print the first line of the CUT[] field. +// - for (j=0; jgetTrack() != field[i]) { - continue; - } - if (subfield[i] == 'a') { - getSearchPat(buff, field[i], "a"); - if ((strchr(infile.token(line, j)->getSpineInfo().c_str(), '(') == NULL) || - (infile.token(line, j)->getSpineInfo().find(buff) != string::npos)) { - printCotokenInfo(start, infile, line, j, cotokens, spineindex, - subspineindex); - } - } else if (subfield[i] == 'b') { - // this section may need more work... - getSearchPat(buff, field[i], "b"); - if ((strchr(infile.token(line, j)->getSpineInfo().c_str(), '(') == NULL) || - (strstr(infile.token(line, j)->getSpineInfo().c_str(), buff.c_str()) != NULL)) { - printCotokenInfo(start, infile, line, j, cotokens, spineindex, - subspineindex); - } - } else { - printCotokenInfo(start, infile, line, j, cotokens, spineindex, - subspineindex); - } - } +bool Tool_esac2humold::printTitleInfo(vector& song, ostream& out) { + int start = -1; + int stop = -1; + getLineRange(song, "CUT", start, stop); + if (start == -1) { + cerr << "Error: cannot find CUT[] field in song: " << song[0] << endl; + return false; + } + + string buffer; + buffer = song[start].substr(4); + if (buffer.back() == ']') { + buffer.resize((int)buffer.size() - 1); + } + + out << "!!!OTL: "; + for (int i=0; i<(int)buffer.size(); i++) { + printChar(buffer[i], out); } + out << "\n"; + + return true; } ////////////////////////////// // -// Tool_extract::printCotokenInfo -- +// Tool_esac2humold::printChar -- print text characters, translating high-bit data +// if required. // -void Tool_extract::printCotokenInfo(int& start, HumdrumFile& infile, int line, int spine, - vector& cotokens, vector& spineindex, - vector& subspineindex) { - int i; - int found = 0; - for (i=0; i<(int)spineindex.size(); i++) { - if (spineindex[i] == spine) { - if (start == 0) { - start++; - } else { - m_humdrum_text << subtokenseparator; - } - if (i<(int)cotokens.size()) { - m_humdrum_text << cotokens[i]; - } else { - m_humdrum_text << "."; - } - found = 1; - } - } - if (!found) { - if (start == 0) { - start++; - } else { - m_humdrum_text << subtokenseparator; +void Tool_esac2humold::printChar(unsigned char c, ostream& out) { + out << c; +/* + if (c < 128) { + out << c; + } else { + chartable[c]++; + switch (c) { + case 129: out << "ü"; break; + case 130: out << "é"; break; + case 132: out << "ä"; break; + case 134: out << "$c"; break; + case 136: out << "$l"; break; + case 140: out << "î"; break; + case 141: out << "$X"; break; // Z acute + case 142: out << "ä"; break; // ? + case 143: out << "$C"; break; + case 148: out << "ö"; break; + case 151: out << "$S"; break; + case 152: out << "$s"; break; + case 156: out << "$s"; break; // 1250 encoding + case 157: out << "$L"; break; + case 159: out << "$vc"; break; // Cech c with v accent + case 162: out << "ó"; break; + case 163: out << "ú"; break; + case 165: out << "$a"; break; + case 169: out << "$e"; break; + case 171: out << "$y"; break; + case 175: out << "$Z"; break; // 1250 encoding + case 179: out << "$l"; break; // 1250 encoding + case 185: out << "$a"; break; // 1250 encoding + case 189: out << "$Z"; break; // Z dot + case 190: out << "$z"; break; // z dot + case 191: out << "$z"; break; // 1250 encoding + case 224: out << "Ó"; break; + case 225: out << "ß"; break; + case 0xdf: out << "ß"; break; + // Polish version: + // case 228: out << "$n"; break; + // Luxembourg version (for some reason...) + case 228: out << "ä"; break; + case 230: out << "c"; break; // ? + case 231: out << "$vs"; break; // Cech s with v accent + case 234: out << "$e"; break; // 1250 encoding + case 241: out << "$n"; break; // 1250 encoding + case 243: out << "ó"; break; // 1250 encoding + case 252: out << "ü"; break; + default: out << c; } - m_humdrum_text << "."; } +*/ } ////////////////////////////// // -// Tool_extract::dealWithSecondarySubspine -- what to print if a secondary spine -// does not exist on a line. +// Tool_esac2humold::printKeyInfo -- // -void Tool_extract::dealWithSecondarySubspine(vector& field, vector& subfield, - vector& model, int targetindex, HumdrumFile& infile, int line, - int spine, int submodel) { - - int& i = line; - int& j = spine; +void Tool_esac2humold::printKeyInfo(vector& songdata, int tonic, int textQ, + ostream& out) { + vector pitches(40, 0); + int pitchsum = 0; + int pitchcount = 0; + int i; + for (i=0; i<(int)songdata.size(); i++) { + if (songdata[i].pitch >= 0) { + pitches[songdata[i].pitch % 40]++; + pitchsum += Convert::base40ToMidiNoteNumber(songdata[i].pitch); + pitchcount++; + } + } - HumRegex hre; - string buffer; - if (infile[line].isLocalComment()) { - if ((submodel == 'n') || (submodel == 'r')) { - m_humdrum_text << "!"; - } else { - m_humdrum_text << infile.token(i, j); + // generate a clef, choosing either treble or bass clef depending + // on the average pitch. + double averagepitch = pitchsum * 1.0 / pitchcount; + if (averagepitch > 60.0) { + out << "*clefG2"; + if (textQ) { + out << "\t*clefG2"; } - } else if (infile[line].isBarline()) { - m_humdrum_text << infile.token(i, j); - } else if (infile[line].isInterpretation()) { - if ((submodel == 'n') || (submodel == 'r')) { - m_humdrum_text << "*"; - } else { - m_humdrum_text << infile.token(i, j); + out << "\n"; + } else { + out << "*clefF4"; + if (textQ) { + out << "\t*clefF4"; } - } else if (infile[line].isData()) { - if (submodel == 'n') { - m_humdrum_text << "."; - } else if (submodel == 'r') { - if (*infile.token(i, j) == ".") { - m_humdrum_text << "."; - } else if (infile.token(i, j)->find('q') != string::npos) { - m_humdrum_text << "."; - } else if (infile.token(i, j)->find('Q') != string::npos) { - m_humdrum_text << "."; - } else { - buffer = *infile.token(i, j); - if (hre.search(buffer, "{")) { - m_humdrum_text << "{"; - } - // remove secondary chord notes: - hre.replaceDestructive(buffer, "", " .*"); - // remove unnecessary characters (such as stem direction): - hre.replaceDestructive(buffer, "", - "[^}pPqQA-Ga-g0-9.;%#nr-]", "g"); - // change pitch to rest: - hre.replaceDestructive(buffer, "[A-Ga-g#n-]+", "r"); - // add editorial marking unless -Y option is given: - if (editorialInterpretation != "") { - if (hre.search(buffer, "rr")) { - hre.replaceDestructive(buffer, editorialInterpretation, "(?<=rr)"); - hre.replaceDestructive(buffer, "r", "rr"); - } else { - hre.replaceDestructive(buffer, editorialInterpretation, "(?<=r)"); - } - } - m_humdrum_text << buffer; - } - } else { - m_humdrum_text << infile.token(i, j); + out << "\n"; + } + + // generate a key signature + vector diatonic(7, 0); + diatonic[0] = getAccidentalMax(pitches[1], pitches[2], pitches[3]); + diatonic[1] = getAccidentalMax(pitches[7], pitches[8], pitches[9]); + diatonic[2] = getAccidentalMax(pitches[13], pitches[14], pitches[15]); + diatonic[3] = getAccidentalMax(pitches[18], pitches[19], pitches[20]); + diatonic[4] = getAccidentalMax(pitches[24], pitches[25], pitches[26]); + diatonic[5] = getAccidentalMax(pitches[30], pitches[31], pitches[32]); + diatonic[6] = getAccidentalMax(pitches[36], pitches[37], pitches[38]); + + int flatcount = 0; + int sharpcount = 0; + int naturalcount = 0; + for (i=0; i<7; i++) { + switch (diatonic[i]) { + case -1: flatcount++; break; + case 0: naturalcount++; break; + case +1: sharpcount++; break; } + } + + char kbuf[32] = {0}; + if (naturalcount == 7) { + // do nothing + } else if (flatcount > sharpcount) { + // print a flat key signature + if (diatonic[6] == -1) strcat(kbuf, "b-"); else goto keysigend; + if (diatonic[2] == -1) strcat(kbuf, "e-"); else goto keysigend; + if (diatonic[5] == -1) strcat(kbuf, "a-"); else goto keysigend; + if (diatonic[1] == -1) strcat(kbuf, "d-"); else goto keysigend; + if (diatonic[4] == -1) strcat(kbuf, "g-"); else goto keysigend; + if (diatonic[0] == -1) strcat(kbuf, "c-"); else goto keysigend; + if (diatonic[3] == -1) strcat(kbuf, "f-"); else goto keysigend; } else { - m_error_text << "Should not get to this line of code" << endl; - return; + // print a sharp key signature + if (diatonic[3] == +1) strcat(kbuf, "f#"); else goto keysigend; + if (diatonic[0] == +1) strcat(kbuf, "c#"); else goto keysigend; + if (diatonic[4] == +1) strcat(kbuf, "g#"); else goto keysigend; + if (diatonic[1] == +1) strcat(kbuf, "d#"); else goto keysigend; + if (diatonic[5] == +1) strcat(kbuf, "a#"); else goto keysigend; + if (diatonic[2] == +1) strcat(kbuf, "e#"); else goto keysigend; + if (diatonic[6] == +1) strcat(kbuf, "b#"); else goto keysigend; } -} +keysigend: + out << "*k[" << kbuf << "]"; + if (textQ) { + out << "\t*k[" << kbuf << "]"; + } + out << "\n"; + + // look at the third scale degree above the tonic pitch + int minor = pitches[(tonic + 40 + 11) % 40]; + int major = pitches[(tonic + 40 + 12) % 40]; + + if (minor > major) { + // minor key (or related mode) + out << "*" << Convert::base40ToKern(40 * 4 + tonic) << ":"; + if (textQ) { + out << "\t*" << Convert::base40ToKern(40 * 4 + tonic) << ":"; + } + out << "\n"; + } else { + // major key (or related mode) + out << "*" << Convert::base40ToKern(40 * 3 + tonic) << ":"; + if (textQ) { + out << "\t*" << Convert::base40ToKern(40 * 3 + tonic) << ":"; + } + out << "\n"; + } +} ////////////////////////////// // -// Tool_extract::getSearchPat -- +// Tool_esac2humold::getAccidentalMax -- // -void Tool_extract::getSearchPat(string& spat, int target, const string& modifier) { - if (modifier.size() > 20) { - m_error_text << "Error in GetSearchPat" << endl; - return; +int Tool_esac2humold::getAccidentalMax(int a, int b, int c) { + if (a > b && a > c) { + return -1; + } else if (c > a && c > b) { + return +1; + } else { + return 0; } - spat.reserve(16); - spat = "\\("; - spat += to_string(target); - spat += "\\)"; - spat += modifier; } - ////////////////////////////// // -// Tool_extract::dealWithSpineManipulators -- check for proper Humdrum syntax of -// spine manipulators (**, *-, *x, *v, *^) when creating the output. +// Tool_esac2humold::postProcessSongData -- clean up data and do some interpreting. // -void Tool_extract::dealWithSpineManipulators(HumdrumFile& infile, int line, - vector& field, vector& subfield, vector& model) { - - vector vmanip; // counter for *v records on line - vmanip.resize(infile[line].getFieldCount()); - fill(vmanip.begin(), vmanip.end(), 0); - - vector xmanip; // counter for *x record on line - xmanip.resize(infile[line].getFieldCount()); - fill(xmanip.begin(), xmanip.end(), 0); - - int i = 0; - int j; - for (j=0; j<(int)vmanip.size(); j++) { - if (*infile.token(line, j) == "*v") { - vmanip[j] = 1; - } - if (*infile.token(line, j) == "*x") { - xmanip[j] = 1; +void Tool_esac2humold::postProcessSongData(vector& songdata, vector& numerator, + vector& denominator) { + int i, j; + // move phrase start markers off of rests and onto the + // first note that it finds + for (i=0; i<(int)songdata.size()-1; i++) { + if (songdata[i].pitch < 0 && songdata[i].phstart) { + songdata[i+1].phstart = songdata[i].phstart; + songdata[i].phstart = 0; } } - int counter = 1; - for (i=1; i<(int)xmanip.size(); i++) { - if ((xmanip[i] == 1) && (xmanip[i-1] == 1)) { - xmanip[i] = counter; - xmanip[i-1] = counter; - counter++; + // move phrase ending markers off of rests and onto the + // previous note that it finds + for (i=(int)songdata.size()-1; i>0; i--) { + if (songdata[i].pitch < 0 && songdata[i].phend) { + songdata[i-1].phend = songdata[i].phend; + songdata[i].phend = 0; } } - counter = 1; - i = 0; - while (i < (int)vmanip.size()) { - if (vmanip[i] == 1) { - while ((i < (int)vmanip.size()) && (vmanip[i] == 1)) { - vmanip[i] = counter; - i++; - } - counter++; + // examine barline information + double dur = 0.0; + for (i=(int)songdata.size()-1; i>=0; i--) { + if (songdata[i].bar == 1) { + songdata[i].bardur = dur; + dur = songdata[i].duration; + } else { + dur += songdata[i].duration; } - i++; } - vector fieldoccur; // nth occurance of an input spine in the output - fieldoccur.resize(field.size()); - fill(fieldoccur.begin(), fieldoccur.end(), 0); - - vector trackcounter; // counter of input spines occurances in output - trackcounter.resize(infile.getMaxTrack()+1); - fill(trackcounter.begin(), trackcounter.end(), 0); - - for (i=0; i<(int)field.size(); i++) { - if (field[i] != 0) { - trackcounter[field[i]]++; - fieldoccur[i] = trackcounter[field[i]]; + int barnum = 0; + double firstdur = 0.0; + if (numerator.size() == 1 && numerator[0] > 0) { + // handle single non-frei meter + songdata[0].num = numerator[0]; + songdata[0].denom = denominator[0]; + dur = 0; + double meterdur = 4.0 / denominator[0] * numerator[0]; + for (i=0; i<(int)songdata.size(); i++) { + if (songdata[i].bar) { + dur = 0.0; + } else { + dur += songdata[i].duration; + if (fabs(dur - meterdur) < 0.001) { + songdata[i].bar = 1; + songdata[i].barinterp = 1; + dur = 0.0; + } + } } - } - - vector tempout; - vector vserial; - vector xserial; - vector fpos; // input column of output spine - tempout.reserve(1000); - tempout.resize(0); - - vserial.reserve(1000); - vserial.resize(0); - - xserial.reserve(1000); - xserial.resize(0); - - fpos.reserve(1000); - fpos.resize(0); - - string spat; - string spinepat; - HumRegex hre; - int subtarget; - int modeltarget; - int xdebug = 0; - int vdebug = 0; - int suppress = 0; - int target; - int tval; - for (int t=0; t<(int)field.size(); t++) { - target = field[t]; - subtarget = subfield[t]; - modeltarget = model[t]; - if (modeltarget == 0) { - switch (subtarget) { - case 'a': - case 'b': - modeltarget = submodel; - break; - case 'c': - modeltarget = comodel; - } - } - suppress = 0; - if (target == 0) { - if (infile.token(line, 0)->compare(0, 2, "**") == 0) { - storeToken(tempout, blankName); - tval = 0; - vserial.push_back(tval); - xserial.push_back(tval); - fpos.push_back(tval); - } else if (*infile.token(line, 0) == "*-") { - storeToken(tempout, "*-"); - tval = 0; - vserial.push_back(tval); - xserial.push_back(tval); - fpos.push_back(tval); + // readjust measure beat counts + dur = 0.0; + for (i=(int)songdata.size()-1; i>=0; i--) { + if (songdata[i].bar == 1) { + songdata[i].bardur = dur; + dur = songdata[i].duration; } else { - storeToken(tempout, "*"); - tval = 0; - vserial.push_back(tval); - xserial.push_back(tval); - fpos.push_back(tval); + dur += songdata[i].duration; } - } else { - for (j=0; jgetTrack() != target) { - continue; - } - // filter by subfield - if (subtarget == 'a') { - getSearchPat(spat, target, "b"); - if (hre.search(infile.token(line, j)->getSpineInfo(), spat)) { - continue; - } - } else if (subtarget == 'b') { - getSearchPat(spat, target, "a"); - if (hre.search(infile.token(line, j)->getSpineInfo(), spat)) { - continue; - } - } - - switch (subtarget) { - case 'a': - - if (!hre.search(infile.token(line, j)->getSpineInfo(), "\\(")) { - if (*infile.token(line, j) == "*^") { - storeToken(tempout, "*"); - } else { - storeToken(tempout, *infile.token(line, j)); - } - } else { - getSearchPat(spat, target, "a"); - spinepat = infile.token(line, j)->getSpineInfo(); - hre.replaceDestructive(spinepat, "\\(", "\\(", "g"); - hre.replaceDestructive(spinepat, "\\)", "\\)", "g"); - - if ((*infile.token(line, j) == "*v") && - (spinepat == spat)) { - storeToken(tempout, "*"); - } else { - getSearchPat(spat, target, "b"); - if ((spinepat == spat) && - (*infile.token(line, j) == "*v")) { - // do nothing - suppress = 1; - } else { - storeToken(tempout, *infile.token(line, j)); - } - } - } - - break; - case 'b': - - if (!hre.search(infile.token(line, j)->getSpineInfo(), "\\(")) { - if (*infile.token(line, j) == "*^") { - storeToken(tempout, "*"); - } else { - storeToken(tempout, *infile.token(line, j)); - } - } else { - getSearchPat(spat, target, "b"); - spinepat = infile.token(line, j)->getSpineInfo(); - hre.replaceDestructive(spinepat, "\\(", "\\(", "g"); - hre.replaceDestructive(spinepat, "\\)", "\\)", "g"); - - if ((*infile.token(line, j) == "*v") && - (spinepat == spat)) { - storeToken(tempout, "*"); - } else { - getSearchPat(spat, target, "a"); - if ((spinepat == spat) && - (*infile.token(line, j) == "*v")) { - // do nothing - suppress = 1; - } else { - storeToken(tempout, *infile.token(line, j)); - } - } - } - - break; - case 'c': - // work on later - storeToken(tempout, *infile.token(line, j)); - break; - default: - storeToken(tempout, *infile.token(line, j)); - } - - if (suppress) { - continue; - } - - if (tempout[(int)tempout.size()-1] == "*x") { - tval = fieldoccur[t] * 1000 + xmanip[j]; - xserial.push_back(tval); - xdebug = 1; - } else { - tval = 0; - xserial.push_back(tval); - } + } + firstdur = dur; - if (tempout[(int)tempout.size()-1] == "*v") { - tval = fieldoccur[t] * 1000 + vmanip[j]; - vserial.push_back(tval); - vdebug = 1; - } else { - tval = 0; - vserial.push_back(tval); - } + // number the barlines + barnum = 0; + if (fabs(firstdur - meterdur) < 0.001) { + // music for first bar, next bar will be bar 2 + barnum = 2; + } else { + barnum = 1; + // pickup-measure + } + for (i=0; i<(int)songdata.size(); i++) { + if (songdata[i].bar == 1) { + songdata[i].barnum = barnum++; + } + } - fpos.push_back(j); + } else if (numerator.size() == 1 && numerator[0] == -1) { + // handle free meter + // number the barline + firstdur = dur; + barnum = 1; + for (i=0; i<(int)songdata.size(); i++) { + if (songdata[i].bar == 1) { + songdata[i].barnum = barnum++; } } - } - if (debugQ && xdebug) { - m_humdrum_text << "!! *x serials = "; - for (int ii=0; ii<(int)xserial.size(); ii++) { - m_humdrum_text << xserial[ii] << " "; - } - m_humdrum_text << "\n"; - } + } else { + // handle multiple time signatures - if (debugQ && vdebug) { - m_humdrum_text << "!!LINE: " << infile[line] << endl; - m_humdrum_text << "!! *v serials = "; - for (int ii=0; ii<(int)vserial.size(); ii++) { - m_humdrum_text << vserial[ii] << " "; + // get the duration of each type of meter: + vector meterdurs; + meterdurs.resize(numerator.size()); + for (i=0; i<(int)meterdurs.size(); i++) { + meterdurs[i] = 4.0 / denominator[i] * numerator[i]; } - m_humdrum_text << "\n"; - } - // check for proper *x syntax ///////////////////////////////// - for (i=0; i<(int)xserial.size()-1; i++) { - if (!xserial[i]) { - continue; - } - if (xserial[i] != xserial[i+1]) { - if (tempout[i] == "*x") { - xserial[i] = 0; - tempout[i] = "*"; + // measure beat counts: + dur = 0.0; + for (i=(int)songdata.size()-1; i>=0; i--) { + if (songdata[i].bar == 1) { + songdata[i].bardur = dur; + dur = songdata[i].duration; + } else { + dur += songdata[i].duration; } - } else { - i++; } - } + firstdur = dur; - if ((tempout.size() == 1) || (xserial.size() == 1)) { - // get rid of *x if there is only one spine in output - if (xserial[0]) { - xserial[0] = 0; - tempout[0] = "*"; - } - } else if ((int)xserial.size() > 1) { - // check the last item in the list - int index = (int)xserial.size()-1; - if (tempout[index] == "*x") { - if (xserial[index] != xserial[index-1]) { - xserial[index] = 0; - tempout[index] = "*"; + // interpret missing barlines + int currentmeter = 0; + // find first meter + for (i=0; i<(int)numerator.size(); i++) { + if (fabs(firstdur - meterdurs[i]) < 0.001) { + songdata[0].num = numerator[i]; + songdata[0].denom = denominator[i]; + currentmeter = i; } } - } - - // check for proper *v syntax ///////////////////////////////// - vector vsplit; - vsplit.resize((int)vserial.size()); - fill(vsplit.begin(), vsplit.end(), 0); + // now handle the meters in the rest of the music... + int fnd = 0; + dur = 0; + for (i=0; i<(int)songdata.size()-1; i++) { + if (songdata[i].bar) { + if (songdata[i].bardur != meterdurs[currentmeter]) { + // try to find the correct new meter - // identify necessary line splits - for (i=0; i<(int)vserial.size()-1; i++) { - if (!vserial[i]) { - continue; - } - while ((i<(int)vserial.size()-1) && (vserial[i]==vserial[i+1])) { - i++; - } - if ((i<(int)vserial.size()-1) && vserial[i]) { - if (vserial.size() > 1) { - if (vserial[i+1]) { - vsplit[i+1] = 1; + fnd = 0; + for (j=0; j<(int)numerator.size(); j++) { + if (j == currentmeter) { + continue; + } + if (fabs(songdata[i].bardur - meterdurs[j]) < 0.001) { + songdata[i+1].num = numerator[j]; + songdata[i+1].denom = denominator[j]; + currentmeter = j; + fnd = 1; + } + } + if (!fnd) { + for (j=0; j<(int)numerator.size(); j++) { + if (j == currentmeter) { + continue; + } + if (fabs(songdata[i].bardur/2.0 - meterdurs[j]) < 0.001) { + songdata[i+1].num = numerator[j]; + songdata[i+1].denom = denominator[j]; + currentmeter = j; + fnd = 1; + } + } + } + } + dur = 0.0; + } else { + dur += songdata[i].duration; + if (fabs(dur - meterdurs[currentmeter]) < 0.001) { + songdata[i].bar = 1; + songdata[i].barinterp = 1; + dur = 0.0; } } } - } - // remove single *v spines: + // perhaps sum duration of measures again and search for error here? - for (i=0; i<(int)vsplit.size()-1; i++) { - if (vsplit[i] && vsplit[i+1]) { - if (tempout[i] == "*v") { - tempout[i] = "*"; - vsplit[i] = 0; + // finally, number the barlines: + barnum = 1; + for (i=0; i<(int)numerator.size(); i++) { + if (fabs(firstdur - meterdurs[i]) < 0.001) { + barnum = 2; + break; } } - } - - if (debugQ) { - m_humdrum_text << "!!vsplit array: "; - for (i=0; i<(int)vsplit.size(); i++) { - m_humdrum_text << " " << vsplit[i]; - } - m_humdrum_text << endl; - } - - if (vsplit.size() > 0) { - if (vsplit[(int)vsplit.size()-1]) { - if (tempout[(int)tempout.size()-1] == "*v") { - tempout[(int)tempout.size()-1] = "*"; - vsplit[(int)vsplit.size()-1] = 0; + for (i=0; i<(int)songdata.size(); i++) { + if (songdata[i].bar == 1) { + songdata[i].barnum = barnum++; } } - } - int vcount = 0; - for (i=0; i<(int)vsplit.size(); i++) { - vcount += vsplit[i]; - } - if (vcount) { - printMultiLines(vsplit, vserial, tempout); } - int start = 0; - for (i=0; i<(int)tempout.size(); i++) { - if (tempout[i] != "") { - if (start != 0) { - m_humdrum_text << "\t"; - } - m_humdrum_text << tempout[i]; - start++; - } - } - if (start) { - m_humdrum_text << '\n'; - } } ////////////////////////////// // -// Tool_extract::printMultiLines -- print separate *v lines. +// Tool_esac2humold::getMeterInfo -- // -void Tool_extract::printMultiLines(vector& vsplit, vector& vserial, - vector& tempout) { - int i; - - int splitpoint = -1; - for (i=0; i<(int)vsplit.size(); i++) { - if (vsplit[i]) { - splitpoint = i; - break; - } - } - - if (debugQ) { - m_humdrum_text << "!!tempout: "; - for (i=0; i<(int)tempout.size(); i++) { - m_humdrum_text << tempout[i] << " "; - } - m_humdrum_text << endl; - } - - if (splitpoint == -1) { +void Tool_esac2humold::getMeterInfo(string& meter, vector& numerator, + vector& denominator) { + numerator.clear(); + denominator.clear(); + HumRegex hre; + hre.replaceDestructive(meter, "", "^\\s+"); + hre.replaceDestructive(meter, "", "\\s+$"); + if (hre.search(meter, "^(\\d+)/(\\d+)$")) { + numerator.push_back(hre.getMatchInt(1)); + denominator.push_back(hre.getMatchInt(2)); return; } - - int start = 0; - int printv = 0; - for (i=0; i& storage, const string& string) { - storage.push_back(string); -} - -void storeToken(vector& storage, int index, const string& string) { - storage[index] = string; +void Tool_esac2humold::getLineRange(vector& song, const string& field, + int& start, int& stop) { + string searchstring = field;; + searchstring += "["; + start = stop = -1; + for (int i=0; i<(int)song.size(); i++) { + auto loc = song[i].find(']'); + if (song[i].compare(0, searchstring.size(), searchstring) == 0) { + start = i; + if (loc != string::npos) { + stop = i; + break; + } + } else if ((start >= 0) && (loc != string::npos)) { + stop = i; + break; + } + } } ////////////////////////////// // -// Tool_extract::isInList -- returns true if first number found in list of numbers. -// returns the matching index plus one. +// Tool_esac2humold::getNoteList -- get a list of the notes and rests and barlines in +// the MEL field. // -int Tool_extract::isInList(int number, vector& listofnum) { - int i; - for (i=0; i<(int)listofnum.size(); i++) { - if (listofnum[i] == number) { - return i+1; +bool Tool_esac2humold::getNoteList(vector& song, vector& songdata, double mindur, + int tonic) { + songdata.resize(0); + NoteData tempnote; + int melstart = -1; + int melstop = -1; + int i, j; + int octave = 0; + int degree = 0; + int accidental = 0; + double duration = mindur; + int bar = 0; + // int tuplet = 0; + int major[8] = {-1, 0, 6, 12, 17, 23, 29, 35}; + // int oldstate = -1; + int state = -1; + int nextstate = -1; + int phend = 0; + int phnum = 0; + int phstart = 0; + int slend = 0; + int slstart = 0; + int tie = 0; + + getLineRange(song, "MEL", melstart, melstop); + + for (i=melstart; i<=melstop; i++) { + if (song[i].size() < 4) { + cerr << "Error: invalid line in MEL[]: " << song[i] << endl; + return false; } - } - return 0; + j = 4; + phstart = 1; + phend = 0; + // Note Format: (+|-)*[0..7]_*\.*( )? + // ONADB + // Order of data: Octave, Note, Accidental, Duration, Barline -} + #define STATE_SLSTART -1 + #define STATE_OCTAVE 0 + #define STATE_NOTE 1 + #define STATE_ACC 2 + #define STATE_DUR 3 + #define STATE_BAR 4 + #define STATE_SLEND 5 + while (j < 200 && (j < (int)song[i].size())) { + // oldstate = state; + switch (song[i][j]) { + // Octave information: + case '-': octave--; state = STATE_OCTAVE; break; + case '+': octave++; state = STATE_OCTAVE; break; + // Duration information: + case '_': duration *= 2.0; state = STATE_DUR; break; + case '.': duration *= 1.5; state = STATE_DUR; break; -////////////////////////////// -// -// Tool_extract::getTraceData -- -// + // Accidental information: + case 'b': accidental--; state = STATE_ACC; break; + case '#': accidental++; state = STATE_ACC; break; -void Tool_extract::getTraceData(vector& startline, vector >& fields, - const string& tracefile, HumdrumFile& infile) { - char buffer[1024] = {0}; - HumRegex hre; - int linenum; - startline.reserve(10000); - startline.resize(0); - fields.reserve(10000); - fields.resize(0); + // Note information: + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': + degree = major[song[i][j] - '0']; + state = STATE_NOTE; + break; + case 'O': + degree = major[0]; + state = STATE_NOTE; + break; - ifstream input; - input.open(tracefile.c_str()); - if (!input.is_open()) { - m_error_text << "Error: cannot open file for reading: " << tracefile << endl; - return; - } + // Barline information: + case ' ': + state = STATE_BAR; + if (song[i][j+1] == ' ') { + bar = 1; + } + break; - string temps; - vector field; - vector subfield; - vector model; + // Other information: + case '{': slstart = 1; state = STATE_SLSTART; break; + case '}': slend = 1; state = STATE_SLEND; break; + // case '(': tuplet = 1; break; + // case ')': tuplet = 0; break; + case '/': break; + case ']': break; +// case '>': break; // unknown marker +// case '<': break; // + case '^': tie = 1; state = STATE_NOTE; break; + default : cerr << "Error: unknown character " << song[i][j] + << " on the line: " << song[i] << endl; + return false; + } + j++; + switch (song[i][j]) { + case '-': case '+': nextstate = STATE_OCTAVE; break; + case 'O': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': nextstate = STATE_NOTE; break; + case 'b': case '#': nextstate = STATE_ACC; break; + case '_': case '.': nextstate = STATE_DUR; break; + case '{': nextstate = STATE_SLSTART; break; + case '}': nextstate = STATE_SLEND; break; + case '^': nextstate = STATE_NOTE; break; + case ' ': + if (song[i][j+1] == ' ') nextstate = STATE_BAR; + else if (song[i][j+1] == '/') nextstate = -2; + break; + case '\0': + phend = 1; + break; + default: nextstate = -1; + } - input.getline(buffer, 1024); - while (!input.eof()) { - if (hre.search(buffer, "^\\s*$")) { - continue; - } - if (!hre.search(buffer, "(\\d+)")) { - continue; - } - linenum = hre.getMatchInt(1); - linenum--; // adjust so that line 0 is the first line in the file - temps = buffer; - hre.replaceDestructive(temps, "", "\\d+"); - hre.replaceDestructive(temps, "", "[^,\\s\\d\\$\\-].*"); // remove any possible comments - hre.replaceDestructive(temps, "", "\\s", "g"); - if (hre.search(temps, "^\\s*$")) { - // no field data to process online - continue; + if (nextstate < state || + ((nextstate == STATE_NOTE) && (state == nextstate))) { + tempnote.clear(); + if (degree < 0) { // rest + tempnote.pitch = -999; + } else { + tempnote.pitch = degree + 40*(octave + 4) + accidental + tonic; + } + if (tie) { + tempnote.pitch = songdata[(int)songdata.size()-1].pitch; + if (songdata[(int)songdata.size()-1].tieend) { + songdata[(int)songdata.size()-1].tiecont = 1; + songdata[(int)songdata.size()-1].tieend = 0; + } else { + songdata[(int)songdata.size()-1].tiestart = 1; + } + tempnote.tieend = 1; + } + tempnote.duration = duration; + tempnote.phend = phend; + tempnote.bar = bar; + tempnote.phstart = phstart; + tempnote.slstart = slstart; + tempnote.slend = slend; + if (nextstate == -2) { + tempnote.bar = 2; + tempnote.phend = 1; + } + tempnote.phnum = phnum; + + songdata.push_back(tempnote); + duration = mindur; + degree = 0; + bar = 0; + tie = 0; + phend = 0; + phstart = 0; + slend = 0; + slstart = 0; + octave = 0; + accidental = 0; + if (nextstate == -2) { + return true; + } + } } - startline.push_back(linenum); - string ttemp = temps; - fillFieldData(field, subfield, model, ttemp, infile); - fields.push_back(field); - input.getline(buffer, 1024); + phnum++; } + return true; } ////////////////////////////// // -// Tool_extract::extractTrace -- +// Tool_esac2humold::printNoteData -- // -void Tool_extract::extractTrace(HumdrumFile& infile, const string& tracefile) { - vector startline; - vector > fields; - getTraceData(startline, fields, tracefile, infile); - int i, j; +void Tool_esac2humold::printNoteData(NoteData& data, int textQ, ostream& out) { - if (debugQ) { - for (i=0; i<(int)startline.size(); i++) { - m_humdrum_text << "!!TRACE " << startline[i]+1 << ":\t"; - for (j=0; j<(int)fields[i].size(); j++) { - m_humdrum_text << fields[i][j] << " "; - } - m_humdrum_text << "\n"; + if (data.num > 0) { + out << "*M" << data.num << "/" << data.denom; + if (textQ) { + out << "\t*M" << data.num << "/" << data.denom; } + out << "\n"; } - - - if (startline.size() == 0) { - for (i=0; i& field) { - int j; - int t; - int start = 0; - int target; + // print barline information + if (data.bar == 1) { - start = 0; - for (t=0; t<(int)field.size(); t++) { - target = field[t]; - for (j=0; jgetTrack() != target) { - continue; + out << "="; + if (data.barnum > 0) { + out << data.barnum; + } + if (data.barinterp) { + // out << "yy"; + } + if (debugQ) { + if (data.bardur > 0.0) { + out << "[" << data.bardur << "]"; } - if (start != 0) { - m_humdrum_text << '\t'; + } + if (textQ) { + out << "\t"; + out << "="; + if (data.barnum > 0) { + out << data.barnum; + } + if (data.barinterp) { + // out << "yy"; + } + if (debugQ) { + if (data.bardur > 0.0) { + out << "[" << data.bardur << "]"; + } } - start = 1; - m_humdrum_text << infile.token(line, j); } - } - if (start != 0) { - m_humdrum_text << endl; + + out << "\n"; + } else if (data.bar == 2) { + out << "=="; + if (textQ) { + out << "\t=="; + } + out << "\n"; } } @@ -81792,225 +82462,284 @@ void Tool_extract::printTraceLine(HumdrumFile& infile, int line, vector& fi ////////////////////////////// // -// Tool_extract::example -- example usage of the sonority program +// Tool_esac2humold::getKeyInfo -- look for a KEY[] entry and extract the data. +// +// ggg fix this function // -void Tool_extract::example(void) { - m_free_text << - " \n" - << endl; -} +bool Tool_esac2humold::getKeyInfo(vector& song, string& key, double& mindur, + int& tonic, string& meter, ostream& out) { + int i; + for (i=0; i<(int)song.size(); i++) { + if (song[i].compare(0, 4, "KEY[") == 0) { + key = song[i][4]; // letter + key += song[i][5]; // number + key += song[i][6]; // number + key += song[i][7]; // number + key += song[i][8]; // number + if (!isspace(song[i][9])) { + key += song[i][9]; // optional letter (sometimes ' or ") + } + if (!isspace(song[i][10])) { + key += song[i][10]; // illegal but possible extra letter + } + if (song[i][10] != ' ') { + out << "!! Warning key field is not complete" << endl; + out << "!!Key field: " << song[i] << endl; + } + mindur = (song[i][11] - '0') * 10 + (song[i][12] - '0'); + mindur = 4.0 / mindur; + string tonicstr; + if (song[i][14] != ' ') { + tonicstr[0] = song[i][14]; + if (tolower(song[i][15]) == 'b') { + tonicstr[1] = '-'; + } else { + tonicstr[1] = song[i][15]; + } + tonicstr[2] = '\0'; + } else { + tonicstr = song[i][15]; + } -////////////////////////////// -// -// Tool_extract::usage -- gives the usage statement for the sonority program -// + // convert German notation to English for note names + // Hopefully all references to B will mean English B-flat. + if (tonicstr == "B") { + tonicstr = "B-"; + } + if (tonicstr == "H") { + tonicstr = "B"; + } -void Tool_extract::usage(const string& command) { - m_free_text << - " \n" - << endl; + tonic = Convert::kernToBase40(tonicstr); + if (tonic <= 0) { + cerr << "Error: invalid tonic on line: " << song[i] << endl; + return false; + } + tonic = tonic % 40; + meter = song[i].substr(17); + if (meter.back() != ']') { + cerr << "Error with meter on line: " << song[i] << endl; + cerr << "Meter area: " << meter << endl; + cerr << "Expected ] as last character but found " << meter.back() << endl; + return false; + } else { + meter.resize((int)meter.size() - 1); + } + return true; + } + } + cerr << "Error: did not find a KEY field" << endl; + return false; } -////////////////////////////// +/////////////////////////////// // -// Tool_extract::initialize -- +// Tool_esac2humold::getFileContents -- read a file into the array. // -void Tool_extract::initialize(HumdrumFile& infile) { - // handle basic options: - if (getBoolean("author")) { - m_free_text << "Written by Craig Stuart Sapp, " - << "craig@ccrma.stanford.edu, Feb 2008" << endl; - return; - } else if (getBoolean("version")) { - m_free_text << getArg(0) << ", version: Feb 2008" << endl; - m_free_text << "compiled: " << __DATE__ << endl; - return; - } else if (getBoolean("help")) { - usage(getCommand().c_str()); - return; - } else if (getBoolean("example")) { - example(); - return; - } - - excludeQ = getBoolean("x"); - interpQ = getBoolean("i"); - interps = getString("i"); - kernQ = getBoolean("k"); - rkernQ = getBoolean("K"); - - interpstate = 1; - if (!interpQ) { - interpQ = getBoolean("I"); - interpstate = 0; - interps = getString("I"); - } - if (interps.size() > 0) { - if (interps[0] != '*') { - // Automatically add ** if not given on exclusive interpretation - string tstring = "**"; - interps = tstring + interps; - } - } - - removerestQ = getBoolean("no-rest"); - noEmptyQ = getBoolean("no-empty"); - emptyQ = getBoolean("empty"); - fieldQ = getBoolean("f"); - debugQ = getBoolean("debug"); - countQ = getBoolean("count"); - traceQ = getBoolean("trace"); - tracefile = getString("trace"); - reverseQ = getBoolean("reverse"); - expandQ = getBoolean("expand") || getBoolean("E"); - submodel = getString("model").c_str()[0]; - cointerp = getString("cointerp"); - comodel = getString("cospine-model").c_str()[0]; - - if (getBoolean("no-editoral-rests")) { - editorialInterpretation = ""; - } +bool Tool_esac2humold::getFileContents(vector& array, const string& filename) { + ifstream infile(filename.c_str()); + array.reserve(100); + array.resize(0); - if (interpQ) { - fieldQ = 1; + if (!infile.is_open()) { + cerr << "Error: cannot open file: " << filename << endl; + return false; } - if (emptyQ) { - fieldQ = 1; - } + char holdbuffer[1024] = {0}; - if (noEmptyQ) { - fieldQ = 1; + infile.getline(holdbuffer, 256, '\n'); + while (!infile.eof()) { + array.push_back(holdbuffer); + infile.getline(holdbuffer, 256, '\n'); } - if (expandQ) { - fieldQ = 1; - expandInterp = getString("expand-interp"); - } + infile.close(); + return true; +} - if (!reverseQ) { - reverseQ = getBoolean("R"); - if (reverseQ) { - reverseInterp = getString("R"); - } - } - if (reverseQ) { - fieldQ = 1; - } - if (excludeQ) { - fieldstring = getString("x"); - } else if (fieldQ) { - fieldstring = getString("f"); - } else if (kernQ) { - fieldstring = getString("k"); - fieldQ = 1; - } else if (rkernQ) { - fieldstring = getString("K"); - fieldQ = 1; - fieldstring = reverseFieldString(fieldstring, infile.getMaxTrack()); - } +////////////////////////////// +// +// Tool_esac2humold::example -- +// - spineListQ = getBoolean("spine-list"); - grepQ = getBoolean("grep"); - grepString = getString("grep"); +void Tool_esac2humold::example(void) { - if (getBoolean("name")) { - blankName = getString("name"); - if (blankName == "") { - blankName = "**blank"; - } else if (blankName.compare(0, 2, "**") != 0) { - if (blankName.compare(0, 1, "*") != 0) { - blankName = "**" + blankName; - } else { - blankName = "*" + blankName; - } - } - } } + ////////////////////////////// // -// Tool_extract::reverseFieldString -- No dollar expansion for now. +// Tool_esac2humold::usage -- // -string Tool_extract::reverseFieldString(const string& input, int maxval) { - string output; - string number; - for (int i=0; i<(int)input.size(); i++) { - if (isdigit(input[i])) { - number += input[i]; - continue; - } else { - if (!number.empty()) { - int value = (int)strtol(number.c_str(), NULL, 10); - value = maxval - value + 1; - output += to_string(value); - output += input[i]; - number.clear(); - } - } - } - if (!number.empty()) { - int value = (int)strtol(number.c_str(), NULL, 10); - value = maxval - value + 1; - output += to_string(value); - } - return output; +void Tool_esac2humold::usage(const string& command) { + } ////////////////////////////// // -// Tool_fb::Tool_fb -- Set the recognized options for the tool. +// Tool_esac2humold::printBibInfo -- // -Tool_fb::Tool_fb(void) { - define("c|compound=b", "output reasonable figured bass numbers within octave"); - define("a|accidentals|accid|acc=b", "display accidentals in front of the numbers"); - define("b|base|base-track=i:1", "number of the base kern track (compare with -k)"); - define("i|intervallsatz=b", "display numbers under their voice instead of under the base staff"); - define("o|sort|order=b", "sort figured bass numbers by size"); - define("l|lowest=b", "use lowest note as base note"); - define("n|normalize=b", "remove number 8 and doubled numbers; adds -co"); - define("r|reduce|abbreviate|abbr=b", "use abbreviated figures; adds -nco"); - define("t|ties=b", "hide numbers without attack or changing base (needs -i)"); - define("f|figuredbass=b", "shortcut for -acorn3"); - define("3|hide-three=b", "hide number 3 if it has an accidental"); - define("m|negative=b", "show negative numbers"); - define("above=b", "show numbers above the staff (**fba)"); - define("rate=s:", "rate to display the numbers (use a **recip value, e.g. 4, 4.)"); - define("k|kern-tracks=s", "process only the specified kern spines"); - define("s|spine-tracks|spine|spines|track|tracks=s", "Process only the specified spines"); - define("hint=b", "determine harmonic intervals with interval quality"); +void Tool_esac2humold::printBibInfo(vector& song, ostream& out) { + int i, j; + char buffer[32] = {0}; + int start = -1; + int stop = -1; + int count = 0; + string templine; + + for (i=0; i<(int)song.size(); i++) { + if (song[i] == "") { + continue; + } + if (song[i][0] != ' ') { + if (song[i].size() < 4 || song[i][3] != '[') { + if (song[i].compare(0, 2, "!!") != 0) { + out << "!! " << song[i] << "\n"; + } + continue; + } + strncpy(buffer, song[i].c_str(), 3); + buffer[3] = '\0'; + if (strcmp(buffer, "MEL") == 0) continue; + if (strcmp(buffer, "TXT") == 0) continue; + // if (strcmp(buffer, "KEY") == 0) continue; + getLineRange(song, buffer, start, stop); + + // don't print CUT field if only one line. !!!OTL: will contain CUT[] + // if (strcmp(buffer, "CUT") == 0 && start == stop) continue; + + buffer[0] = tolower(buffer[0]); + buffer[1] = tolower(buffer[1]); + buffer[2] = tolower(buffer[2]); + + count = 1; + templine = ""; + for (j=start; j<=stop; j++) { + if (song[j].size() < 4) { + continue; + } + if (stop - start == 0) { + templine = song[j].substr(4); + auto loc = templine.find(']'); + if (loc != string::npos) { + templine.resize(loc); + } + if (templine != "") { + out << "!!!" << buffer << ": "; + printString(templine, out); + out << "\n"; + } + + } else if (j==start) { + out << "!!!" << buffer << count++ << ": "; + printString(song[j].substr(4), out); + out << "\n"; + } else if (j==stop) { + templine = song[j].substr(4); + auto loc = templine.find(']'); + if (loc != string::npos) { + templine.resize(loc); + } + if (templine != "") { + out << "!!!" << buffer << count++ << ": "; + printString(templine, out); + out << "\n"; + } + } else { + out << "!!!" << buffer << count++ << ": "; + printString(&(song[j][4]), out); + out << "\n"; + } + } + } + } } ////////////////////////////// // -// Tool_fb::run -- Do the main work of the tool. +// Tool_esac2humold::printString -- print characters in string. // -bool Tool_fb::run(HumdrumFileSet &infiles) { +void Tool_esac2humold::printString(const string& string, ostream& out) { + for (int i=0; i<(int)string.size(); i++) { + printChar(string[i], out); + } +} + + + + + +///////////////////////////////// +// +// Tool_extract::Tool_extract -- Set the recognized options for the tool. +// + +Tool_extract::Tool_extract(void) { + define("P|F|S|x|exclude=s:", "remove listed spines from output"); + define("i=s:", "exclusive interpretation list to extract from input"); + define("I=s:", "exclusive interpretation exclusion list"); + define("f|p|s|field|path|spine=s:", "for extraction of particular spines"); + define("C|count=b", "print a count of the number of spines in file"); + define("c|cointerp=s:**kern", "exclusive interpretation for cospines"); + define("g|grep=s:", "extract spines which match a given regex."); + define("r|reverse=b", "reverse order of spines by **kern group"); + define("R=s:**kern", "reverse order of spine by exinterp group"); + define("t|trace=s:", "use a trace file to extract data"); + define("e|expand=b", "expand spines with subspines"); + define("k|kern=s", "extract by kern spine group"); + define("K|reverse-kern=s", "extract by kern spine group top to bottom numbering"); + define("E|expand-interp=s:", "expand subspines limited to exinterp"); + define("m|model|method=s:d", "method for extracting secondary spines"); + define("M|cospine-model=s:d", "method for extracting cospines"); + define("Y|no-editoral-rests=b", "do not display yy marks on interpreted rests"); + define("n|name|b|blank=s:**blank", "name if exinterp added with 0"); + 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 the program"); + define("version=b", "compilation info"); + define("example=b", "example usages"); + define("h|help=b", "short description"); +} + + + +///////////////////////////////// +// +// Tool_extract::run -- Primary interfaces to the tool. +// + +bool Tool_extract::run(HumdrumFileSet& infiles) { bool status = true; - for (int i = 0; i < infiles.getCount(); i++) { + for (int i=0; i Tool_extract::getNullDataTracks(HumdrumFile& infile) { + vector output(infile.getMaxTrack() + 1, 1); + for (int i=0; igetTrack(); + if (!output[track]) { + continue; + } + if (!token->isNull()) { + output[track] = 0; + } + } + // maybe exit here if all tracks are non-null + } - NoteGrid grid(infile); + return output; +} - vector numbers; - vector kernspines = infile.getKernSpineStartList(); - int maxTrack = infile.getMaxTrack(); +////////////////////////////// +// +// Tool_extract::fillFieldDataByEmpty -- Only keep the spines which contain only +// null data tokens. +// - // Do nothing if base track not withing kern track range - if (m_baseTrackQ < 1 || m_baseTrackQ > maxTrack) { - return; - } +void Tool_extract::fillFieldDataByEmpty(vector& field, vector& subfield, + vector& model, HumdrumFile& infile, int negate) { - m_selectedKernSpines.resize(maxTrack + 1); // +1 is needed since track=0 is not used - // By default, process all tracks: - fill(m_selectedKernSpines.begin(), m_selectedKernSpines.end(), true); - // Otherwise, select which **kern track, or spine tracks to process selectively: + 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 nullTrack = getNullDataTracks(infile); - // Calculate which input spines to process based on -s or -k option: - if (!m_kernTracks.empty()) { - vector ktracks = Convert::extractIntegerList(m_kernTracks, maxTrack); - fill(m_selectedKernSpines.begin(), m_selectedKernSpines.end(), false); - for (int i=0; i<(int)ktracks.size(); i++) { - int index = ktracks[i] - 1; - if ((index < 0) || (index >= (int)kernspines.size())) { - continue; + int zero = 0; + for (int i=1; i<(int)nullTrack.size(); i++) { + if (negate) { + if (!nullTrack[i]) { + field.push_back(i); + subfield.push_back(zero); + model.push_back(zero); + } + } else { + if (nullTrack[i]) { + field.push_back(i); + subfield.push_back(zero); + model.push_back(zero); } - int track = kernspines.at(ktracks[i] - 1)->getTrack(); - m_selectedKernSpines.at(track) = true; } - } else if (!m_spineTracks.empty()) { - infile.makeBooleanTrackList(m_selectedKernSpines, m_spineTracks); } - vector> lastNumbers = {}; - lastNumbers.resize((int)grid.getVoiceCount()); - vector> currentNumbers = {}; +} - // Interate through the NoteGrid and fill the numbers vector with - // all generated FiguredBassNumbers - for (int i=0; i<(int)grid.getSliceCount(); i++) { - currentNumbers.clear(); - currentNumbers.resize((int)grid.getVoiceCount()); - // Reset usedBaseKernTrack - int usedBaseKernTrack = m_baseTrackQ; - // Overwrite usedBaseKernTrack with the lowest voice index of the lowest pitched note - if (m_lowestQ) { - int lowestNotePitch = 99999; - for (int k=0; k<(int)grid.getVoiceCount(); k++) { - NoteCell* checkCell = grid.cell(k, i); - HTp currentToken = checkCell->getToken(); - int initialTokenTrack = currentToken->getTrack(); +////////////////////////////// +// +// Tool_extract::fillFieldDataByNoEmpty -- Only keep spines which are not all +// null data tokens. +// - // Handle spine splits - do { - HTp resolvedToken = currentToken->resolveNull(); - int lowest = getLowestBase40Pitch(resolvedToken->getBase40Pitches()); +void Tool_extract::fillFieldDataByNoEmpty(vector& field, vector& subfield, + vector& model, HumdrumFile& infile, int negate) { - if (abs(lowest) < lowestNotePitch) { - lowestNotePitch = abs(lowest); - usedBaseKernTrack = k + 1; - } + 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 nullTrack = getNullDataTracks(infile); + for (int i=1; i<(int)nullTrack.size(); i++) { + nullTrack[i] = !nullTrack[i]; + } - HTp nextToken = currentToken->getNextField(); - if (nextToken && (initialTokenTrack == nextToken->getTrack())) { - currentToken = nextToken; - } else { - // Break loop if nextToken is not the same track as initialTokenTrack - break; - } - } while (currentToken); + int zero = 0; + for (int i=1; i<(int)nullTrack.size(); i++) { + if (negate) { + if (!nullTrack[i]) { + field.push_back(i); + subfield.push_back(zero); + model.push_back(zero); + } + } else { + if (nullTrack[i]) { + field.push_back(i); + subfield.push_back(zero); + model.push_back(zero); } } + } +} - NoteCell* baseCell = grid.cell(usedBaseKernTrack - 1, i); - // Ignore grace notes - if (baseCell->getToken()->getOwner()->getDuration() == 0) { - continue; - } - string keySignature = getKeySignature(infile, baseCell->getLineIndex()); +////////////////////////////// +// +// 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). If there are +// *part# interpretations in the data, then any spine which is all rests +// will not be removed if there is another **kern spine with the same +// part number if it is also not all rests. +// - // Hide numbers if they do not match rhythmic position of --rate - if (!m_rateQ.empty()) { - // Get time signatures - vector> timeSigs; - infile.getTimeSigs(timeSigs, baseCell->getToken()->getTrack()); - // Ignore numbers if they don't fit - if (hideNumbersForTokenLine(baseCell->getToken(), timeSigs[baseCell->getLineIndex()])) { - continue; - } - } +void Tool_extract::fillFieldDataByNoRest(vector& field, vector& subfield, + vector& model, const string& searchstring, HumdrumFile& infile, + int state) { + field.clear(); + subfield.clear(); + model.clear(); - HTp currentToken = baseCell->getToken(); - int initialTokenTrack = baseCell->getToken()->getTrack(); - int lowestBaseNoteBase40Pitch = 9999; - // Handle spine splits - do { - HTp resolvedToken = currentToken->resolveNull(); - int lowest = getLowestBase40Pitch(resolvedToken->getBase40Pitches()); + // Check every **kern spine for any notes. If there is a note + // then the tracks variable for that spine will be marked + // as non-zero. + vector tracks(infile.getMaxTrack() + 1, 0); + int track; + int partline = 0; + bool dataQ = false; + for (int i=0; iisKern()) { + continue; } - - HTp nextToken = currentToken->getNextField(); - if (nextToken && (initialTokenTrack == nextToken->getTrack())) { - currentToken = nextToken; - } else { - // Break loop if nextToken is not the same track as initialTokenTrack - break; + if (token->isNull()) { + continue; } - } while (currentToken); - - // Ignore if base is a rest or silent note - if ((lowestBaseNoteBase40Pitch == 0) || (lowestBaseNoteBase40Pitch == -1000) || (lowestBaseNoteBase40Pitch == -2000) || (lowestBaseNoteBase40Pitch == 9999)) { - continue; + if (token->isRest()) { + continue; + } + track = token->getTrack(); + tracks[track] = 1; } + } - // Interate through each voice - for (int j=0; j<(int)grid.getVoiceCount(); j++) { - NoteCell* targetCell = grid.cell(j, i); - - // Ignore voice if track is not active by --kern-tracks or --spine-tracks - if (m_selectedKernSpines.at(targetCell->getToken()->getTrack()) == false) { + // Go back and mark any empty spines as non-empty if they + // are in a part that contains multiple staves. I.e., only + // delete a staff if all staves for the part are empty. + // There should be a single *part# line at the start of the + // score. + if (partline > 0) { + vector kerns; + for (int i=0; iisKern()) { continue; } - - HTp currentToken = targetCell->getToken(); - int initialTokenTrack = targetCell->getToken()->getTrack(); - vector chordNumbers = {}; - - // Handle spine splits - do { - HTp resolvedToken = currentToken->resolveNull(); - for (int subtokenBase40: resolvedToken->getBase40Pitches()) { - - // Ignore if target is a rest or silent note - if ((subtokenBase40 == 0) || (subtokenBase40 == -1000) || (subtokenBase40 == -2000)) { - continue; - } - - // Ignore if same pitch as base voice - if ((abs(lowestBaseNoteBase40Pitch) == abs(subtokenBase40)) && (baseCell->getToken()->getTrack() == initialTokenTrack)) { - continue; - } - - // Create FiguredBassNumber - FiguredBassNumber* number = createFiguredBassNumber(abs(lowestBaseNoteBase40Pitch), abs(subtokenBase40), targetCell->getVoiceIndex(), targetCell->getLineIndex(), targetCell->isAttack(), keySignature); - - currentNumbers[j].push_back(number->m_number); - chordNumbers.push_back(number); + kerns.push_back(token); + } + for (int i=0; i<(int)kerns.size(); i++) { + for (int j=i+1; j<(int)kerns.size(); j++) { + if (*kerns[i] != *kerns[j]) { + continue; } - - HTp nextToken = currentToken->getNextField(); - if (nextToken && (initialTokenTrack == nextToken->getTrack())) { - currentToken = nextToken; - } else { - // Break loop if nextToken is not the same track as initialTokenTrack - break; + if (kerns[i]->find("*part") == string::npos) { + continue; } - } while (currentToken); - - // Sort chord numbers by size - sort(chordNumbers.begin(), chordNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { - return a->m_number > b->m_number; - }); - - // Then add to numbers vector - for (FiguredBassNumber* num: chordNumbers) { - if (lastNumbers[j].size() != 0) { - // If a number belongs to a sustained note but the base note did change - // the new numbers need to be displayable - num->m_baseOfSustainedNoteDidChange = !num->m_isAttack && std::find(lastNumbers[j].begin(), lastNumbers[j].end(), num->m_number) == lastNumbers[j].end(); + int track1 = kerns[i]->getTrack(); + int track2 = kerns[j]->getTrack(); + int state1 = tracks[track1]; + int state2 = tracks[track2]; + if ((state1 && !state2) || (state2 && !state1)) { + // Prevent empty staff from being removed + // from a multi-staff part: + tracks[track1] = 1; + tracks[track2] = 1; } - numbers.push_back(num); } } - - // Set current numbers as the new last numbers - lastNumbers = currentNumbers; } - string exinterp = m_aboveQ ? "**fba" : "**fb"; - if (m_hintQ) { - exinterp = "**hint"; + // 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; + } } - if (m_intervallsatzQ) { - // Create **fb spine for each voice - for (int voiceIndex = 0; voiceIndex < grid.getVoiceCount(); voiceIndex++) { - vector trackData = getTrackDataForVoice(voiceIndex, numbers, infile.getLineCount()); - if (voiceIndex + 1 < grid.getVoiceCount()) { - int trackIndex = kernspines[voiceIndex + 1]->getTrack(); - infile.insertDataSpineBefore(trackIndex, trackData, ".", exinterp); - } else { - infile.appendDataSpine(trackData, ".", exinterp); + // 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; } - } else { - // Create **fb spine and bind it to the base voice - vector trackData = getTrackData(numbers, infile.getLineCount()); - if (m_baseTrackQ < grid.getVoiceCount()) { - int trackIndex = kernspines[m_baseTrackQ]->getTrack(); - infile.insertDataSpineBefore(trackIndex, trackData, ".", exinterp); - } else { - infile.appendDataSpine(trackData, ".", exinterp); + } + + int zero = 0; + for (int 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); } } - // Enables usage in verovio (`!!!filter: fb`) - m_humdrum_text << infile; } ////////////////////////////// // -// Tool_fb::hideNumbersForTokenLine -- Checks if rhythmic position of line should display numbers +// Tool_extract::fillFieldDataByGrep -- // -bool Tool_fb::hideNumbersForTokenLine(HTp token, pair timeSig) { - // Get note duration from --rate option - HumNum rateDuration = Convert::recipToDuration(m_rateQ); - if (rateDuration.toFloat() != 0) { - double timeSigBarDuration = timeSig.first * Convert::recipToDuration(to_string(timeSig.second.getInteger())).toFloat(); - double durationFromBarline = token->getDurationFromBarline().toFloat(); - // Handle upbeats - if (token->getBarlineDuration().toFloat() < timeSigBarDuration) { - // Fix durationFromBarline when current bar duration is shorter than - // the bar duration of the time signature - durationFromBarline = timeSigBarDuration - token->getDurationToBarline().toFloat(); +void Tool_extract::fillFieldDataByGrep(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); + HumRegex hre; + int track; + + int i, j; + for (i=0; igetTrack(); + tracks[track] = 1; + } + } + } + + 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); } - // Checks if rhythmic position is divisible by rateDuration - return fmod(durationFromBarline, rateDuration.toFloat()) != 0; } - return false; } ////////////////////////////// // -// Tool_fb::getTrackData -- Create **fb spine data with formatted numbers for all voices +// Tool_extract::getInterpretationFields -- // -vector Tool_fb::getTrackData(const vector& numbers, int lineCount) { - vector trackData; - trackData.resize(lineCount); +void Tool_extract::getInterpretationFields(vector& field, vector& subfield, + vector& model, HumdrumFile& infile, string& interps, int state) { + vector sstrings; // search strings + sstrings.reserve(100); + sstrings.resize(0); - for (int i = 0; i < lineCount; i++) { - vector sliceNumbers = filterFiguredBassNumbersForLine(numbers, i); - if (sliceNumbers.size() > 0) { - trackData[i] = formatFiguredBassNumbers(sliceNumbers); - } + int i, j, k; + string buffer; + buffer = interps; + + HumRegex hre; + hre.replaceDestructive(buffer, "", "\\s+", "g"); + + int start = 0; + while (hre.search(buffer, start, "^([^,]+)")) { + sstrings.push_back(hre.getMatch(1)); + start = hre.getMatchEndIndex(1); } - return trackData; -} + if (debugQ) { + m_humdrum_text << "!! Interpretation strings to search for: " << endl; + for (i=0; i<(int)sstrings.size(); i++) { + m_humdrum_text << "!!\t" << sstrings[i] << endl; + } + } + vector tracks; + tracks.resize(infile.getMaxTrack()+1); + fill(tracks.begin(), tracks.end(), 0); + // Algorithm below could be made more efficient by + // not searching the entire file... + for (i=0; igetTrack()] = 1; + } + } + } + } -////////////////////////////// -// -// Tool_fb::getTrackDataForVoice -- Create **fb spine data with formatted numbers for passed voiceIndex -// + field.reserve(tracks.size()); + subfield.reserve(tracks.size()); + model.reserve(tracks.size()); -vector Tool_fb::getTrackDataForVoice(int voiceIndex, const vector& numbers, int lineCount) { - vector trackData; - trackData.resize(lineCount); + field.resize(0); + subfield.resize(0); + model.resize(0); - for (int i = 0; i < lineCount; i++) { - vector sliceNumbers = filterFiguredBassNumbersForLineAndVoice(numbers, i, voiceIndex); - if (sliceNumbers.size() > 0) { - trackData[i] = formatFiguredBassNumbers(sliceNumbers); + 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); } } - return trackData; } ////////////////////////////// // -// Tool_fb::createFiguredBassNumber -- Create FiguredBassNumber from a NoteCell. -// The figured bass number (num) is calculated with a base and target NoteCell -// as well as a passed key signature. +// Tool_extract::expandSpines -- // -FiguredBassNumber* Tool_fb::createFiguredBassNumber(int basePitchBase40, int targetPitchBase40, int voiceIndex, int lineIndex, bool isAttack, string keySignature) { +void Tool_extract::expandSpines(vector& field, vector& subfield, vector& model, + HumdrumFile& infile, string& interp) { - // Calculate figured bass number - int baseDiatonicPitch = Convert::base40ToDiatonic(basePitchBase40); - int targetDiatonicPitch = Convert::base40ToDiatonic(targetPitchBase40); - int diff = abs(targetDiatonicPitch) - abs(baseDiatonicPitch); - int num; + vector splits; + splits.resize(infile.getMaxTrack()+1); + fill(splits.begin(), splits.end(), 0); - if ((baseDiatonicPitch == 0) || (targetDiatonicPitch == 0)) { - num = 0; - } else if (diff == 0) { - num = 1; - } else if (diff > 0) { - num = diff + 1; - } else { - num = diff - 1; + int i, j; + for (i=0; igetSpineInfo().c_str(), '(') != NULL) { + splits[infile[i].token(j)->getTrack()] = 1; + } + } } - // Transform key signature to lower case - transform(keySignature.begin(), keySignature.end(), keySignature.begin(), [](unsigned char c) { - return tolower(c); - }); + field.reserve(infile.getMaxTrack()*2); + field.resize(0); - char targetPitchName = Convert::kernToDiatonicLC(Convert::base40ToKern(targetPitchBase40)); - int targetAccidNr = Convert::base40ToAccidental(targetPitchBase40); - string targetAccid; - for (int i=0; i dummyfield; + vector dummysubfield; + vector dummymodel; + getInterpretationFields(dummyfield, dummysubfield, model, infile, interp, 1); - // Show natural accidentals when they are alterations of the key signature - if ((targetAccidNr == 0) && (keySignature.find(targetPitchName + targetAccid) != std::string::npos)) { - accid = "n"; - showAccid = true; + vector interptracks; + + interptracks.resize(infile.getMaxTrack()+1); + fill(interptracks.begin(), interptracks.end(), 0); + + for (i=0; i<(int)dummyfield.size(); i++) { + interptracks[dummyfield[i]] = 1; } - // Show accidentlas when pitch class of base and target is equal but alteration is different - if (basePitchName == targetPitchName) { - if (baseAccidNr == targetAccidNr) { - showAccid = false; + int aval = 'a'; + int bval = 'b'; + int zero = 0; + for (i=1; i<(int)splits.size(); i++) { + if (splits[i] && (allQ || interptracks[i])) { + field.push_back(i); + subfield.push_back(aval); + model.push_back(zero); + field.push_back(i); + subfield.push_back(bval); + model.push_back(zero); } else { - accid = (targetAccidNr == 0) ? "n" : targetAccid; - showAccid = true; + field.push_back(i); + subfield.push_back(zero); + model.push_back(zero); } } - string intervalQuality = getIntervalQuality(basePitchBase40, targetPitchBase40); - - FiguredBassNumber* number = new FiguredBassNumber(num, accid, showAccid, voiceIndex, lineIndex, isAttack, m_intervallsatzQ, intervalQuality, m_hintQ); - - return number; + if (debugQ) { + m_humdrum_text << "!!expand: "; + for (i=0; i<(int)field.size(); i++) { + m_humdrum_text << field[i]; + if (subfield[i]) { + m_humdrum_text << (char)subfield[i]; + } + if (i < (int)field.size()-1) { + m_humdrum_text << ","; + } + } + m_humdrum_text << endl; + } } ////////////////////////////// // -// Tool_fb::filterNegativeNumbers -- Hide negative numbers if m_showNegativeQ if not true +// Tool_extract::reverseSpines -- reverse the order of spines, grouped by the +// given exclusive interpretation. // -vector Tool_fb::filterNegativeNumbers(vector numbers) { - - vector filteredNumbers; - - bool mQ = m_showNegativeQ; - copy_if(numbers.begin(), numbers.end(), back_inserter(filteredNumbers), [mQ](FiguredBassNumber* num) { - return mQ ? true : (num->m_number > 0); - }); +void Tool_extract::reverseSpines(vector& field, vector& subfield, + vector& model, HumdrumFile& infile, const string& exinterp) { - return filteredNumbers; -} + vector target; + target.resize(infile.getMaxTrack()+1); + fill(target.begin(), target.end(), 0); + vector trackstarts; + infile.getSpineStartList(trackstarts); + for (int t=0; t<(int)trackstarts.size(); t++) { + if (trackstarts[t]->isDataType(exinterp)) { + target.at(t + 1) = 1; + } + } -////////////////////////////// -// -// Tool_fb::filterFiguredBassNumbersForLine -- Find all FiguredBassNumber objects for a slice (line index) of the music. -// + field.reserve(infile.getMaxTrack()*2); + field.resize(0); -vector Tool_fb::filterFiguredBassNumbersForLine(vector numbers, int lineIndex) { + int lasti = (int)target.size(); + for (int i=(int)target.size()-1; i>0; i--) { + if (target[i]) { + lasti = i; + field.push_back(i); + for (int j=i+1; j<(int)target.size(); j++) { + if (!target.at(j)) { + field.push_back(j); + } else { + break; + } + } + } + } - vector filteredNumbers; + // if the grouping spine is not first, then preserve the + // locations of the pre-spines. + int extras = 0; + if (lasti != 1) { + extras = lasti - 1; + field.resize(field.size()+extras); + for (int i=0; i<(int)field.size()-extras; i++) { + field[(int)field.size()-1-i] = field[(int)field.size()-1-extras-i]; + } + for (int i=0; im_lineIndex == lineIndex; - }); + if (debugQ) { + m_humdrum_text << "!!reverse: "; + for (int i=0; i<(int)field.size(); i++) { + m_humdrum_text << field[i] << " "; + } + m_humdrum_text << endl; + } - // sort by voiceIndex - sort(filteredNumbers.begin(), filteredNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { - return a->m_voiceIndex > b->m_voiceIndex; - }); + subfield.resize(field.size()); + fill(subfield.begin(), subfield.end(), 0); - return filterNegativeNumbers(filteredNumbers); + model.resize(field.size()); + fill(model.begin(), model.end(), 0); } ////////////////////////////// // -// Tool_fb::filterFiguredBassNumbersForLineAndVoice -- +// Tool_extract::fillFieldData -- // -vector Tool_fb::filterFiguredBassNumbersForLineAndVoice(vector numbers, int lineIndex, int voiceIndex) { +void Tool_extract::fillFieldData(vector& field, vector& subfield, + vector& model, string& fieldstring, HumdrumFile& infile) { - vector filteredNumbers; + int maxtrack = infile.getMaxTrack(); - // filter numbers with passed lineIndex and passed voiceIndex - copy_if(numbers.begin(), numbers.end(), back_inserter(filteredNumbers), [lineIndex, voiceIndex](FiguredBassNumber* num) { - return (num->m_lineIndex == lineIndex) && (num->m_voiceIndex == voiceIndex); - }); + field.reserve(maxtrack); + field.resize(0); - // sort by voiceIndex (probably not needed here) - sort(filteredNumbers.begin(), filteredNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { - return a->m_voiceIndex > b->m_voiceIndex; - }); + subfield.reserve(maxtrack); + subfield.resize(0); - return filterNegativeNumbers(filteredNumbers); + model.reserve(maxtrack); + model.resize(0); + + HumRegex hre; + string buffer = fieldstring; + hre.replaceDestructive(buffer, "", "\\s", "gs"); + int start = 0; + string tempstr; + vector tempfield; + vector tempsubfield; + vector tempmodel; + while (hre.search(buffer, start, "^([^,]+,?)")) { + tempfield.clear(); + tempsubfield.clear(); + tempmodel.clear(); + processFieldEntry(tempfield, tempsubfield, tempmodel, hre.getMatch(1), infile); + start += hre.getMatchEndIndex(1); + field.insert(field.end(), tempfield.begin(), tempfield.end()); + subfield.insert(subfield.end(), tempsubfield.begin(), tempsubfield.end()); + model.insert(model.end(), tempmodel.begin(), tempmodel.end()); + } } ////////////////////////////// // -// Tool_fb::formatFiguredBassNumbers -- Create a **fb data record string out of the passed FiguredBassNumber objects +// Tool_extract::processFieldEntry -- +// 3-6 expands to 3 4 5 6 +// $ expands to maximum spine track +// $-1 expands to maximum spine track minus 1, etc. // -string Tool_fb::formatFiguredBassNumbers(const vector& numbers) { +void Tool_extract::processFieldEntry(vector& field, + vector& subfield, vector& model, const string& astring, + HumdrumFile& infile) { - vector formattedNumbers; + int finitsize = (int)field.size(); + int maxtrack = infile.getMaxTrack(); - // Normalize numbers (remove 8 and 1, sort by size, remove duplicate numbers) - if (m_normalizeQ) { - bool aQ = m_accidentalsQ; - // remove 8 and 1 but keep them if they have an accidental - copy_if(numbers.begin(), numbers.end(), back_inserter(formattedNumbers), [aQ](FiguredBassNumber* num) { - return ((num->getNumberWithinOctave() != 8) && (num->getNumberWithinOctave() != 1)) || (aQ && num->m_showAccidentals); - }); - // sort by size - sort(formattedNumbers.begin(), formattedNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { - return a->getNumberWithinOctave() < b->getNumberWithinOctave(); - }); - // remove duplicate numbers - formattedNumbers.erase(unique(formattedNumbers.begin(), formattedNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) { - return a->getNumberWithinOctave() == b->getNumberWithinOctave(); - }), formattedNumbers.end()); - } else { - formattedNumbers = numbers; - } + vector ktracks; + infile.getKernSpineStartList(ktracks); + int maxkerntrack = (int)ktracks.size(); - // Hide numbers if they have no attack - if (m_intervallsatzQ && m_attackQ) { - vector attackNumbers; - copy_if(formattedNumbers.begin(), formattedNumbers.end(), back_inserter(attackNumbers), [](FiguredBassNumber* num) { - return num->m_isAttack || num->m_baseOfSustainedNoteDidChange; - }); - formattedNumbers = attackNumbers; - } + int modletter; + int subletter; - // Analysze before sorting - if (m_compoundQ) { - formattedNumbers = analyzeChordNumbers(formattedNumbers); - } + HumRegex hre; + string buffer = astring; - // Sort numbers by size - if (m_sortQ) { - bool cQ = m_compoundQ; - sort(formattedNumbers.begin(), formattedNumbers.end(), [cQ](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { - // sort by getNumberWithinOctave if compoundQ is true otherwise sort by number - return (cQ) ? a->getNumberWithinOctave() > b->getNumberWithinOctave() : a->m_number > b->m_number; - }); - } + // remove any comma left at end of input astring (or anywhere else) + hre.replaceDestructive(buffer, "", ",", "g"); - if (m_reduceQ) { - // Overwrite formattedNumbers with abbreviated numbers - formattedNumbers = getAbbreviatedNumbers(formattedNumbers); + // first remove $ symbols and replace with the correct values + if (kernQ) { + removeDollarsFromString(buffer, maxkerntrack); + } else { + removeDollarsFromString(buffer, maxtrack); } - // join numbers - string str = ""; - bool first = true; - for (FiguredBassNumber* number: formattedNumbers) { - string num = number->toString(m_compoundQ, m_accidentalsQ, m_hideThreeQ); - if (num.length() > 0) { - if (!first) str += " "; - first = false; - str += num; - } - } - return str; -} + int zero = 0; + if (hre.search(buffer, "^(\\d+)-(\\d+)$")) { + int firstone = hre.getMatchInt(1); + int lastone = hre.getMatchInt(2); + if ((firstone < 1) && (firstone != 0)) { + m_error_text << "Error: range token: \"" << astring << "\"" + << " contains too small a number at start: " << firstone << endl; + m_error_text << "Minimum number allowed is " << 1 << endl; + return; + } + if ((lastone < 1) && (lastone != 0)) { + m_error_text << "Error: range token: \"" << astring << "\"" + << " contains too small a number at end: " << lastone << endl; + m_error_text << "Minimum number allowed is " << 1 << endl; + return; + } + if (firstone > maxtrack) { + m_error_text << "Error: range token: \"" << astring << "\"" + << " contains number too large at start: " << firstone << endl; + m_error_text << "Maximum number allowed is " << maxtrack << endl; + return; + } + if (lastone > maxtrack) { + m_error_text << "Error: range token: \"" << astring << "\"" + << " contains number too large at end: " << lastone << endl; + m_error_text << "Maximum number allowed is " << maxtrack << endl; + return; + } + if (firstone > lastone) { + for (int i=firstone; i>=lastone; i--) { + field.push_back(i); + subfield.push_back(zero); + model.push_back(zero); + } + } else { + for (int i=firstone; i<=lastone; i++) { + field.push_back(i); + subfield.push_back(zero); + model.push_back(zero); + } + } + } else if (hre.search(buffer, "^(\\d+)([a-z]*)")) { + int value = hre.getMatchInt(1); + modletter = 0; + subletter = 0; + if (hre.getMatch(2) == "a") { + subletter = 'a'; + } + if (hre.getMatch(2) == "b") { + subletter = 'b'; + } + if (hre.getMatch(2) == "c") { + subletter = 'c'; + } + if (hre.getMatch(2) == "d") { + modletter = 'd'; + } + if (hre.getMatch(2) == "n") { + modletter = 'n'; + } + if (hre.getMatch(2) == "r") { + modletter = 'r'; + } -////////////////////////////// -// -// Tool_fb::getAbbreviatedNumbers -- Get abbreviated figured bass numbers -// If no abbreviation is found all numbers will be shown + if ((value < 1) && (value != 0)) { + m_error_text << "Error: range token: \"" << astring << "\"" + << " contains too small a number at end: " << value << endl; + m_error_text << "Minimum number allowed is " << 1 << endl; + return; + } + if (value > maxtrack) { + m_error_text << "Error: range token: \"" << astring << "\"" + << " contains number too large at start: " << value << endl; + m_error_text << "Maximum number allowed is " << maxtrack << endl; + return; + } + field.push_back(value); + if (value == 0) { + subfield.push_back(zero); + model.push_back(zero); + } else { + subfield.push_back(subletter); + model.push_back(modletter); + } + } -vector Tool_fb::getAbbreviatedNumbers(const vector& numbers) { + if (!kernQ) { + return; + } - vector abbreviatedNumbers; + // Insert fields to next **kern spine. + vector newfield; + vector newsubfield; + vector newmodel; - string numberString = getNumberString(numbers); + vector trackstarts; + infile.getTrackStartList(trackstarts); + int spine; - // Check if an abbreviation exists for passed numbers - auto it = find_if(FiguredBassAbbreviationMapping::s_mappings.begin(), FiguredBassAbbreviationMapping::s_mappings.end(), [&numberString](const FiguredBassAbbreviationMapping& abbr) { - return abbr.m_str == numberString; - }); + // convert kern tracks into spine tracks: + for (int i=finitsize; i<(int)field.size(); i++) { + if (field[i] > 0) { + spine = ktracks[field[i]-1]->getTrack(); + field[i] = spine; + } + } - if (it != FiguredBassAbbreviationMapping::s_mappings.end()) { - const FiguredBassAbbreviationMapping& abbr = *it; - bool aQ = m_accidentalsQ; - // Store numbers to display by the abbreviation mapping in abbreviatedNumbers - copy_if(numbers.begin(), numbers.end(), back_inserter(abbreviatedNumbers), [&abbr, aQ](FiguredBassNumber* num) { - const vector& nums = abbr.m_numbers; - // Show numbers if they are part of the abbreviation mapping or if they have an accidental - return (find(nums.begin(), nums.end(), num->getNumberWithinOctave()) != nums.end()) || (num->m_showAccidentals && aQ); - }); + int startspineindex, stopspineindex; + for (int i=0; i<(int)field.size(); i++) { + newfield.push_back(field[i]); // copy **kern spine index into new list + newsubfield.push_back(subfield[i]); + newmodel.push_back(model[i]); - return abbreviatedNumbers; + // search for non **kern spines after specified **kern spine: + startspineindex = field[i] + 1 - 1; + stopspineindex = maxtrack; + for (int j=startspineindex; jisKern()) { + break; + } + newfield.push_back(j+1); + newsubfield.push_back(zero); + newmodel.push_back(zero); + } } - return numbers; + field = newfield; + subfield = newsubfield; + model = newmodel; } ////////////////////////////// // -// Tool_fb::analyzeChordNumbers -- Analyze chord numbers and improve them -// Set m_convert2To9 to true when a 3 is included in the chord numbers. - -vector Tool_fb::analyzeChordNumbers(const vector& numbers) { +// Tool_extract::removeDollarsFromString -- substitute $ sign for maximum track count. +// - vector analyzedNumbers = numbers; +void Tool_extract::removeDollarsFromString(string& buffer, int maxtrack) { + HumRegex hre; + char buf2[128] = {0}; + int value2; - // Check if compound numbers 3 is withing passed numbers (chord) - auto it = find_if(analyzedNumbers.begin(), analyzedNumbers.end(), [](FiguredBassNumber* number) { - return number->getNumberWithinOctave() == 3; - }); - if (it != analyzedNumbers.end()) { - for (auto &number : analyzedNumbers) { - number->m_convert2To9 = true; - } + if (hre.search(buffer, "\\$$")) { + snprintf(buf2, 128, "%d", maxtrack); + hre.replaceDestructive(buffer, buf2, "\\$$"); } - return analyzedNumbers; -} - - + if (hre.search(buffer, "\\$(?![\\d-])")) { + // don't know how this case could happen, however... + snprintf(buf2, 128, "%d", maxtrack); + hre.replaceDestructive(buffer, buf2, "\\$(?![\\d-])", "g"); + } -////////////////////////////// -// -// Tool_fb::getNumberString -- Get only the numbers (without accidentals) of passed FiguredBassNumbers -// + if (hre.search(buffer, "\\$0")) { + // replace $0 with maxtrack (used for reverse orderings) + snprintf(buf2, 128, "%d", maxtrack); + hre.replaceDestructive(buffer, buf2, "\\$0", "g"); + } -string Tool_fb::getNumberString(vector numbers) { - // Sort numbers by size - sort(numbers.begin(), numbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { - return a->getNumberWithinOctave() > b->getNumberWithinOctave(); - }); - // join numbers - string str = ""; - bool first = true; - for (FiguredBassNumber* nr: numbers) { - int num = nr->getNumberWithinOctave(); - if (num > 0) { - if (!first) str += " "; - first = false; - str += to_string(num); - } + while (hre.search(buffer, "\\$(-?\\d+)")) { + value2 = maxtrack - abs(hre.getMatchInt(1)); + snprintf(buf2, 128, "%d", value2); + hre.replaceDestructive(buffer, buf2, "\\$-?\\d+"); } - return str; } ////////////////////////////// // -// Tool_fb::getKeySignature -- Get the key signature for a line index of the input file +// Tool_extract::excludeFields -- print all spines except the ones in the list of fields. // -string Tool_fb::getKeySignature(HumdrumFile& infile, int lineIndex) { - string keySignature = ""; - [&] { - for (int i = 0; i < infile.getLineCount(); i++) { - if (i > lineIndex) { - return; - } - HLp line = infile.getLine(i); - for (int j = 0; j < line->getFieldCount(); j++) { - if (line->token(j)->isKeySignature()) { - keySignature = line->getTokenString(j); +void Tool_extract::excludeFields(HumdrumFile& infile, vector& field, + vector& subfield, vector& model) { + int start = 0; + for (int i=0; igetTrack(), field)) { + continue; + } + if (start != 0) { + m_humdrum_text << '\t'; } + start = 1; + m_humdrum_text << infile.token(i, j); + } + if (start != 0) { + m_humdrum_text << endl; } } - }(); - return keySignature; -} - - - -////////////////////////////// -// -// Tool_fb::getLowestBase40Pitch -- Get lowest base 40 pitch that is not a rest or silent -// TODO: Handle negative values and sustained notes -// - -int Tool_fb::getLowestBase40Pitch(vector base40Pitches) { - vector filteredBase40Pitches; - copy_if(base40Pitches.begin(), base40Pitches.end(), std::back_inserter(filteredBase40Pitches), [](int base40Pitch) { - // Ignore if base is a rest or silent note - return (base40Pitch != -1000) && (base40Pitch != -2000) && (base40Pitch != 0); - }); - - if (filteredBase40Pitches.size() == 0) { - return -2000; } - - return *min_element(begin(filteredBase40Pitches), end(filteredBase40Pitches)); } ////////////////////////////// // -// Tool_fb::getIntervalQuality -- Return interval quality prefix string +// Tool_extract::extractFields -- print all spines in the list of fields. // -string Tool_fb::getIntervalQuality(int basePitchBase40, int targetPitchBase40) { - - int diff = (targetPitchBase40 - basePitchBase40) % 40; - - diff = diff < -2 ? abs(diff) : diff; - - // See https://wiki.ccarh.org/wiki/Base_40 - string quality; - switch (diff) { - // 1 - case -2: - case 38: - quality = "dd"; break; - case -1: - case 39: - quality = "d"; break; - case 0: quality = "P"; break; - case 1: quality = "A"; break; - case 2: quality = "AA"; break; - - // 2 - case 3: quality = "dd"; break; - case 4: quality = "d"; break; - case 5: quality = "m"; break; - case 6: quality = "M"; break; - case 7: quality = "A"; break; - case 8: quality = "AA"; break; +void Tool_extract::extractFields(HumdrumFile& infile, vector& field, + vector& subfield, vector& model) { - // 3 - case 9: quality = "dd"; break; - case 10: quality = "d"; break; - case 11: quality = "m"; break; - case 12: quality = "M"; break; - case 13: quality = "A"; break; - case 14: quality = "AA"; break; + HumRegex hre; + int start = 0; + int target; + int subtarget; + int modeltarget; + string spat; + bool foundBarline = true; - // 4 - case 15: quality = "dd"; break; - case 16: quality = "d"; break; - case 17: quality = "P"; break; - case 18: quality = "A"; break; - case 19: quality = "AA"; break; + for (int i=0; iisExpansionLabel()) { + m_humdrum_text << token; + } else if (token->isExpansionList()) { + m_humdrum_text << token; + } else { + if (addRestsQ) { + printInterpretationForKernSpine(infile, i); + } else { + m_humdrum_text << "*"; + } + } + } + } + } else { + for (int j=0; jgetTrack() != target) { + continue; + } + switch (subtarget) { + case 'a': + getSearchPat(spat, target, "a"); + if (hre.search(infile.token(i,j)->getSpineInfo(), spat) || + !hre.search(infile.token(i, j)->getSpineInfo(), "\\(")) { + if (start != 0) { + m_humdrum_text << '\t'; + } + start = 1; + m_humdrum_text << infile.token(i, j); + } + break; + case 'b': + getSearchPat(spat, target, "b"); + if (hre.search(infile.token(i, j)->getSpineInfo(), spat)) { + if (start != 0) { + m_humdrum_text << '\t'; + } + start = 1; + m_humdrum_text << infile.token(i, j); + } else if (!hre.search(infile.token(i, j)->getSpineInfo(), + "\\(")) { + if (start != 0) { + m_humdrum_text << '\t'; + } + start = 1; + dealWithSecondarySubspine(field, subfield, model, t, + infile, i, j, modeltarget); + } + break; + case 'c': + if (start != 0) { + m_humdrum_text << '\t'; + } + start = 1; + dealWithCospine(field, subfield, model, t, infile, i, j, + modeltarget, modeltarget, cointerp); + break; + default: + if (start != 0) { + m_humdrum_text << '\t'; + } + start = 1; + m_humdrum_text << infile.token(i, j); + } + } + } + } - // 7 - case 32: quality = "dd"; break; - case 33: quality = "d"; break; - case 34: quality = "m"; break; - case 35: quality = "M"; break; - case 36: quality = "A"; break; - case 37: quality = "AA"; break; + if (infile[i].isData()) { + foundBarline = false; + } - default: quality = "?"; break; + if (start != 0) { + m_humdrum_text << endl; + } } - - return quality; - } ////////////////////////////// // -// FiguredBassNumber::FiguredBassNumber -- Constructor +// Tool_extract::printInterpretationForKernSpine -- // -FiguredBassNumber::FiguredBassNumber(int num, string accid, bool showAccid, int voiceIdx, int lineIdx, bool isAtk, bool intervallsatz, string intervalQuality, bool hint) { - m_number = num; - m_accidentals = accid; - m_voiceIndex = voiceIdx; - m_lineIndex = lineIdx; - m_showAccidentals = showAccid; - m_isAttack = isAtk; - m_intervallsatz = intervallsatz; - m_intervalQuality = intervalQuality; - m_hint = hint; -} - +void Tool_extract::printInterpretationForKernSpine(HumdrumFile& infile, int index) { + HTp kerntok = NULL; + for (int j=0; jisKern()) { + continue; + } + kerntok = token; + break; + } + if (kerntok == NULL) { + m_humdrum_text << "*"; + return; + } -////////////////////////////// -// -// FiguredBassNumber::toString -- Convert FiguredBassNumber to a string (accidental + number) -// + if (*kerntok == "*") { + m_humdrum_text << kerntok; + return; + } -string FiguredBassNumber::toString(bool compoundQ, bool accidentalsQ, bool hideThreeQ) { - int num = (compoundQ) ? getNumberWithinOctave() : m_number; - if (m_hint) { - return m_intervalQuality + to_string(abs(num)); + if (kerntok->isKeySignature()) { + m_humdrum_text << kerntok; + return; } - string accid = (accidentalsQ && m_showAccidentals) ? m_accidentals : ""; - if (((num == 3) || (num == -3)) && accidentalsQ && m_showAccidentals && hideThreeQ) { - return accid; + if (kerntok->isKeyDesignation()) { + m_humdrum_text << kerntok; + return; } - if (num > 0) { - return accid + to_string(num); + if (kerntok->isTimeSignature()) { + m_humdrum_text << kerntok; + return; } - if (num < 0) { - return accid + "~" + to_string(abs(num)); + if (kerntok->isMensurationSymbol()) { + m_humdrum_text << kerntok; + return; } - return ""; + if (kerntok->isTempo()) { + m_humdrum_text << kerntok; + return; + } + if (kerntok->isInstrumentName()) { + m_humdrum_text << "*I\""; + return; + } + if (kerntok->isInstrumentAbbreviation()) { + m_humdrum_text << "*I'"; + return; + } + + m_humdrum_text << "*"; } ////////////////////////////// // -// FiguredBassNumber::getNumberWithinOctave -- Get a reasonable figured bass number -// Replace 0 with 7 and -7 -// Replace 1 with 8 and -8 -// Replace 2 with 9 if it is a suspension of the ninth -// Allow 1 (unisono) in intervallsatz +// Tool_extract::dealWithCospine -- extract the required token(s) from a co-spine. +// -int FiguredBassNumber::getNumberWithinOctave(void) { - int num = m_number % 7; +void Tool_extract::dealWithCospine(vector& field, vector& subfield, vector& model, + int targetindex, HumdrumFile& infile, int line, int cospine, + int comodel, int submodel, const string& cointerp) { - // Replace 0 with 7 and -7 - if ((abs(m_number) > 0) && (m_number % 7 == 0)) { - return m_number < 0 ? -7 : 7; - } + vector cotokens; + cotokens.reserve(50); - // Replace 1 with 8 and -8 - if (abs(num) == 1) { - // Allow unisono in intervallsatz - if (m_intervallsatz || m_hint) { - if (abs(m_number) == 1) { - return 1; + string buffer; + int i, j, k; + int index; + + if (infile[line].isInterpretation()) { + m_humdrum_text << infile.token(line, cospine); + return; + } + + if (infile[line].isBarline()) { + m_humdrum_text << infile.token(line, cospine); + return; + } + + if (infile[line].isLocalComment()) { + m_humdrum_text << infile.token(line, cospine); + return; + } + + int count = infile[line].token(cospine)->getSubtokenCount(); + for (k=0; kgetSubtoken(k); + cotokens.resize(cotokens.size()+1); + index = (int)cotokens.size()-1; + cotokens[index] = buffer; + } + + vector spineindex; + vector subspineindex; + + spineindex.reserve(infile.getMaxTrack()*2); + spineindex.resize(0); + + subspineindex.reserve(infile.getMaxTrack()*2); + subspineindex.resize(0); + + for (j=0; jisDataType(cointerp)) { + continue; + } + if (*infile.token(line, j) == ".") { + continue; + } + count = infile[line].token(j)->getSubtokenCount(); + for (k=0; kgetSubtoken(k); + if (comodel == 'r') { + if (buffer == "r") { + continue; + } } + spineindex.push_back(j); + subspineindex.push_back(k); } - return m_number < 0 ? -8 : 8; } - // Replace 2 with 9 if m_convert2To9 is true (e.g. when a 3 is included in the chord numbers) - if (m_convert2To9 && (num == 2)) { - return 9; + if (debugQ) { + m_humdrum_text << "\n!!codata:\n"; + for (i=0; i<(int)cotokens.size(); i++) { + m_humdrum_text << "!!\t" << i << "\t" << cotokens[i]; + if (i < (int)spineindex.size()) { + m_humdrum_text << "\tspine=" << spineindex[i]; + m_humdrum_text << "\tsubspine=" << subspineindex[i]; + } else { + m_humdrum_text << "\tspine=."; + m_humdrum_text << "\tsubspine=."; + } + m_humdrum_text << endl; + } } - return num; + string buff; + + int start = 0; + for (i=0; i<(int)field.size(); i++) { + if (infile.token(line, field[i])->isDataType(cointerp)) { + continue; + } + + for (j=0; jgetTrack() != field[i]) { + continue; + } + if (subfield[i] == 'a') { + getSearchPat(buff, field[i], "a"); + if ((strchr(infile.token(line, j)->getSpineInfo().c_str(), '(') == NULL) || + (infile.token(line, j)->getSpineInfo().find(buff) != string::npos)) { + printCotokenInfo(start, infile, line, j, cotokens, spineindex, + subspineindex); + } + } else if (subfield[i] == 'b') { + // this section may need more work... + getSearchPat(buff, field[i], "b"); + if ((strchr(infile.token(line, j)->getSpineInfo().c_str(), '(') == NULL) || + (strstr(infile.token(line, j)->getSpineInfo().c_str(), buff.c_str()) != NULL)) { + printCotokenInfo(start, infile, line, j, cotokens, spineindex, + subspineindex); + } + } else { + printCotokenInfo(start, infile, line, j, cotokens, spineindex, + subspineindex); + } + } + } } ////////////////////////////// // -// FiguredBassAbbreviationMapping::FiguredBassAbbreviationMapping -- Constructor -// Helper class to store the mappings for abbreviate figured bass numbers +// Tool_extract::printCotokenInfo -- // -FiguredBassAbbreviationMapping::FiguredBassAbbreviationMapping(string s, vector n) { - m_str = s; - m_numbers = n; +void Tool_extract::printCotokenInfo(int& start, HumdrumFile& infile, int line, int spine, + vector& cotokens, vector& spineindex, + vector& subspineindex) { + int i; + int found = 0; + for (i=0; i<(int)spineindex.size(); i++) { + if (spineindex[i] == spine) { + if (start == 0) { + start++; + } else { + m_humdrum_text << subtokenseparator; + } + if (i<(int)cotokens.size()) { + m_humdrum_text << cotokens[i]; + } else { + m_humdrum_text << "."; + } + found = 1; + } + } + if (!found) { + if (start == 0) { + start++; + } else { + m_humdrum_text << subtokenseparator; + } + m_humdrum_text << "."; + } } ////////////////////////////// // -// FiguredBassAbbreviationMapping::s_mappings -- Mapping to abbreviate figured bass numbers +// Tool_extract::dealWithSecondarySubspine -- what to print if a secondary spine +// does not exist on a line. // -const vector FiguredBassAbbreviationMapping::s_mappings = { - FiguredBassAbbreviationMapping("3", {}), - FiguredBassAbbreviationMapping("5", {}), - FiguredBassAbbreviationMapping("5 3", {}), - FiguredBassAbbreviationMapping("6 3", {6}), - FiguredBassAbbreviationMapping("5 4", {4}), - FiguredBassAbbreviationMapping("7 5 3", {7}), - FiguredBassAbbreviationMapping("7 3", {7}), - FiguredBassAbbreviationMapping("7 5", {7}), - FiguredBassAbbreviationMapping("6 5 3", {6, 5}), - FiguredBassAbbreviationMapping("6 4 3", {4, 3}), - FiguredBassAbbreviationMapping("6 4 2", {4, 2}), - FiguredBassAbbreviationMapping("9 5 3", {9}), - FiguredBassAbbreviationMapping("9 5", {9}), - FiguredBassAbbreviationMapping("9 3", {9}), -}; - - - -#define RUNTOOL(NAME, INFILE, COMMAND, STATUS) \ - Tool_##NAME *tool = new Tool_##NAME; \ - tool->process(COMMAND); \ - tool->run(INFILE); \ - if (tool->hasError()) { \ - status = false; \ - tool->getError(cerr); \ - delete tool; \ - break; \ - } else if (tool->hasHumdrumText()) { \ - INFILE.readString(tool->getHumdrumText()); \ - } \ - delete tool; +void Tool_extract::dealWithSecondarySubspine(vector& field, vector& subfield, + vector& model, int targetindex, HumdrumFile& infile, int line, + int spine, int submodel) { -#define RUNTOOL2(NAME, INFILE1, INFILE2, COMMAND, STATUS) \ - Tool_##NAME *tool = new Tool_##NAME; \ - tool->process(COMMAND); \ - tool->run(INFILE1, INFILE2); \ - if (tool->hasError()) { \ - status = false; \ - tool->getError(cerr); \ - delete tool; \ - break; \ - } else if (tool->hasHumdrumText()) { \ - INFILE1.readString(tool->getHumdrumText()); \ - } \ - delete tool; + int& i = line; + int& j = spine; -#define RUNTOOLSET(NAME, INFILES, COMMAND, STATUS) \ - Tool_##NAME *tool = new Tool_##NAME; \ - tool->process(COMMAND); \ - tool->run(INFILES); \ - if (tool->hasError()) { \ - status = false; \ - tool->getError(cerr); \ - delete tool; \ - break; \ - } else if (tool->hasHumdrumText()) { \ - INFILES.readString(tool->getHumdrumText()); \ - } \ - delete tool; + HumRegex hre; + string buffer; + if (infile[line].isLocalComment()) { + if ((submodel == 'n') || (submodel == 'r')) { + m_humdrum_text << "!"; + } else { + m_humdrum_text << infile.token(i, j); + } + } else if (infile[line].isBarline()) { + m_humdrum_text << infile.token(i, j); + } else if (infile[line].isInterpretation()) { + if ((submodel == 'n') || (submodel == 'r')) { + m_humdrum_text << "*"; + } else { + m_humdrum_text << infile.token(i, j); + } + } else if (infile[line].isData()) { + if (submodel == 'n') { + m_humdrum_text << "."; + } else if (submodel == 'r') { + if (*infile.token(i, j) == ".") { + m_humdrum_text << "."; + } else if (infile.token(i, j)->find('q') != string::npos) { + m_humdrum_text << "."; + } else if (infile.token(i, j)->find('Q') != string::npos) { + m_humdrum_text << "."; + } else { + buffer = *infile.token(i, j); + if (hre.search(buffer, "{")) { + m_humdrum_text << "{"; + } + // remove secondary chord notes: + hre.replaceDestructive(buffer, "", " .*"); + // remove unnecessary characters (such as stem direction): + hre.replaceDestructive(buffer, "", + "[^}pPqQA-Ga-g0-9.;%#nr-]", "g"); + // change pitch to rest: + hre.replaceDestructive(buffer, "[A-Ga-g#n-]+", "r"); + // add editorial marking unless -Y option is given: + if (editorialInterpretation != "") { + if (hre.search(buffer, "rr")) { + hre.replaceDestructive(buffer, editorialInterpretation, "(?<=rr)"); + hre.replaceDestructive(buffer, "r", "rr"); + } else { + hre.replaceDestructive(buffer, editorialInterpretation, "(?<=r)"); + } + } + m_humdrum_text << buffer; + } + } else { + m_humdrum_text << infile.token(i, j); + } + } else { + m_error_text << "Should not get to this line of code" << endl; + return; + } +} -#define RUNTOOLSTREAM(NAME, INFILES, COMMAND, STATUS) \ - Tool_##NAME *tool = new Tool_##NAME; \ - tool->process(COMMAND); \ - tool->run(INFILES); \ - if (tool->hasError()) { \ - status = false; \ - tool->getError(cerr); \ - delete tool; \ - break; \ - } else if (tool->hasHumdrumText()) { \ - INFILES.readString(tool->getHumdrumText()); \ - } \ - delete tool; -//////////////////////////////// +////////////////////////////// // -// Tool_filter::Tool_filter -- Set the recognized options for the tool. +// Tool_extract::getSearchPat -- // -Tool_filter::Tool_filter(void) { - define("debug=b", "print debug statement"); - define("v|variant=s:", "Run filters labeled with the given variant"); +void Tool_extract::getSearchPat(string& spat, int target, const string& modifier) { + if (modifier.size() > 20) { + m_error_text << "Error in GetSearchPat" << endl; + return; + } + spat.reserve(16); + spat = "\\("; + spat += to_string(target); + spat += "\\)"; + spat += modifier; } -///////////////////////////////// +////////////////////////////// // -// Tool_filter::run -- Primary interfaces to the tool. +// Tool_extract::dealWithSpineManipulators -- check for proper Humdrum syntax of +// spine manipulators (**, *-, *x, *v, *^) when creating the output. // -bool Tool_filter::run(const string& indata) { - HumdrumFileSet infiles(indata); - bool status = run(infiles); - return status; -} - +void Tool_extract::dealWithSpineManipulators(HumdrumFile& infile, int line, + vector& field, vector& subfield, vector& model) { -bool Tool_filter::run(HumdrumFile& infile) { - HumdrumFileSet infiles; - infiles.appendHumdrumPointer(&infile); - bool status = run(infiles); - infiles.clearNoFree(); - return status; -} + vector vmanip; // counter for *v records on line + vmanip.resize(infile[line].getFieldCount()); + fill(vmanip.begin(), vmanip.end(), 0); -bool Tool_filter::runUniversal(HumdrumFileSet& infiles) { - bool status = true; - vector > commands; - getUniversalCommandList(commands, infiles); + vector xmanip; // counter for *x record on line + xmanip.resize(infile[line].getFieldCount()); + fill(xmanip.begin(), xmanip.end(), 0); - for (int i=0; i<(int)commands.size(); i++) { - if (commands[i].first == "humdiff") { - RUNTOOLSET(humdiff, infiles, commands[i].second, status); - } else if (commands[i].first == "chooser") { - RUNTOOLSET(chooser, infiles, commands[i].second, status); - } else if (commands[i].first == "myank") { - RUNTOOL(myank, infiles, commands[i].second, status); + int i = 0; + int j; + for (j=0; j<(int)vmanip.size(); j++) { + if (*infile.token(line, j) == "*v") { + vmanip[j] = 1; + } + if (*infile.token(line, j) == "*x") { + xmanip[j] = 1; } } - removeUniversalFilterLines(infiles); + int counter = 1; + for (i=1; i<(int)xmanip.size(); i++) { + if ((xmanip[i] == 1) && (xmanip[i-1] == 1)) { + xmanip[i] = counter; + xmanip[i-1] = counter; + counter++; + } + } - return status; -} + counter = 1; + i = 0; + while (i < (int)vmanip.size()) { + if (vmanip[i] == 1) { + while ((i < (int)vmanip.size()) && (vmanip[i] == 1)) { + vmanip[i] = counter; + i++; + } + counter++; + } + i++; + } + vector fieldoccur; // nth occurance of an input spine in the output + fieldoccur.resize(field.size()); + fill(fieldoccur.begin(), fieldoccur.end(), 0); -// -// In-place processing of file: -// + vector trackcounter; // counter of input spines occurances in output + trackcounter.resize(infile.getMaxTrack()+1); + fill(trackcounter.begin(), trackcounter.end(), 0); -bool Tool_filter::run(HumdrumFileSet& infiles) { - if (infiles.getCount() == 0) { - return false; + for (i=0; i<(int)field.size(); i++) { + if (field[i] != 0) { + trackcounter[field[i]]++; + fieldoccur[i] = trackcounter[field[i]]; + } } - initialize(infiles[0]); + vector tempout; + vector vserial; + vector xserial; + vector fpos; // input column of output spine - HumdrumFile& infile = infiles[0]; + tempout.reserve(1000); + tempout.resize(0); - #ifdef __EMSCRIPTEN__ - bool optionList = getBoolean("options"); - if (optionList) { - printEmscripten(m_humdrum_text); - m_humdrum_text << infile; - } - #endif + vserial.reserve(1000); + vserial.resize(0); - bool status = true; - vector > commands; - getCommandList(commands, infile); - for (int i=0; i<(int)commands.size(); i++) { - if (commands[i].first == "addic") { - RUNTOOL(addic, infile, commands[i].second, status); - } else if (commands[i].first == "addkey") { - RUNTOOL(addkey, infile, commands[i].second, status); - } else if (commands[i].first == "addlabels") { - RUNTOOL(addlabels, infile, commands[i].second, status); - } else if (commands[i].first == "addtempo") { - RUNTOOL(addtempo, infile, commands[i].second, status); - } else if (commands[i].first == "autoaccid") { - RUNTOOL(autoaccid, infile, commands[i].second, status); - } else if (commands[i].first == "autobeam") { - RUNTOOL(autobeam, infile, commands[i].second, status); - } else if (commands[i].first == "autostem") { - RUNTOOL(autostem, infile, commands[i].second, status); - } else if (commands[i].first == "binroll") { - RUNTOOL(binroll, infile, commands[i].second, status); - } else if (commands[i].first == "chantize") { - RUNTOOL(chantize, infile, commands[i].second, status); - } else if (commands[i].first == "chint") { - RUNTOOL(chint, infile, commands[i].second, status); - } else if (commands[i].first == "chord") { - RUNTOOL(chord, infile, commands[i].second, status); - } else if (commands[i].first == "cint") { - RUNTOOL(cint, infile, commands[i].second, status); - } else if (commands[i].first == "cmr") { - RUNTOOL(cmr, infile, commands[i].second, status); - } else if (commands[i].first == "composite") { - RUNTOOL(composite, infile, commands[i].second, status); - } else if (commands[i].first == "dissonant") { - RUNTOOL(dissonant, infile, commands[i].second, status); - } else if (commands[i].first == "double") { - RUNTOOL(double, infile, commands[i].second, status); - } else if (commands[i].first == "fb") { - RUNTOOL(fb, infile, commands[i].second, status); - } else if (commands[i].first == "flipper") { - RUNTOOL(flipper, infile, commands[i].second, status); - } else if (commands[i].first == "filter") { - RUNTOOL(filter, infile, commands[i].second, status); - } else if (commands[i].first == "gasparize") { - RUNTOOL(gasparize, infile, commands[i].second, status); - } else if (commands[i].first == "half") { - RUNTOOL(half, infile, commands[i].second, status); - } else if (commands[i].first == "hands") { - RUNTOOL(hands, infile, commands[i].second, status); - } else if (commands[i].first == "homorhythm") { - RUNTOOL(homorhythm, infile, commands[i].second, status); - } else if (commands[i].first == "homorhythm2") { - RUNTOOL(homorhythm2, infile, commands[i].second, status); - } else if (commands[i].first == "hproof") { - RUNTOOL(hproof, infile, commands[i].second, status); - } else if (commands[i].first == "humbreak") { - RUNTOOL(humbreak, infile, commands[i].second, status); - } else if (commands[i].first == "humsheet") { - RUNTOOL(humsheet, infile, commands[i].second, status); - } else if (commands[i].first == "humtr") { - RUNTOOL(humtr, infile, commands[i].second, status); - } else if (commands[i].first == "imitation") { - RUNTOOL(imitation, infile, commands[i].second, status); - } else if (commands[i].first == "instinfo") { - RUNTOOL(instinfo, infile, commands[i].second, status); - } else if (commands[i].first == "kern2mens") { - RUNTOOL(kern2mens, infile, commands[i].second, status); - } else if (commands[i].first == "kernify") { - RUNTOOL(kernify, infile, commands[i].second, status); - } else if (commands[i].first == "kernview") { - RUNTOOL(kernview, infile, commands[i].second, status); - } else if (commands[i].first == "melisma") { - RUNTOOL(melisma, infile, commands[i].second, status); - } else if (commands[i].first == "mens2kern") { - RUNTOOL(mens2kern, infile, commands[i].second, status); - } else if (commands[i].first == "meter") { - RUNTOOL(meter, infile, commands[i].second, status); - } else if (commands[i].first == "metlev") { - RUNTOOL(metlev, infile, commands[i].second, status); - } else if (commands[i].first == "modori") { - RUNTOOL(modori, infile, commands[i].second, status); - } else if (commands[i].first == "msearch") { - RUNTOOL(msearch, infile, commands[i].second, status); - } else if (commands[i].first == "nproof") { - RUNTOOL(nproof, infile, commands[i].second, status); - } else if (commands[i].first == "ordergps") { - RUNTOOL(ordergps, infile, commands[i].second, status); - } else if (commands[i].first == "phrase") { - RUNTOOL(phrase, infile, commands[i].second, status); - } else if (commands[i].first == "pline") { - RUNTOOL(pline, infile, commands[i].second, status); - } else if (commands[i].first == "prange") { - RUNTOOL(prange, infile, commands[i].second, status); - } else if (commands[i].first == "recip") { - RUNTOOL(recip, infile, commands[i].second, status); - } else if (commands[i].first == "restfill") { - RUNTOOL(restfill, infile, commands[i].second, status); - } else if (commands[i].first == "sab2gs") { - RUNTOOL(sab2gs, infile, commands[i].second, status); - } else if (commands[i].first == "scordatura") { - RUNTOOL(scordatura, infile, commands[i].second, status); - } else if (commands[i].first == "semitones") { - RUNTOOL(semitones, infile, commands[i].second, status); - } else if (commands[i].first == "shed") { - RUNTOOL(shed, infile, commands[i].second, status); - } else if (commands[i].first == "sic") { - RUNTOOL(sic, infile, commands[i].second, status); - } else if (commands[i].first == "simat") { - RUNTOOL2(simat, infile, infile, commands[i].second, status); - } else if (commands[i].first == "slurcheck") { - RUNTOOL(slurcheck, infile, commands[i].second, status); - } else if (commands[i].first == "slur") { - RUNTOOL(slurcheck, infile, commands[i].second, status); - } else if (commands[i].first == "spinetrace") { - RUNTOOL(spinetrace, infile, commands[i].second, status); - } else if (commands[i].first == "strophe") { - RUNTOOL(strophe, infile, commands[i].second, status); - } else if (commands[i].first == "synco") { - RUNTOOL(synco, infile, commands[i].second, status); - } else if (commands[i].first == "tabber") { - RUNTOOL(tabber, infile, commands[i].second, status); - } else if (commands[i].first == "tassoize") { - RUNTOOL(tassoize, infile, commands[i].second, status); - } else if (commands[i].first == "tassoise") { - RUNTOOL(tassoize, infile, commands[i].second, status); - } else if (commands[i].first == "tasso") { - RUNTOOL(tassoize, infile, commands[i].second, status); - } else if (commands[i].first == "textdur") { - RUNTOOL(textdur, infile, commands[i].second, status); - } else if (commands[i].first == "tie") { - RUNTOOL(tie, infile, commands[i].second, status); - } else if (commands[i].first == "tspos") { - RUNTOOL(tspos, infile, commands[i].second, status); - } else if (commands[i].first == "transpose") { - RUNTOOL(transpose, infile, commands[i].second, status); - } else if (commands[i].first == "tremolo") { - RUNTOOL(tremolo, infile, commands[i].second, status); - } else if (commands[i].first == "trillspell") { - RUNTOOL(trillspell, infile, commands[i].second, status); - } else if (commands[i].first == "vcross") { - RUNTOOL(vcross, infile, commands[i].second, status); + xserial.reserve(1000); + xserial.resize(0); - // filters with aliases: + fpos.reserve(1000); + fpos.resize(0); - } else if (commands[i].first == "colortriads") { - RUNTOOL(colortriads, infile, commands[i].second, status); - } else if (commands[i].first == "colourtriads") { - // British spelling - RUNTOOL(colortriads, infile, commands[i].second, status); + string spat; + string spinepat; + HumRegex hre; + int subtarget; + int modeltarget; + int xdebug = 0; + int vdebug = 0; + int suppress = 0; + int target; + int tval; + for (int t=0; t<(int)field.size(); t++) { + target = field[t]; + subtarget = subfield[t]; + modeltarget = model[t]; + if (modeltarget == 0) { + switch (subtarget) { + case 'a': + case 'b': + modeltarget = submodel; + break; + case 'c': + modeltarget = comodel; + } + } + suppress = 0; + if (target == 0) { + if (infile.token(line, 0)->compare(0, 2, "**") == 0) { + storeToken(tempout, blankName); + tval = 0; + vserial.push_back(tval); + xserial.push_back(tval); + fpos.push_back(tval); + } else if (*infile.token(line, 0) == "*-") { + storeToken(tempout, "*-"); + tval = 0; + vserial.push_back(tval); + xserial.push_back(tval); + fpos.push_back(tval); + } else { + storeToken(tempout, "*"); + tval = 0; + vserial.push_back(tval); + xserial.push_back(tval); + fpos.push_back(tval); + } + } else { + for (j=0; jgetTrack() != target) { + continue; + } + // filter by subfield + if (subtarget == 'a') { + getSearchPat(spat, target, "b"); + if (hre.search(infile.token(line, j)->getSpineInfo(), spat)) { + continue; + } + } else if (subtarget == 'b') { + getSearchPat(spat, target, "a"); + if (hre.search(infile.token(line, j)->getSpineInfo(), spat)) { + continue; + } + } - } else if (commands[i].first == "colorthirds") { - RUNTOOL(tspos, infile, commands[i].second, status); - } else if (commands[i].first == "colourthirds") { - // British spelling - RUNTOOL(tspos, infile, commands[i].second, status); + switch (subtarget) { + case 'a': - } else if (commands[i].first == "colorgroups") { - RUNTOOL(colorgroups, infile, commands[i].second, status); - } else if (commands[i].first == "colourgroups") { // British spelling - RUNTOOL(colorgroups, infile, commands[i].second, status); + if (!hre.search(infile.token(line, j)->getSpineInfo(), "\\(")) { + if (*infile.token(line, j) == "*^") { + storeToken(tempout, "*"); + } else { + storeToken(tempout, *infile.token(line, j)); + } + } else { + getSearchPat(spat, target, "a"); + spinepat = infile.token(line, j)->getSpineInfo(); + hre.replaceDestructive(spinepat, "\\(", "\\(", "g"); + hre.replaceDestructive(spinepat, "\\)", "\\)", "g"); - } else if (commands[i].first == "deg") { // humlib version of Humdrum Toolkit deg tool - RUNTOOL(deg, infile, commands[i].second, status); - } else if (commands[i].first == "degx") { // humlib cli name - RUNTOOL(deg, infile, commands[i].second, status); + if ((*infile.token(line, j) == "*v") && + (spinepat == spat)) { + storeToken(tempout, "*"); + } else { + getSearchPat(spat, target, "b"); + if ((spinepat == spat) && + (*infile.token(line, j) == "*v")) { + // do nothing + suppress = 1; + } else { + storeToken(tempout, *infile.token(line, j)); + } + } + } - } else if (commands[i].first == "extract") { // humlib version of Humdrum Toolkit extract tool - RUNTOOL(extract, infile, commands[i].second, status); - } else if (commands[i].first == "extractx") { // humlib cli name - RUNTOOL(extract, infile, commands[i].second, status); + break; + case 'b': - } else if (commands[i].first == "grep") { - RUNTOOL(grep, infile, commands[i].second, status); - } else if (commands[i].first == "humgrep") { - RUNTOOL(grep, infile, commands[i].second, status); + if (!hre.search(infile.token(line, j)->getSpineInfo(), "\\(")) { + if (*infile.token(line, j) == "*^") { + storeToken(tempout, "*"); + } else { + storeToken(tempout, *infile.token(line, j)); + } + } else { + getSearchPat(spat, target, "b"); + spinepat = infile.token(line, j)->getSpineInfo(); + hre.replaceDestructive(spinepat, "\\(", "\\(", "g"); + hre.replaceDestructive(spinepat, "\\)", "\\)", "g"); - } else if (commands[i].first == "myank") { // humlib version of Humdrum Extras myank tool - RUNTOOL(myank, infile, commands[i].second, status); - } else if (commands[i].first == "myankx") { // humlib cli name - RUNTOOL(myank, infile, commands[i].second, status); + if ((*infile.token(line, j) == "*v") && + (spinepat == spat)) { + storeToken(tempout, "*"); + } else { + getSearchPat(spat, target, "a"); + if ((spinepat == spat) && + (*infile.token(line, j) == "*v")) { + // do nothing + suppress = 1; + } else { + storeToken(tempout, *infile.token(line, j)); + } + } + } - } else if (commands[i].first == "rid") { // humlib version of Humdrum Toolkit deg tool - RUNTOOL(rid, infile, commands[i].second, status); - } else if (commands[i].first == "ridx") { // Humdrum Extra cli name - RUNTOOL(rid, infile, commands[i].second, status); - } else if (commands[i].first == "ridxx") { // humlib cli name - RUNTOOL(rid, infile, commands[i].second, status); + break; + case 'c': + // work on later + storeToken(tempout, *infile.token(line, j)); + break; + default: + storeToken(tempout, *infile.token(line, j)); + } - } else if (commands[i].first == "satb2gs") { // humlib version of Humdrum Extras satg2gs tool - RUNTOOL(satb2gs, infile, commands[i].second, status); - } else if (commands[i].first == "satb2gsx") { // humlib cli name - RUNTOOL(satb2gs, infile, commands[i].second, status); + if (suppress) { + continue; + } - } else if (commands[i].first == "thru") { // humlib version of Humdrum Toolkit thru tool - RUNTOOL(thru, infile, commands[i].second, status); - } else if (commands[i].first == "thrux") { // Humdrum Extras cli name - RUNTOOL(thru, infile, commands[i].second, status); - } else if (commands[i].first == "thruxx") { // humlib cli name - RUNTOOL(thru, infile, commands[i].second, status); + if (tempout[(int)tempout.size()-1] == "*x") { + tval = fieldoccur[t] * 1000 + xmanip[j]; + xserial.push_back(tval); + xdebug = 1; + } else { + tval = 0; + xserial.push_back(tval); + } - } else if (commands[i].first == "timebase") { // humlib version of Humdrum Toolkit timebase tool - RUNTOOL(timebase, infile, commands[i].second, status); - } else if (commands[i].first == "timebasex") { // humlib cli name - RUNTOOL(timebase, infile, commands[i].second, status); - } else { - cerr << "UNKNOWN FILTER: " << commands[i].first << " OPTIONS: " << commands[i].second << endl; - } + if (tempout[(int)tempout.size()-1] == "*v") { + tval = fieldoccur[t] * 1000 + vmanip[j]; + vserial.push_back(tval); + vdebug = 1; + } else { + tval = 0; + vserial.push_back(tval); + } - } + fpos.push_back(j); - removeGlobalFilterLines(infile); + } + } + } - // Re-load the text for each line from their tokens in case any - // updates are needed from token changes. - infile.createLinesFromTokens(); - return status; -} + if (debugQ && xdebug) { + m_humdrum_text << "!! *x serials = "; + for (int ii=0; ii<(int)xserial.size(); ii++) { + m_humdrum_text << xserial[ii] << " "; + } + m_humdrum_text << "\n"; + } + if (debugQ && vdebug) { + m_humdrum_text << "!!LINE: " << infile[line] << endl; + m_humdrum_text << "!! *v serials = "; + for (int ii=0; ii<(int)vserial.size(); ii++) { + m_humdrum_text << vserial[ii] << " "; + } + m_humdrum_text << "\n"; + } + // check for proper *x syntax ///////////////////////////////// + for (i=0; i<(int)xserial.size()-1; i++) { + if (!xserial[i]) { + continue; + } + if (xserial[i] != xserial[i+1]) { + if (tempout[i] == "*x") { + xserial[i] = 0; + tempout[i] = "*"; + } + } else { + i++; + } + } -////////////////////////////// -// -// Tool_filter::removeGlobalFilterLines -- -// + if ((tempout.size() == 1) || (xserial.size() == 1)) { + // get rid of *x if there is only one spine in output + if (xserial[0]) { + xserial[0] = 0; + tempout[0] = "*"; + } + } else if ((int)xserial.size() > 1) { + // check the last item in the list + int index = (int)xserial.size()-1; + if (tempout[index] == "*x") { + if (xserial[index] != xserial[index-1]) { + xserial[index] = 0; + tempout[index] = "*"; + } + } + } -void Tool_filter::removeGlobalFilterLines(HumdrumFile& infile) { - HumRegex hre; - string text; + // check for proper *v syntax ///////////////////////////////// + vector vsplit; + vsplit.resize((int)vserial.size()); + fill(vsplit.begin(), vsplit.end(), 0); - string maintag = "!!!filter:"; - string mainXtag = "!!!Xfilter:"; - string maintagQuery = "^!!!filter:"; + // identify necessary line splits + for (i=0; i<(int)vserial.size()-1; i++) { + if (!vserial[i]) { + continue; + } + while ((i<(int)vserial.size()-1) && (vserial[i]==vserial[i+1])) { + i++; + } + if ((i<(int)vserial.size()-1) && vserial[i]) { + if (vserial.size() > 1) { + if (vserial[i+1]) { + vsplit[i+1] = 1; + } + } + } + } - string maintagV; - string mainXtagV; - string maintagQueryV; + // remove single *v spines: - if (m_variant.size() > 0) { - maintagV = "!!!filter-" + m_variant + ":"; - mainXtagV = "!!!Xfilter-" + m_variant + ":"; - maintagQueryV = "^!!!filter-" + m_variant + ":"; + for (i=0; i<(int)vsplit.size()-1; i++) { + if (vsplit[i] && vsplit[i+1]) { + if (tempout[i] == "*v") { + tempout[i] = "*"; + vsplit[i] = 0; + } + } } - for (int i=0; i 0) { - if (infile.token(i, 0)->compare(0, maintagV.size(), maintagV) == 0) { - text = infile.token(i, 0)->getText(); - hre.replaceDestructive(text, mainXtagV, maintagQueryV); - infile.token(i, 0)->setText(text); + if (vsplit.size() > 0) { + if (vsplit[(int)vsplit.size()-1]) { + if (tempout[(int)tempout.size()-1] == "*v") { + tempout[(int)tempout.size()-1] = "*"; + vsplit[(int)vsplit.size()-1] = 0; } - } else { - if (infile.token(i, 0)->compare(0, maintag.size(), maintag) == 0) { - text = infile.token(i, 0)->getText(); - hre.replaceDestructive(text, mainXtag, maintagQuery); - infile.token(i, 0)->setText(text); + } + } + + int vcount = 0; + for (i=0; i<(int)vsplit.size(); i++) { + vcount += vsplit[i]; + } + + if (vcount) { + printMultiLines(vsplit, vserial, tempout); + } + + int start = 0; + for (i=0; i<(int)tempout.size(); i++) { + if (tempout[i] != "") { + if (start != 0) { + m_humdrum_text << "\t"; } + m_humdrum_text << tempout[i]; + start++; } } + if (start) { + m_humdrum_text << '\n'; + } } ////////////////////////////// // -// Tool_filter::removeUniversalFilterLines -- +// Tool_extract::printMultiLines -- print separate *v lines. // -void Tool_filter::removeUniversalFilterLines(HumdrumFileSet& infiles) { - HumRegex hre; - string text; +void Tool_extract::printMultiLines(vector& vsplit, vector& vserial, + vector& tempout) { + int i; - string maintag = "!!!!filter:"; - string mainXtag = "!!!!Xfilter:"; - string maintagQuery = "^!!!!filter:"; + int splitpoint = -1; + for (i=0; i<(int)vsplit.size(); i++) { + if (vsplit[i]) { + splitpoint = i; + break; + } + } - string maintagV; - string mainXtagV; - string maintagQueryV; + if (debugQ) { + m_humdrum_text << "!!tempout: "; + for (i=0; i<(int)tempout.size(); i++) { + m_humdrum_text << tempout[i] << " "; + } + m_humdrum_text << endl; + } - if (m_variant.size() > 0) { - maintagV = "!!!!filter-" + m_variant + ":"; - mainXtagV = "!!!!Xfilter-" + m_variant + ":"; - maintagQueryV = "^!!!!filter-" + m_variant + ":"; + if (splitpoint == -1) { + return; } - for (int i=0; i 0) { - if (token->compare(0, maintagV.size(), maintagV) == 0) { - text = token->getText(); - hre.replaceDestructive(text, mainXtagV, maintagQueryV); - token->setText(text); - infile[j].createLineFromTokens(); + m_humdrum_text << tempout[i]; + start = 1; + if (tempout[i] == "*v") { + if (printv) { + tempout[i] = ""; + } else { + tempout[i] = "*"; + printv = 1; } } else { - if (token->compare(0, maintag.size(), maintag) == 0) { - text = token->getText(); - hre.replaceDestructive(text, mainXtag, maintagQuery); - token->setText(text); - infile[j].createLineFromTokens(); - } + tempout[i] = "*"; + } + } + } + + for (i=splitpoint; i<(int)vsplit.size(); i++) { + if (tempout[i] != "") { + if (start) { + m_humdrum_text << "\t"; } + m_humdrum_text << "*"; } } + + if (start) { + m_humdrum_text << "\n"; + } + + vsplit[splitpoint] = 0; + + printMultiLines(vsplit, vserial, tempout); } ////////////////////////////// // -// Tool_filter::getCommandList -- +// Tool_extract::storeToken -- // -void Tool_filter::getCommandList(vector >& commands, - HumdrumFile& infile) { +void Tool_extract::storeToken(vector& storage, const string& string) { + storage.push_back(string); +} - vector refs = infile.getReferenceRecords(); - pair entry; - string tag = "filter"; - if (m_variant.size() > 0) { - tag += "-"; - tag += m_variant; - } - vector clist; - HumRegex hre; - for (int i=0; i<(int)refs.size(); i++) { - string refkey = refs[i]->getGlobalReferenceKey(); - if (refkey != tag) { - continue; - } - string command = refs[i]->getGlobalReferenceValue(); - splitPipeline(clist, command); - for (int j=0; j<(int)clist.size(); j++) { - if (hre.search(clist[j], "^\\s*([^\\s]+)")) { - entry.first = hre.getMatch(1); - entry.second = clist[j]; - commands.push_back(entry); - } +void storeToken(vector& storage, int index, const string& string) { + storage[index] = string; +} + + + +////////////////////////////// +// +// Tool_extract::isInList -- returns true if first number found in list of numbers. +// returns the matching index plus one. +// + +int Tool_extract::isInList(int number, vector& listofnum) { + int i; + for (i=0; i<(int)listofnum.size(); i++) { + if (listofnum[i] == number) { + return i+1; } } + return 0; + } ////////////////////////////// // -// Tool_filter::splitPipeline -- +// Tool_extract::getTraceData -- // -void Tool_filter::splitPipeline(vector& clist, const string& command) { - clist.clear(); - clist.resize(1); - clist[0] = ""; - int inDoubleQuotes = -1; - int inSingleQuotes = -1; - char ch = '\0'; - char lastch; - for (int i=0; i<(int)command.size(); i++) { - lastch = ch; - ch = command[i]; +void Tool_extract::getTraceData(vector& startline, vector >& fields, + const string& tracefile, HumdrumFile& infile) { + char buffer[1024] = {0}; + HumRegex hre; + int linenum; + startline.reserve(10000); + startline.resize(0); + fields.reserve(10000); + fields.resize(0); - if (ch == '"') { - if (lastch == '\\') { - // escaped double quote, so treat as regular character - clist.back() += ch; - continue; - } else if (inDoubleQuotes >= 0) { - // turn off previous double quote sequence - clist.back() += ch; - inDoubleQuotes = -1; - continue; - } else if (inSingleQuotes >= 0) { - // in an active single quote, so this is not a closing double quote - clist.back() += ch; - continue; - } else { - // this is the start of a double quote sequence - clist.back() += ch; - inDoubleQuotes = i; - continue; + ifstream input; + input.open(tracefile.c_str()); + if (!input.is_open()) { + m_error_text << "Error: cannot open file for reading: " << tracefile << endl; + return; + } + + string temps; + vector field; + vector subfield; + vector model; + + input.getline(buffer, 1024); + while (!input.eof()) { + if (hre.search(buffer, "^\\s*$")) { + continue; + } + if (!hre.search(buffer, "(\\d+)")) { + continue; + } + linenum = hre.getMatchInt(1); + linenum--; // adjust so that line 0 is the first line in the file + temps = buffer; + hre.replaceDestructive(temps, "", "\\d+"); + hre.replaceDestructive(temps, "", "[^,\\s\\d\\$\\-].*"); // remove any possible comments + hre.replaceDestructive(temps, "", "\\s", "g"); + if (hre.search(temps, "^\\s*$")) { + // no field data to process online + continue; + } + startline.push_back(linenum); + string ttemp = temps; + fillFieldData(field, subfield, model, ttemp, infile); + fields.push_back(field); + input.getline(buffer, 1024); + } + +} + + + +////////////////////////////// +// +// Tool_extract::extractTrace -- +// + +void Tool_extract::extractTrace(HumdrumFile& infile, const string& tracefile) { + vector startline; + vector > fields; + getTraceData(startline, fields, tracefile, infile); + int i, j; + + if (debugQ) { + for (i=0; i<(int)startline.size(); i++) { + m_humdrum_text << "!!TRACE " << startline[i]+1 << ":\t"; + for (j=0; j<(int)fields[i].size(); j++) { + m_humdrum_text << fields[i][j] << " "; } + m_humdrum_text << "\n"; } + } - if (ch == '\'') { - if (lastch == '\\') { - // escaped single quote, so treat as regular character - clist.back() += ch; - continue; - } else if (inSingleQuotes >= 0) { - // turn off previous single quote sequence - clist.back() += ch; - inSingleQuotes = -1; - continue; - } else if (inDoubleQuotes >= 0) { - // in an active double quote, so this is not a closing single quote - clist.back() += ch; - continue; - } else { - // this is the start of a single quote sequence - clist.back() += ch; - inSingleQuotes = i; - continue; + + if (startline.size() == 0) { + for (i=0; i -1) || (inDoubleQuotes > -1)) { - // pipe character - clist.back() += ch; - continue; + for (i=0; i -1)) && (!(inDoubleQuotes > -1))) { - if (isspace(lastch)) { - // don't repeat spaces outside of quotes. + + +////////////////////////////// +// +// Tool_extract::printTraceLine -- +// + +void Tool_extract::printTraceLine(HumdrumFile& infile, int line, vector& field) { + int j; + int t; + int start = 0; + int target; + + start = 0; + for (t=0; t<(int)field.size(); t++) { + target = field[t]; + for (j=0; jgetTrack() != target) { continue; } + if (start != 0) { + m_humdrum_text << '\t'; + } + start = 1; + m_humdrum_text << infile.token(line, j); } - - // regular character - clist.back() += ch; } - - // remove leading and trailing spaces - HumRegex hre; - for (int i=0; i<(int)clist.size(); i++) { - hre.replaceDestructive(clist[i], "", "^\\s+"); - hre.replaceDestructive(clist[i], "", "\\s+$"); + if (start != 0) { + m_humdrum_text << endl; } +} + + + +////////////////////////////// +// +// Tool_extract::example -- example usage of the sonority program +// +void Tool_extract::example(void) { + m_free_text << + " \n" + << endl; } ////////////////////////////// // -// Tool_filter::getUniversalCommandList -- +// Tool_extract::usage -- gives the usage statement for the sonority program // -void Tool_filter::getUniversalCommandList(vector >& commands, - HumdrumFileSet& infiles) { +void Tool_extract::usage(const string& command) { + m_free_text << + " \n" + << endl; +} - vector refs = infiles.getUniversalReferenceRecords(); - pair entry; - string tag = "filter"; - if (m_variant.size() > 0) { - tag += "-"; - tag += m_variant; + + +////////////////////////////// +// +// Tool_extract::initialize -- +// + +void Tool_extract::initialize(HumdrumFile& infile) { + // handle basic options: + if (getBoolean("author")) { + m_free_text << "Written by Craig Stuart Sapp, " + << "craig@ccrma.stanford.edu, Feb 2008" << endl; + return; + } else if (getBoolean("version")) { + m_free_text << getArg(0) << ", version: Feb 2008" << endl; + m_free_text << "compiled: " << __DATE__ << endl; + return; + } else if (getBoolean("help")) { + usage(getCommand().c_str()); + return; + } else if (getBoolean("example")) { + example(); + return; } - vector clist; - HumRegex hre; - for (int i=0; i<(int)refs.size(); i++) { - if (refs[i]->getUniversalReferenceKey() != tag) { - continue; + + excludeQ = getBoolean("x"); + interpQ = getBoolean("i"); + interps = getString("i"); + kernQ = getBoolean("k"); + rkernQ = getBoolean("K"); + + interpstate = 1; + if (!interpQ) { + interpQ = getBoolean("I"); + interpstate = 0; + interps = getString("I"); + } + if (interps.size() > 0) { + if (interps[0] != '*') { + // Automatically add ** if not given on exclusive interpretation + string tstring = "**"; + interps = tstring + interps; } - string command = refs[i]->getUniversalReferenceValue(); - hre.split(clist, command, "\\s*\\|\\s*"); - for (int j=0; j<(int)clist.size(); j++) { - if (hre.search(clist[j], "^\\s*([^\\s]+)")) { - entry.first = hre.getMatch(1); - entry.second = clist[j]; - commands.push_back(entry); + } + + removerestQ = getBoolean("no-rest"); + noEmptyQ = getBoolean("no-empty"); + emptyQ = getBoolean("empty"); + fieldQ = getBoolean("f"); + debugQ = getBoolean("debug"); + countQ = getBoolean("count"); + traceQ = getBoolean("trace"); + tracefile = getString("trace"); + reverseQ = getBoolean("reverse"); + expandQ = getBoolean("expand") || getBoolean("E"); + submodel = getString("model").c_str()[0]; + cointerp = getString("cointerp"); + comodel = getString("cospine-model").c_str()[0]; + + if (getBoolean("no-editoral-rests")) { + editorialInterpretation = ""; + } + + if (interpQ) { + fieldQ = true; + } + + if (emptyQ) { + fieldQ = true; + } + + if (noEmptyQ) { + fieldQ = true; + } + + if (expandQ) { + fieldQ = true; + expandInterp = getString("expand-interp"); + } + + if (!reverseQ) { + reverseQ = getBoolean("R"); + if (reverseQ) { + reverseInterp = getString("R"); + } + } + + if (reverseQ) { + fieldQ = true; + } + + if (excludeQ) { + fieldstring = getString("x"); + } else if (fieldQ) { + fieldstring = getString("f"); + } else if (kernQ) { + fieldstring = getString("k"); + fieldQ = true; + } else if (rkernQ) { + fieldstring = getString("K"); + fieldQ = true; + fieldstring = reverseFieldString(fieldstring, infile.getMaxTrack()); + } + + spineListQ = getBoolean("spine-list"); + grepQ = getBoolean("grep"); + grepString = getString("grep"); + + if (getBoolean("name")) { + blankName = getString("name"); + if (blankName == "") { + blankName = "**blank"; + } else if (blankName.compare(0, 2, "**") != 0) { + if (blankName.compare(0, 1, "*") != 0) { + blankName = "**" + blankName; + } else { + blankName = "*" + blankName; } } + if (blankName == "**kern") { + addRestsQ = true; + } } -} +} ////////////////////////////// // -// Tool_filter::initialize -- extract time signature lines for -// each **kern spine in file. +// Tool_extract::reverseFieldString -- No dollar expansion for now. // -void Tool_filter::initialize(HumdrumFile& infile) { - m_debugQ = getBoolean("debug"); - m_variant.clear(); - if (getBoolean("variant")) { - m_variant = getString("variant"); +string Tool_extract::reverseFieldString(const string& input, int maxval) { + string output; + string number; + for (int i=0; i<(int)input.size(); i++) { + if (isdigit(input[i])) { + number += input[i]; + continue; + } else { + if (!number.empty()) { + int value = (int)strtol(number.c_str(), NULL, 10); + value = maxval - value + 1; + output += to_string(value); + output += input[i]; + number.clear(); + } + } + } + if (!number.empty()) { + int value = (int)strtol(number.c_str(), NULL, 10); + value = maxval - value + 1; + output += to_string(value); } + return output; } - - -///////////////////////////////// +////////////////////////////// // -// Tool_fixps::Tool_fixps -- Set the recognized options for the tool. +// Tool_fb::Tool_fb -- Set the recognized options for the tool. // -Tool_fixps::Tool_fixps(void) { - // define ("n|only-remove-empty-transpositions=b", "Only remove empty transpositions"); +Tool_fb::Tool_fb(void) { + define("c|compound=b", "output reasonable figured bass numbers within octave"); + define("a|accidentals|accid|acc=b", "display accidentals in front of the numbers"); + define("b|base|base-track=i:1", "number of the base kern track (compare with -k)"); + define("i|intervallsatz=b", "display numbers under their voice instead of under the base staff"); + define("o|sort|order=b", "sort figured bass numbers by size"); + define("l|lowest=b", "use lowest note as base note"); + define("n|normalize=b", "remove number 8 and doubled numbers; adds -co"); + define("r|reduce|abbreviate|abbr=b", "use abbreviated figures; adds -nco"); + define("t|ties=b", "hide numbers without attack or changing base (needs -i)"); + define("f|figuredbass=b", "shortcut for -acorn3"); + define("3|hide-three=b", "hide number 3 if it has an accidental"); + define("m|negative=b", "show negative numbers"); + define("above=b", "show numbers above the staff (**fba)"); + define("rate=s:", "rate to display the numbers (use a **recip value, e.g. 4, 4.)"); + define("k|kern-tracks=s", "process only the specified kern spines"); + define("s|spine-tracks|spine|spines|track|tracks=s", "Process only the specified spines"); + define("hint=b", "determine harmonic intervals with interval quality"); } -///////////////////////////////// +////////////////////////////// // -// Tool_fixps::run -- Primary interfaces to the tool. +// Tool_fb::run -- Do the main work of the tool. // -bool Tool_fixps::run(HumdrumFileSet& infiles) { +bool Tool_fb::run(HumdrumFileSet &infiles) { bool status = true; - for (int i=0; i> newlist; - removeEmpties(newlist, infile); - outputNewSpining(newlist, infile); -} +void Tool_fb::initialize(void) { + m_compoundQ = getBoolean("compound"); + m_accidentalsQ = getBoolean("accidentals"); + m_baseTrackQ = getInteger("base"); + m_intervallsatzQ = getBoolean("intervallsatz"); + m_sortQ = getBoolean("sort"); + m_lowestQ = getBoolean("lowest"); + m_normalizeQ = getBoolean("normalize"); + m_reduceQ = getBoolean("reduce"); + m_attackQ = getBoolean("ties"); + m_figuredbassQ = getBoolean("figuredbass"); + m_hideThreeQ = getBoolean("hide-three"); + m_showNegativeQ = getBoolean("negative"); + m_aboveQ = getBoolean("above"); + m_rateQ = getString("rate"); + m_hintQ = getBoolean("hint"); + if (getBoolean("spine-tracks")) { + m_spineTracks = getString("spine-tracks"); + } else if (getBoolean("kern-tracks")) { + m_kernTracks = getString("kern-tracks"); + } + if (m_normalizeQ) { + m_compoundQ = true; + m_sortQ = true; + } -////////////////////////////// -// -// Tool_fixps::outputNewSpining -- -// + if (m_reduceQ) { + m_normalizeQ = true; + m_compoundQ = true; + m_sortQ = true; + } -void Tool_fixps::outputNewSpining(vector>& newlist, HumdrumFile& infile) { - for (int i=0; i 0) && (!newlist[i].empty()) && newlist[i][0]->isCommentLocal()) { - if (!newlist[i-1].empty() && newlist[i-1][0]->isCommentLocal()) { - if (newlist[i].size() == newlist[i-1].size()) { - bool same = true; - for (int j=0; j<(int)newlist[i].size(); j++) { - if (*(newlist[i][j]) != *(newlist[i-1][j])) { -cerr << "GOT HERE " << i << " " << j << endl; -cerr << infile[i-1] << endl; -cerr << infile[i] << endl; -cerr << endl; - same = false; - break; - } - } - if (same) { - continue; - } - } - } - } - if (!infile[i].isManipulator()) { - m_humdrum_text << newlist[i].at(0); - for (int j=1; j<(int)newlist[i].size(); j++) { - m_humdrum_text << "\t"; - m_humdrum_text << newlist[i].at(j); - } - m_humdrum_text << endl; - continue; - } - if ((i > 0) && !infile[i-1].isManipulator()) { - printNewManipulator(infile, newlist, i); - } + if (m_figuredbassQ) { + m_reduceQ = true; + m_normalizeQ = true; + m_compoundQ = true; + m_sortQ = true; + m_accidentalsQ = true; + m_hideThreeQ = true; + } + + if (m_hintQ) { + m_showNegativeQ = true; + // m_lowestQ = true; } } + ////////////////////////////// // -// Tool_fixps::printNewManipulator -- +// Tool_fb::processFile -- // -void Tool_fixps::printNewManipulator(HumdrumFile& infile, vector>& newlist, int line) { - HTp token = infile.token(line, 0); - if (*token == "*-") { - m_humdrum_text << infile[line] << endl; - return; - } - if (token->compare(0, 2, "**") == 0) { - m_humdrum_text << infile[line] << endl; +void Tool_fb::processFile(HumdrumFile& infile) { + + NoteGrid grid(infile); + + vector numbers; + + vector kernspines = infile.getKernSpineStartList(); + + int maxTrack = infile.getMaxTrack(); + + // Do nothing if base track not withing kern track range + if (m_baseTrackQ < 1 || m_baseTrackQ > maxTrack) { return; } - m_humdrum_text << "++++++++++++++++++++" << endl; -} -////////////////////////////// -// -// Tool_fixps::removeDuplicateDynamics -- -// + m_selectedKernSpines.resize(maxTrack + 1); // +1 is needed since track=0 is not used + // By default, process all tracks: + fill(m_selectedKernSpines.begin(), m_selectedKernSpines.end(), true); + // Otherwise, select which **kern track, or spine tracks to process selectively: -void Tool_fixps::removeDuplicateDynamics(HumdrumFile& infile) { - int scount = infile.getStrandCount(); - for (int i=0; iisDataType("**dynam")) { - continue; - } - HTp send = infile.getStrandEnd(i); - HTp current = sstart; - while (current && (current != send)) { - vector subtoks = current->getSubtokens(); - if (subtoks.size() % 2 == 1) { - current = current->getNextToken(); + // Calculate which input spines to process based on -s or -k option: + if (!m_kernTracks.empty()) { + vector ktracks = Convert::extractIntegerList(m_kernTracks, maxTrack); + fill(m_selectedKernSpines.begin(), m_selectedKernSpines.end(), false); + for (int i=0; i<(int)ktracks.size(); i++) { + int index = ktracks[i] - 1; + if ((index < 0) || (index >= (int)kernspines.size())) { continue; } - bool equal = true; - int half = (int)subtoks.size() / 2; - for (int j=0; jsetText(newtext); - } + int track = kernspines.at(ktracks[i] - 1)->getTrack(); + m_selectedKernSpines.at(track) = true; } + } else if (!m_spineTracks.empty()) { + infile.makeBooleanTrackList(m_selectedKernSpines, m_spineTracks); } -} + vector> lastNumbers = {}; + lastNumbers.resize((int)grid.getVoiceCount()); + vector> currentNumbers = {}; + // Interate through the NoteGrid and fill the numbers vector with + // all generated FiguredBassNumbers + for (int i=0; i<(int)grid.getSliceCount(); i++) { + currentNumbers.clear(); + currentNumbers.resize((int)grid.getVoiceCount()); -////////////////////////////// -// -// Tool_fixps::removeEmpties -- -// + // Reset usedBaseKernTrack + int usedBaseKernTrack = m_baseTrackQ; -void Tool_fixps::removeEmpties(vector>& newlist, HumdrumFile& infile) { - newlist.resize(infile.getLineCount()); - for (int i=0; igetToken(); + int initialTokenTrack = currentToken->getTrack(); + + // Handle spine splits + do { + HTp resolvedToken = currentToken->resolveNull(); + int lowest = getLowestBase40Pitch(resolvedToken->getBase40Pitches()); + + if (abs(lowest) < lowestNotePitch) { + lowestNotePitch = abs(lowest); + usedBaseKernTrack = k + 1; + } + + HTp nextToken = currentToken->getNextField(); + if (nextToken && (initialTokenTrack == nextToken->getTrack())) { + currentToken = nextToken; + } else { + // Break loop if nextToken is not the same track as initialTokenTrack + break; + } + } while (currentToken); + } } - if (infile[i].isManipulator()) { + + NoteCell* baseCell = grid.cell(usedBaseKernTrack - 1, i); + + // Ignore grace notes + if (baseCell->getToken()->getOwner()->getDuration() == 0) { continue; } - for (int j=0; jgetValue("delete"); - if (value == "true") { + + string keySignature = getKeySignature(infile, baseCell->getLineIndex()); + + // Hide numbers if they do not match rhythmic position of --rate + if (!m_rateQ.empty()) { + // Get time signatures + vector> timeSigs; + infile.getTimeSigs(timeSigs, baseCell->getToken()->getTrack()); + // Ignore numbers if they don't fit + if (hideNumbersForTokenLine(baseCell->getToken(), timeSigs[baseCell->getLineIndex()])) { continue; } - newlist[i].push_back(token); } - } -} + HTp currentToken = baseCell->getToken(); + int initialTokenTrack = baseCell->getToken()->getTrack(); + int lowestBaseNoteBase40Pitch = 9999; -////////////////////////////// -// -// Tool_fixps::markEmptyVoices -- -// + // Handle spine splits + do { + HTp resolvedToken = currentToken->resolveNull(); + int lowest = getLowestBase40Pitch(resolvedToken->getBase40Pitches()); -void Tool_fixps::markEmptyVoices(HumdrumFile& infile) { - HLp barline = NULL; - for (int i=0; icompare(0, 2, "**")) { - barline = &infile[i]; + // Ignore if base is a rest or silent note + if ((lowest != 0) && (lowest != -1000) && (lowest != -2000)) { + if(abs(lowest) < lowestBaseNoteBase40Pitch) { + lowestBaseNoteBase40Pitch = abs(lowest); + } } + + HTp nextToken = currentToken->getNextField(); + if (nextToken && (initialTokenTrack == nextToken->getTrack())) { + currentToken = nextToken; + } else { + // Break loop if nextToken is not the same track as initialTokenTrack + break; + } + } while (currentToken); + + // Ignore if base is a rest or silent note + if ((lowestBaseNoteBase40Pitch == 0) || (lowestBaseNoteBase40Pitch == -1000) || (lowestBaseNoteBase40Pitch == -2000) || (lowestBaseNoteBase40Pitch == 9999)) { continue; } - if (infile[i].isBarline()) { - barline = &infile[i]; - } - if (!infile[i].isData()) { - continue; - } - if (!barline) { - continue; - } - // check on the data line if: - // * it is in the first subspine - // * it is an invisible rest - // * it takes the full duration of the measure - // If so, then mark the tokens for deletion in that layer. - for (int j=0; jgetTrack(); - int subtrack = token->getSubtrack(); - if (subtrack != 1) { - continue; - } - if (token->find("yy") == string::npos) { - continue; - } - if (!token->isRest()) { + + // Interate through each voice + for (int j=0; j<(int)grid.getVoiceCount(); j++) { + NoteCell* targetCell = grid.cell(j, i); + + // Ignore voice if track is not active by --kern-tracks or --spine-tracks + if (m_selectedKernSpines.at(targetCell->getToken()->getTrack()) == false) { continue; } - HumNum duration = token->getDuration(); - HumNum bardur = token->getDurationToBarline(); - HTp current = token; - while (current) { - subtrack = current->getSubtrack(); - if (subtrack != 1) { - break; - } - current->setValue("delete", "true"); - if (current->isBarline()) { - break; - } - current = current->getNextToken(); - } - current = token; - current = current->getPreviousToken(); - while (current) { - if (current->isManipulator()) { - break; + + HTp currentToken = targetCell->getToken(); + int initialTokenTrack = targetCell->getToken()->getTrack(); + vector chordNumbers = {}; + + // Handle spine splits + do { + HTp resolvedToken = currentToken->resolveNull(); + for (int subtokenBase40: resolvedToken->getBase40Pitches()) { + + // Ignore if target is a rest or silent note + if ((subtokenBase40 == 0) || (subtokenBase40 == -1000) || (subtokenBase40 == -2000)) { + continue; + } + + // Ignore if same pitch as base voice + if ((abs(lowestBaseNoteBase40Pitch) == abs(subtokenBase40)) && (baseCell->getToken()->getTrack() == initialTokenTrack)) { + continue; + } + + // Create FiguredBassNumber + FiguredBassNumber* number = createFiguredBassNumber(abs(lowestBaseNoteBase40Pitch), abs(subtokenBase40), targetCell->getVoiceIndex(), targetCell->getLineIndex(), targetCell->isAttack(), keySignature); + + currentNumbers[j].push_back(number->m_number); + chordNumbers.push_back(number); } - if (current->isBarline()) { + + HTp nextToken = currentToken->getNextField(); + if (nextToken && (initialTokenTrack == nextToken->getTrack())) { + currentToken = nextToken; + } else { + // Break loop if nextToken is not the same track as initialTokenTrack break; } - subtrack = current->getSubtrack(); - if (subtrack != 1) { - break; + } while (currentToken); + + // Sort chord numbers by size + sort(chordNumbers.begin(), chordNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { + return a->m_number > b->m_number; + }); + + // Then add to numbers vector + for (FiguredBassNumber* num: chordNumbers) { + if (lastNumbers[j].size() != 0) { + // If a number belongs to a sustained note but the base note did change + // the new numbers need to be displayable + num->m_baseOfSustainedNoteDidChange = !num->m_isAttack && std::find(lastNumbers[j].begin(), lastNumbers[j].end(), num->m_number) == lastNumbers[j].end(); } - current->setValue("delete", "true"); - current = current->getPreviousToken(); + numbers.push_back(num); } } - } - -} - + // Set current numbers as the new last numbers + lastNumbers = currentNumbers; + } + string exinterp = m_aboveQ ? "**fba" : "**fb"; + if (m_hintQ) { + exinterp = "**hint"; + } -///////////////////////////////// -// -// Tool_flipper::Tool_flipper -- Set the recognized options for the tool. -// + if (m_intervallsatzQ) { + // Create **fb spine for each voice + for (int voiceIndex = 0; voiceIndex < grid.getVoiceCount(); voiceIndex++) { + vector trackData = getTrackDataForVoice(voiceIndex, numbers, infile.getLineCount()); + if (voiceIndex + 1 < grid.getVoiceCount()) { + int trackIndex = kernspines[voiceIndex + 1]->getTrack(); + infile.insertDataSpineBefore(trackIndex, trackData, ".", exinterp); + } else { + infile.appendDataSpine(trackData, ".", exinterp); + } + } + } else { + // Create **fb spine and bind it to the base voice + vector trackData = getTrackData(numbers, infile.getLineCount()); + if (m_baseTrackQ < grid.getVoiceCount()) { + int trackIndex = kernspines[m_baseTrackQ]->getTrack(); + infile.insertDataSpineBefore(trackIndex, trackData, ".", exinterp); + } else { + infile.appendDataSpine(trackData, ".", exinterp); + } + } -Tool_flipper::Tool_flipper(void) { - define("k|keep=b", "keep *flip/*Xflip instructions"); - define("a|all=b", "flip globally, not just inside *flip/*Xflip regions"); - define("s|strophe=b", "flip inside of strophes as well"); - define("S|strophe-only|only-strophe=b", "flip only inside of strophes as well"); - define("i|interp=s:kern", "flip only in this interpretation"); + // Enables usage in verovio (`!!!filter: fb`) + m_humdrum_text << infile; } -///////////////////////////////// +////////////////////////////// // -// Tool_flipper::run -- Do the main work of the tool. +// Tool_fb::hideNumbersForTokenLine -- Checks if rhythmic position of line should display numbers // -bool Tool_flipper::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i timeSig) { + // Get note duration from --rate option + HumNum rateDuration = Convert::recipToDuration(m_rateQ); + if (rateDuration.toFloat() != 0) { + double timeSigBarDuration = timeSig.first * Convert::recipToDuration(to_string(timeSig.second.getInteger())).toFloat(); + double durationFromBarline = token->getDurationFromBarline().toFloat(); + // Handle upbeats + if (token->getBarlineDuration().toFloat() < timeSigBarDuration) { + // Fix durationFromBarline when current bar duration is shorter than + // the bar duration of the time signature + durationFromBarline = timeSigBarDuration - token->getDurationToBarline().toFloat(); + } + // Checks if rhythmic position is divisible by rateDuration + return fmod(durationFromBarline, rateDuration.toFloat()) != 0; } - return status; + return false; } -bool Tool_flipper::run(const string& indata, ostream& out) { - HumdrumFile infile(indata); - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; - } - return status; -} +////////////////////////////// +// +// Tool_fb::getTrackData -- Create **fb spine data with formatted numbers for all voices +// -bool Tool_flipper::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; - } - return status; -} +vector Tool_fb::getTrackData(const vector& numbers, int lineCount) { + vector trackData; + trackData.resize(lineCount); + for (int i = 0; i < lineCount; i++) { + vector sliceNumbers = filterFiguredBassNumbersForLine(numbers, i); + if (sliceNumbers.size() > 0) { + trackData[i] = formatFiguredBassNumbers(sliceNumbers); + } + } -bool Tool_flipper::run(HumdrumFile& infile) { - initialize(); - processFile(infile); - return true; + return trackData; } ////////////////////////////// // -// Tool_flipper::initialize -- Initializations that only have to be done once -// for all HumdrumFile segments. +// Tool_fb::getTrackDataForVoice -- Create **fb spine data with formatted numbers for passed voiceIndex // -void Tool_flipper::initialize(void) { - m_allQ = getBoolean("all"); - m_keepQ = getBoolean("keep"); - m_kernQ = true; - m_stropheQ = getBoolean("strophe"); - m_interp = getString("interp"); - if (m_interp != "kern") { - m_kernQ = false; +vector Tool_fb::getTrackDataForVoice(int voiceIndex, const vector& numbers, int lineCount) { + vector trackData; + trackData.resize(lineCount); + + for (int i = 0; i < lineCount; i++) { + vector sliceNumbers = filterFiguredBassNumbersForLineAndVoice(numbers, i, voiceIndex); + if (sliceNumbers.size() > 0) { + trackData[i] = formatFiguredBassNumbers(sliceNumbers); + } } + + return trackData; } ////////////////////////////// // -// Tool_flipper::processFile -- +// Tool_fb::createFiguredBassNumber -- Create FiguredBassNumber from a NoteCell. +// The figured bass number (num) is calculated with a base and target NoteCell +// as well as a passed key signature. // -void Tool_flipper::processFile(HumdrumFile& infile) { +FiguredBassNumber* Tool_fb::createFiguredBassNumber(int basePitchBase40, int targetPitchBase40, int voiceIndex, int lineIndex, bool isAttack, string keySignature) { - m_fliplines.resize(infile.getLineCount()); - fill(m_fliplines.begin(), m_fliplines.end(), false); + // Calculate figured bass number + int baseDiatonicPitch = Convert::base40ToDiatonic(basePitchBase40); + int targetDiatonicPitch = Convert::base40ToDiatonic(targetPitchBase40); + int diff = abs(targetDiatonicPitch) - abs(baseDiatonicPitch); + int num; - m_flipState.resize(infile.getMaxTrack()+1); - if (m_allQ) { - fill(m_flipState.begin(), m_flipState.end(), true); + if ((baseDiatonicPitch == 0) || (targetDiatonicPitch == 0)) { + num = 0; + } else if (diff == 0) { + num = 1; + } else if (diff > 0) { + num = diff + 1; } else { - fill(m_flipState.begin(), m_flipState.end(), false); + num = diff - 1; } - m_strophe.resize(infile.getMaxTrack()+1); - fill(m_strophe.begin(), m_strophe.end(), false); + // Transform key signature to lower case + transform(keySignature.begin(), keySignature.end(), keySignature.begin(), [](unsigned char c) { + return tolower(c); + }); - for (int i=0; igetTrack(); - m_strophe[track] = true; - } else if (*token == "*Xstrophe") { - track = token->getTrack(); - m_strophe[track] = false; + // Show accidentlas when pitch class of base and target is equal but alteration is different + if (basePitchName == targetPitchName) { + if (baseAccidNr == targetAccidNr) { + showAccid = false; + } else { + accid = (targetAccidNr == 0) ? "n" : targetAccid; + showAccid = true; } } + string intervalQuality = getIntervalQuality(basePitchBase40, targetPitchBase40); - if (m_allQ) { - // state always stays on in this case - return; - } - - for (int i=0; igetTrack(); - m_flipState[track] = true; - m_fliplines[i] = true; - } else if (*token == "*Xflip") { - track = token->getTrack(); - m_flipState[track] = false; - m_fliplines[i] = true; - } - } + FiguredBassNumber* number = new FiguredBassNumber(num, accid, showAccid, voiceIndex, lineIndex, isAttack, m_intervallsatzQ, intervalQuality, m_hintQ); + return number; } ////////////////////////////// // -// Tool_flipper::processLine -- +// Tool_fb::filterNegativeNumbers -- Hide negative numbers if m_showNegativeQ if not true // -void Tool_flipper::processLine(HumdrumFile& infile, int index) { - if (!infile[index].hasSpines()) { - return; - } - if (infile[index].isInterpretation()) { - checkForFlipChanges(infile, index); - } +vector Tool_fb::filterNegativeNumbers(vector numbers) { - vector> flipees; - extractFlipees(flipees, infile, index); - if (!flipees.empty()) { - int status = flipSubspines(flipees); - if (status) { - infile[index].createLineFromTokens(); - } - } + vector filteredNumbers; + + bool mQ = m_showNegativeQ; + copy_if(numbers.begin(), numbers.end(), back_inserter(filteredNumbers), [mQ](FiguredBassNumber* num) { + return mQ ? true : (num->m_number > 0); + }); + + return filteredNumbers; } ////////////////////////////// // -// Tool_flipper::flipSubspines -- +// Tool_fb::filterFiguredBassNumbersForLine -- Find all FiguredBassNumber objects for a slice (line index) of the music. // -bool Tool_flipper::flipSubspines(vector>& flipees) { - bool regenerateLine = false; - for (int i=0; i<(int)flipees.size(); i++) { - if (flipees[i].size() > 1) { - flipSpineTokens(flipees[i]); - regenerateLine = true; - } - } - return regenerateLine; -} - - -////////////////////////////// -// -// Tool_flipper::flipSpineTokens -- -// - -void Tool_flipper::flipSpineTokens(vector& subtokens) { - if (subtokens.size() < 2) { - return; - } - int count = (int)subtokens.size(); - count = count / 2; - HTp tok1; - HTp tok2; - string str1; - string str2; - for (int i=0; isetText(str2); - tok2->setText(str1); - } -} - +vector Tool_fb::filterFiguredBassNumbersForLine(vector numbers, int lineIndex) { + vector filteredNumbers; -////////////////////////////// -// -// Tool_flipper::extractFlipees -- -// + // filter numbers with passed lineIndex + copy_if(numbers.begin(), numbers.end(), back_inserter(filteredNumbers), [lineIndex](FiguredBassNumber* num) { + return num->m_lineIndex == lineIndex; + }); -void Tool_flipper::extractFlipees(vector>& flipees, - HumdrumFile& infile, int index) { - flipees.clear(); + // sort by voiceIndex + sort(filteredNumbers.begin(), filteredNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { + return a->m_voiceIndex > b->m_voiceIndex; + }); - HLp line = &infile[index]; - int track; - int lastInsertTrack = -1; - for (int i=0; igetFieldCount(); i++) { - HTp token = line->token(i); - track = token->getTrack(); - if ((!m_stropheQ) && m_strophe[track]) { - continue; - } - if (!m_flipState[track]) { - continue; - } - if (m_kernQ) { - if (!token->isKern()) { - continue; - } - } else { - if (!token->isDataType(m_interp)) { - continue; - } - } - if (lastInsertTrack != track) { - flipees.resize(flipees.size() + 1); - lastInsertTrack = track; - } - flipees.back().push_back(token); - } + return filterNegativeNumbers(filteredNumbers); } - - -///////////////////////////////// +////////////////////////////// // -// Tool_gasparize::Tool_gasparize -- Set the recognized options for the tool. +// Tool_fb::filterFiguredBassNumbersForLineAndVoice -- // -Tool_gasparize::Tool_gasparize(void) { - define("R|no-reference-records=b", "do not add reference records"); - define("r|only-add-reference-records=b", "only add reference records"); - - define("B|do-not-delete-breaks=b", "do not delete system/page break markers"); - define("b|only-delete-breaks=b", "only delete breaks"); - - define("A|do-not-fix-instrument-abbreviations=b", "do not fix instrument abbreviations"); - define("a|only-fix-instrument-abbreviations=b", "only fix instrument abbreviations"); +vector Tool_fb::filterFiguredBassNumbersForLineAndVoice(vector numbers, int lineIndex, int voiceIndex) { - define("E|do-not-fix-editorial-accidentals=b", "do not fix instrument abbreviations"); - define("e|only-fix-editorial-accidentals=b", "only fix editorial accidentals"); + vector filteredNumbers; - define("T|do-not-add-terminal-longs=b", "do not add terminal long markers"); - define("t|only-add-terminal-longs=b", "only add terminal longs"); + // filter numbers with passed lineIndex and passed voiceIndex + copy_if(numbers.begin(), numbers.end(), back_inserter(filteredNumbers), [lineIndex, voiceIndex](FiguredBassNumber* num) { + return (num->m_lineIndex == lineIndex) && (num->m_voiceIndex == voiceIndex); + }); - define("no-ties=b", "do not fix tied notes"); + // sort by voiceIndex (probably not needed here) + sort(filteredNumbers.begin(), filteredNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { + return a->m_voiceIndex > b->m_voiceIndex; + }); - define("N|do-not-remove-empty-transpositions=b", "do not remove empty transposition instructions"); - define ("n|only-remove-empty-transpositions=b", "only remove empty transpositions"); + return filterNegativeNumbers(filteredNumbers); } -///////////////////////////////// +////////////////////////////// // -// Tool_gasparize::run -- Primary interfaces to the tool. +// Tool_fb::formatFiguredBassNumbers -- Create a **fb data record string out of the passed FiguredBassNumber objects // -bool Tool_gasparize::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i& numbers) { + vector formattedNumbers; -bool Tool_gasparize::run(const string& indata, ostream& out) { - HumdrumFile infile(indata); - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); + // Normalize numbers (remove 8 and 1, sort by size, remove duplicate numbers) + if (m_normalizeQ) { + bool aQ = m_accidentalsQ; + // remove 8 and 1 but keep them if they have an accidental + copy_if(numbers.begin(), numbers.end(), back_inserter(formattedNumbers), [aQ](FiguredBassNumber* num) { + return ((num->getNumberWithinOctave() != 8) && (num->getNumberWithinOctave() != 1)) || (aQ && num->m_showAccidentals); + }); + // sort by size + sort(formattedNumbers.begin(), formattedNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { + return a->getNumberWithinOctave() < b->getNumberWithinOctave(); + }); + // remove duplicate numbers + formattedNumbers.erase(unique(formattedNumbers.begin(), formattedNumbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) { + return a->getNumberWithinOctave() == b->getNumberWithinOctave(); + }), formattedNumbers.end()); } else { - out << infile; + formattedNumbers = numbers; } - return status; -} - -bool Tool_gasparize::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; + // Hide numbers if they have no attack + if (m_intervallsatzQ && m_attackQ) { + vector attackNumbers; + copy_if(formattedNumbers.begin(), formattedNumbers.end(), back_inserter(attackNumbers), [](FiguredBassNumber* num) { + return num->m_isAttack || num->m_baseOfSustainedNoteDidChange; + }); + formattedNumbers = attackNumbers; } - return status; -} -// -// In-place processing of file: -// + // Analysze before sorting + if (m_compoundQ) { + formattedNumbers = analyzeChordNumbers(formattedNumbers); + } -bool Tool_gasparize::run(HumdrumFile& infile) { - processFile(infile); + // Sort numbers by size + if (m_sortQ) { + bool cQ = m_compoundQ; + sort(formattedNumbers.begin(), formattedNumbers.end(), [cQ](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { + // sort by getNumberWithinOctave if compoundQ is true otherwise sort by number + return (cQ) ? a->getNumberWithinOctave() > b->getNumberWithinOctave() : a->m_number > b->m_number; + }); + } - // Re-load the text for each line from their tokens. - infile.createLinesFromTokens(); + if (m_reduceQ) { + // Overwrite formattedNumbers with abbreviated numbers + formattedNumbers = getAbbreviatedNumbers(formattedNumbers); + } - // Need to adjust the line numbers for tokens for later - // processing. - m_humdrum_text << infile; - return true; + // join numbers + string str = ""; + bool first = true; + for (FiguredBassNumber* number: formattedNumbers) { + string num = number->toString(m_compoundQ, m_accidentalsQ, m_hideThreeQ); + if (num.length() > 0) { + if (!first) str += " "; + first = false; + str += num; + } + } + return str; } ////////////////////////////// // -// Tool_gasparize::processFile -- -// - -void Tool_gasparize::processFile(HumdrumFile& infile) { - - bool mensurationQ = true; - bool articulationsQ = true; - bool abbreviationsQ = true; - bool accidentalsQ = true; - bool referencesQ = true; - bool terminalsQ = true; - bool breaksQ = true; - bool transpositionsQ = true; - bool tieQ = true; - bool teditQ = true; - bool instrumentQ = true; - bool removekeydesigQ = true; - bool fixbarlinesQ = true; - bool parenthesesQ = true; - - if (getBoolean("no-reference-records")) { referencesQ = false; } - if (getBoolean("only-add-reference-records")) { - abbreviationsQ = false; - accidentalsQ = false; - referencesQ = true; - terminalsQ = false; - breaksQ = false; - transpositionsQ = false; - } - - if (getBoolean("do-not-delete-breaks")) { breaksQ = false; } - if (getBoolean("only-delete-breaks")) { - abbreviationsQ = false; - accidentalsQ = false; - referencesQ = false; - terminalsQ = false; - breaksQ = true; - transpositionsQ = false; - } +// Tool_fb::getAbbreviatedNumbers -- Get abbreviated figured bass numbers +// If no abbreviation is found all numbers will be shown - if (getBoolean("do-not-fix-instrument-abbreviations")) { abbreviationsQ = false; } - if (getBoolean("only-fix-instrument-abbreviations")) { - abbreviationsQ = true; - accidentalsQ = false; - referencesQ = false; - terminalsQ = false; - breaksQ = false; - transpositionsQ = false; - } +vector Tool_fb::getAbbreviatedNumbers(const vector& numbers) { - if (getBoolean("do-not-fix-editorial-accidentals")) { accidentalsQ = false; } - if (getBoolean("only-fix-editorial-accidentals")) { - abbreviationsQ = false; - accidentalsQ = true; - referencesQ = false; - terminalsQ = false; - breaksQ = false; - transpositionsQ = false; - } + vector abbreviatedNumbers; - if (getBoolean("do-not-add-terminal-longs")) { terminalsQ = false; } - if (getBoolean("only-add-terminal-longs")) { - abbreviationsQ = false; - accidentalsQ = false; - referencesQ = false; - terminalsQ = true; - breaksQ = false; - transpositionsQ = false; - } + string numberString = getNumberString(numbers); - if (getBoolean("do-not-remove-empty-transpositions")) { transpositionsQ = false; } + // Check if an abbreviation exists for passed numbers + auto it = find_if(FiguredBassAbbreviationMapping::s_mappings.begin(), FiguredBassAbbreviationMapping::s_mappings.end(), [&numberString](const FiguredBassAbbreviationMapping& abbr) { + return abbr.m_str == numberString; + }); - if (getBoolean("no-ties")) { tieQ = false; } + if (it != FiguredBassAbbreviationMapping::s_mappings.end()) { + const FiguredBassAbbreviationMapping& abbr = *it; + bool aQ = m_accidentalsQ; + // Store numbers to display by the abbreviation mapping in abbreviatedNumbers + copy_if(numbers.begin(), numbers.end(), back_inserter(abbreviatedNumbers), [&abbr, aQ](FiguredBassNumber* num) { + const vector& nums = abbr.m_numbers; + // Show numbers if they are part of the abbreviation mapping or if they have an accidental + return (find(nums.begin(), nums.end(), num->getNumberWithinOctave()) != nums.end()) || (num->m_showAccidentals && aQ); + }); - if (getBoolean("only-remove-empty-transpositions")) { - abbreviationsQ = false; - accidentalsQ = false; - referencesQ = false; - terminalsQ = false; - breaksQ = false; - transpositionsQ = true; + return abbreviatedNumbers; } - if (articulationsQ) { removeArticulations(infile); } - if (fixbarlinesQ) { fixBarlines(infile); } - if (tieQ) { fixTies(infile); } - if (abbreviationsQ) { fixInstrumentAbbreviations(infile); } - if (accidentalsQ) { fixEditorialAccidentals(infile); } - if (parenthesesQ) { createJEditorialAccidentals(infile); } - if (referencesQ) { addBibliographicRecords(infile); } - if (breaksQ) { deleteBreaks(infile); } - if (terminalsQ) { addTerminalLongs(infile); } - if (transpositionsQ) { deleteDummyTranspositions(infile); } - if (mensurationQ) { addMensurations(infile); } - if (teditQ) { createEditText(infile); } - if (instrumentQ) { adjustIntrumentNames(infile); } - if (removekeydesigQ) { removeKeyDesignations(infile); } - - adjustSystemDecoration(infile); - - // Input lyrics may contain "=" signs which are to be converted into - // spaces in **text data, and into elisions when displaying with verovio. - Tool_shed shed; - vector argv; - argv.push_back("shed"); - argv.push_back("-x"); // only apply to **text spines - argv.push_back("text"); - argv.push_back("-e"); - argv.push_back("s/=/ /g"); - shed.process(argv); - shed.run(infile); + return numbers; } ////////////////////////////// // -// Tool_gasparize::removeArticulations -- -// +// Tool_fb::analyzeChordNumbers -- Analyze chord numbers and improve them +// Set m_convert2To9 to true when a 3 is included in the chord numbers. -void Tool_gasparize::removeArticulations(HumdrumFile& infile) { - HumRegex hre; - for (int i=0; iisKern()) { - continue; - } - if (token->isNull()) { - continue; - } - bool changed = false; - string text = token->getText(); - if (text.find("'") != string::npos) { - // remove staccatos - changed = true; - hre.replaceDestructive(text, "", "'", "g"); - } - if (text.find("~") != string::npos) { - // remove tenutos - changed = true; - hre.replaceDestructive(text, "", "~", "g"); - } - if (changed) { - token->setText(text); - } +vector Tool_fb::analyzeChordNumbers(const vector& numbers) { + + vector analyzedNumbers = numbers; + + // Check if compound numbers 3 is withing passed numbers (chord) + auto it = find_if(analyzedNumbers.begin(), analyzedNumbers.end(), [](FiguredBassNumber* number) { + return number->getNumberWithinOctave() == 3; + }); + if (it != analyzedNumbers.end()) { + for (auto &number : analyzedNumbers) { + number->m_convert2To9 = true; } } + + return analyzedNumbers; } ////////////////////////////// // -// Tool_gasparize::adjustSystemDecoration -- -// !!!system-decoration: [(s1)(s2)(s3)(s4)] -// to: -// !!!system-decoration: [*] +// Tool_fb::getNumberString -- Get only the numbers (without accidentals) of passed FiguredBassNumbers // -void Tool_gasparize::adjustSystemDecoration(HumdrumFile& infile) { - for (int i=infile.getLineCount() - 1; i>=0; i--) { - if (!infile[i].isReference()) { - continue; - } - HTp token = infile.token(i, 0); - if (token->compare(0, 21, "!!!system-decoration:") == 0) { - token->setText("!!!system-decoration: [*]"); - break; +string Tool_fb::getNumberString(vector numbers) { + // Sort numbers by size + sort(numbers.begin(), numbers.end(), [](FiguredBassNumber* a, FiguredBassNumber* b) -> bool { + return a->getNumberWithinOctave() > b->getNumberWithinOctave(); + }); + // join numbers + string str = ""; + bool first = true; + for (FiguredBassNumber* nr: numbers) { + int num = nr->getNumberWithinOctave(); + if (num > 0) { + if (!first) str += " "; + first = false; + str += to_string(num); } } + return str; } ////////////////////////////// // -// Tool_gasparize::deleteDummyTranspositions -- Somehow empty -// transpositions that go to the same pitch can appear in the -// MusicXML data, so remove them here. Example: -// *Trd0c0 +// Tool_fb::getKeySignature -- Get the key signature for a line index of the input file // -void Tool_gasparize::deleteDummyTranspositions(HumdrumFile& infile) { - vector ldel; - for (int i=0; iisKern()) { - empty = false; - continue; +string Tool_fb::getKeySignature(HumdrumFile& infile, int lineIndex) { + string keySignature = ""; + [&] { + for (int i = 0; i < infile.getLineCount(); i++) { + if (i > lineIndex) { + return; } - if (*token == "*Trd0c0") { - token->setText("*"); - } else { - empty = false; + HLp line = infile.getLine(i); + for (int j = 0; j < line->getFieldCount(); j++) { + if (line->token(j)->isKeySignature()) { + keySignature = line->getTokenString(j); + } } } - if (empty) { - ldel.push_back(i); - } - } - - if (ldel.size() == 1) { - infile.deleteLine(ldel[0]); - } else if (ldel.size() > 1) { - cerr << "Warning: multiple transposition lines, not deleting them" << endl; - } - + }(); + return keySignature; } + ////////////////////////////// // -// Tool_gasparize::fixEditorialAccidentals -- checkDataLine() does -// all of the work for this function, which only manages -// key signature and barline processing. -// Rules for accidentals in Tasso in Music Project: -// (1) Only note accidentals printed in the source editions -// are displayed as regular accidentals. These accidentals -// are postfixed with an "X" in the **kern data. -// (2) Editorial accidentals are given an "i" marker but not -// a "X" marker in the **kern data. This editorial accidental -// is displayed above the note. -// This algorithm makes adjustments to the input data because -// Sibelius will drop editorial information after the frist -// editorial accidental on that pitch in the measure. -// (3) If a note is the same pitch as a previous note in the -// measure and the previous note has an editorial accidental, -// then make the note an editorial note. However, if the -// accidental state of the note matches the key-signature, -// then do not add an editorial accidental, and there will be -// no accidental displayed on the note. In that case, add a "y" -// after the accidental to indicate that it is interpreted -// and not visible in the original score. +// Tool_fb::getLowestBase40Pitch -- Get lowest base 40 pitch that is not a rest or silent +// TODO: Handle negative values and sustained notes // -void Tool_gasparize::fixEditorialAccidentals(HumdrumFile& infile) { - removeDoubledAccidentals(infile); - - m_pstates.resize(infile.getMaxTrack() + 1); - m_estates.resize(infile.getMaxTrack() + 1); - m_kstates.resize(infile.getMaxTrack() + 1); +int Tool_fb::getLowestBase40Pitch(vector base40Pitches) { + vector filteredBase40Pitches; + copy_if(base40Pitches.begin(), base40Pitches.end(), std::back_inserter(filteredBase40Pitches), [](int base40Pitch) { + // Ignore if base is a rest or silent note + return (base40Pitch != -1000) && (base40Pitch != -2000) && (base40Pitch != 0); + }); - for (int i=0; i<(int)m_pstates.size(); i++) { - m_pstates[i].resize(70); - fill(m_pstates[i].begin(), m_pstates[i].end(), 0); - m_kstates[i].resize(70); - fill(m_kstates[i].begin(), m_kstates[i].end(), 0); - m_estates[i].resize(70); - fill(m_estates[i].begin(), m_estates[i].end(), false); + if (filteredBase40Pitches.size() == 0) { + return -2000; } - for (int i=0; iisKern()) { - continue; - } - if (token->isNull()) { - continue; - } - if (token->isRest()) { - continue; - } - if (token->find("--") != string::npos) { - string text = *token; - hre.replaceDestructive(text, "-", "--", "g"); - } else if (token->find("--") != string::npos) { - string text = *token; - hre.replaceDestructive(text, "#", "##", "g"); - } - } +string Tool_fb::getIntervalQuality(int basePitchBase40, int targetPitchBase40) { + + int diff = (targetPitchBase40 - basePitchBase40) % 40; + + diff = diff < -2 ? abs(diff) : diff; + + // See https://wiki.ccarh.org/wiki/Base_40 + string quality; + switch (diff) { + // 1 + case -2: + case 38: + quality = "dd"; break; + case -1: + case 39: + quality = "d"; break; + case 0: quality = "P"; break; + case 1: quality = "A"; break; + case 2: quality = "AA"; break; + + // 2 + case 3: quality = "dd"; break; + case 4: quality = "d"; break; + case 5: quality = "m"; break; + case 6: quality = "M"; break; + case 7: quality = "A"; break; + case 8: quality = "AA"; break; + + // 3 + case 9: quality = "dd"; break; + case 10: quality = "d"; break; + case 11: quality = "m"; break; + case 12: quality = "M"; break; + case 13: quality = "A"; break; + case 14: quality = "AA"; break; + + // 4 + case 15: quality = "dd"; break; + case 16: quality = "d"; break; + case 17: quality = "P"; break; + case 18: quality = "A"; break; + case 19: quality = "AA"; break; + + case 20: quality = ""; break; + + // 5 + case 21: quality = "dd"; break; + case 22: quality = "d"; break; + case 23: quality = "P"; break; + case 24: quality = "A"; break; + case 25: quality = "AA"; break; + + // 6 + case 26: quality = "dd"; break; + case 27: quality = "d"; break; + case 28: quality = "m"; break; + case 29: quality = "M"; break; + case 30: quality = "A"; break; + case 31: quality = "AA"; break; + + // 7 + case 32: quality = "dd"; break; + case 33: quality = "d"; break; + case 34: quality = "m"; break; + case 35: quality = "M"; break; + case 36: quality = "A"; break; + case 37: quality = "AA"; break; + + default: quality = "?"; break; } + + return quality; + } ////////////////////////////// // -// Tool_gasparize::addTerminalLongs -- Convert all last notes to terminal longs -// Also probably add terminal longs before double barlines as in JRP. +// FiguredBassNumber::FiguredBassNumber -- Constructor // -void Tool_gasparize::addTerminalLongs(HumdrumFile& infile) { - int scount = infile.getStrandCount(); - for (int i=0; iisKern()) { - continue; - } - while (cur) { - if (!cur->isData()) { - cur = cur->getPreviousToken(); - continue; - } - if (cur->isNull()) { - cur = cur->getPreviousToken(); - continue; - } - if (cur->isRest()) { - cur = cur->getPreviousToken(); - continue; - } - if (cur->isSecondaryTiedNote()) { - cur = cur->getPreviousToken(); - continue; - } - if (cur->find("l") != string::npos) { - // already marked so do not do it again - break; - } - // mark this note with "l" - string newtext = *cur; - newtext += "l"; - cur->setText(newtext); - break; - } - } +FiguredBassNumber::FiguredBassNumber(int num, string accid, bool showAccid, int voiceIdx, int lineIdx, bool isAtk, bool intervallsatz, string intervalQuality, bool hint) { + m_number = num; + m_accidentals = accid; + m_voiceIndex = voiceIdx; + m_lineIndex = lineIdx; + m_showAccidentals = showAccid; + m_isAttack = isAtk; + m_intervallsatz = intervallsatz; + m_intervalQuality = intervalQuality; + m_hint = hint; } ////////////////////////////// // -// Tool_gasparize::fixInstrumentAbbreviations -- +// FiguredBassNumber::toString -- Convert FiguredBassNumber to a string (accidental + number) // -void Tool_gasparize::fixInstrumentAbbreviations(HumdrumFile& infile) { - int iline = -1; - int aline = -1; - - vector kerns = infile.getKernSpineStartList(); - if (kerns.empty()) { - return; - } - - HTp cur = kerns[0]; - while (cur) { - if (cur->isData()) { - break; - } - if (cur->compare(0, 3, "*I\"") == 0) { - iline = cur->getLineIndex(); - } else if (cur->compare(0, 3, "*I'") == 0) { - aline = cur->getLineIndex(); - } - cur = cur->getNextToken(); - } - - if (iline < 0) { - // no names to create abbreviations for - return; +string FiguredBassNumber::toString(bool compoundQ, bool accidentalsQ, bool hideThreeQ) { + int num = (compoundQ) ? getNumberWithinOctave() : m_number; + if (m_hint) { + return m_intervalQuality + to_string(abs(num)); } - if (aline < 0) { - // not creating a new abbreviation for now - // (could add later). - return; + string accid = (accidentalsQ && m_showAccidentals) ? m_accidentals : ""; + if (((num == 3) || (num == -3)) && accidentalsQ && m_showAccidentals && hideThreeQ) { + return accid; } - if (infile[iline].getFieldCount() != infile[aline].getFieldCount()) { - // no spine splitting between the two lines. - return; + if (num > 0) { + return accid + to_string(num); } - // Maybe also require them to be adjacent to each other. - HumRegex hre; - for (int j=0; j<(int)infile[iline].getFieldCount(); j++) { - if (!infile.token(iline, j)->isKern()) { - continue; - } - if (!hre.search(*infile.token(iline, j), "([A-Za-z][A-Za-z .0-9]+)")) { - continue; - } - string name = hre.getMatch(1); - string abbr = "*I'"; - if (name == "Basso Continuo") { - abbr += "BC"; - } else if (name == "Basso continuo") { - abbr += "BC"; - } else if (name == "basso continuo") { - abbr += "BC"; - } else { - abbr += toupper(name[0]); - } - // check for numbers after the end of the name and add to abbreviation - infile.token(aline, j)->setText(abbr); + if (num < 0) { + return accid + "~" + to_string(abs(num)); } + return ""; } ////////////////////////////// // -// Tool_gasparize::convertBreaks -- -// +// FiguredBassNumber::getNumberWithinOctave -- Get a reasonable figured bass number +// Replace 0 with 7 and -7 +// Replace 1 with 8 and -8 +// Replace 2 with 9 if it is a suspension of the ninth +// Allow 1 (unisono) in intervallsatz -void Tool_gasparize::convertBreaks(HumdrumFile& infile) { - HumRegex hre; - for (int i=infile.getLineCount()-1; i>= 0; i--) { - if (!infile[i].isGlobalComment()) { - continue; - } - if (hre.search(*infile.token(i, 0), "linebreak\\s*:\\s*original")) { - string text = "!!LO:LB:g=original"; - infile[i].setText(text); - } - else if (hre.search(*infile.token(i, 0), "pagebreak\\s*:\\s*original")) { - string text = "!!LO:PB:g=original"; - infile[i].setText(text); +int FiguredBassNumber::getNumberWithinOctave(void) { + int num = m_number % 7; + + // Replace 0 with 7 and -7 + if ((abs(m_number) > 0) && (m_number % 7 == 0)) { + return m_number < 0 ? -7 : 7; + } + + // Replace 1 with 8 and -8 + if (abs(num) == 1) { + // Allow unisono in intervallsatz + if (m_intervallsatz || m_hint) { + if (abs(m_number) == 1) { + return 1; + } } + return m_number < 0 ? -8 : 8; + } + + // Replace 2 with 9 if m_convert2To9 is true (e.g. when a 3 is included in the chord numbers) + if (m_convert2To9 && (num == 2)) { + return 9; } + + return num; } ////////////////////////////// // -// Tool_gasparize::deleteBreaks -- +// FiguredBassAbbreviationMapping::FiguredBassAbbreviationMapping -- Constructor +// Helper class to store the mappings for abbreviate figured bass numbers // -void Tool_gasparize::deleteBreaks(HumdrumFile& infile) { - HumRegex hre; - for (int i=infile.getLineCount()-1; i>= 0; i--) { - if (!infile[i].isGlobalComment()) { - continue; - } - if (hre.search(*infile.token(i, 0), "linebreak\\s*:\\s*original")) { - infile.deleteLine(i); - } - else if (hre.search(*infile.token(i, 0), "pagebreak\\s*:\\s*original")) { - infile.deleteLine(i); - } - } +FiguredBassAbbreviationMapping::FiguredBassAbbreviationMapping(string s, vector n) { + m_str = s; + m_numbers = n; } -//////////////////////////////// -// -// Tool_gasparize::addBibliographicRecords -- -// -// !!!COM: -// !!!CDT: -// !!!OTL: -// !!!AGN: -// !!!SCT: -// !!!SCA: -// !!!voices: + +////////////////////////////// // -// At end: -// !!!RDF**kern: l = terminal long -// !!!RDF**kern: i = editorial accidental -// !!!EED: -// !!!EEV: $DATE +// FiguredBassAbbreviationMapping::s_mappings -- Mapping to abbreviate figured bass numbers // -void Tool_gasparize::addBibliographicRecords(HumdrumFile& infile) { - vector refinfo = infile.getReferenceRecords(); - map refs; - for (int i=0; i<(int)refinfo.size(); i++) { - string key = refinfo[i]->getReferenceKey(); - refs[key] = refinfo[i]; - } +const vector FiguredBassAbbreviationMapping::s_mappings = { + FiguredBassAbbreviationMapping("3", {}), + FiguredBassAbbreviationMapping("5", {}), + FiguredBassAbbreviationMapping("5 3", {}), + FiguredBassAbbreviationMapping("6 3", {6}), + FiguredBassAbbreviationMapping("5 4", {4}), + FiguredBassAbbreviationMapping("7 5 3", {7}), + FiguredBassAbbreviationMapping("7 3", {7}), + FiguredBassAbbreviationMapping("7 5", {7}), + FiguredBassAbbreviationMapping("6 5 3", {6, 5}), + FiguredBassAbbreviationMapping("6 4 3", {4, 3}), + FiguredBassAbbreviationMapping("6 4 2", {4, 2}), + FiguredBassAbbreviationMapping("9 5 3", {9}), + FiguredBassAbbreviationMapping("9 5", {9}), + FiguredBassAbbreviationMapping("9 3", {9}), +}; - // header records - if (refs.find("voices") == refs.end()) { - if (infile.token(0, 0)->find("!!!OTL") != string::npos) { - infile.insertLine(1, "!!!voices:"); - } else { - infile.insertLine(0, "!!!voices:"); - } - } - if (refs.find("SCA") == refs.end()) { - if (infile.token(0, 0)->find("!!!OTL") != string::npos) { - infile.insertLine(1, "!!!SCA:"); - } else { - infile.insertLine(0, "!!!SCA:"); - } - } - if (refs.find("SCT") == refs.end()) { - if (infile.token(0, 0)->find("!!!OTL") != string::npos) { - infile.insertLine(1, "!!!SCT:"); - } else { - infile.insertLine(0, "!!!SCT:"); - } - } - if (refs.find("AGN") == refs.end()) { - if (infile.token(0, 0)->find("!!!OTL") != string::npos) { - infile.insertLine(1, "!!!AGN:"); - } else { - infile.insertLine(0, "!!!AGN:"); - } - } - if (refs.find("OTL") == refs.end()) { - infile.insertLine(0, "!!!OTL:"); - } - if (refs.find("CDT") == refs.end()) { - infile.insertLine(0, "!!!CDT: ~1450-~1517"); - } - if (refs.find("COM") == refs.end()) { - infile.insertLine(0, "!!!COM: Gaspar van Weerbeke"); - } - // trailer records - bool foundi = false; - bool foundj = false; - bool foundl = false; - for (int i=0; ifind("!!!RDF**kern:") == string::npos) { - continue; - } - if (token->find("terminal breve") != string::npos) { - foundl = true; - } else if (token->find("editorial accidental") != string::npos) { - if (token->find("i =") != string::npos) { - foundi = true; - } else if (token->find("j =") != string::npos) { - foundj = true; - } - } - } - if (!foundj) { - infile.appendLine("!!!RDF**kern: j = editorial accidental, optional, paren up"); - } - if (!foundi) { - infile.appendLine("!!!RDF**kern: i = editorial accidental"); - } - if (!foundl) { - infile.appendLine("!!!RDF**kern: l = terminal long"); - } +#define RUNTOOL(NAME, INFILE, COMMAND, STATUS) \ + Tool_##NAME *tool = new Tool_##NAME; \ + tool->process(COMMAND); \ + tool->run(INFILE); \ + if (tool->hasError()) { \ + status = false; \ + tool->getError(cerr); \ + delete tool; \ + break; \ + } else if (tool->hasHumdrumText()) { \ + INFILE.readString(tool->getHumdrumText()); \ + } \ + delete tool; - if (refs.find("PTL") == refs.end()) { - infile.appendLine("!!!PTL: Gaspar van Weerbeke: Collected Works. V. Settings of Liturgical Texts, Songs, and Instrumental Works"); - } - if (refs.find("PPR") == refs.end()) { - infile.appendLine("!!!PPR: American Institute of Musicology"); - } - if (refs.find("PC#") == refs.end()) { - infile.appendLine("!!!PC#: Corpus Mensurabilis Musicae 106/V"); - } - if (refs.find("PDT") == refs.end()) { - infile.appendLine("!!!PDT: {YEAR}"); - } - if (refs.find("PED") == refs.end()) { - infile.appendLine("!!!PED: Kolb, Paul"); - infile.appendLine("!!!PED: Pavanello, Agnese"); - } - if (refs.find("YEC") == refs.end()) { - infile.appendLine("!!!YEC: Copyright {YEAR}, Kolb, Paul"); - infile.appendLine("!!!YEC: Copyright {YEAR}, Pavanello, Agnese"); - } - if (refs.find("YEM") == refs.end()) { - infile.appendLine("!!!YEM: CC-BY-SA 4.0 (https://creativecommons.org/licenses/by-nc/4.0/legalcode)"); - } - if (refs.find("EED") == refs.end()) { - infile.appendLine("!!!EED: Zybina, Karina"); - infile.appendLine("!!!EED: Mair-Gruber, Roland"); - } - if (refs.find("EEV") == refs.end()) { - string date = getDate(); - string line = "!!!EEV: " + date; - infile.appendLine(line); - } -} +#define RUNTOOL2(NAME, INFILE1, INFILE2, COMMAND, STATUS) \ + Tool_##NAME *tool = new Tool_##NAME; \ + tool->process(COMMAND); \ + tool->run(INFILE1, INFILE2); \ + if (tool->hasError()) { \ + status = false; \ + tool->getError(cerr); \ + delete tool; \ + break; \ + } else if (tool->hasHumdrumText()) { \ + INFILE1.readString(tool->getHumdrumText()); \ + } \ + delete tool; + +#define RUNTOOLSET(NAME, INFILES, COMMAND, STATUS) \ + Tool_##NAME *tool = new Tool_##NAME; \ + tool->process(COMMAND); \ + tool->run(INFILES); \ + if (tool->hasError()) { \ + status = false; \ + tool->getError(cerr); \ + delete tool; \ + break; \ + } else if (tool->hasHumdrumText()) { \ + INFILES.readString(tool->getHumdrumText()); \ + } \ + delete tool; + +#define RUNTOOLSTREAM(NAME, INFILES, COMMAND, STATUS) \ + Tool_##NAME *tool = new Tool_##NAME; \ + tool->process(COMMAND); \ + tool->run(INFILES); \ + if (tool->hasError()) { \ + status = false; \ + tool->getError(cerr); \ + delete tool; \ + break; \ + } else if (tool->hasHumdrumText()) { \ + INFILES.readString(tool->getHumdrumText()); \ + } \ + delete tool; //////////////////////////////// // -// Tool_gasparize::checkDataLine -- +// Tool_filter::Tool_filter -- Set the recognized options for the tool. // -void Tool_gasparize::checkDataLine(HumdrumFile& infile, int lineindex) { - HumdrumLine& line = infile[lineindex]; +Tool_filter::Tool_filter(void) { + define("debug=b", "print debug statement"); + define("v|variant=s:", "Run filters labeled with the given variant"); +} - HumRegex hre; - HTp token; - bool haseditQ; - int base7; - int accid; - int track; - bool removeQ; - for (int i=0; igetTrack(); - if (!token->isKern()) { - continue; - } - if (token->isNull()) { - continue; - } - if (token->isRest()) { - continue; - } - if (token->find('j') != string::npos) { - continue; - } - if (token->isSecondaryTiedNote()) { - continue; - } - base7 = Convert::kernToBase7(token); - accid = Convert::kernToAccidentalCount(token); - haseditQ = false; - removeQ = false; - // Hard-wired to "i" as editorial accidental marker - if (token->find("ni") != string::npos) { - haseditQ = true; - } else if (token->find("-i") != string::npos) { - haseditQ = true; - } else if (token->find("#i") != string::npos) { - haseditQ = true; - } else if (token->find("nXi") != string::npos) { - haseditQ = true; - removeQ = true; - } else if (token->find("-Xi") != string::npos) { - haseditQ = true; - removeQ = true; - } else if (token->find("#Xi") != string::npos) { - haseditQ = true; - removeQ = true; - } +///////////////////////////////// +// +// Tool_filter::run -- Primary interfaces to the tool. +// - if (removeQ) { - string temp = *token; - hre.replaceDestructive(temp, "", "X"); - token->setText(temp); - } +bool Tool_filter::run(const string& indata) { + HumdrumFileSet infiles(indata); + bool status = run(infiles); + return status; +} - bool explicitQ = false; - if (token->find("#X") != string::npos) { - explicitQ = true; - } else if (token->find("-X") != string::npos) { - explicitQ = true; - } else if (token->find("nX") != string::npos) { - explicitQ = true; - } else if (token->find("n") != string::npos) { - // add an explicit accidental marker - explicitQ = true; - string text = *token; - hre.replaceDestructive(text, "nX", "n"); - token->setText(text); - } - if (haseditQ) { - // Store new editorial pitch state. - m_estates.at(track).at(base7) = true; - m_pstates.at(track).at(base7) = accid; - continue; - } +bool Tool_filter::run(HumdrumFile& infile) { + HumdrumFileSet infiles; + infiles.appendHumdrumPointer(&infile); + bool status = run(infiles); + infiles.clearNoFree(); + return status; +} - if (explicitQ) { - // No need to make editorial since it is visible. - m_estates.at(track).at(base7) = false; - m_pstates.at(track).at(base7) = accid; - continue; +bool Tool_filter::runUniversal(HumdrumFileSet& infiles) { + bool status = true; + vector > commands; + getUniversalCommandList(commands, infiles); + + for (int i=0; i<(int)commands.size(); i++) { + if (commands[i].first == "humdiff") { + RUNTOOLSET(humdiff, infiles, commands[i].second, status); + } else if (commands[i].first == "chooser") { + RUNTOOLSET(chooser, infiles, commands[i].second, status); + } else if (commands[i].first == "myank") { + RUNTOOL(myank, infiles, commands[i].second, status); } + } - if (accid == m_kstates.at(track).at(base7)) { - // !m_estates.at(track).at(base7)) { - // add !m_estates.at(track).at(base) as a condition if - // you want editorial accidentals to be added to return the - // note to the accidental in the key. - // - // The accidental matches the key-signature state, - // so it should not be made editorial eventhough - // it is not visible. - m_pstates.at(track).at(base7) = accid; + removeUniversalFilterLines(infiles); - // Add a "y" marker of there is an interpreted accidental - // state (flat or sharp) that is part of the key signature. - int hasaccid = false; - if (token->find("#") != string::npos) { - hasaccid = true; - } else if (token->find("-") != string::npos) { - hasaccid = true; - } - int hashide = false; - if (token->find("-y") != string::npos) { - hashide = true; - } - else if (token->find("#y") != string::npos) { - hashide = true; - } - if (hasaccid && !hashide) { - string text = *token; - hre.replaceDestructive(text, "#y", "#"); - hre.replaceDestructive(text, "-y", "-"); - token->setText(text); - } + return status; +} - continue; - } - // At this point the previous note with this pitch class - // had an editorial accidental, and this note also has the - // same accidental, or there was a previous visual accidental - // outside of the key signature that will cause this note to have - // an editorial accidental mark applied (Sibelius will drop - // secondary editorial accidentals in a measure when exporting, - // MusicXML, which is why this function is needed). +// +// In-place processing of file: +// - m_estates[track][base7] = true; - m_pstates[track][base7] = accid; +bool Tool_filter::run(HumdrumFileSet& infiles) { + if (infiles.getCount() == 0) { + return false; + } - string text = token->getText(); - HumRegex hre; - hre.replaceDestructive(text, "#", "##+", "g"); - hre.replaceDestructive(text, "-", "--+", "g"); - string output = ""; - bool foundQ = false; - for (int j=0; j<(int)text.size(); j++) { - if (text[j] == 'n') { - output += "ni"; - foundQ = true; - } else if (text[j] == '#') { - output += "#i"; - foundQ = true; - } else if (text[j] == '-') { - output += "-i"; - foundQ = true; - } else { - output += text[j]; - } - } + initialize(infiles[0]); - if (foundQ) { - token->setText(output); - continue; - } + HumdrumFile& infile = infiles[0]; - // The note is natural, but has no natural sign. - // add the natural sign and editorial mark. - for (int j=(int)output.size()-1; j>=0; j--) { - if ((tolower(output[j]) >= 'a') && (tolower(output[j]) <= 'g')) { - output.insert(j+1, "ni"); - break; - } - } - token->setText(output); + #ifdef __EMSCRIPTEN__ + bool optionList = getBoolean("options"); + if (optionList) { + printEmscripten(m_humdrum_text); + m_humdrum_text << infile; } -} + #endif + + bool status = true; + vector > commands; + getCommandList(commands, infile); + for (int i=0; i<(int)commands.size(); i++) { + if (commands[i].first == "addic") { + RUNTOOL(addic, infile, commands[i].second, status); + } else if (commands[i].first == "addkey") { + RUNTOOL(addkey, infile, commands[i].second, status); + } else if (commands[i].first == "addlabels") { + RUNTOOL(addlabels, infile, commands[i].second, status); + } else if (commands[i].first == "addtempo") { + RUNTOOL(addtempo, infile, commands[i].second, status); + } else if (commands[i].first == "autoaccid") { + RUNTOOL(autoaccid, infile, commands[i].second, status); + } else if (commands[i].first == "autobeam") { + RUNTOOL(autobeam, infile, commands[i].second, status); + } else if (commands[i].first == "autostem") { + RUNTOOL(autostem, infile, commands[i].second, status); + } else if (commands[i].first == "binroll") { + RUNTOOL(binroll, infile, commands[i].second, status); + } else if (commands[i].first == "chantize") { + RUNTOOL(chantize, infile, commands[i].second, status); + } else if (commands[i].first == "chint") { + RUNTOOL(chint, infile, commands[i].second, status); + } else if (commands[i].first == "chord") { + RUNTOOL(chord, infile, commands[i].second, status); + } else if (commands[i].first == "cint") { + RUNTOOL(cint, infile, commands[i].second, status); + } else if (commands[i].first == "cmr") { + RUNTOOL(cmr, infile, commands[i].second, status); + } else if (commands[i].first == "composite") { + RUNTOOL(composite, infile, commands[i].second, status); + } else if (commands[i].first == "dissonant") { + RUNTOOL(dissonant, infile, commands[i].second, status); + } else if (commands[i].first == "double") { + RUNTOOL(double, infile, commands[i].second, status); + } else if (commands[i].first == "fb") { + RUNTOOL(fb, infile, commands[i].second, status); + } else if (commands[i].first == "flipper") { + RUNTOOL(flipper, infile, commands[i].second, status); + } else if (commands[i].first == "filter") { + RUNTOOL(filter, infile, commands[i].second, status); + } else if (commands[i].first == "gasparize") { + RUNTOOL(gasparize, infile, commands[i].second, status); + } else if (commands[i].first == "half") { + RUNTOOL(half, infile, commands[i].second, status); + } else if (commands[i].first == "hands") { + RUNTOOL(hands, infile, commands[i].second, status); + } else if (commands[i].first == "homorhythm") { + RUNTOOL(homorhythm, infile, commands[i].second, status); + } else if (commands[i].first == "homorhythm2") { + RUNTOOL(homorhythm2, infile, commands[i].second, status); + } else if (commands[i].first == "hproof") { + RUNTOOL(hproof, infile, commands[i].second, status); + } else if (commands[i].first == "humbreak") { + RUNTOOL(humbreak, infile, commands[i].second, status); + } else if (commands[i].first == "humsheet") { + RUNTOOL(humsheet, infile, commands[i].second, status); + } else if (commands[i].first == "humtr") { + RUNTOOL(humtr, infile, commands[i].second, status); + } else if (commands[i].first == "imitation") { + RUNTOOL(imitation, infile, commands[i].second, status); + } else if (commands[i].first == "instinfo") { + RUNTOOL(instinfo, infile, commands[i].second, status); + } else if (commands[i].first == "kern2mens") { + RUNTOOL(kern2mens, infile, commands[i].second, status); + } else if (commands[i].first == "kernify") { + RUNTOOL(kernify, infile, commands[i].second, status); + } else if (commands[i].first == "kernview") { + RUNTOOL(kernview, infile, commands[i].second, status); + } else if (commands[i].first == "melisma") { + RUNTOOL(melisma, infile, commands[i].second, status); + } else if (commands[i].first == "mens2kern") { + RUNTOOL(mens2kern, infile, commands[i].second, status); + } else if (commands[i].first == "meter") { + RUNTOOL(meter, infile, commands[i].second, status); + } else if (commands[i].first == "metlev") { + RUNTOOL(metlev, infile, commands[i].second, status); + } else if (commands[i].first == "modori") { + RUNTOOL(modori, infile, commands[i].second, status); + } else if (commands[i].first == "msearch") { + RUNTOOL(msearch, infile, commands[i].second, status); + } else if (commands[i].first == "nproof") { + RUNTOOL(nproof, infile, commands[i].second, status); + } else if (commands[i].first == "ordergps") { + RUNTOOL(ordergps, infile, commands[i].second, status); + } else if (commands[i].first == "pbar") { + RUNTOOL(pbar, infile, commands[i].second, status); + } else if (commands[i].first == "phrase") { + RUNTOOL(phrase, infile, commands[i].second, status); + } else if (commands[i].first == "pline") { + RUNTOOL(pline, infile, commands[i].second, status); + } else if (commands[i].first == "prange") { + RUNTOOL(prange, infile, commands[i].second, status); + } else if (commands[i].first == "recip") { + RUNTOOL(recip, infile, commands[i].second, status); + } else if (commands[i].first == "restfill") { + RUNTOOL(restfill, infile, commands[i].second, status); + } else if (commands[i].first == "rphrase") { + RUNTOOL(rphrase, infile, commands[i].second, status); + } else if (commands[i].first == "sab2gs") { + RUNTOOL(sab2gs, infile, commands[i].second, status); + } else if (commands[i].first == "scordatura") { + RUNTOOL(scordatura, infile, commands[i].second, status); + } else if (commands[i].first == "semitones") { + RUNTOOL(semitones, infile, commands[i].second, status); + } else if (commands[i].first == "shed") { + RUNTOOL(shed, infile, commands[i].second, status); + } else if (commands[i].first == "sic") { + RUNTOOL(sic, infile, commands[i].second, status); + } else if (commands[i].first == "simat") { + RUNTOOL2(simat, infile, infile, commands[i].second, status); + } else if (commands[i].first == "slurcheck") { + RUNTOOL(slurcheck, infile, commands[i].second, status); + } else if (commands[i].first == "slur") { + RUNTOOL(slurcheck, infile, commands[i].second, status); + } else if (commands[i].first == "spinetrace") { + RUNTOOL(spinetrace, infile, commands[i].second, status); + } else if (commands[i].first == "strophe") { + RUNTOOL(strophe, infile, commands[i].second, status); + } else if (commands[i].first == "synco") { + RUNTOOL(synco, infile, commands[i].second, status); + } else if (commands[i].first == "tabber") { + RUNTOOL(tabber, infile, commands[i].second, status); + } else if (commands[i].first == "tandeminfo") { + RUNTOOL(tandeminfo, infile, commands[i].second, status); + } else if (commands[i].first == "tassoize") { + RUNTOOL(tassoize, infile, commands[i].second, status); + } else if (commands[i].first == "tassoise") { + RUNTOOL(tassoize, infile, commands[i].second, status); + } else if (commands[i].first == "tasso") { + RUNTOOL(tassoize, infile, commands[i].second, status); + } else if (commands[i].first == "textdur") { + RUNTOOL(textdur, infile, commands[i].second, status); + } else if (commands[i].first == "tie") { + RUNTOOL(tie, infile, commands[i].second, status); + } else if (commands[i].first == "tspos") { + RUNTOOL(tspos, infile, commands[i].second, status); + } else if (commands[i].first == "transpose") { + RUNTOOL(transpose, infile, commands[i].second, status); + } else if (commands[i].first == "tremolo") { + RUNTOOL(tremolo, infile, commands[i].second, status); + } else if (commands[i].first == "trillspell") { + RUNTOOL(trillspell, infile, commands[i].second, status); + } else if (commands[i].first == "vcross") { + RUNTOOL(vcross, infile, commands[i].second, status); + // filters with aliases: + } else if (commands[i].first == "colortriads") { + RUNTOOL(colortriads, infile, commands[i].second, status); + } else if (commands[i].first == "colourtriads") { + // British spelling + RUNTOOL(colortriads, infile, commands[i].second, status); -//////////////////////////////// -// -// Tool_gasparize::updateKeySignatures -- Fill in the accidental -// states for each diatonic pitch. -// + } else if (commands[i].first == "colorthirds") { + RUNTOOL(tspos, infile, commands[i].second, status); + } else if (commands[i].first == "colourthirds") { + // British spelling + RUNTOOL(tspos, infile, commands[i].second, status); -void Tool_gasparize::updateKeySignatures(HumdrumFile& infile, int lineindex) { - HumdrumLine& line = infile[lineindex]; - int track; - for (int i=0; iisKeySignature()) { - continue; - } - HTp token = line.token(i); - track = token->getTrack(); - string text = token->getText(); - fill(m_kstates[track].begin(), m_kstates[track].end(), 0); - for (int j=3; j<(int)text.size()-1; j++) { - if (text[j] == ']') { - break; - } - switch (text[j]) { - case 'a': case 'A': - switch (text[j+1]) { - case '#': m_kstates[track][5] = +1; - break; - case '-': m_kstates[track][5] = -1; - break; - } - break; + } else if (commands[i].first == "colorgroups") { + RUNTOOL(colorgroups, infile, commands[i].second, status); + } else if (commands[i].first == "colourgroups") { // British spelling + RUNTOOL(colorgroups, infile, commands[i].second, status); - case 'b': case 'B': - switch (text[j+1]) { - case '#': m_kstates[track][6] = +1; - break; - case '-': m_kstates[track][6] = -1; - break; - } - break; + } else if (commands[i].first == "deg") { // humlib version of Humdrum Toolkit deg tool + RUNTOOL(deg, infile, commands[i].second, status); + } else if (commands[i].first == "degx") { // humlib cli name + RUNTOOL(deg, infile, commands[i].second, status); - case 'c': case 'C': - switch (text[j+1]) { - case '#': m_kstates[track][0] = +1; - break; - case '-': m_kstates[track][0] = -1; - break; - } - break; + } else if (commands[i].first == "extract") { // humlib version of Humdrum Toolkit extract tool + RUNTOOL(extract, infile, commands[i].second, status); + } else if (commands[i].first == "extractx") { // humlib cli name + RUNTOOL(extract, infile, commands[i].second, status); - case 'd': case 'D': - switch (text[j+1]) { - case '#': m_kstates[track][1] = +1; - break; - case '-': m_kstates[track][1] = -1; - break; - } - break; + } else if (commands[i].first == "grep") { + RUNTOOL(grep, infile, commands[i].second, status); + } else if (commands[i].first == "humgrep") { + RUNTOOL(grep, infile, commands[i].second, status); - case 'e': case 'E': - switch (text[j+1]) { - case '#': m_kstates[track][2] = +1; - break; - case '-': m_kstates[track][2] = -1; - break; - } - break; + } else if (commands[i].first == "myank") { // humlib version of Humdrum Extras myank tool + RUNTOOL(myank, infile, commands[i].second, status); + } else if (commands[i].first == "myankx") { // humlib cli name + RUNTOOL(myank, infile, commands[i].second, status); - case 'f': case 'F': - switch (text[j+1]) { - case '#': m_kstates[track][3] = +1; - break; - case '-': m_kstates[track][3] = -1; - break; - } - break; + } else if (commands[i].first == "rid") { // humlib version of Humdrum Toolkit deg tool + RUNTOOL(rid, infile, commands[i].second, status); + } else if (commands[i].first == "ridx") { // Humdrum Extra cli name + RUNTOOL(rid, infile, commands[i].second, status); + } else if (commands[i].first == "ridxx") { // humlib cli name + RUNTOOL(rid, infile, commands[i].second, status); - case 'g': case 'G': - switch (text[j+1]) { - case '#': m_kstates[track][4] = +1; - break; - case '-': m_kstates[track][4] = -1; - break; - } - break; - } - for (int j=0; j<7; j++) { - if (m_kstates[track][j] == 0) { - continue; - } - for (int k=1; k<10; k++) { - m_kstates[track][j+k*7] = m_kstates[track][j]; - } - } - } - } + } else if (commands[i].first == "satb2gs") { // humlib version of Humdrum Extras satg2gs tool + RUNTOOL(satb2gs, infile, commands[i].second, status); + } else if (commands[i].first == "satb2gsx") { // humlib cli name + RUNTOOL(satb2gs, infile, commands[i].second, status); - // initialize m_pstates with contents of m_kstates - for (int i=0; i<(int)m_kstates.size(); i++) { - for (int j=0; j<(int)m_kstates[i].size(); j++) { - m_pstates[i][j] = m_kstates[i][j]; + } else if (commands[i].first == "thru") { // humlib version of Humdrum Toolkit thru tool + RUNTOOL(thru, infile, commands[i].second, status); + } else if (commands[i].first == "thrux") { // Humdrum Extras cli name + RUNTOOL(thru, infile, commands[i].second, status); + } else if (commands[i].first == "thruxx") { // humlib cli name + RUNTOOL(thru, infile, commands[i].second, status); + + } else if (commands[i].first == "timebase") { // humlib version of Humdrum Toolkit timebase tool + RUNTOOL(timebase, infile, commands[i].second, status); + } else if (commands[i].first == "timebasex") { // humlib cli name + RUNTOOL(timebase, infile, commands[i].second, status); + } else { + cerr << "UNKNOWN FILTER: " << commands[i].first << " OPTIONS: " << commands[i].second << endl; } + } + removeGlobalFilterLines(infile); + + // Re-load the text for each line from their tokens in case any + // updates are needed from token changes. + infile.createLinesFromTokens(); + return status; } -//////////////////////////////// +////////////////////////////// // -// Tool_gasparize::clearStates -- +// Tool_filter::removeGlobalFilterLines -- // -void Tool_gasparize::clearStates(void) { - for (int i=0; i<(int)m_pstates.size(); i++) { - fill(m_pstates[i].begin(), m_pstates[i].end(), 0); +void Tool_filter::removeGlobalFilterLines(HumdrumFile& infile) { + HumRegex hre; + string text; + + string maintag = "!!!filter:"; + string mainXtag = "!!!Xfilter:"; + string maintagQuery = "^!!!filter:"; + + string maintagV; + string mainXtagV; + string maintagQueryV; + + if (m_variant.size() > 0) { + maintagV = "!!!filter-" + m_variant + ":"; + mainXtagV = "!!!Xfilter-" + m_variant + ":"; + maintagQueryV = "^!!!filter-" + m_variant + ":"; } - for (int i=0; i<(int)m_estates.size(); i++) { - fill(m_estates[i].begin(), m_estates[i].end(), false); + + for (int i=0; i 0) { + if (infile.token(i, 0)->compare(0, maintagV.size(), maintagV) == 0) { + text = infile.token(i, 0)->getText(); + hre.replaceDestructive(text, mainXtagV, maintagQueryV); + infile.token(i, 0)->setText(text); + } + } else { + if (infile.token(i, 0)->compare(0, maintag.size(), maintag) == 0) { + text = infile.token(i, 0)->getText(); + hre.replaceDestructive(text, mainXtag, maintagQuery); + infile.token(i, 0)->setText(text); + } + } } } + ////////////////////////////// // -// Tool_gasparize::getDate -- +// Tool_filter::removeUniversalFilterLines -- // -string Tool_gasparize::getDate(void) { - time_t t = time(NULL); - tm* timeptr = localtime(&t); - stringstream ss; - int year = timeptr->tm_year + 1900; - int month = timeptr->tm_mon + 1; - int day = timeptr->tm_mday; - ss << year << "/"; - if (month < 10) { - ss << "0"; +void Tool_filter::removeUniversalFilterLines(HumdrumFileSet& infiles) { + HumRegex hre; + string text; + + string maintag = "!!!!filter:"; + string mainXtag = "!!!!Xfilter:"; + string maintagQuery = "^!!!!filter:"; + + string maintagV; + string mainXtagV; + string maintagQueryV; + + if (m_variant.size() > 0) { + maintagV = "!!!!filter-" + m_variant + ":"; + mainXtagV = "!!!!Xfilter-" + m_variant + ":"; + maintagQueryV = "^!!!!filter-" + m_variant + ":"; } - ss << month << "/"; - if (day < 10) { - ss << "0"; + + for (int i=0; i 0) { + if (token->compare(0, maintagV.size(), maintagV) == 0) { + text = token->getText(); + hre.replaceDestructive(text, mainXtagV, maintagQueryV); + token->setText(text); + infile[j].createLineFromTokens(); + } + } else { + if (token->compare(0, maintag.size(), maintag) == 0) { + text = token->getText(); + hre.replaceDestructive(text, mainXtag, maintagQuery); + token->setText(text); + infile[j].createLineFromTokens(); + } + } + } } - ss << day; - return ss.str(); } ////////////////////////////// // -// Tool_gasparize::fixTies -- -// If a tie is unclosed or if a note is followed by an invisible rest, then fix. +// Tool_filter::getCommandList -- // -void Tool_gasparize::fixTies(HumdrumFile& infile) { - int strands = infile.getStrandCount(); - for (int i=0; i >& commands, + HumdrumFile& infile) { + + vector refs = infile.getReferenceRecords(); + pair entry; + string tag = "filter"; + if (m_variant.size() > 0) { + tag += "-"; + tag += m_variant; + } + vector clist; + HumRegex hre; + for (int i=0; i<(int)refs.size(); i++) { + string refkey = refs[i]->getGlobalReferenceKey(); + if (refkey != tag) { continue; } - if (!sstart->isKern()) { - continue; + string command = refs[i]->getGlobalReferenceValue(); + splitPipeline(clist, command); + for (int j=0; j<(int)clist.size(); j++) { + if (hre.search(clist[j], "^\\s*([^\\s]+)")) { + entry.first = hre.getMatch(1); + entry.second = clist[j]; + commands.push_back(entry); + } } - HTp send = infile.getStrandEnd(i); - fixTiesForStrand(sstart, send); } - fixTieStartEnd(infile); } -void Tool_gasparize::fixTieStartEnd(HumdrumFile& infile) { - int strands = infile.getStrandCount(); - for (int i=0; i& clist, const string& command) { + clist.clear(); + clist.resize(1); + clist[0] = ""; + int inDoubleQuotes = -1; + int inSingleQuotes = -1; + char ch = '\0'; + char lastch; + for (int i=0; i<(int)command.size(); i++) { + lastch = ch; + ch = command[i]; + + if (ch == '"') { + if (lastch == '\\') { + // escaped double quote, so treat as regular character + clist.back() += ch; + continue; + } else if (inDoubleQuotes >= 0) { + // turn off previous double quote sequence + clist.back() += ch; + inDoubleQuotes = -1; + continue; + } else if (inSingleQuotes >= 0) { + // in an active single quote, so this is not a closing double quote + clist.back() += ch; + continue; + } else { + // this is the start of a double quote sequence + clist.back() += ch; + inDoubleQuotes = i; + continue; + } } - if (!sstart->isKern()) { - continue; + + if (ch == '\'') { + if (lastch == '\\') { + // escaped single quote, so treat as regular character + clist.back() += ch; + continue; + } else if (inSingleQuotes >= 0) { + // turn off previous single quote sequence + clist.back() += ch; + inSingleQuotes = -1; + continue; + } else if (inDoubleQuotes >= 0) { + // in an active double quote, so this is not a closing single quote + clist.back() += ch; + continue; + } else { + // this is the start of a single quote sequence + clist.back() += ch; + inSingleQuotes = i; + continue; + } + } + + if (ch == '|') { + if ((inSingleQuotes > -1) || (inDoubleQuotes > -1)) { + // pipe character + clist.back() += ch; + continue; + } else { + // this is a real pipe + clist.resize(clist.size() + 1); + continue; + } } - HTp send = infile.getStrandEnd(i); - fixTiesStartEnd(sstart, send); - } -} + if (isspace(ch) && (!(inSingleQuotes > -1)) && (!(inDoubleQuotes > -1))) { + if (isspace(lastch)) { + // don't repeat spaces outside of quotes. + continue; + } + } + // regular character + clist.back() += ch; + } -void Tool_gasparize::fixTiesStartEnd(HTp starts, HTp ends) { - HTp current = starts; + // remove leading and trailing spaces HumRegex hre; - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if ((current->find('[') != string::npos) && - (current->find(']') != string::npos) && - (current->find(' ') == string::npos)) { - string text = *current; - hre.replaceDestructive(text, "", "\\[", "g"); - hre.replaceDestructive(text, "_", "\\]", "g"); - current->setText(text); - } - current = current->getNextToken(); + for (int i=0; i<(int)clist.size(); i++) { + hre.replaceDestructive(clist[i], "", "^\\s+"); + hre.replaceDestructive(clist[i], "", "\\s+$"); } + } + ////////////////////////////// // -// Tool_gasparize::fixTiesForStrand -- +// Tool_filter::getUniversalCommandList -- // -void Tool_gasparize::fixTiesForStrand(HTp sstart, HTp send) { - if (!sstart) { - return; +void Tool_filter::getUniversalCommandList(vector >& commands, + HumdrumFileSet& infiles) { + + vector refs = infiles.getUniversalReferenceRecords(); + pair entry; + string tag = "filter"; + if (m_variant.size() > 0) { + tag += "-"; + tag += m_variant; } - HTp current = sstart; - HTp last = NULL; - current = current->getNextToken(); - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); - continue; - } - if (last == NULL) { - last = current; - current = current->getNextToken(); + vector clist; + HumRegex hre; + for (int i=0; i<(int)refs.size(); i++) { + if (refs[i]->getUniversalReferenceKey() != tag) { continue; } - if (current->find("yy") != string::npos) { - fixTieToInvisibleRest(last, current); - } else if (((last->find("[") != string::npos) || (last->find("_") != string::npos)) - && ((current->find("]") == string::npos) && (current->find("_") == string::npos))) { - fixHangingTie(last, current); + string command = refs[i]->getUniversalReferenceValue(); + hre.split(clist, command, "\\s*\\|\\s*"); + for (int j=0; j<(int)clist.size(); j++) { + if (hre.search(clist[j], "^\\s*([^\\s]+)")) { + entry.first = hre.getMatch(1); + entry.second = clist[j]; + commands.push_back(entry); + } } - last = current; - current = current->getNextToken(); } } @@ -85265,518 +86498,308 @@ void Tool_gasparize::fixTiesForStrand(HTp sstart, HTp send) { ////////////////////////////// // -// Tool_gasparize::fixTieToInvisibleRest -- +// Tool_filter::initialize -- extract time signature lines for +// each **kern spine in file. // -void Tool_gasparize::fixTieToInvisibleRest(HTp first, HTp second) { - if (second->find("yy") == string::npos) { - return; +void Tool_filter::initialize(HumdrumFile& infile) { + m_debugQ = getBoolean("debug"); + m_variant.clear(); + if (getBoolean("variant")) { + m_variant = getString("variant"); } - if ((first->find("[") == string::npos) && (first->find("_") == string::npos)) { - string ftext = *first; - ftext = "[" + ftext; - first->setText(ftext); +} + + + + + +///////////////////////////////// +// +// Tool_fixps::Tool_fixps -- Set the recognized options for the tool. +// + +Tool_fixps::Tool_fixps(void) { + // define ("n|only-remove-empty-transpositions=b", "Only remove empty transpositions"); +} + + + +///////////////////////////////// +// +// Tool_fixps::run -- Primary interfaces to the tool. +// + +bool Tool_fixps::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; isetText(text); + return status; +} + + +bool Tool_fixps::run(HumdrumFile& infile, ostream& out) { + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; + } + return status; +} + +// +// In-place processing of file: +// + +bool Tool_fixps::run(HumdrumFile& infile) { + processFile(infile); + return true; } ////////////////////////////// // -// Tool_gasparize::fixHangingTie -- Not dealing with chain of missing ties. +// Tool_fixps::processFile -- // -void Tool_gasparize::fixHangingTie(HTp first, HTp second) { - string text = *second; - text += "]"; - second->setText(text); +void Tool_fixps::processFile(HumdrumFile& infile) { + removeDuplicateDynamics(infile); + markEmptyVoices(infile); + vector> newlist; + removeEmpties(newlist, infile); + outputNewSpining(newlist, infile); } ////////////////////////////// // -// Tool_gasparize::addMensurations -- Add mensurations. +// Tool_fixps::outputNewSpining -- // -void Tool_gasparize::addMensurations(HumdrumFile& infile) { - HumRegex hre; - for (int i=infile.getLineCount() - 1; i>=0; i--) { - if (!infile[i].isInterpretation()) { +void Tool_fixps::outputNewSpining(vector>& newlist, HumdrumFile& infile) { + for (int i=0; i 0) && (!newlist[i].empty()) && newlist[i][0]->isCommentLocal()) { + if (!newlist[i-1].empty() && newlist[i-1][0]->isCommentLocal()) { + if (newlist[i].size() == newlist[i-1].size()) { + bool same = true; + for (int j=0; j<(int)newlist[i].size(); j++) { + if (*(newlist[i][j]) != *(newlist[i-1][j])) { +cerr << "GOT HERE " << i << " " << j << endl; +cerr << infile[i-1] << endl; +cerr << infile[i] << endl; +cerr << endl; + same = false; + break; + } + } + if (same) { + continue; + } + } } } + if (!infile[i].isManipulator()) { + m_humdrum_text << newlist[i].at(0); + for (int j=1; j<(int)newlist[i].size(); j++) { + m_humdrum_text << "\t"; + m_humdrum_text << newlist[i].at(j); + } + m_humdrum_text << endl; + continue; + } + if ((i > 0) && !infile[i-1].isManipulator()) { + printNewManipulator(infile, newlist, i); + } } } - ////////////////////////////// // -// Tool_gasparize::addMensuration -- +// Tool_fixps::printNewManipulator -- // -void Tool_gasparize::addMensuration(int top, HumdrumFile& infile, int index) { - HTp checktoken = infile[index+1].token(0); - if (!checktoken) { +void Tool_fixps::printNewManipulator(HumdrumFile& infile, vector>& newlist, int line) { + HTp token = infile.token(line, 0); + if (*token == "*-") { + m_humdrum_text << infile[line] << endl; return; } - if (checktoken->find("met") != string::npos) { + if (token->compare(0, 2, "**") == 0) { + m_humdrum_text << infile[line] << endl; return; } - int fieldcount = infile[index].getFieldCount(); - string line = "*"; - HTp token = infile[index].token(0); - if (token->isKern()) { - if (top == 2) { - line += "met(C|)"; - } else { - line += "met(O)"; + m_humdrum_text << "++++++++++++++++++++" << endl; +} + +////////////////////////////// +// +// Tool_fixps::removeDuplicateDynamics -- +// + +void Tool_fixps::removeDuplicateDynamics(HumdrumFile& infile) { + int scount = infile.getStrandCount(); + for (int i=0; iisDataType("**dynam")) { + continue; } - } - for (int i=1; iisKern()) { - if (top == 2) { - line += "met(C|)"; - } else { - line += "met(O)"; + HTp send = infile.getStrandEnd(i); + HTp current = sstart; + while (current && (current != send)) { + vector subtoks = current->getSubtokens(); + if (subtoks.size() % 2 == 1) { + current = current->getNextToken(); + continue; + } + bool equal = true; + int half = (int)subtoks.size() / 2; + for (int j=0; jsetText(newtext); } } } - infile.insertLine(index+1, line); } -/////////////////////////////// + +////////////////////////////// // -// Tool_gasparize::createEditText -- Convert markers into *edit interps. +// Tool_fixps::removeEmpties -- // -void Tool_gasparize::createEditText(HumdrumFile& infile) { - // previous process manipulated the structure so reanalyze here for now: - infile.analyzeBaseFromTokens(); - infile.analyzeStructureNoRhythm(); - - int strands = infile.getStrandCount(); - for (int i=0; i>& newlist, HumdrumFile& infile) { + newlist.resize(infile.getLineCount()); + for (int i=0; iisDataType("**text")) { + if (infile[i].isManipulator()) { continue; } - HTp send = infile.getStrandEnd(i); - bool status = addEditStylingForText(infile, sstart, send); - if (status) { - infile.analyzeBaseFromTokens(); - infile.analyzeStructureNoRhythm(); + for (int j=0; jgetValue("delete"); + if (value == "true") { + continue; + } + newlist[i].push_back(token); } } } + ////////////////////////////// // -// Tool_gasparize::addEditStylingForText -- +// Tool_fixps::markEmptyVoices -- // -bool Tool_gasparize::addEditStylingForText(HumdrumFile& infile, HTp sstart, HTp send) { - HTp current = send->getPreviousToken(); - bool output = false; - string state = ""; - string laststate = ""; - HumRegex hre; - HTp lastdata = NULL; - bool italicQ = false; - while (current && (current != sstart)) { - if (!current->isData()) { - current = current->getPreviousToken(); +void Tool_fixps::markEmptyVoices(HumdrumFile& infile) { + HLp barline = NULL; + for (int i=0; iisNull()) { - current = current->getPreviousToken(); + if (infile[i].isManipulator()) { continue; } - italicQ = false; - string text = current->getText(); - if (text.find("") != string::npos) { - italicQ = true; - hre.replaceDestructive(text, "", "", "g"); - hre.replaceDestructive(text, "", "", "g"); - current->setText(text); - } else { -} - if (laststate == "") { - if (italicQ) { - laststate = "italic"; - } else { - laststate = "regular"; - } - current = current->getPreviousToken(); - continue; - } else { - if (italicQ) { - state = "italic"; - } else { - state = "regular"; - } - } - if (state != laststate) { - if (lastdata && (laststate == "italic")) { - output = true; - if (!insertEditText("*edit", infile, lastdata->getLineIndex() - 1, lastdata->getFieldIndex())) { - string line = getEditLine("*edit", lastdata->getFieldIndex(), lastdata->getOwner()); - infile.insertLine(lastdata->getLineIndex(), line); - } - } else if (lastdata && (laststate == "regular")) { - output = true; - if (!insertEditText("*Xedit", infile, lastdata->getLineIndex() - 1, lastdata->getFieldIndex())) { - string line = getEditLine("*Xedit", lastdata->getFieldIndex(), lastdata->getOwner()); - infile.insertLine(lastdata->getLineIndex(), line); - } + if (infile[i].isInterpretation()) { + if (infile.token(i, 0)->compare(0, 2, "**")) { + barline = &infile[i]; } - } - laststate = state; - lastdata = current; - current = current->getPreviousToken(); - } - - if (lastdata && italicQ) { - // add *edit before first syllable in **text. - output = true; - if (!insertEditText("*edit", infile, lastdata->getLineIndex() - 1, lastdata->getFieldIndex())) { - string line = getEditLine("*edit", lastdata->getFieldIndex(), lastdata->getOwner()); - infile.insertLine(lastdata->getLineIndex(), line); - } - } - - return output; -} - - - -////////////////////////////// -// -// Tool_gasparize::insertEditText -- -// - -bool Tool_gasparize::insertEditText(const string& text, HumdrumFile& infile, int line, int field) { - if (!infile[line].isInterpretation()) { - return false; - } - HTp token; - for (int i=0; iisNull()) { continue; } - if (token->find("edit") != string::npos) { - break; - } - return false; - } - token = infile.token(line, field); - token->setText(text); - - return true; -} - - - -///////////////////// -// -// Tool_gasparize::getEditLine -- -// - -string Tool_gasparize::getEditLine(const string& text, int fieldindex, HLp line) { - string output; - for (int i=0; igetFieldCount()) { - output += "\t"; - } - } - output += text; - if (fieldindex < line->getFieldCount()) { - output += "\t"; - } - for (int i=fieldindex+1; igetFieldCount(); i++) { - output += "*"; - if (i < line->getFieldCount()) { - output += "\t"; - } - } - return output; -} - - - -////////////////////////////// -// -// adjustIntrumentNames -- -// - -void Tool_gasparize::adjustIntrumentNames(HumdrumFile& infile) { - int instrumentLine = -1; - int abbrLine = -1; - for (int i=0; icompare(0, 3, "*I\"") == 0) { - instrumentLine = i; - } - if (token->compare(0, 3, "*I'") == 0) { - abbrLine = i; - } - } - } - if (instrumentLine < 0) { - return; - } - for (int i=0; isetText("*I\"Contratenor 1"); - } else if (*token == "*I\"CTI") { - token->setText("*I\"Contratenor 1"); - } else if (*token == "*I\"CTII") { - token->setText("*I\"Contratenor 2"); - } else if (*token == "*I\"CT II") { - token->setText("*I\"Contratenor 2"); - } else if (*token == "*I\"CT") { - token->setText("*I\"Contratenor"); - } else if (*token == "*I\"S") { - token->setText("*I\"Superius"); - } else if (*token == "*I\"A") { - token->setText("*I\"Altus"); - } else if (*token == "*I\"T") { - token->setText("*I\"Tenor"); - } else if (*token == "*I\"B") { - token->setText("*I\"Bassus"); - } else if (*token == "*I\"V") { - token->setText("*I\"Quintus"); - } else if (*token == "*I\"VI") { - token->setText("*I\"Sextus"); - } - } - if (abbrLine >= 0) { - return; - } - string abbr; - HumRegex hre; - for (int i=0; i=0; i--) { - if (!infile[i].isInterpretation()) { + if (!barline) { continue; } + // check on the data line if: + // * it is in the first subspine + // * it is an invisible rest + // * it takes the full duration of the measure + // If so, then mark the tokens for deletion in that layer. for (int j=0; jgetTrack(); + int subtrack = token->getSubtrack(); + if (subtrack != 1) { continue; } - if (!token->isKern()) { + if (token->find("yy") == string::npos) { continue; } - if (hre.search(token, "^\\*[A-Ga-g][#n-]*:$")) { - // suppress the key desingation - infile.deleteLine(i); - break; - } - } - } - -} - - -////////////////////////////// -// -// Tool_gasparize::fixBarlines -- Add final double barline and convert -// any intermediate final barlines to double barlines. -// - -void Tool_gasparize::fixBarlines(HumdrumFile& infile) { - fixFinalBarline(infile); - HumRegex hre; - - for (int i=0; ifind("==") == string::npos) { + if (!token->isRest()) { continue; } - if (hre.search(token, "^==(\\d*)")) { - string text = "="; - text += hre.getMatch(1); - text += "||"; - token->setText(text); + HumNum duration = token->getDuration(); + HumNum bardur = token->getDurationToBarline(); + HTp current = token; + while (current) { + subtrack = current->getSubtrack(); + if (subtrack != 1) { + break; + } + current->setValue("delete", "true"); + if (current->isBarline()) { + break; + } + current = current->getNextToken(); } - } - } -} - - - -////////////////////////////// -// -// Tool_gasparize::fixFinalBarline -- -// - -void Tool_gasparize::fixFinalBarline(HumdrumFile& infile) { - for (int i=infile.getLineCount() - 1; i>=0; i--) { - if (infile[i].isData()) { - break; - } - if (!infile[i].isBarline()) { - continue; - } - for (int j=0; jsetText("=="); + current = token; + current = current->getPreviousToken(); + while (current) { + if (current->isManipulator()) { + break; + } + if (current->isBarline()) { + break; + } + subtrack = current->getSubtrack(); + if (subtrack != 1) { + break; + } + current->setValue("delete", "true"); + current = current->getPreviousToken(); } } } -} - - - -////////////////////////////// -// -// Tool_gasparize::createJEditorialAccidentals -- -// convert -// !LO:TX:a:t=( ) -// 4F# -// - -void Tool_gasparize::createJEditorialAccidentals(HumdrumFile& infile) { - int strands = infile.getStrandCount(); - for (int i=0; iisKern()) { - continue; - } - HTp send = infile.getStrandEnd(i); - createJEditorialAccidentals(sstart, send); - } -} - -void Tool_gasparize::createJEditorialAccidentals(HTp sstart, HTp send) { - HTp current = sstart->getNextToken(); - HumRegex hre; - while (current && (current != send)) { - if (!current->isCommentLocal()) { - current = current->getNextToken(); - continue; - } - if (hre.search(current, "^!LO:TX:a:t=\\(\\s*\\)$")) { - current->setText("!"); - convertNextNoteToJAccidental(current); - } - current = current->getNextToken(); - } -} -void Tool_gasparize::convertNextNoteToJAccidental(HTp current) { - current = current->getNextToken(); - HumRegex hre; - while (current) { - if (!current->isData()) { - // Does not handle LO for non-data. - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - break; - } - if (current->isRest()) { - break; - } - string text = *current; - if (hre.search(text, "i")) { - hre.replaceDestructive(text, "j", "i"); - current->setText(text); - break; - } else if (hre.search(text, "[-#n]")) { - hre.replaceDestructive(text, "$1j", "(.*[-#n]+)"); - current->setText(text); - break; - } else { - // Need to add a natural sign as well. - hre.replaceDestructive(text, "$1nj", "(.*[A-Ga-g]+)"); - current->setText(text); - break; - } - break; - } - current = current->getNextToken(); } @@ -85785,21 +86808,25 @@ void Tool_gasparize::convertNextNoteToJAccidental(HTp current) { ///////////////////////////////// // -// Tool_grep::Tool_grep -- Set the recognized options for the tool. +// Tool_flipper::Tool_flipper -- Set the recognized options for the tool. // -Tool_grep::Tool_grep(void) { - define("v|remove-matching-lines=b", "remove lines that match regex"); - define("e|regex|regular-expression=s", "regular expression to search with"); +Tool_flipper::Tool_flipper(void) { + define("k|keep=b", "keep *flip/*Xflip instructions"); + define("a|all=b", "flip globally, not just inside *flip/*Xflip regions"); + define("s|strophe=b", "flip inside of strophes as well"); + define("S|strophe-only|only-strophe=b", "flip only inside of strophes as well"); + define("i|interp=s:kern", "flip only in this interpretation"); } + ///////////////////////////////// // -// Tool_grep::run -- Do the main work of the tool. +// Tool_flipper::run -- Do the main work of the tool. // -bool Tool_grep::run(HumdrumFileSet& infiles) { +bool Tool_flipper::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; igetTrack(); + m_strophe[track] = true; + } else if (*token == "*Xstrophe") { + track = token->getTrack(); + m_strophe[track] = false; + } } - return status; -} -bool Tool_half::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; + if (m_allQ) { + // state always stays on in this case + return; } - return status; -} -// -// In-place processing of file: -// - -bool Tool_half::run(HumdrumFile& infile) { - processFile(infile); - - // Re-load the text for each line from their tokens. - infile.createLinesFromTokens(); + for (int i=0; igetTrack(); + m_flipState[track] = true; + m_fliplines[i] = true; + } else if (*token == "*Xflip") { + track = token->getTrack(); + m_flipState[track] = false; + m_fliplines[i] = true; + } + } - // Need to adjust the line numbers for tokens for later - // processing. - m_humdrum_text << infile; - return true; } ////////////////////////////// // -// Tool_half::processFile -- +// Tool_flipper::processLine -- // -void Tool_half::processFile(HumdrumFile& infile) { - m_lyricBreakQ = getBoolean("lyric-beam-break"); - terminalLongToTerminalBreve(infile); - halfRhythms(infile); - adjustBeams(infile); +void Tool_flipper::processLine(HumdrumFile& infile, int index) { + if (!infile[index].hasSpines()) { + return; + } + if (infile[index].isInterpretation()) { + checkForFlipChanges(infile, index); + } + + vector> flipees; + extractFlipees(flipees, infile, index); + if (!flipees.empty()) { + int status = flipSubspines(flipees); + if (status) { + infile[index].createLineFromTokens(); + } + } } ////////////////////////////// // -// Tool_half::adjustBeams -- +// Tool_flipper::flipSubspines -- // -void Tool_half::adjustBeams(HumdrumFile& infile) { - Tool_autobeam autobeam; - vector argv; - argv.push_back("autobeam"); - if (m_lyricBreakQ) { - argv.push_back("-l"); +bool Tool_flipper::flipSubspines(vector>& flipees) { + bool regenerateLine = false; + for (int i=0; i<(int)flipees.size(); i++) { + if (flipees[i].size() > 1) { + flipSpineTokens(flipees[i]); + regenerateLine = true; + } } - autobeam.process(argv); - autobeam.run(infile); + return regenerateLine; } - ////////////////////////////// // -// Tool_half::halfRhythms -- +// Tool_flipper::flipSpineTokens -- // -void Tool_half::halfRhythms(HumdrumFile& infile) { - HumRegex hre; - for (int i=0; iisKern()) { - continue; - } - if (token->isNull()) { - continue; - } - - string text = *token; - // extract duration without dot - HumNum durnodot = Convert::recipToDurationNoDots(text); - durnodot /= 2; - string newrhythm = Convert::durationToRecip(durnodot); - hre.replaceDestructive(text, newrhythm, "\\d+%?\\d*"); - token->setText(text); - } - } else if (infile[i].isInterpretation()) { - // half time signatures - for (int j=0; jsetText(text); - } else { - string text = *token; - string replacement = "/" + to_string(bot1); - replacement += "%" + to_string(bot2); - hre.replaceDestructive(text, replacement, "/\\d+"); - token->setText(text); - } - } else if (hre.search(token, "^\\*M(\\d+)/(\\d+)")) { - int bot = hre.getMatchInt(2); - if (bot == 4) { - bot = 8; - } else if (bot == 2) { - bot = 4; - } else if (bot == 3) { - bot = 6; - } else if (bot == 1) { - bot = 2; - } else if (bot == 0) { - bot = 1; - } else { - cerr << "Warning: ignored time signature: " << token << endl; - } - string text = *token; - string replacement = "/" + to_string(bot); - hre.replaceDestructive(text, replacement, "/\\d+"); - token->setText(text); - } - } - } +void Tool_flipper::flipSpineTokens(vector& subtokens) { + if (subtokens.size() < 2) { + return; + } + int count = (int)subtokens.size(); + count = count / 2; + HTp tok1; + HTp tok2; + string str1; + string str2; + for (int i=0; isetText(str2); + tok2->setText(str1); } } @@ -86054,69 +87038,81 @@ void Tool_half::halfRhythms(HumdrumFile& infile) { ////////////////////////////// // -// Tool_half::terminalLongToTerminalBreve -- +// Tool_flipper::extractFlipees -- // -void Tool_half::terminalLongToTerminalBreve(HumdrumFile& infile) { - HumRegex hre; - for (int i=0; i>& flipees, + HumdrumFile& infile, int index) { + flipees.clear(); + + HLp line = &infile[index]; + int track; + int lastInsertTrack = -1; + for (int i=0; igetFieldCount(); i++) { + HTp token = line->token(i); + track = token->getTrack(); + if ((!m_stropheQ) && m_strophe[track]) { continue; } - HTp token = infile.token(i, 0); - if (token->find("terminal long") == string::npos) { + if (!m_flipState[track]) { continue; } - string text = *token; - hre.replaceDestructive(text, "terminal breve", "terminal long", "g"); - token->setText(text); + if (m_kernQ) { + if (!token->isKern()) { + continue; + } + } else { + if (!token->isDataType(m_interp)) { + continue; + } + } + if (lastInsertTrack != track) { + flipees.resize(flipees.size() + 1); + lastInsertTrack = track; + } + flipees.back().push_back(token); } } + ///////////////////////////////// // -// Tool_hands::Tool_hands -- Set the recognized options for the tool. +// Tool_gasparize::Tool_gasparize -- Set the recognized options for the tool. // -Tool_hands::Tool_hands(void) { - define("c|color=b", "color right-hand notes red and left-hand notes blue"); - define("lcolor|left-color=s:dodgerblue", "color of left-hand notes"); - define("rcolor|right-color=s:crimson", "color of right-hand notes"); - define("l|left-only=b", "remove right-hand notes"); - define("r|right-only=b", "remove left-hand notes"); - define("m|mark=b", "mark left and right-hand notes"); - define("a|attacks-only=b", "only mark note attacks and not note sustains"); -} +Tool_gasparize::Tool_gasparize(void) { + define("R|no-reference-records=b", "do not add reference records"); + define("r|only-add-reference-records=b", "only add reference records"); + define("B|do-not-delete-breaks=b", "do not delete system/page break markers"); + define("b|only-delete-breaks=b", "only delete breaks"); + define("A|do-not-fix-instrument-abbreviations=b", "do not fix instrument abbreviations"); + define("a|only-fix-instrument-abbreviations=b", "only fix instrument abbreviations"); -////////////////////////////// -// -// Tool_hands::initialize -- Initializations that only have to be done once -// for all HumdrumFile segments. -// + define("E|do-not-fix-editorial-accidentals=b", "do not fix instrument abbreviations"); + define("e|only-fix-editorial-accidentals=b", "only fix editorial accidentals"); -void Tool_hands::initialize(void) { - m_colorQ = getBoolean("color"); - m_leftColor = getString("left-color"); - m_rightColor = getString("right-color"); - m_leftOnlyQ = getBoolean("left-only"); - m_rightOnlyQ = getBoolean("right-only"); - m_markQ = getBoolean("mark"); - m_attacksOnlyQ = getBoolean("attacks-only"); + define("T|do-not-add-terminal-longs=b", "do not add terminal long markers"); + define("t|only-add-terminal-longs=b", "only add terminal longs"); + + define("no-ties=b", "do not fix tied notes"); + + define("N|do-not-remove-empty-transpositions=b", "do not remove empty transposition instructions"); + define ("n|only-remove-empty-transpositions=b", "only remove empty transpositions"); } ///////////////////////////////// // -// Tool_hands::run -- Do the main work of the tool. +// Tool_gasparize::run -- Primary interfaces to the tool. // -bool Tool_hands::run(HumdrumFileSet& infiles) { +bool Tool_gasparize::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; igetExclusiveInterpretation(); - int hasHandMarkup = xtok->getValueInt("auto", "hand"); - if (!hasHandMarkup) { - continue; - } - HTp send = infile.getStrandEnd(i); - removeNotes(sstart, send, htype); - counter++; - } + if (getBoolean("no-ties")) { tieQ = false; } - - if (counter) { - infile.createLinesFromTokens(); + if (getBoolean("only-remove-empty-transpositions")) { + abbreviationsQ = false; + accidentalsQ = false; + referencesQ = false; + terminalsQ = false; + breaksQ = false; + transpositionsQ = true; } -} + if (articulationsQ) { removeArticulations(infile); } + if (fixbarlinesQ) { fixBarlines(infile); } + if (tieQ) { fixTies(infile); } + if (abbreviationsQ) { fixInstrumentAbbreviations(infile); } + if (accidentalsQ) { fixEditorialAccidentals(infile); } + if (parenthesesQ) { createJEditorialAccidentals(infile); } + if (referencesQ) { addBibliographicRecords(infile); } + if (breaksQ) { deleteBreaks(infile); } + if (terminalsQ) { addTerminalLongs(infile); } + if (transpositionsQ) { deleteDummyTranspositions(infile); } + if (mensurationQ) { addMensurations(infile); } + if (teditQ) { createEditText(infile); } + if (instrumentQ) { adjustIntrumentNames(infile); } + if (removekeydesigQ) { removeKeyDesignations(infile); } -void Tool_hands::removeNotes(HTp sstart, HTp send, const string& htype) { - HTp current = sstart; - while (current && (current != send)) { - if (!current->isData() || current->isNull()) { - current = current->getNextToken(); - continue; - } + adjustSystemDecoration(infile); - HumRegex hre; - string ttype = current->getValue("auto", "hand"); - if (ttype != htype) { - current = current->getNextToken(); - continue; - } - string text = *current; - hre.replaceDestructive(text, "", "[^0-9.%q ]", "g"); - hre.replaceDestructive(text, "ryy ", " ", "g"); - text += "ryy"; - current->setText(text); - current = current->getNextToken(); - } + // Input lyrics may contain "=" signs which are to be converted into + // spaces in **text data, and into elisions when displaying with verovio. + Tool_shed shed; + vector argv; + argv.push_back("shed"); + argv.push_back("-x"); // only apply to **text spines + argv.push_back("text"); + argv.push_back("-e"); + argv.push_back("s/=/ /g"); + shed.process(argv); + shed.run(infile); } ////////////////////////////// // -// Tool_hands::markNotes -- +// Tool_gasparize::removeArticulations -- // -void Tool_hands::markNotes(HumdrumFile& infile) { +void Tool_gasparize::removeArticulations(HumdrumFile& infile) { HumRegex hre; - - int counter = 0; - int scount = infile.getStrandCount(); - for (int i=0; igetExclusiveInterpretation(); - int hasHandMarkup = xtok->getValueInt("auto", "hand"); - if (!hasHandMarkup) { + for (int i=0; iisKern()) { + continue; + } + if (token->isNull()) { + continue; + } + bool changed = false; + string text = token->getText(); + if (text.find("'") != string::npos) { + // remove staccatos + changed = true; + hre.replaceDestructive(text, "", "'", "g"); + } + if (text.find("~") != string::npos) { + // remove tenutos + changed = true; + hre.replaceDestructive(text, "", "~", "g"); + } + if (changed) { + token->setText(text); + } + } } } -void Tool_hands::markNotes(HTp sstart, HTp send) { - HTp current = sstart; - while (current && (current != send)) { - if (!current->isData() || current->isNull() || current->isRest()) { - current = current->getNextToken(); + +////////////////////////////// +// +// Tool_gasparize::adjustSystemDecoration -- +// !!!system-decoration: [(s1)(s2)(s3)(s4)] +// to: +// !!!system-decoration: [*] +// + +void Tool_gasparize::adjustSystemDecoration(HumdrumFile& infile) { + for (int i=infile.getLineCount() - 1; i>=0; i--) { + if (!infile[i].isReference()) { continue; } - - HumRegex hre; - string text = *current; - string htype = current->getValue("auto", "hand"); - if (htype == "LH") { - hre.replaceDestructive(text, " " + m_leftMarker, " +", "g"); - text = m_leftMarker + text; - } else if (htype == "RH") { - hre.replaceDestructive(text, " " + m_rightMarker, " +", "g"); - text = m_rightMarker + text; + HTp token = infile.token(i, 0); + if (token->compare(0, 21, "!!!system-decoration:") == 0) { + token->setText("!!!system-decoration: [*]"); + break; } - current->setText(text); - current = current->getNextToken(); } } @@ -86290,1179 +87343,1280 @@ void Tool_hands::markNotes(HTp sstart, HTp send) { ////////////////////////////// // -// Tool_hands::colorHands -- Convert for example *LH into *color:dodgerblue. +// Tool_gasparize::deleteDummyTranspositions -- Somehow empty +// transpositions that go to the same pitch can appear in the +// MusicXML data, so remove them here. Example: +// *Trd0c0 // -void Tool_hands::colorHands(HumdrumFile& infile) { - string left = "*color:" + m_leftColor; - string right = "*color:" + m_rightColor; +void Tool_gasparize::deleteDummyTranspositions(HumdrumFile& infile) { + vector ldel; for (int i=0; iisKern()) { + empty = false; continue; } - if (*token == "*LH") { - token->setText(left); - changed = true; - } - if (*token == "*RH") { - token->setText(right); - changed = true; + if (*token == "*Trd0c0") { + token->setText("*"); + } else { + empty = false; } } - if (changed) { - infile[i].createLineFromTokens(); + if (empty) { + ldel.push_back(i); } } -} - + if (ldel.size() == 1) { + infile.deleteLine(ldel[0]); + } else if (ldel.size() > 1) { + cerr << "Warning: multiple transposition lines, not deleting them" << endl; + } - -///////////////////////////////// -// -// Tool_homorhythm::Tool_homorhythm -- Set the recognized options for the tool. -// - -Tool_homorhythm::Tool_homorhythm(void) { - define("a|append=b", "append analysis to end of input data"); - define("attacks=b", "append attack counts for each sonority"); - define("p|prepend=b", "prepend analysis to end of input data"); - define("r|raw-sonority=b", "display individual sonority scores only"); - define("raw-score=b", "display accumulated scores"); - define("M|no-marks=b", "do not mark homorhythm section notes"); - define("f|fraction=b", "calculate fraction of music that is homorhythm"); - define("v|voice=b", "display voice information or fraction results"); - define("F|filename=b", "show filename for f option"); - define("n|t|threshold=d:4.0", "threshold score sum required for homorhythm texture detection"); - define("s|score=d:1.0", "score assigned to a sonority with three or more attacks"); - define("m|intermediate-score=d:0.5", "score to give sonority between two adjacent attack sonoroties"); - define("l|letter=b", "display letter scoress before calculations"); } - -///////////////////////////////// +////////////////////////////// // -// Tool_homorhythm::run -- Do the main work of the tool. +// Tool_gasparize::fixEditorialAccidentals -- checkDataLine() does +// all of the work for this function, which only manages +// key signature and barline processing. +// Rules for accidentals in Tasso in Music Project: +// (1) Only note accidentals printed in the source editions +// are displayed as regular accidentals. These accidentals +// are postfixed with an "X" in the **kern data. +// (2) Editorial accidentals are given an "i" marker but not +// a "X" marker in the **kern data. This editorial accidental +// is displayed above the note. +// This algorithm makes adjustments to the input data because +// Sibelius will drop editorial information after the frist +// editorial accidental on that pitch in the measure. +// (3) If a note is the same pitch as a previous note in the +// measure and the previous note has an editorial accidental, +// then make the note an editorial note. However, if the +// accidental state of the note matches the key-signature, +// then do not add an editorial accidental, and there will be +// no accidental displayed on the note. In that case, add a "y" +// after the accidental to indicate that it is interpreted +// and not visible in the original score. // -bool Tool_homorhythm::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; iisKern()) { + continue; + } + if (token->isNull()) { + continue; + } + if (token->isRest()) { + continue; + } + if (token->find("--") != string::npos) { + string text = *token; + hre.replaceDestructive(text, "-", "--", "g"); + } else if (token->find("--") != string::npos) { + string text = *token; + hre.replaceDestructive(text, "#", "##", "g"); + } + } + } } ////////////////////////////// // -// Tool_homorhythm::initialize -- +// Tool_gasparize::addTerminalLongs -- Convert all last notes to terminal longs +// Also probably add terminal longs before double barlines as in JRP. // -void Tool_homorhythm::initialize(void) { - m_threshold = getInteger("threshold"); - if (m_threshold < 1.0) { - m_threshold = 1.0; - } - - m_score = getDouble("score"); - if (m_score < 1.0) { - m_score = 1.0; - } - - m_intermediate_score = getDouble("intermediate-score"); - if (m_intermediate_score < 0.0) { - m_intermediate_score = 0.0; - } - - if (m_intermediate_score > m_score) { - m_intermediate_score = m_score; +void Tool_gasparize::addTerminalLongs(HumdrumFile& infile) { + int scount = infile.getStrandCount(); + for (int i=0; iisKern()) { + continue; + } + while (cur) { + if (!cur->isData()) { + cur = cur->getPreviousToken(); + continue; + } + if (cur->isNull()) { + cur = cur->getPreviousToken(); + continue; + } + if (cur->isRest()) { + cur = cur->getPreviousToken(); + continue; + } + if (cur->isSecondaryTiedNote()) { + cur = cur->getPreviousToken(); + continue; + } + if (cur->find("l") != string::npos) { + // already marked so do not do it again + break; + } + // mark this note with "l" + string newtext = *cur; + newtext += "l"; + cur->setText(newtext); + break; + } } - } ////////////////////////////// // -// Tool_homorhythm::processFile -- +// Tool_gasparize::fixInstrumentAbbreviations -- // -void Tool_homorhythm::processFile(HumdrumFile& infile) { - vector data; - data.reserve(infile.getLineCount()); - - m_homorhythm.clear(); - m_homorhythm.resize(infile.getLineCount()); - - m_notecount.clear(); - m_notecount.resize(infile.getLineCount()); - fill(m_notecount.begin(), m_notecount.end(), 0); - - m_attacks.clear(); - m_attacks.resize(infile.getLineCount()); - fill(m_attacks.begin(), m_attacks.end(), 0); - - m_notes.clear(); - m_notes.resize(infile.getLineCount()); - - for (int i=0; i score(infile.getLineCount(), 0); - vector raw(infile.getLineCount(), 0); +void Tool_gasparize::fixInstrumentAbbreviations(HumdrumFile& infile) { + int iline = -1; + int aline = -1; - double sum = 0.0; - for (int i=0; i<(int)data.size(); i++) { - if (m_homorhythm[data[i]].find("Y") != string::npos) { - if (m_homorhythm[data[i]].find("N") != string::npos) { - // sonority between two homorhythm-like sonorities. - // maybe also differentiate based on metric position. - sum += m_intermediate_score; - raw[data[i]] = m_intermediate_score; - } else { - sum += m_score; - raw[data[i]] = m_score; - } - } else { - sum = 0.0; - } - score[data[i]] = sum; + vector kerns = infile.getKernSpineStartList(); + if (kerns.empty()) { + return; } - for (int i=(int)data.size()-2; i>=0; i--) { - if (score[data[i]] == 0) { - continue; + HTp cur = kerns[0]; + while (cur) { + if (cur->isData()) { + break; } - if (score[data[i+1]] > score[data[i]]) { - score[data[i]] = score[data[i+1]]; + if (cur->compare(0, 3, "*I\"") == 0) { + iline = cur->getLineIndex(); + } else if (cur->compare(0, 3, "*I'") == 0) { + aline = cur->getLineIndex(); } + cur = cur->getNextToken(); } - if (getBoolean("raw-score")) { - addAccumulatedScores(infile, score); - } - - if (getBoolean("raw-sonority")) { - addRawAnalysis(infile, raw); - } - if (getBoolean("raw-score")) { - addAccumulatedScores(infile, score); + if (iline < 0) { + // no names to create abbreviations for + return; } - - if (getBoolean("fraction")) { - addFractionAnalysis(infile, score); + if (aline < 0) { + // not creating a new abbreviation for now + // (could add later). + return; } - - if (getBoolean("attacks")) { - addAttacks(infile, m_attacks); + if (infile[iline].getFieldCount() != infile[aline].getFieldCount()) { + // no spine splitting between the two lines. + return; } - - if (!getBoolean("fraction")) { - // Color the notes within homorhythm textures. - // mark homorhythm regions in red, - // non-homorhythm sonorities within these regions in green - // and non-homorhythm regions in black. - if (m_letterQ) { - infile.appendDataSpine(m_homorhythm, "", "**hp"); + // Maybe also require them to be adjacent to each other. + HumRegex hre; + for (int j=0; j<(int)infile[iline].getFieldCount(); j++) { + if (!infile.token(iline, j)->isKern()) { + continue; } - for (int i=0; i<(int)data.size(); i++) { - if (score[data[i]] >= m_threshold) { - if (m_attacks[data[i]] < (int)m_notes[data[i]].size() - 1) { - m_homorhythm[data[i]] = "dodgerblue"; - } else { - m_homorhythm[data[i]] = "red"; - } - } else { - m_homorhythm[data[i]] = "black"; - } + if (!hre.search(*infile.token(iline, j), "([A-Za-z][A-Za-z .0-9]+)")) { + continue; } - infile.appendDataSpine(m_homorhythm, "", "**color"); - - // problem with **color spine in javascript, so output via humdrum text - m_humdrum_text << infile; + string name = hre.getMatch(1); + string abbr = "*I'"; + if (name == "Basso Continuo") { + abbr += "BC"; + } else if (name == "Basso continuo") { + abbr += "BC"; + } else if (name == "basso continuo") { + abbr += "BC"; + } else { + abbr += toupper(name[0]); + } + // check for numbers after the end of the name and add to abbreviation + infile.token(aline, j)->setText(abbr); } - } ////////////////////////////// // -// Tool_homorhythm::addAccumulatedScores -- +// Tool_gasparize::convertBreaks -- // -void Tool_homorhythm::addAccumulatedScores(HumdrumFile& infile, vector& score) { - infile.appendDataSpine(score, "", "**score", false); +void Tool_gasparize::convertBreaks(HumdrumFile& infile) { + HumRegex hre; + for (int i=infile.getLineCount()-1; i>= 0; i--) { + if (!infile[i].isGlobalComment()) { + continue; + } + if (hre.search(*infile.token(i, 0), "linebreak\\s*:\\s*original")) { + string text = "!!LO:LB:g=original"; + infile[i].setText(text); + } + else if (hre.search(*infile.token(i, 0), "pagebreak\\s*:\\s*original")) { + string text = "!!LO:PB:g=original"; + infile[i].setText(text); + } + } } ////////////////////////////// // -// Tool_homorhythm::addRawAnalysis -- +// Tool_gasparize::deleteBreaks -- // -void Tool_homorhythm::addRawAnalysis(HumdrumFile& infile, vector& raw) { - infile.appendDataSpine(raw, "", "**raw", false); +void Tool_gasparize::deleteBreaks(HumdrumFile& infile) { + HumRegex hre; + for (int i=infile.getLineCount()-1; i>= 0; i--) { + if (!infile[i].isGlobalComment()) { + continue; + } + if (hre.search(*infile.token(i, 0), "linebreak\\s*:\\s*original")) { + infile.deleteLine(i); + } + else if (hre.search(*infile.token(i, 0), "pagebreak\\s*:\\s*original")) { + infile.deleteLine(i); + } + } } - -////////////////////////////// +//////////////////////////////// // -// Tool_homorhythm::addAttacks -- +// Tool_gasparize::addBibliographicRecords -- // - -void Tool_homorhythm::addAttacks(HumdrumFile& infile, vector& attacks) { - infile.appendDataSpine(attacks, "", "**atks"); -} - - - -////////////////////////////// +// !!!COM: +// !!!CDT: +// !!!OTL: +// !!!AGN: +// !!!SCT: +// !!!SCA: +// !!!voices: // -// Tool_homorhythm::addFractionAnalysis -- +// At end: +// !!!RDF**kern: l = terminal long +// !!!RDF**kern: i = editorial accidental +// !!!EED: +// !!!EEV: $DATE // -void Tool_homorhythm::addFractionAnalysis(HumdrumFile& infile, vector& score) { - double sum = 0.0; - for (int i=0; i m_threshold) { - sum += infile[i].getDuration().getFloat(); +void Tool_gasparize::addBibliographicRecords(HumdrumFile& infile) { + vector refinfo = infile.getReferenceRecords(); + map refs; + for (int i=0; i<(int)refinfo.size(); i++) { + string key = refinfo[i]->getReferenceKey(); + refs[key] = refinfo[i]; + } + + // header records + if (refs.find("voices") == refs.end()) { + if (infile.token(0, 0)->find("!!!OTL") != string::npos) { + infile.insertLine(1, "!!!voices:"); + } else { + infile.insertLine(0, "!!!voices:"); } } - double total = infile.getScoreDuration().getFloat(); - int ocount = getOriginalVoiceCount(infile); - double fraction = sum / total; - double percent = int(fraction * 1000.0 + 0.5)/10.0; - if (getBoolean("filename")) { - m_free_text << infile.getFilename() << "\t"; + if (refs.find("SCA") == refs.end()) { + if (infile.token(0, 0)->find("!!!OTL") != string::npos) { + infile.insertLine(1, "!!!SCA:"); + } else { + infile.insertLine(0, "!!!SCA:"); + } } - if (getBoolean("voice")) { - m_free_text << ocount; - m_free_text << "\t"; - m_free_text << m_voice_count; - m_free_text << "\t"; - if (ocount == m_voice_count) { - m_free_text << "complete" << "\t"; + if (refs.find("SCT") == refs.end()) { + if (infile.token(0, 0)->find("!!!OTL") != string::npos) { + infile.insertLine(1, "!!!SCT:"); } else { - m_free_text << "incomplete" << "\t"; + infile.insertLine(0, "!!!SCT:"); } } - if (m_voice_count < 2) { - m_free_text << -1; - } else { - m_free_text << percent; + if (refs.find("AGN") == refs.end()) { + if (infile.token(0, 0)->find("!!!OTL") != string::npos) { + infile.insertLine(1, "!!!AGN:"); + } else { + infile.insertLine(0, "!!!AGN:"); + } } - m_free_text << endl; -} - + if (refs.find("OTL") == refs.end()) { + infile.insertLine(0, "!!!OTL:"); + } + if (refs.find("CDT") == refs.end()) { + infile.insertLine(0, "!!!CDT: ~1450-~1517"); + } + if (refs.find("COM") == refs.end()) { + infile.insertLine(0, "!!!COM: Gaspar van Weerbeke"); + } -////////////////////////////// -// -// Tool_homorhythm::getOriginalVoiceCount -- -// - -int Tool_homorhythm::getOriginalVoiceCount(HumdrumFile& infile) { - HumRegex hre; + // trailer records + bool foundi = false; + bool foundj = false; + bool foundl = false; for (int i=0; ifind("!!!RDF**kern:") == string::npos) { + continue; + } + if (token->find("terminal breve") != string::npos) { + foundl = true; + } else if (token->find("editorial accidental") != string::npos) { + if (token->find("i =") != string::npos) { + foundi = true; + } else if (token->find("j =") != string::npos) { + foundj = true; } - return count; } } - return 0; + if (!foundj) { + infile.appendLine("!!!RDF**kern: j = editorial accidental, optional, paren up"); + } + if (!foundi) { + infile.appendLine("!!!RDF**kern: i = editorial accidental"); + } + if (!foundl) { + infile.appendLine("!!!RDF**kern: l = terminal long"); + } + + if (refs.find("PTL") == refs.end()) { + infile.appendLine("!!!PTL: Gaspar van Weerbeke: Collected Works. V. Settings of Liturgical Texts, Songs, and Instrumental Works"); + } + if (refs.find("PPR") == refs.end()) { + infile.appendLine("!!!PPR: American Institute of Musicology"); + } + if (refs.find("PC#") == refs.end()) { + infile.appendLine("!!!PC#: Corpus Mensurabilis Musicae 106/V"); + } + if (refs.find("PDT") == refs.end()) { + infile.appendLine("!!!PDT: {YEAR}"); + } + if (refs.find("PED") == refs.end()) { + infile.appendLine("!!!PED: Kolb, Paul"); + infile.appendLine("!!!PED: Pavanello, Agnese"); + } + if (refs.find("YEC") == refs.end()) { + infile.appendLine("!!!YEC: Copyright {YEAR}, Kolb, Paul"); + infile.appendLine("!!!YEC: Copyright {YEAR}, Pavanello, Agnese"); + } + if (refs.find("YEM") == refs.end()) { + infile.appendLine("!!!YEM: CC-BY-SA 4.0 (https://creativecommons.org/licenses/by-nc/4.0/legalcode)"); + } + if (refs.find("EED") == refs.end()) { + infile.appendLine("!!!EED: Zybina, Karina"); + infile.appendLine("!!!EED: Mair-Gruber, Roland"); + } + if (refs.find("EEV") == refs.end()) { + string date = getDate(); + string line = "!!!EEV: " + date; + infile.appendLine(line); + } } -////////////////////////////// +//////////////////////////////// // -// Tool_homorhythm::getExtantVoiceCount -- +// Tool_gasparize::checkDataLine -- // -int Tool_homorhythm::getExtantVoiceCount(HumdrumFile& infile) { - vector spines = infile.getKernSpineStartList(); - return (int)spines.size(); -} +void Tool_gasparize::checkDataLine(HumdrumFile& infile, int lineindex) { + HumdrumLine& line = infile[lineindex]; + + HumRegex hre; + HTp token; + bool haseditQ; + int base7; + int accid; + int track; + bool removeQ; + for (int i=0; igetTrack(); + if (!token->isKern()) { + continue; + } + if (token->isNull()) { + continue; + } + if (token->isRest()) { + continue; + } + if (token->find('j') != string::npos) { + continue; + } + if (token->isSecondaryTiedNote()) { + continue; + } + base7 = Convert::kernToBase7(token); + accid = Convert::kernToAccidentalCount(token); + haseditQ = false; + removeQ = false; + // Hard-wired to "i" as editorial accidental marker + if (token->find("ni") != string::npos) { + haseditQ = true; + } else if (token->find("-i") != string::npos) { + haseditQ = true; + } else if (token->find("#i") != string::npos) { + haseditQ = true; + } else if (token->find("nXi") != string::npos) { + haseditQ = true; + removeQ = true; + } else if (token->find("-Xi") != string::npos) { + haseditQ = true; + removeQ = true; + } else if (token->find("#Xi") != string::npos) { + haseditQ = true; + removeQ = true; + } -////////////////////////////// -// -// Tool_homorhythm::analyzeLine -- -// + if (removeQ) { + string temp = *token; + hre.replaceDestructive(temp, "", "X"); + token->setText(temp); + } -void Tool_homorhythm::analyzeLine(HumdrumFile& infile, int line) { - m_notes[line].reserve(10); - HPNote note; - if (!infile[line].isData()) { - return; - } - int nullQ = 0; - for (int i=0; iisKern()) { + bool explicitQ = false; + if (token->find("#X") != string::npos) { + explicitQ = true; + } else if (token->find("-X") != string::npos) { + explicitQ = true; + } else if (token->find("nX") != string::npos) { + explicitQ = true; + } else if (token->find("n") != string::npos) { + // add an explicit accidental marker + explicitQ = true; + string text = *token; + hre.replaceDestructive(text, "nX", "n"); + token->setText(text); + } + + if (haseditQ) { + // Store new editorial pitch state. + m_estates.at(track).at(base7) = true; + m_pstates.at(track).at(base7) = accid; continue; } - if (token->isRest()) { + + if (explicitQ) { + // No need to make editorial since it is visible. + m_estates.at(track).at(base7) = false; + m_pstates.at(track).at(base7) = accid; continue; } - if (token->isNull()) { - nullQ = 1; - token = token->resolveNull(); - if (!token) { - continue; + + if (accid == m_kstates.at(track).at(base7)) { + // !m_estates.at(track).at(base7)) { + // add !m_estates.at(track).at(base) as a condition if + // you want editorial accidentals to be added to return the + // note to the accidental in the key. + // + // The accidental matches the key-signature state, + // so it should not be made editorial eventhough + // it is not visible. + m_pstates.at(track).at(base7) = accid; + + // Add a "y" marker of there is an interpreted accidental + // state (flat or sharp) that is part of the key signature. + int hasaccid = false; + if (token->find("#") != string::npos) { + hasaccid = true; + } else if (token->find("-") != string::npos) { + hasaccid = true; } - if (token->isRest()) { - continue; + int hashide = false; + if (token->find("-y") != string::npos) { + hashide = true; } - } else { - nullQ = 0; + else if (token->find("#y") != string::npos) { + hashide = true; + } + if (hasaccid && !hashide) { + string text = *token; + hre.replaceDestructive(text, "#y", "#"); + hre.replaceDestructive(text, "-y", "-"); + token->setText(text); + } + + continue; } - int track = token->getTrack(); - vector subtokens = token->getSubtokens(); - for (int j=0; j<(int)subtokens.size(); j++) { - note.track = track; - note.line = token->getLineIndex(); - note.field = token->getFieldIndex(); - note.subfield = j; - note.token = token; - note.text = subtokens[j]; - note.duration = Convert::recipToDuration(note.text); - if (nullQ) { - note.attack = false; - note.nullQ = true; + + // At this point the previous note with this pitch class + // had an editorial accidental, and this note also has the + // same accidental, or there was a previous visual accidental + // outside of the key signature that will cause this note to have + // an editorial accidental mark applied (Sibelius will drop + // secondary editorial accidentals in a measure when exporting, + // MusicXML, which is why this function is needed). + + m_estates[track][base7] = true; + m_pstates[track][base7] = accid; + + string text = token->getText(); + HumRegex hre; + hre.replaceDestructive(text, "#", "##+", "g"); + hre.replaceDestructive(text, "-", "--+", "g"); + string output = ""; + bool foundQ = false; + for (int j=0; j<(int)text.size(); j++) { + if (text[j] == 'n') { + output += "ni"; + foundQ = true; + } else if (text[j] == '#') { + output += "#i"; + foundQ = true; + } else if (text[j] == '-') { + output += "-i"; + foundQ = true; } else { - note.nullQ = false; - if ((note.text.find("_") != string::npos) || - (note.text.find("]") != string::npos)) { - note.attack = false; - } else { - note.attack = true; - } + output += text[j]; } - m_notes[line].push_back(note); } - } - // There must be at least three attacks to be considered homorhythm - // maybe adjust to N-1 or three voices, or a similar rule. - vector adurs; - for (int i=0; i<(int)m_notes[line].size(); i++) { - if (m_notes[line][i].attack) { - adurs.push_back(m_notes[line][i].duration); - m_attacks[line]++; + if (foundQ) { + token->setText(output); + continue; } - } - // if ((int)m_attacks[line] >= (int)m_notes[line].size() - 1) { - if ((int)m_attacks[line] >= 3) { - string value = "Y"; - // value += to_string(m_attacks[line]); - m_homorhythm[line] = value; - } else if ((m_voice_count == 3) && (m_attacks[line] == 2)) { - if ((adurs.size() >= 2) && (adurs[0] == adurs[1])) { - m_homorhythm[line] = "Y"; - } else { - m_homorhythm[line] = "N"; + + // The note is natural, but has no natural sign. + // add the natural sign and editorial mark. + for (int j=(int)output.size()-1; j>=0; j--) { + if ((tolower(output[j]) >= 'a') && (tolower(output[j]) <= 'g')) { + output.insert(j+1, "ni"); + break; + } } - } else { - string value = "N"; - // value += to_string(m_attacks[line]); - m_homorhythm[line] = value; - } - // redundant or three-or-more case: - if (m_notes[line].size() <= 2) { - m_homorhythm[line] = "N"; + token->setText(output); } } - -///////////////////////////////// +//////////////////////////////// // -// Tool_homorhythm2::Tool_homorhythm -- Set the recognized options for the tool. +// Tool_gasparize::updateKeySignatures -- Fill in the accidental +// states for each diatonic pitch. // -Tool_homorhythm2::Tool_homorhythm2(void) { - define("t|threshold=d:1.6", "threshold score sum required for homorhythm texture detection"); - define("u|threshold2=d:1.3", "threshold score sum required for semi-homorhythm texture detection"); - define("s|score=b", "show numeric scores"); - define("n|length=i:4", "sonority length to calculate"); - define("f|fraction=b", "report fraction of music that is homorhythm"); -} +void Tool_gasparize::updateKeySignatures(HumdrumFile& infile, int lineindex) { + HumdrumLine& line = infile[lineindex]; + int track; + for (int i=0; iisKeySignature()) { + continue; + } + HTp token = line.token(i); + track = token->getTrack(); + string text = token->getText(); + fill(m_kstates[track].begin(), m_kstates[track].end(), 0); + for (int j=3; j<(int)text.size()-1; j++) { + if (text[j] == ']') { + break; + } + switch (text[j]) { + case 'a': case 'A': + switch (text[j+1]) { + case '#': m_kstates[track][5] = +1; + break; + case '-': m_kstates[track][5] = -1; + break; + } + break; + case 'b': case 'B': + switch (text[j+1]) { + case '#': m_kstates[track][6] = +1; + break; + case '-': m_kstates[track][6] = -1; + break; + } + break; + case 'c': case 'C': + switch (text[j+1]) { + case '#': m_kstates[track][0] = +1; + break; + case '-': m_kstates[track][0] = -1; + break; + } + break; -///////////////////////////////// -// -// Tool_homorhythm2::run -- Do the main work of the tool. -// + case 'd': case 'D': + switch (text[j+1]) { + case '#': m_kstates[track][1] = +1; + break; + case '-': m_kstates[track][1] = -1; + break; + } + break; -bool Tool_homorhythm2::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; itm_year + 1900; + int month = timeptr->tm_mon + 1; + int day = timeptr->tm_mday; + ss << year << "/"; + if (month < 10) { + ss << "0"; } - if (m_threshold < m_threshold2) { - double temp = m_threshold; - m_threshold = m_threshold2; - m_threshold2 = temp; + ss << month << "/"; + if (day < 10) { + ss << "0"; } - + ss << day; + return ss.str(); } ////////////////////////////// // -// Tool_homorhythm2::processFile -- +// Tool_gasparize::fixTies -- +// If a tie is unclosed or if a note is followed by an invisible rest, then fix. // -void Tool_homorhythm2::processFile(HumdrumFile& infile) { - infile.analyzeStructure(); - NoteGrid grid(infile); - m_score.resize(infile.getLineCount()); - fill(m_score.begin(), m_score.end(), 0.0); - - double score; - int count; - int wsize = getInteger("length"); - - for (int i=0; iisRest()) { - continue; - } - NoteCell* cell2 = grid.cell(k, i+m); - if (cell2->isRest()) { - continue; - } - count++; - if (cell1->isAttack() && cell2->isAttack()) { - score += 1.0; - } - } - } +void Tool_gasparize::fixTies(HumdrumFile& infile) { + int strands = infile.getStrandCount(); + for (int i=0; iisKern()) { + continue; + } + HTp send = infile.getStrandEnd(i); + fixTiesForStrand(sstart, send); } + fixTieStartEnd(infile); +} - for (int i=grid.getSliceCount()-1; i>=wsize; i--) { - score = 0; - count = 0; - for (int j=0; jisRest()) { - continue; - } - NoteCell* cell2 = grid.cell(k, i-m); - if (cell2->isRest()) { - continue; - } - count++; - if (cell1->isAttack() && cell2->isAttack()) { - score += 1.0; - } - } - } - } - int index = grid.getLineIndex(i); - m_score[index] += score / count; - } - - - for (int i=0; i<(int)m_score.size(); i++) { - m_score[i] = int(m_score[i] * 100.0 + 0.5) / 100.0; - } - vector color(infile.getLineCount());; - for (int i=0; i= m_threshold) { - color[i] = "red"; - } else if (m_score[i] >= m_threshold2) { - color[i] = "orange"; - } else { - color[i] = "black"; + if (!sstart->isKern()) { + continue; } + HTp send = infile.getStrandEnd(i); + fixTiesStartEnd(sstart, send); } +} - if (getBoolean("fraction")) { - HumNum sum = 0; - HumNum total = infile.getScoreDuration(); - for (int i=0; i<(int)m_score.size(); i++) { - if (m_score[i] >= m_threshold2) { - sum += infile[i].getDuration(); - } + + +void Tool_gasparize::fixTiesStartEnd(HTp starts, HTp ends) { + HTp current = starts; + HumRegex hre; + while (current) { + if (!current->isData()) { + current = current->getNextToken(); + continue; } - HumNum fraction = sum / total; - m_free_text << int(fraction.getFloat() * 1000.0 + 0.5) / 10.0 << endl; - } else { - if (getBoolean("score")) { - infile.appendDataSpine(m_score, ".", "**cdata", false); + if ((current->find('[') != string::npos) && + (current->find(']') != string::npos) && + (current->find(' ') == string::npos)) { + string text = *current; + hre.replaceDestructive(text, "", "\\[", "g"); + hre.replaceDestructive(text, "_", "\\]", "g"); + current->setText(text); } - infile.appendDataSpine(color, ".", "**color", true); - infile.createLinesFromTokens(); - - // problem within emscripten-compiled version, so force to output as string: - m_humdrum_text << infile; + current = current->getNextToken(); } - } - - - - -///////////////////////////////// +////////////////////////////// // -// Tool_gridtest::Tool_hproof -- Set the recognized options for the tool. +// Tool_gasparize::fixTiesForStrand -- // -Tool_hproof::Tool_hproof(void) { - // put option definitions here +void Tool_gasparize::fixTiesForStrand(HTp sstart, HTp send) { + if (!sstart) { + return; + } + HTp current = sstart; + HTp last = NULL; + current = current->getNextToken(); + while (current) { + if (!current->isData()) { + current = current->getNextToken(); + continue; + } + if (current->isNull()) { + current = current->getNextToken(); + continue; + } + if (last == NULL) { + last = current; + current = current->getNextToken(); + continue; + } + if (current->find("yy") != string::npos) { + fixTieToInvisibleRest(last, current); + } else if (((last->find("[") != string::npos) || (last->find("_") != string::npos)) + && ((current->find("]") == string::npos) && (current->find("_") == string::npos))) { + fixHangingTie(last, current); + } + last = current; + current = current->getNextToken(); + } } -/////////////////////////////// +////////////////////////////// // -// Tool_hproof::run -- Primary interfaces to the tool. +// Tool_gasparize::fixTieToInvisibleRest -- // -bool Tool_hproof::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; ifind("yy") == string::npos) { + return; } - return status; -} - -bool Tool_hproof::run(const string& indata, ostream& out) { - HumdrumFile infile(indata); - return run(infile, out); + if ((first->find("[") == string::npos) && (first->find("_") == string::npos)) { + string ftext = *first; + ftext = "[" + ftext; + first->setText(ftext); + } + HumRegex hre; + if (!hre.search(first, "([A-Ga-g#n-]+)")) { + return; + } + string pitch = hre.getMatch(1); + pitch += "]"; + string text = *second; + hre.replaceDestructive(text, pitch, "ryy"); + second->setText(text); } -bool Tool_hproof::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - out << infile; - return status; -} +////////////////////////////// +// +// Tool_gasparize::fixHangingTie -- Not dealing with chain of missing ties. +// -bool Tool_hproof::run(HumdrumFile& infile) { - markNonChordTones(infile); - infile.appendLine("!!!RDF**kern: N = marked note, color=chocolate (non-chord tone)"); - infile.appendLine("!!!RDF**kern: Z = marked note, color=black (chord tone)"); - infile.createLinesFromTokens(); - return true; +void Tool_gasparize::fixHangingTie(HTp first, HTp second) { + string text = *second; + text += "]"; + second->setText(text); } ////////////////////////////// // -// Tool_hproof::markNonChordTones -- Mark +// Tool_gasparize::addMensurations -- Add mensurations. // -void Tool_hproof::markNonChordTones(HumdrumFile& infile) { - vector list; - infile.getSpineStartList(list); - vector hlist; - for (auto it : list) { - if (*it == "**harm") { - hlist.push_back(it); +void Tool_gasparize::addMensurations(HumdrumFile& infile) { + HumRegex hre; + for (int i=infile.getLineCount() - 1; i>=0; i--) { + if (!infile[i].isInterpretation()) { + continue; } - if (*it == "**rhrm") { - hlist.push_back(it); + for (int j=0; jgetNextNNDT(); - while (token) { - markNotesInRange(infile, token, ntoken, key); - if (!ntoken) { - break; +void Tool_gasparize::addMensuration(int top, HumdrumFile& infile, int index) { + HTp checktoken = infile[index+1].token(0); + if (!checktoken) { + return; + } + if (checktoken->find("met") != string::npos) { + return; + } + int fieldcount = infile[index].getFieldCount(); + string line = "*"; + HTp token = infile[index].token(0); + if (token->isKern()) { + if (top == 2) { + line += "met(C|)"; + } else { + line += "met(O)"; } - if (ntoken && token) { - getNewKey(token, ntoken, key); + } + for (int i=1; iisKern()) { + if (top == 2) { + line += "met(C|)"; + } else { + line += "met(O)"; + } } - token = ntoken; - ntoken = ntoken->getNextNNDT(); } + infile.insertLine(index+1, line); } - -////////////////////////////// +/////////////////////////////// // -// Tool_hproof::getNewKey -- +// Tool_gasparize::createEditText -- Convert markers into *edit interps. // -void Tool_hproof::getNewKey(HTp token, HTp ntoken, string& key) { - token = token->getNextToken(); - while (token && (token != ntoken)) { - if (token->isKeyDesignation()) { - key = *token; +void Tool_gasparize::createEditText(HumdrumFile& infile) { + // previous process manipulated the structure so reanalyze here for now: + infile.analyzeBaseFromTokens(); + infile.analyzeStructureNoRhythm(); + + int strands = infile.getStrandCount(); + for (int i=0; iisDataType("**text")) { + continue; + } + HTp send = infile.getStrandEnd(i); + bool status = addEditStylingForText(infile, sstart, send); + if (status) { + infile.analyzeBaseFromTokens(); + infile.analyzeStructureNoRhythm(); } - token = token->getNextToken(); } } - ////////////////////////////// // -// Tool_hproof::markNotesInRange -- +// Tool_gasparize::addEditStylingForText -- // -void Tool_hproof::markNotesInRange(HumdrumFile& infile, HTp ctoken, HTp ntoken, const string& key) { - if (!ctoken) { - return; - } - int startline = ctoken->getLineIndex(); - int stopline = infile.getLineCount(); - if (ntoken) { - stopline = ntoken->getLineIndex(); - } - vector cts; - cts = Convert::harmToBase40(ctoken, key); - for (int i=startline; igetPreviousToken(); + bool output = false; + string state = ""; + string laststate = ""; + HumRegex hre; + HTp lastdata = NULL; + bool italicQ = false; + while (current && (current != sstart)) { + if (!current->isData()) { + current = current->getPreviousToken(); continue; } - for (int j=0; jisKern()) { - continue; + if (current->isNull()) { + current = current->getPreviousToken(); + continue; + } + italicQ = false; + string text = current->getText(); + if (text.find("") != string::npos) { + italicQ = true; + hre.replaceDestructive(text, "", "", "g"); + hre.replaceDestructive(text, "", "", "g"); + current->setText(text); + } else { +} + if (laststate == "") { + if (italicQ) { + laststate = "italic"; + } else { + laststate = "regular"; } - HTp tok = infile.token(i, j); - if (tok->isNull()) { - continue; + current = current->getPreviousToken(); + continue; + } else { + if (italicQ) { + state = "italic"; + } else { + state = "regular"; } - if (tok->isRest()) { - continue; + } + if (state != laststate) { + if (lastdata && (laststate == "italic")) { + output = true; + if (!insertEditText("*edit", infile, lastdata->getLineIndex() - 1, lastdata->getFieldIndex())) { + string line = getEditLine("*edit", lastdata->getFieldIndex(), lastdata->getOwner()); + infile.insertLine(lastdata->getLineIndex(), line); + } + } else if (lastdata && (laststate == "regular")) { + output = true; + if (!insertEditText("*Xedit", infile, lastdata->getLineIndex() - 1, lastdata->getFieldIndex())) { + string line = getEditLine("*Xedit", lastdata->getFieldIndex(), lastdata->getOwner()); + infile.insertLine(lastdata->getLineIndex(), line); + } } - markHarmonicTones(tok, cts); } + laststate = state; + lastdata = current; + current = current->getPreviousToken(); } -// cerr << "TOK\t" << ctoken << "\tLINES\t" << startline << "\t" << stopline << "\t"; -// for (int i=0; igetLineIndex() - 1, lastdata->getFieldIndex())) { + string line = getEditLine("*edit", lastdata->getFieldIndex(), lastdata->getOwner()); + infile.insertLine(lastdata->getLineIndex(), line); + } + } + return output; } ////////////////////////////// // -// Tool_hproof::markHarmonicTones -- +// Tool_gasparize::insertEditText -- // -void Tool_hproof::markHarmonicTones(HTp tok, vector& cts) { - int count = tok->getSubtokenCount(); - vector notes = cts; - string output; - for (int i=0; igetSubtoken(i); - int pitch = Convert::kernToBase40(subtok); - if (i > 0) { - output += " "; - } - bool found = false; - for (int j=0; j<(int)cts.size(); j++) { - if (pitch % 40 == cts[j] % 40) { - output += subtok; - output += "Z"; - found = true; - break; - } +bool Tool_gasparize::insertEditText(const string& text, HumdrumFile& infile, int line, int field) { + if (!infile[line].isInterpretation()) { + return false; + } + HTp token; + for (int i=0; iisNull()) { + continue; } - if (!found) { - output += subtok; - output += "N"; + if (token->find("edit") != string::npos) { + break; } + return false; } - tok->setText(output); -} - - - - - -///////////////////////////////// -// -// Tool_humbreak::Tool_humbreak -- Set the recognized options for the tool. -// + token = infile.token(line, field); + token->setText(text); -Tool_humbreak::Tool_humbreak(void) { - define("m|measures=s", "measures numbers to place linebreaks before"); - define("p|page-breaks=s", "measure numbers to place page breaks before"); - define("g|group=s:original", "line/page break group"); - define("r|remove|remove-breaks=b", "remove line/page breaks"); - define("l|page-to-line-breaks=b", "convert page breaks to line breaks"); + return true; } -///////////////////////////////// +///////////////////// // -// Tool_humbreak::run -- Do the main work of the tool. +// Tool_gasparize::getEditLine -- // -bool Tool_humbreak::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; igetFieldCount()) { + output += "\t"; + } } - return status; -} - - -bool Tool_humbreak::run(const string& indata, ostream& out) { - HumdrumFile infile(indata); - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; + output += text; + if (fieldindex < line->getFieldCount()) { + output += "\t"; } - return status; -} - - -bool Tool_humbreak::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; + for (int i=fieldindex+1; igetFieldCount(); i++) { + output += "*"; + if (i < line->getFieldCount()) { + output += "\t"; + } } - return status; -} - - -bool Tool_humbreak::run(HumdrumFile& infile) { - processFile(infile); - return true; + return output; } ////////////////////////////// // -// Tool_humbreak::initialize -- Initializations that only have to be done once -// for all HumdrumFile segments. +// adjustIntrumentNames -- // -void Tool_humbreak::initialize(void) { - string systemMeasures = getString("measures"); - string pageMeasures = getString("page-breaks"); - m_group = getString("group"); - m_removeQ = getBoolean("remove-breaks"); - m_page2lineQ = getBoolean("page-to-line-breaks"); - - vector lbs; - vector pbs; - HumRegex hre; - hre.split(lbs, systemMeasures, "[^\\da-z]+"); - hre.split(pbs, pageMeasures, "[^\\da-z]+"); - - for (int i=0; i<(int)lbs.size(); i++) { - if (hre.search(lbs[i], "^(p?)(\\d+)([a-z]?)")) { - int number = hre.getMatchInt(2); - if (!hre.getMatch(1).empty()) { - m_pageMeasures[number] = 1; - int offset = 0; - string letter; - if (!hre.getMatch(3).empty()) { - letter = hre.getMatch(3); - offset = letter.at(0) - 'a'; - } - m_pageOffset[number] = offset; - } else { - m_lineMeasures[number] = 1; - int offset = 0; - if (!hre.getMatch(3).empty()) { - string letter = hre.getMatch(3); - offset = letter.at(0) - 'a'; - } - m_lineOffset[number] = offset; - } +void Tool_gasparize::adjustIntrumentNames(HumdrumFile& infile) { + int instrumentLine = -1; + int abbrLine = -1; + for (int i=0; icompare(0, 3, "*I\"") == 0) { + instrumentLine = i; } - m_pageOffset[number] = offset; + if (token->compare(0, 3, "*I'") == 0) { + abbrLine = i; + } + } + } + if (instrumentLine < 0) { + return; + } + for (int i=0; isetText("*I\"Contratenor 1"); + } else if (*token == "*I\"CTI") { + token->setText("*I\"Contratenor 1"); + } else if (*token == "*I\"CTII") { + token->setText("*I\"Contratenor 2"); + } else if (*token == "*I\"CT II") { + token->setText("*I\"Contratenor 2"); + } else if (*token == "*I\"CT") { + token->setText("*I\"Contratenor"); + } else if (*token == "*I\"S") { + token->setText("*I\"Superius"); + } else if (*token == "*I\"A") { + token->setText("*I\"Altus"); + } else if (*token == "*I\"T") { + token->setText("*I\"Tenor"); + } else if (*token == "*I\"B") { + token->setText("*I\"Bassus"); + } else if (*token == "*I\"V") { + token->setText("*I\"Quintus"); + } else if (*token == "*I\"VI") { + token->setText("*I\"Sextus"); + } + } + if (abbrLine >= 0) { + return; + } + string abbr; + HumRegex hre; + for (int i=0; i pbreak; - vector lbreak; +void Tool_gasparize::removeKeyDesignations(HumdrumFile& infile) { HumRegex hre; - map used; - - for (int i=0; i=0; i--) { + if (!infile[i].isInterpretation()) { continue; } - - int status = m_lineMeasures[barnum]; - if (status) { - HLp line = &infile[i]; - int offset = m_lineOffset[barnum]; - if (offset && (used[barnum] == 0)) { - used[barnum] = offset; - int ocounter = 0; - lbreak.clear(); - pbreak.clear(); - for (int j=i+1; jsetValue("auto", "barnum", barnum + 1); - } else { - line->setValue("auto", "barnum", barnum + 1); - } - } else { - line->setValue("auto", "barnum", barnum + 1); + for (int j=0; jsetValue("auto", "barnum", barnum + 1); - pbreak.back()->setValue("auto", "page", 1); - } - } else { - line->setValue("auto", "barnum", barnum + 1); - line->setValue("auto", "page", 1); + if (!token->isKern()) { + continue; + } + if (hre.search(token, "^\\*[A-Ga-g][#n-]*:$")) { + // suppress the key desingation + infile.deleteLine(i); + break; } } } -} +} ////////////////////////////// // -// Tool_humbreak::addBreaks -- +// Tool_gasparize::fixBarlines -- Add final double barline and convert +// any intermediate final barlines to double barlines. // -void Tool_humbreak::addBreaks(HumdrumFile& infile) { - markLineBreakMeasures(infile); - +void Tool_gasparize::fixBarlines(HumdrumFile& infile) { + fixFinalBarline(infile); HumRegex hre; + for (int i=0; iisBarline()) { - int measure = infile[i+1].getBarNumber(); - int pbStatus = m_pageMeasures[measure]; - if (pbStatus) { - string query = "\\b" + m_group + "\\b"; - if (!hre.match(token, query)) { - m_humdrum_text << token << ", " << m_group << endl; - } else { - m_humdrum_text << token << endl; - } - } else { - m_humdrum_text << token << endl; - } - m_humdrum_text << infile[i+1] << endl; - i++; - continue; - } - } else if (hre.search(token, "^!!LO:LB:")) { - // Add group to existing LO:LB: - HTp token = infile.token(i, 0); - HTp barToken = infile.token(i+1, 0); - if (barToken->isBarline()) { - int measure = infile[i+1].getBarNumber(); - int lbStatus = m_lineMeasures[measure]; - if (lbStatus) { - string query = "\\b" + m_group + "\\b"; - if (!hre.match(token, query)) { - m_humdrum_text << token << ", " << m_group << endl; - } else { - m_humdrum_text << token << endl; - } - } else { - m_humdrum_text << token << endl; - } - m_humdrum_text << infile[i+1] << endl; - i++; - continue; - } + for (int j=0; jfind("==") == string::npos) { + continue; + } + if (hre.search(token, "^==(\\d*)")) { + string text = "="; + text += hre.getMatch(1); + text += "||"; + token->setText(text); } } - - if (pageQ) { - m_humdrum_text << "!!LO:PB:g=" << m_group << endl; - } else { - m_humdrum_text << "!!LO:LB:g=" << m_group << endl; - } - m_humdrum_text << infile[i] << endl; } } @@ -87470,17 +88624,23 @@ void Tool_humbreak::addBreaks(HumdrumFile& infile) { ////////////////////////////// // -// Tool_humbreak::processFile -- +// Tool_gasparize::fixFinalBarline -- // -void Tool_humbreak::processFile(HumdrumFile& infile) { - initialize(); - if (m_removeQ) { - removeBreaks(infile); - } else if (m_page2lineQ) { - convertPageToLine(infile); - } else { - addBreaks(infile); +void Tool_gasparize::fixFinalBarline(HumdrumFile& infile) { + for (int i=infile.getLineCount() - 1; i>=0; i--) { + if (infile[i].isData()) { + break; + } + if (!infile[i].isBarline()) { + continue; + } + for (int j=0; jsetText("=="); + } + } } } @@ -87488,113 +88648,133 @@ void Tool_humbreak::processFile(HumdrumFile& infile) { ////////////////////////////// // -// Tool_humbreak::removeBreaks -- +// Tool_gasparize::createJEditorialAccidentals -- +// convert +// !LO:TX:a:t=( ) +// 4F# // -void Tool_humbreak::removeBreaks(HumdrumFile& infile) { - for (int i=0; icompare(0, 7, "!!LO:LB") == 0) { +void Tool_gasparize::createJEditorialAccidentals(HumdrumFile& infile) { + int strands = infile.getStrandCount(); + for (int i=0; icompare(0, 7, "!!LO:PB") == 0) { + if (!sstart->isKern()) { continue; } - m_humdrum_text << infile[i] << endl; + HTp send = infile.getStrandEnd(i); + createJEditorialAccidentals(sstart, send); } } +void Tool_gasparize::createJEditorialAccidentals(HTp sstart, HTp send) { + HTp current = sstart->getNextToken(); + HumRegex hre; + while (current && (current != send)) { + if (!current->isCommentLocal()) { + current = current->getNextToken(); + continue; + } + if (hre.search(current, "^!LO:TX:a:t=\\(\\s*\\)$")) { + current->setText("!"); + convertNextNoteToJAccidental(current); + } + current = current->getNextToken(); + } +} - -////////////////////////////// -// -// Tool_humbreak::convertPageToLine -- -// - -void Tool_humbreak::convertPageToLine(HumdrumFile& infile) { +void Tool_gasparize::convertNextNoteToJAccidental(HTp current) { + current = current->getNextToken(); HumRegex hre; - for (int i=0; icompare(0, 7, "!!LO:PB") == 0) { - string text = *infile[i].token(0); - hre.replaceDestructive(text, "!!LO:LB", "!!LO:PB"); - m_humdrum_text << text << endl; + while (current) { + if (!current->isData()) { + // Does not handle LO for non-data. + current = current->getNextToken(); continue; } - m_humdrum_text << infile[i] << endl; + if (current->isNull()) { + break; + } + if (current->isRest()) { + break; + } + string text = *current; + if (hre.search(text, "i")) { + hre.replaceDestructive(text, "j", "i"); + current->setText(text); + break; + } else if (hre.search(text, "[-#n]")) { + hre.replaceDestructive(text, "$1j", "(.*[-#n]+)"); + current->setText(text); + break; + } else { + // Need to add a natural sign as well. + hre.replaceDestructive(text, "$1nj", "(.*[A-Ga-g]+)"); + current->setText(text); + break; + } + break; } + current = current->getNextToken(); } + ///////////////////////////////// // -// Tool_humdiff::Tool_humdiff -- Set the recognized options for the tool. +// Tool_grep::Tool_grep -- Set the recognized options for the tool. // -Tool_humdiff::Tool_humdiff(void) { - define("r|reference=i:1", "sequence number of reference score"); - define("report=b", "display report of differences"); - define("time-points|times=b", "display timepoint lists for each file"); - define("note-points|notes=b", "display notepoint lists for each file"); - define("c|color=s:red", "color for difference markers"); +Tool_grep::Tool_grep(void) { + define("v|remove-matching-lines=b", "remove lines that match regex"); + define("e|regex|regular-expression=s", "regular expression to search with"); } - -////////////////////////////// +///////////////////////////////// // -// Tool_humdiff::run -- +// Tool_grep::run -- Do the main work of the tool. // -bool Tool_humdiff::run(HumdrumFileSet& infiles) { - int reference = getInteger("reference") - 1; - if (reference < 0) { - cerr << "Error: reference has to be 1 or higher" << endl; - return false; - } - if (reference > infiles.getCount()) { - cerr << "Error: reference number is too large: " << reference << endl; - cerr << "Maximum is " << infiles.getCount() << endl; - return false; +bool Tool_grep::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; i> timepoints(2); - extractTimePoints(timepoints.at(0), reference); - extractTimePoints(timepoints.at(1), alternate); - - if (getBoolean("time-points")) { - printTimePoints(timepoints[0]); - printTimePoints(timepoints[1]); - } - - compareTimePoints(timepoints, reference, alternate); +void Tool_grep::initialize(void) { + m_negateQ = getBoolean("remove-matching-lines"); + m_regex = getString("regular-expression"); } ////////////////////////////// // -// Tool_humdiff::printTimePoints -- +// Tool_grep::processFile -- // -void Tool_humdiff::printTimePoints(vector& timepoints) { - for (int i=0; i<(int)timepoints.size(); i++) { - m_free_text << "TIMEPOINT " << i << ":" << endl; - m_free_text << timepoints[i] << endl; +void Tool_grep::processFile(HumdrumFile& infile) { + HumRegex hre; + bool match; + for (int i=0; i>& timepoints, - HumdrumFile& reference, HumdrumFile& alternate) { - vector indexes(timepoints.size(), 0); - HumNum minval; - HumNum value; - int found; +Tool_half::Tool_half(void) { + define("l|lyric-beam-break=b", "Break beams at syllable starts"); +} - vector infiles(2, NULL); - infiles[0] = &reference; - infiles[1] = &alternate; - vector increment(timepoints.size(), 0); - while ((1)) { - if (indexes.at(0) >= (int)timepoints.at(0).size()) { - // at the end of the list of notes for the first file. - // break from the comparison for now and figure out how - // to report differences of added notes in the other file(s) - // later. - break; - } - timepoints.at(0).at(indexes.at(0)).index.resize(timepoints.size()); - for (int i=1; i<(int)timepoints.size(); i++) { - timepoints.at(0).at(indexes.at(0)).index.at(i) = -1; - } - minval = timepoints.at(0).at(indexes.at(0)).timestamp; - for (int i=1; i<(int)timepoints.size(); i++) { - if (indexes.at(i) >= (int)timepoints.at(i).size()) { - continue; - } - value = timepoints.at(i).at(indexes.at(i)).timestamp; - if (value < minval) { - minval = value; - } - } - found = 0; - fill(increment.begin(), increment.end(), 0); - - for (int i=0; i<(int)timepoints.size(); i++) { - if (indexes.at(i) >= (int)timepoints.at(i).size()) { - // index is too large for file, so skip checking it. - continue; - } - found = 1; - value = timepoints.at(i).at(indexes.at(i)).timestamp; - - if (value == minval) { - timepoints.at(0).at(indexes.at(0)).index.at(i) = timepoints.at(i).at(indexes.at(i)).index.at(0); - increment.at(i)++; - } - } - if (!found) { - break; - } else { - compareLines(minval, indexes, timepoints, infiles); - } - for (int i=0; i<(int)increment.size(); i++) { - indexes.at(i) += increment.at(i); - } - } -} - - - -////////////////////////////// +///////////////////////////////// // -// Tool_humdiff::printNotePoints -- +// Tool_half::run -- Primary interfaces to the tool. // -void Tool_humdiff::printNotePoints(vector& notelist) { - m_free_text << "vvvvvvvvvvvvvvvvvvvvvvvvv" << endl; - for (int i=0; i<(int)notelist.size(); i++) { - m_free_text << "NOTE " << i << endl; - m_free_text << notelist.at(i) << endl; +bool Tool_half::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; iisChord()) { - string contents = *token; - contents += "@"; - token->setText(contents); - return; - } - vector tokens = token->getSubtokens(); - tokens[np.subindex] += "@"; - string output = tokens[0]; - for (int i=1; i<(int)tokens.size(); i++) { - output += " "; - output += tokens[i]; +bool Tool_half::run(const string& indata, ostream& out) { + HumdrumFile infile(indata); + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; } - token->setText(output); + return status; } +bool Tool_half::run(HumdrumFile& infile, ostream& out) { + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; + } + return status; +} -////////////////////////////// // -// Tool_humdiff::compareLines -- +// In-place processing of file: // -void Tool_humdiff::compareLines(HumNum minval, vector& indexes, - vector>& timepoints, vector infiles) { - - bool reportQ = getBoolean("report"); - - // cerr << "COMPARING LINES ====================================" << endl; - vector> notelist(indexes.size()); - - // Note: timepoints size must be 2 - // and infiles size must be 2 - for (int i=0; i<(int)timepoints.size(); i++) { - if (indexes.at(i) >= (int)timepoints.at(i).size()) { - continue; - } - if (timepoints.at(i).at(indexes.at(i)).timestamp != minval) { - // not at the same time - continue; - } - - getNoteList(notelist.at(i), *infiles[i], - timepoints.at(i).at(indexes.at(i)).index[0], - timepoints.at(i).at(indexes.at(i)).measure, i, indexes.at(i)); - - - } - for (int i=0; i<(int)notelist.at(0).size(); i++) { - notelist.at(0).at(i).matched.resize(notelist.size()); - fill(notelist.at(0).at(i).matched.begin(), notelist.at(0).at(i).matched.end(), -1); - notelist.at(0).at(i).matched.at(0) = i; - for (int j=1; j<(int)notelist.size(); j++) { - int status = findNoteInList(notelist.at(0).at(i), notelist.at(j)); - notelist.at(0).at(i).matched.at(j) = status; - if ((status < 0) && !reportQ) { - markNote(notelist.at(0).at(i)); - } - } - } +bool Tool_half::run(HumdrumFile& infile) { + processFile(infile); - if (getBoolean("notes")) { - for (int i=0; i<(int)notelist.size(); i++) { - cerr << "========== NOTES FOR I=" << i << endl; - printNotePoints(notelist.at(i)); - cerr << endl; - } - } + // Re-load the text for each line from their tokens. + infile.createLinesFromTokens(); - if (!reportQ) { - return; - } + // Need to adjust the line numbers for tokens for later + // processing. + m_humdrum_text << infile; + return true; +} - // report - for (int i=0; i<(int)notelist.at(0).size(); i++) { - for (int j=1; j<(int)notelist.at(0).at(i).matched.size(); j++) { - if (notelist.at(0).at(i).matched.at(j) < 0) { - cout << "NOTE " << notelist.at(0).at(i).subtoken - << " DOES NOT HAVE EXACT MATCH IN SOURCE " << j << endl; - int humindex = notelist.at(0).at(i).token->getLineIndex(); - cout << "\tREFERENCE MEASURE\t: " << notelist.at(0).at(i).measure << endl; - cout << "\tREFERENCE LINE NO.\t: " << humindex+1 << endl; - cout << "\tREFERENCE LINE TEXT\t: " << (*infiles[0])[humindex] << endl; - cout << "\tTARGET " << j << " LINE NO. "; - if (j < 10) { - cout << " "; - } - cout << ":\t" << "X" << endl; - cout << "\tTARGET " << j << " LINE TEXT"; - if (j < 10) { - cout << " "; - } - cout << ":\t" << "X" << endl; +////////////////////////////// +// +// Tool_half::processFile -- +// - cout << endl; - } - } - } +void Tool_half::processFile(HumdrumFile& infile) { + m_lyricBreakQ = getBoolean("lyric-beam-break"); + terminalLongToTerminalBreve(infile); + halfRhythms(infile); + adjustBeams(infile); } ////////////////////////////// // -// Tool_humdiff::findNoteInList -- +// Tool_half::adjustBeams -- // -int Tool_humdiff::findNoteInList(NotePoint& np, vector& nps) { - for (int i=0; i<(int)nps.size(); i++) { - // cerr << "COMPARING " << np.token << " (" << np.b40 << ") TO " << nps.at(i).token << " (" << nps.at(i).b40 << ") " << endl; - if (nps.at(i).processed) { - continue; - } - if (nps.at(i).b40 != np.b40) { - continue; - } - if (nps.at(i).duration != np.duration) { - continue; - } - return i; +void Tool_half::adjustBeams(HumdrumFile& infile) { + Tool_autobeam autobeam; + vector argv; + argv.push_back("autobeam"); + if (m_lyricBreakQ) { + argv.push_back("-l"); } - // cerr << "\tCannot find note " << np.token << " on line " << np.token->getLineIndex() << " in other work" << endl; - return -1; + autobeam.process(argv); + autobeam.run(infile); } - ////////////////////////////// // -// Tool_humdiff::getNoteList -- +// Tool_half::halfRhythms -- // -void Tool_humdiff::getNoteList(vector& notelist, HumdrumFile& infile, int line, int measure, int sourceindex, int tpindex) { - for (int i=0; iisKern()) { - continue; - } - if (token->isNull()) { - continue; - } - if (token->isRest()) { - continue; - } - int scount = token->getSubtokenCount(); - int track = token->getTrack(); - int layer = token->getSubtrack(); - for (int j=0; jgetSubtoken(j); - if (subtok.find("]") != string::npos) { - continue; +void Tool_half::halfRhythms(HumdrumFile& infile) { + HumRegex hre; + for (int i=0; iisKern()) { + continue; + } + if (token->isNull()) { + continue; + } + + string text = *token; + // extract duration without dot + HumNum durnodot = Convert::recipToDurationNoDots(text); + durnodot /= 2; + string newrhythm = Convert::durationToRecip(durnodot); + hre.replaceDestructive(text, newrhythm, "\\d+%?\\d*"); + token->setText(text); } - if (subtok.find("_") != string::npos) { - continue; + } else if (infile[i].isInterpretation()) { + // half time signatures + for (int j=0; jsetText(text); + } else { + string text = *token; + string replacement = "/" + to_string(bot1); + replacement += "%" + to_string(bot2); + hre.replaceDestructive(text, replacement, "/\\d+"); + token->setText(text); + } + } else if (hre.search(token, "^\\*M(\\d+)/(\\d+)")) { + int bot = hre.getMatchInt(2); + if (bot == 4) { + bot = 8; + } else if (bot == 2) { + bot = 4; + } else if (bot == 3) { + bot = 6; + } else if (bot == 1) { + bot = 2; + } else if (bot == 0) { + bot = 1; + } else { + cerr << "Warning: ignored time signature: " << token << endl; + } + string text = *token; + string replacement = "/" + to_string(bot); + hre.replaceDestructive(text, replacement, "/\\d+"); + token->setText(text); + } } - // found a note to store; - notelist.resize(notelist.size() + 1); - notelist.back().token = token; - notelist.back().subtoken = subtok; - notelist.back().subindex = j; - notelist.back().measurequarter = token->getDurationFromBarline(); - notelist.back().measure = - notelist.back().track = track; - notelist.back().layer = layer; - notelist.back().sourceindex = sourceindex; - notelist.back().tpindex = tpindex; - notelist.back().duration = token->getTiedDuration(); - notelist.back().b40 = Convert::kernToBase40(subtok); } } } @@ -87910,113 +88995,69 @@ void Tool_humdiff::getNoteList(vector& notelist, HumdrumFile& infile, ////////////////////////////// // -// Tool_humdiff::extractTimePoints -- Extract a list of the timestamps in a file. +// Tool_half::terminalLongToTerminalBreve -- // -void Tool_humdiff::extractTimePoints(vector& points, HumdrumFile& infile) { - TimePoint tp; - points.clear(); +void Tool_half::terminalLongToTerminalBreve(HumdrumFile& infile) { HumRegex hre; - points.reserve(infile.getLineCount()); - int measure = -1; for (int i=0; ifind("terminal long") == string::npos) { continue; } - tp.clear(); - tp.file.push_back(&infile); - tp.index.push_back(i); - tp.timestamp = infile[i].getDurationFromStart(); - tp.measure = measure; - points.push_back(tp); + string text = *token; + hre.replaceDestructive(text, "terminal breve", "terminal long", "g"); + token->setText(text); } } -////////////////////////////// -// -// operator<< == print a TimePoint -// - -ostream& operator<<(ostream& out, TimePoint& tp) { - out << "\ttimestamp:\t" << tp.timestamp.getFloat() << endl; - out << "\tmeasure:\t" << tp.measure << endl; - out << "\tindexes:\t" << endl; - for (int i=0; i<(int)tp.index.size(); i++) { - out << "\t\tindex " << i << " is:\t" << tp.index[i] << "\t" << (*tp.file[i])[tp.index[i]] << endl; - } - return out; -} - - -////////////////////////////// +///////////////////////////////// // -// operator<< == print a NotePoint +// Tool_hands::Tool_hands -- Set the recognized options for the tool. // -ostream& operator<<(ostream& out, NotePoint& np) { - if (np.token) { - out << "\ttoken:\t\t" << np.token << endl; - } - out << "\ttoken index:\t" << np.subindex << endl; - if (!np.subtoken.empty()) { - out << "\tsubtoken:\t" << np.subtoken << endl; - } - out << "\tmeasure:\t" << np.measure << endl; - out << "\tmquarter:\t" << np.measurequarter << endl; - out << "\ttrack:\t\t" << np.track << endl; - out << "\tlayer:\t\t" << np.layer << endl; - out << "\tduration:\t" << np.duration << endl; - out << "\tb40:\t\t" << np.b40 << endl; - out << "\tprocessed:\t" << np.processed << endl; - out << "\tsourceindex:\t" << np.sourceindex << endl; - out << "\ttpindex:\t" << np.tpindex << endl; - out << "\tmatched:\t" << endl; - for (int i=0; i<(int)np.matched.size(); i++) { - out << "\t\tindex " << i << " is:\t" << np.matched[i] << endl; - } - return out; +Tool_hands::Tool_hands(void) { + define("c|color=b", "color right-hand notes red and left-hand notes blue"); + define("lcolor|left-color=s:dodgerblue", "color of left-hand notes"); + define("rcolor|right-color=s:crimson", "color of right-hand notes"); + define("l|left-only=b", "remove right-hand notes"); + define("r|right-only=b", "remove left-hand notes"); + define("m|mark=b", "mark left and right-hand notes"); + define("a|attacks-only=b", "only mark note attacks and not note sustains"); } - - -///////////////////////////////// +////////////////////////////// // -// Tool_humsheet::Tool_humsheet -- Set the recognized options for the tool. +// Tool_hands::initialize -- Initializations that only have to be done once +// for all HumdrumFile segments. // -Tool_humsheet::Tool_humsheet(void) { - define("h|H|html|HTML=b", "output table in HTML wrapper"); - define("i|id|ID=b", "include ID for each cell"); - define("z|zebra=b", "add zebra striping by spine to style"); - define("y|z2|zebra2|zebra-2=b", "zebra striping by data type"); - define("t|tab-index=b", "vertical tab indexing"); - define("X|no-exinterp=b", "do not embed exclusive interp data"); - define("J|no-javascript=b", "do not embed javascript code"); - define("S|no-style=b", "do not embed CSS style element"); +void Tool_hands::initialize(void) { + m_colorQ = getBoolean("color"); + m_leftColor = getString("left-color"); + m_rightColor = getString("right-color"); + m_leftOnlyQ = getBoolean("left-only"); + m_rightOnlyQ = getBoolean("right-only"); + m_markQ = getBoolean("mark"); + m_attacksOnlyQ = getBoolean("attacks-only"); } ///////////////////////////////// // -// Tool_humsheet::run -- Do the main work of the tool. +// Tool_hands::run -- Do the main work of the tool. // -bool Tool_humsheet::run(HumdrumFileSet& infiles) { +bool Tool_hands::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i\n"; - for (int i=0; i"; - printRowContents(infile, i); - m_free_text << "\n"; - } - m_free_text << ""; - if (m_htmlQ) { - if (m_javascriptQ) { - printJavascript(); +void Tool_hands::removeNotes(HumdrumFile& infile, const string& htype) { + int counter = 0; + int scount = infile.getStrandCount(); + for (int i=0; igetExclusiveInterpretation(); + int hasHandMarkup = xtok->getValueInt("auto", "hand"); + if (!hasHandMarkup) { + continue; } - printHtmlFooter(); + HTp send = infile.getStrandEnd(i); + removeNotes(sstart, send, htype); + counter++; } -} + + if (counter) { + infile.createLinesFromTokens(); + } +} -////////////////////////////// -// -// Tool_humsheet::printTitle -- -// +void Tool_hands::removeNotes(HTp sstart, HTp send, const string& htype) { + HTp current = sstart; + while (current && (current != send)) { + if (!current->isData() || current->isNull()) { + current = current->getNextToken(); + continue; + } -void Tool_humsheet::printTitle(HumdrumFile& infile, int line) { - if (!infile[line].isReference()) { - return; - } - string meaning = Convert::getReferenceKeyMeaning(infile[line].token(0)); - if (!meaning.empty()) { - m_free_text << " title=\"" << meaning << "\""; + HumRegex hre; + string ttype = current->getValue("auto", "hand"); + if (ttype != htype) { + current = current->getNextToken(); + continue; + } + string text = *current; + hre.replaceDestructive(text, "", "[^0-9.%q ]", "g"); + hre.replaceDestructive(text, "ryy ", " ", "g"); + text += "ryy"; + current->setText(text); + current = current->getNextToken(); } } @@ -88130,637 +89176,2532 @@ void Tool_humsheet::printTitle(HumdrumFile& infile, int line) { ////////////////////////////// // -// Tool_humsheet::printRowData -- +// Tool_hands::markNotes -- // -void Tool_humsheet::printRowData(HumdrumFile& infile, int line) { - m_free_text << " data-line=\"" << line << "\""; +void Tool_hands::markNotes(HumdrumFile& infile) { + HumRegex hre; + + int counter = 0; + int scount = infile.getStrandCount(); + for (int i=0; igetExclusiveInterpretation(); + int hasHandMarkup = xtok->getValueInt("auto", "hand"); + if (!hasHandMarkup) { + continue; + } + HTp send = infile.getStrandEnd(i); + markNotes(sstart, send); + counter++; + } + + if (counter) { + infile.appendLine("!!!RDF**kern: " + m_leftMarker + " = marked note, color=\"" + m_leftColor + "\", left-hand note"); + infile.appendLine("!!!RDF**kern: " + m_rightMarker + " = marked note, color=\"" + m_rightColor + "\", right-hand note"); + infile.createLinesFromTokens(); + } } +void Tool_hands::markNotes(HTp sstart, HTp send) { + HTp current = sstart; + while (current && (current != send)) { + if (!current->isData() || current->isNull() || current->isRest()) { + current = current->getNextToken(); + continue; + } -/////////////////////////////// + HumRegex hre; + string text = *current; + string htype = current->getValue("auto", "hand"); + if (htype == "LH") { + hre.replaceDestructive(text, " " + m_leftMarker, " +", "g"); + text = m_leftMarker + text; + } else if (htype == "RH") { + hre.replaceDestructive(text, " " + m_rightMarker, " +", "g"); + text = m_rightMarker + text; + } + current->setText(text); + current = current->getNextToken(); + } +} + + + +////////////////////////////// // -// printHtmlHeader -- +// Tool_hands::colorHands -- Convert for example *LH into *color:dodgerblue. // -void Tool_humsheet::printHtmlHeader(void) { - m_free_text << "\n"; - m_free_text << "\n"; - m_free_text << "\n"; - m_free_text << "UNTITLED\n"; - m_free_text << "\n"; - m_free_text << "\n"; - m_free_text << "\n"; +void Tool_hands::colorHands(HumdrumFile& infile) { + string left = "*color:" + m_leftColor; + string right = "*color:" + m_rightColor; + for (int i=0; iisKern()) { + continue; + } + if (*token == "*LH") { + token->setText(left); + changed = true; + } + if (*token == "*RH") { + token->setText(right); + changed = true; + } + } + if (changed) { + infile[i].createLineFromTokens(); + } + } } -/////////////////////////////// + +///////////////////////////////// // -// printHtmlFooter -- +// Tool_homorhythm::Tool_homorhythm -- Set the recognized options for the tool. // -void Tool_humsheet::printHtmlFooter(void) { - m_free_text << "\n"; - m_free_text << "\n"; +Tool_homorhythm::Tool_homorhythm(void) { + define("a|append=b", "append analysis to end of input data"); + define("attacks=b", "append attack counts for each sonority"); + define("p|prepend=b", "prepend analysis to end of input data"); + define("r|raw-sonority=b", "display individual sonority scores only"); + define("raw-score=b", "display accumulated scores"); + define("M|no-marks=b", "do not mark homorhythm section notes"); + define("f|fraction=b", "calculate fraction of music that is homorhythm"); + define("v|voice=b", "display voice information or fraction results"); + define("F|filename=b", "show filename for f option"); + define("n|t|threshold=d:4.0", "threshold score sum required for homorhythm texture detection"); + define("s|score=d:1.0", "score assigned to a sonority with three or more attacks"); + define("m|intermediate-score=d:0.5", "score to give sonority between two adjacent attack sonoroties"); + define("l|letter=b", "display letter scoress before calculations"); } -/////////////////////////////// +///////////////////////////////// // -// printRowClasses -- +// Tool_homorhythm::run -- Do the main work of the tool. // -void Tool_humsheet::printRowClasses(HumdrumFile& infile, int row) { - string classes; - HLp hl = &infile[row]; - if (hl->hasSpines()) { - classes += "spined "; - } - if (hl->isEmpty()) { - classes += "empty "; - } - if (hl->isData()) { - classes += "data "; - } - if (hl->isInterpretation()) { - classes += "interp "; - HTp token = hl->token(0); - if (token->compare(0, 2, "*>") == 0) { - classes += "label "; - } - } - if (hl->isLocalComment()) { - classes += "lcomment "; - if (isLayout(hl)) { - classes += "layout "; - } - } - HTp token = hl->token(0); - if (token->compare(0, 2, "!!") == 0) { - if ((token->size() == 2) || (token->at(3) != '!')) { - classes += "gcommet "; - } +bool Tool_homorhythm::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; iisUniversalReference()) { - if (token->compare(0, 11, "!!!!filter:") == 0) { - classes += "ufilter "; - } else if (token->compare(0, 12, "!!!!Xfilter:") == 0) { - classes += "usedufilter "; - } else { - classes += "ureference "; - if (token->compare(0, 12, "!!!!SEGMENT:") == 0) { - classes += "segment "; - } - } - } else if (hl->isCommentUniversal()) { - classes += "ucomment "; - } else if (hl->isReference()) { - classes += "reference "; - } else if (hl->isGlobalComment()) { - HTp token = hl->token(0); - if (token->compare(0, 10, "!!!filter:") == 0) { - classes += "filter "; - } else if (token->compare(0, 11, "!!!Xfilter:") == 0) { - classes += "usedfilter "; - } else { - classes += "gcomment "; - if (isLayout(hl)) { - classes += "layout "; - } - } - } - if (hl->isBarline()) { - classes += "barline "; - } - if (hl->isManipulator()) { - HTp token = hl->token(0); - if (token->compare(0, 2, "**") == 0) { - classes += "exinterp "; - } else { - classes += "manip "; - } +bool Tool_homorhythm::run(const string& indata, ostream& out) { + HumdrumFile infile; + infile.readStringNoRhythm(indata); + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; } - if (!classes.empty()) { - // remove space. - classes.resize((int)classes.size() - 1); - m_free_text << " class=\"" << classes << "\""; + return status; +} + + +bool Tool_homorhythm::run(HumdrumFile& infile, ostream& out) { + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; } + return status; +} + + +bool Tool_homorhythm::run(HumdrumFile& infile) { + initialize(); + infile.analyzeStructure(); + m_voice_count = getExtantVoiceCount(infile); + m_letterQ = getBoolean("letter"); + processFile(infile); + infile.createLinesFromTokens(); + return true; } ////////////////////////////// // -// Tool_humsheet::isLayout -- check to see if any cell -// starts with "!LO:". +// Tool_homorhythm::markHomophonicNotes -- // -bool Tool_humsheet::isLayout(HLp line) { - if (line->hasSpines()) { - if (!line->isCommentLocal()) { - return false; - } - for (int i=0; igetFieldCount(); i++) { - HTp token = line->token(i); - if (token->compare(0, 4, "!LO:") == 0) { - return true; - } - } - } else { - HTp token = line->token(0); - if (token->compare(0, 5, "!!LO:") == 0) { - return true; - } - } - return false; +void Tool_homorhythm::markHomophonicNotes(void) { + // currently done with a **color spine } -/////////////////////////////// +////////////////////////////// // -// Tool_humsheet::printRowContents -- +// Tool_homorhythm::initialize -- // -void Tool_humsheet::printRowContents(HumdrumFile& infile, int row) { - int fieldcount = infile[row].getFieldCount(); - for (int i=0; i"; - printToken(token); - m_free_text << ""; +void Tool_homorhythm::initialize(void) { + m_threshold = getInteger("threshold"); + if (m_threshold < 1.0) { + m_threshold = 1.0; } + + m_score = getDouble("score"); + if (m_score < 1.0) { + m_score = 1.0; + } + + m_intermediate_score = getDouble("intermediate-score"); + if (m_intermediate_score < 0.0) { + m_intermediate_score = 0.0; + } + + if (m_intermediate_score > m_score) { + m_intermediate_score = m_score; + } + } ////////////////////////////// // -// Tool_humsheet::printCellData -- +// Tool_homorhythm::processFile -- // -void Tool_humsheet::printCellData(HTp token) { - int field = token->getFieldIndex(); - m_free_text << " data-field=\"" << field << "\""; +void Tool_homorhythm::processFile(HumdrumFile& infile) { + vector data; + data.reserve(infile.getLineCount()); + m_homorhythm.clear(); + m_homorhythm.resize(infile.getLineCount()); - if (token->getOwner()->hasSpines()) { - int spine = token->getTrack() - 1; - m_free_text << " data-spine=\"" << spine << "\""; + m_notecount.clear(); + m_notecount.resize(infile.getLineCount()); + fill(m_notecount.begin(), m_notecount.end(), 0); - int subspine = token->getSubtrack(); - if (subspine > 0) { - m_free_text << " data-subspine=\"" << subspine << "\""; + m_attacks.clear(); + m_attacks.resize(infile.getLineCount()); + fill(m_attacks.begin(), m_attacks.end(), 0); + + m_notes.clear(); + m_notes.resize(infile.getLineCount()); + + for (int i=0; igetDataType().substr(2); - if (m_exinterpQ && !exinterp.empty()) { - m_free_text << " data-x=\"" << exinterp << "\""; + // change Y N Y patterns to Y Y Y + for (int i=1; i<(int)data.size() - 1; i++) { + if (m_homorhythm[data[i]] == "Y") { + continue; + } + if (m_homorhythm[data[i+1]] == "N") { + continue; + } + if (m_homorhythm[data[i-1]] == "N") { + continue; } + m_homorhythm[data[i]] = "NY"; // not homphonic by will get intermediate score. + } + + vector score(infile.getLineCount(), 0); + vector raw(infile.getLineCount(), 0); + + double sum = 0.0; + for (int i=0; i<(int)data.size(); i++) { + if (m_homorhythm[data[i]].find("Y") != string::npos) { + if (m_homorhythm[data[i]].find("N") != string::npos) { + // sonority between two homorhythm-like sonorities. + // maybe also differentiate based on metric position. + sum += m_intermediate_score; + raw[data[i]] = m_intermediate_score; + } else { + sum += m_score; + raw[data[i]] = m_score; + } + } else { + sum = 0.0; + } + score[data[i]] = sum; } -} + for (int i=(int)data.size()-2; i>=0; i--) { + if (score[data[i]] == 0) { + continue; + } + if (score[data[i+1]] > score[data[i]]) { + score[data[i]] = score[data[i+1]]; + } + } + if (getBoolean("raw-score")) { + addAccumulatedScores(infile, score); + } -////////////////////////////// -// -// Tool_humsheet::printToken -- -// + if (getBoolean("raw-sonority")) { + addRawAnalysis(infile, raw); + } + if (getBoolean("raw-score")) { + addAccumulatedScores(infile, score); + } -void Tool_humsheet::printToken(HTp token) { - for (int i=0; i<(int)token->size(); i++) { - switch (token->at(i)) { - case '>': - m_free_text << ">"; - break; - case '<': - m_free_text << "<"; - break; - default: - m_free_text << token->at(i); + if (getBoolean("fraction")) { + addFractionAnalysis(infile, score); + } + + if (getBoolean("attacks")) { + addAttacks(infile, m_attacks); + } + + if (!getBoolean("fraction")) { + // Color the notes within homorhythm textures. + // mark homorhythm regions in red, + // non-homorhythm sonorities within these regions in green + // and non-homorhythm regions in black. + if (m_letterQ) { + infile.appendDataSpine(m_homorhythm, "", "**hp"); + } + for (int i=0; i<(int)data.size(); i++) { + if (score[data[i]] >= m_threshold) { + if (m_attacks[data[i]] < (int)m_notes[data[i]].size() - 1) { + m_homorhythm[data[i]] = "dodgerblue"; + } else { + m_homorhythm[data[i]] = "red"; + } + } else { + m_homorhythm[data[i]] = "black"; + } } + infile.appendDataSpine(m_homorhythm, "", "**color"); + + // problem with **color spine in javascript, so output via humdrum text + m_humdrum_text << infile; } + } -/////////////////////////////// +////////////////////////////// // -// Tool_humsheet::printId -- +// Tool_homorhythm::addAccumulatedScores -- // -void Tool_humsheet::printId(HTp token) { - int line = token->getLineNumber(); - int field = token->getFieldNumber(); - string id = "tok-L"; - id += to_string(line); - id += "F"; - id += to_string(field); - m_free_text << " id=\"" << id << "\""; +void Tool_homorhythm::addAccumulatedScores(HumdrumFile& infile, vector& score) { + infile.appendDataSpine(score, "", "**score", false); } -/////////////////////////////// +////////////////////////////// // -// Tool_humsheet::printTabIndex -- +// Tool_homorhythm::addRawAnalysis -- // -void Tool_humsheet::printTabIndex(HTp token) { - string number = token->getValue("auto", "tabindex"); - if (number.empty()) { - return; - } - m_free_text << " tabindex=\"" << number << "\""; +void Tool_homorhythm::addRawAnalysis(HumdrumFile& infile, vector& raw) { + infile.appendDataSpine(raw, "", "**raw", false); } ////////////////////////////// // -// Tool_humsheet::printColspan -- print any necessary colspan values for -// token (to align by primary spines) +// Tool_homorhythm::addAttacks -- // -void Tool_humsheet::printColSpan(HTp token) { - if (!token->getOwner()->hasSpines()) { - m_free_text << " colspan=\"" << m_max_field << "\""; - return; - } - int track = token->getTrack() - 1; - int scount = m_max_subtrack.at(track); - int subtrack = token->getSubtrack(); - if (subtrack > 1) { - subtrack--; - } - HTp nexttok = token->getNextFieldToken(); - int ntrack = -1; - if (nexttok) { - ntrack = nexttok->getTrack() - 1; - } - if ((ntrack < 0) || (ntrack != track)) { - // at the end of a primary spine, so do a colspan with the remaining subtracks - if (subtrack < scount-1) { - int colspan = scount - subtrack; - m_free_text << " colspan=\"" << colspan << "\""; - } - } else { - // do nothing - } +void Tool_homorhythm::addAttacks(HumdrumFile& infile, vector& attacks) { + infile.appendDataSpine(attacks, "", "**atks"); } -/////////////////////////////// +////////////////////////////// // -// printCellClasses -- +// Tool_homorhythm::addFractionAnalysis -- // -void Tool_humsheet::printCellClasses(HTp token) { - int track = token->getTrack(); - string classlist; +void Tool_homorhythm::addFractionAnalysis(HumdrumFile& infile, vector& score) { + double sum = 0.0; + for (int i=0; i m_threshold) { + sum += infile[i].getDuration().getFloat(); + } + } + double total = infile.getScoreDuration().getFloat(); + int ocount = getOriginalVoiceCount(infile); + double fraction = sum / total; + double percent = int(fraction * 1000.0 + 0.5)/10.0; + if (getBoolean("filename")) { + m_free_text << infile.getFilename() << "\t"; + } + if (getBoolean("voice")) { + m_free_text << ocount; + m_free_text << "\t"; + m_free_text << m_voice_count; + m_free_text << "\t"; + if (ocount == m_voice_count) { + m_free_text << "complete" << "\t"; + } else { + m_free_text << "incomplete" << "\t"; + } + } + if (m_voice_count < 2) { + m_free_text << -1; + } else { + m_free_text << percent; + } + m_free_text << endl; +} - if (m_zebraQ) { - if (track % 2 == 0) { - classlist = "zebra "; + + +////////////////////////////// +// +// Tool_homorhythm::getOriginalVoiceCount -- +// + +int Tool_homorhythm::getOriginalVoiceCount(HumdrumFile& infile) { + HumRegex hre; + for (int i=0; igetOwner()->hasSpines()) { - int length = (int)token->size(); - if (length > 20) { - classlist += "long "; + + +////////////////////////////// +// +// Tool_homorhythm::getExtantVoiceCount -- +// + +int Tool_homorhythm::getExtantVoiceCount(HumdrumFile& infile) { + vector spines = infile.getKernSpineStartList(); + return (int)spines.size(); +} + + + +////////////////////////////// +// +// Tool_homorhythm::analyzeLine -- +// + +void Tool_homorhythm::analyzeLine(HumdrumFile& infile, int line) { + m_notes[line].reserve(10); + HPNote note; + if (!infile[line].isData()) { + return; + } + int nullQ = 0; + for (int i=0; iisKern()) { + continue; + } + if (token->isRest()) { + continue; + } + if (token->isNull()) { + nullQ = 1; + token = token->resolveNull(); + if (!token) { + continue; + } + if (token->isRest()) { + continue; + } + } else { + nullQ = 0; + } + int track = token->getTrack(); + vector subtokens = token->getSubtokens(); + for (int j=0; j<(int)subtokens.size(); j++) { + note.track = track; + note.line = token->getLineIndex(); + note.field = token->getFieldIndex(); + note.subfield = j; + note.token = token; + note.text = subtokens[j]; + note.duration = Convert::recipToDuration(note.text); + if (nullQ) { + note.attack = false; + note.nullQ = true; + } else { + note.nullQ = false; + if ((note.text.find("_") != string::npos) || + (note.text.find("]") != string::npos)) { + note.attack = false; + } else { + note.attack = true; + } + } + m_notes[line].push_back(note); } } - if (!classlist.empty()) { - classlist.resize((int)classlist.size() - 1); - m_free_text << " class=\"" << classlist << "\""; + // There must be at least three attacks to be considered homorhythm + // maybe adjust to N-1 or three voices, or a similar rule. + vector adurs; + for (int i=0; i<(int)m_notes[line].size(); i++) { + if (m_notes[line][i].attack) { + adurs.push_back(m_notes[line][i].duration); + m_attacks[line]++; + } + } + // if ((int)m_attacks[line] >= (int)m_notes[line].size() - 1) { + if ((int)m_attacks[line] >= 3) { + string value = "Y"; + // value += to_string(m_attacks[line]); + m_homorhythm[line] = value; + } else if ((m_voice_count == 3) && (m_attacks[line] == 2)) { + if ((adurs.size() >= 2) && (adurs[0] == adurs[1])) { + m_homorhythm[line] = "Y"; + } else { + m_homorhythm[line] = "N"; + } + } else { + string value = "N"; + // value += to_string(m_attacks[line]); + m_homorhythm[line] = value; + } + // redundant or three-or-more case: + if (m_notes[line].size() <= 2) { + m_homorhythm[line] = "N"; } +} + + + +///////////////////////////////// +// +// Tool_homorhythm2::Tool_homorhythm -- Set the recognized options for the tool. +// + +Tool_homorhythm2::Tool_homorhythm2(void) { + define("t|threshold=d:1.6", "threshold score sum required for homorhythm texture detection"); + define("u|threshold2=d:1.3", "threshold score sum required for semi-homorhythm texture detection"); + define("s|score=b", "show numeric scores"); + define("n|length=i:4", "sonority length to calculate"); + define("f|fraction=b", "report fraction of music that is homorhythm"); } -////////////////////////////// +///////////////////////////////// // -// Tool_humsheet::printStyle -- +// Tool_homorhythm2::run -- Do the main work of the tool. // -void Tool_humsheet::printStyle(HumdrumFile& infile) { +bool Tool_homorhythm2::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; i\n"; - m_free_text << "body {\n"; - m_free_text << " padding: 20px;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum {\n"; - m_free_text << " border-collapse: collapse;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum td:focus {\n"; - m_free_text << " background: #ff000033 !important;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum td {\n"; - m_free_text << " outline: none;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum td[data-subspine='1'],\n"; - m_free_text << "table.humdrum td[data-subspine='2'],\n"; - m_free_text << "table.humdrum td[data-subspine='3'],\n"; - m_free_text << "table.humdrum td[data-subspine='4'],\n"; - m_free_text << "table.humdrum td[data-subspine='5'],\n"; - m_free_text << "table.humdrum td[data-subspine='6'],\n"; - m_free_text << "table.humdrum td[data-subspine='7'],\n"; - m_free_text << "table.humdrum td[data-subspine='8'],\n"; - m_free_text << "table.humdrum td[data-subspine='9'] {\n"; - m_free_text << " border-right: solid #0000000A 1px;\n"; - m_free_text << " padding-left: 3px;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.ucomment {\n"; - m_free_text << " color: chocolate;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.segment {\n"; - m_free_text << " color: chocolate;\n"; - m_free_text << " background: rgb(255,99,71,0.25);\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.ureference {\n"; - m_free_text << " color: chocolate;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.reference {\n"; - m_free_text << " color: green;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.gcomment {\n"; - m_free_text << " color: blue;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.ucomment {\n"; - m_free_text << " color: violet;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.lcomment {\n"; - m_free_text << " color: $#2fc584;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.interp.manip {\n"; - m_free_text << " color: magenta;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.interp.exinterp {\n"; - m_free_text << " color: red;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.interp {\n"; - m_free_text << " color: darkviolet;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.filter {\n"; - m_free_text << " color: limegreen;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.usedfilter {\n"; - m_free_text << " color: olive;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.ufilter {\n"; - m_free_text << " color: limegreen;\n"; - m_free_text << " background: rgba(0,0,aa,0.3);\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.usedufilter {\n"; - m_free_text << " color: olive;\n"; - m_free_text << " background: rgba(0,0,aa,0.3);\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.interp.label {\n"; - m_free_text << " background: rgba(75,0,130,0.3);\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.layout {\n"; - m_free_text << " color: orange;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum tr.barline {\n"; - m_free_text << " color: gray;\n"; - m_free_text << " background: rgba(0, 0, 0, 0.06);\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum td.long {\n"; - m_free_text << " white-space: nowrap;\n"; - m_free_text << " max-width: 200px;\n"; - m_free_text << " background-image: linear-gradient(to right, cornsilk 95%, crimson 100%);\n"; - m_free_text << " overflow: scroll;\n"; - m_free_text << "}\n"; - if (m_zebraQ) { - m_free_text << "table.humdrum .zebra {\n"; - m_free_text << " background: #ccccff33;\n"; - m_free_text << "}\n"; - } else if (m_zebra2Q) { - m_free_text << "table.humdrum td[data-x='kern'] {\n"; - m_free_text << " background: #ffcccc33;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum td[data-x='dynam'] {\n"; - m_free_text << " background: #ccccff33;\n"; - m_free_text << "}\n"; - m_free_text << "table.humdrum td[data-x='text'] {\n"; - m_free_text << " background: #ccffcc33;\n"; - m_free_text << "}\n"; +bool Tool_homorhythm2::run(const string& indata, ostream& out) { + HumdrumFile infile; + infile.read(indata); + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; } + return status; +} - m_free_text << "\n"; + +bool Tool_homorhythm2::run(HumdrumFile& infile, ostream& out) { + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; + } + return status; +} + + +bool Tool_homorhythm2::run(HumdrumFile& infile) { + initialize(); + processFile(infile); + return true; } ////////////////////////////// // -// Tool_humsheet::printJavascript -- +// Tool_homorhythm2::initialize -- // -void Tool_humsheet::printJavascript(void) { +void Tool_homorhythm2::initialize(void) { + m_threshold = getDouble("threshold"); + if (m_threshold < 0.0) { + m_threshold = 0.0; + } + m_threshold2 = getDouble("threshold2"); + if (m_threshold2 < 0.0) { + m_threshold2 = 0.0; + } + if (m_threshold < m_threshold2) { + double temp = m_threshold; + m_threshold = m_threshold2; + m_threshold2 = temp; + } - m_free_text << "\n"; + out << " \n"; + out << " \n"; + out << "\n"; + out << " \n"; + out << " \n"; + out << " \n"; + out << "

Pitch-class histogram

\n"; + printVegaLiteHtml(jsonvar, target, datavar, infile); + out << "\n"; + out << "\n"; +} + + + +////////////////////////////// +// +// Tool_pccount::printVegaLiteHtml -- +// + +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"; +} + + + +////////////////////////////// +// +// Tool_pccount::printVegaLiteScript -- // -void Tool_myank::printMeasureStart(HumdrumFile& infile, int line, const string& style) { - if (!infile[line].isBarline()) { - m_humdrum_text << infile[line] << "\n"; - return; - } +void Tool_pccount::printVegaLiteScript(const string& jsonvar, + const string& target, const string& datavar, HumdrumFile& infile) { + stringstream& out = m_free_text; - HumRegex hre; - int j; - for (j=0; j m_maxpc) { + m_maxpc = m_counts[0][i]; + } + } + out << "[\n"; + int commacounter = 0; + double percent = 100.0; + for (int i=1; i<(int)m_counts.size(); i++) { + for (int j=0; j<(int)m_counts[i].size(); j++) { + if (m_counts[i][j] == 0.0) { + continue; + } + if (commacounter > 0) { + out << ",\n\t"; } else { - m_humdrum_text << "="; - m_humdrum_text << hre.getMatch(1); - m_humdrum_text << style; + out << "\t"; } - } else { - if (style == "==") { - m_humdrum_text << "=="; + commacounter++; + if (m_attack) { + out << "{\"count\":" << m_counts[i][j] << ", "; } else { - m_humdrum_text << "=" << style; + out << "{\"percent\":" << m_counts[i][j]/m_maxpc*percent << ", "; } - } - if (j < infile[line].getFieldCount()-1) { - m_humdrum_text << "\t"; + out << "\"pitch class\":\"" << getPitchClassString(j) << "\", "; + out << "\"voice\":\"" << m_names[i] << "\""; + out << "}"; } } - m_humdrum_text << "\n"; + out << "\n]\n"; +} - if (m_barnumtextQ) { - int barline = 0; - sscanf(infile.token(line, 0)->c_str(), "=%d", &barline); - if (barline > 0) { - m_humdrum_text << "!!LO:TX:Z=20:X=-25:t=" << barline << endl; + + +////////////////////////////// +// +// Tool_pccount::setFactorMaximum -- normalize by the maximum pitch-class value. +// + +void Tool_pccount::setFactorMaximum(void) { + m_factor = 0.0; + for (int i=0; i<(int)m_counts[0].size(); i++) { + if (m_counts[0][i] > m_factor) { + m_factor = m_counts[0][i]; } } } + ////////////////////////////// // -// Tool_myank::printDoubleBarline -- +// Tool_pccount::setFactorNormalize -- normalize by the sum of all pitch class values. // -void Tool_myank::printDoubleBarline(HumdrumFile& infile, int line) { +void Tool_pccount::setFactorNormalize(void) { + m_factor = 0.0; + for (int i=0; i<(int)m_counts[0].size(); i++) { + m_factor += m_counts[0][i]; + } +} - if (!infile[line].isBarline()) { - m_humdrum_text << infile[line] << "\n"; - return; + + +////////////////////////////// +// +// Tool_pccount::printHumdrumTable -- +// + +void Tool_pccount::printHumdrumTable(void) { + + double factor = 0.0; + + if (m_maximum) { + setFactorMaximum(); + m_free_text << "!!!MAX: " << m_factor << endl; + } else if (m_normalize) { + setFactorNormalize(); + m_free_text << "!!!TOTAL: " << factor << endl; } - HumRegex hre; - int j; - for (j=0; jc_str(), "=%d", &barline); - if (barline > 0) { - m_humdrum_text << "!!LO:TX:Z=20:X=-25:t=" << barline << endl; + for (int i=0; i<(int)m_counts[0].size(); i++) { + if (m_counts[0][i] == 0) { + continue; + } + if ((i == 5) || (i == 11) || (i == 22) || (i == 28) || (i == 34)) { + continue; } + string pitch = Convert::base40ToKern(i + 4*40); + m_free_text << pitch; + for (int j=0; j<(int)m_counts.size(); j++) { + if (m_normalize) { + m_free_text << "\t" << m_counts[j][i] / m_factor; + } else if (m_maximum) { + m_free_text << "\t" << m_counts[j][i] / m_factor; + } else { + m_free_text << "\t" << m_counts[j][i]; + } + } + m_free_text << endl; } + int columns = (int)m_counts.size() + 1; + for (int i=0; ifind('-') != string::npos) { - m_humdrum_text << infile.token(line, j); - if (j < infile[line].getFieldCount()-1) { - m_humdrum_text << "\t"; - } + // fill in sum for all parts + for (int i=0; i<(int)m_counts[0].size(); i++) { + for (int j=1; j<(int)m_counts.size(); j++) { + m_counts[0][i] += m_counts[j][i]; + } + } + +} + + +////////////////////////////// +// +// Tool_pccount::addCounts -- +// + +void Tool_pccount::addCounts(HTp sstart, HTp send) { + if (!sstart) { + return; + } + if (!sstart->isKern()) { + return; + } + int track = sstart->getTrack(); + int kindex = m_rkern[track]; + HTp current = sstart; + while (current && (current != send)) { + if (!current->isData()) { + current = current->getNextToken(); continue; } - if (hre.search(infile.token(line, j), "(=\\d*)(.*)", "")) { - m_humdrum_text << hre.getMatch(1); - // m_humdrum_text << "-"; - m_humdrum_text << hre.getMatch(2); - } else { - m_humdrum_text << infile.token(line, j); + if (current->isNull() || current->isRest()) { + current = current->getNextToken(); + continue; } - if (j < infile[line].getFieldCount()-1) { - m_humdrum_text << "\t"; + vector subtokens = current->getSubtokens(); + for (int i=0; i<(int)subtokens.size(); i++) { + if (m_attack) { + // ignore sustained parts of notes when counting attacks + if (subtokens[i].find("_") != string::npos) { + continue; + } + if (subtokens[i].find("]") != string::npos) { + continue; + } + } + int b40 = Convert::kernToBase40(subtokens[i]); + if (m_attack) { + m_counts[kindex][b40%40]++; + } else { + double duration = Convert::recipToDuration(subtokens[i]).getFloat(); + m_counts[kindex][b40%40] += duration; + } } + current = current->getNextToken(); } - m_humdrum_text << "\n"; } + ////////////////////////////// // -// Tool_myank::reconcileSpineBoundary -- merge spines correctly between segments. -// will not be able to handle all permutations of spine manipulators. -// So don't expect exotic manipulators to work... +// Tool_pccount::initializePartInfo -- // -void Tool_myank::reconcileSpineBoundary(HumdrumFile& infile, int index1, int index2) { +void Tool_pccount::initializePartInfo(HumdrumFile& infile) { + m_names.clear(); + m_abbreviations.clear(); + m_parttracks.clear(); + m_rkern.clear(); - if (m_debugQ) { - m_humdrum_text << "RECONCILING LINES " << index1+1 << " and " << index2+1 << endl; - m_humdrum_text << "FIELD COUNT OF " << index1+1 << " is " - << infile[index1].getFieldCount() << endl; - m_humdrum_text << "FIELD COUNT OF " << index2+1 << " is " - << infile[index2].getFieldCount() << endl; - } + m_rkern.resize(infile.getTrackCount() + 1); + fill(m_rkern.begin(), m_rkern.end(), -1); - // check to see if any changes need reconciling; otherwise, exit function - int i, j; - if (infile[index1].getFieldCount() == infile[index2].getFieldCount()) { - int same = 1; - for (i=0; igetSpineInfo() != infile.token(index2, i)->getSpineInfo()) { - same = 0; - } - } - if (same != 0) { - return; - } - } + m_parttracks.push_back(-1); + m_names.push_back("all"); + m_abbreviations.push_back("all"); - // handle splits all at once - string buff1; - string buff2; + vector starts = infile.getKernSpineStartList(); - vector splits(infile[index1].getFieldCount()); - fill(splits.begin(), splits.end(), 0); + int foundpart = false; + int foundabbr = false; - int hassplit = 0; - for (i=0; igetSpineInfo(); - buff1 += ")"; - buff2 = buff1; - buff1 += "a"; - buff2 += "b"; - for (j=0; jgetSpineInfo() - && (buff2 == infile.token(index2,j+1)->getSpineInfo()))) { - splits[i] = 1; - hassplit++; - } + int track = 0; + for (int i=0; i<(int)starts.size(); i++) { + track = starts[i]->getTrack(); + m_rkern[track] = i+1; + m_parttracks.push_back(track); + HTp current = starts[i]; + foundpart = false; + foundabbr = false; + if (!current->isKern()) { + continue; } - } - - if (hassplit) { - for (i=0; i<(int)splits.size(); i++) { - if (splits[i]) { - m_humdrum_text << "*^"; - } else { - m_humdrum_text << '*'; + while (current) { + if (current->isData()) { + break; } - if (i < (int)splits.size()-1) { - m_humdrum_text << '\t'; + if ((!foundpart) && (current->compare(0, 3, "*I\"") == 0)) { + m_names.emplace_back(current->substr(3)); + foundpart = true; + } else if ((!foundabbr) && (current->compare(0, 3, "*I\'") == 0)) { + m_abbreviations.emplace_back(current->substr(3)); + foundabbr = true; } + current = current->getNextToken(); } - m_humdrum_text << '\n'; + //if (!foundpart) { + // m_names.emplace_back(""); + //} + //if (!foundabbr) { + // m_names.emplace_back(""); + //} } - // make splits cumulative; - //for (i=1; i<(int)splits.size(); i++) { - // splits[i] += splits[i-1]; - //} +} - HumRegex hre1; - HumRegex hre2; - // handle joins one at a time, only look for binary joins at the moment. - // assuming that no *x has been used to mix the voices up. - for (i=0; igetSpineInfo(), "\\((.*)\\)a")) { - continue; - } - if (!hre2.search(infile.token(index1, i+1)->getSpineInfo(), "\\((.*)\\)b")) { - continue; - } - if (hre1.getMatch(1) != hre2.getMatch(1)) { - // spines are not split from same source - continue; + +////////////////////////////// +// +// printVegaLiteJsonTemplate -- +// + +void Tool_pccount::printVegaLiteJsonTemplate(const string& datavariable, HumdrumFile& infile) { + stringstream& out = m_free_text; + + string idinfo; + if (m_id.empty() || m_id == "id") { + // do nothing + } else { + idinfo = "for " + m_id; + } + out << "{\n"; + out << " \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0-beta.1.json\",\n"; + out << " \"data\": {\"values\": " << datavariable << "},\n"; + if (getBoolean("title")) { + out << " \"title\": \"" << m_title << "\",\n"; + } else { + if (m_attack) { + out << " \"title\": \"Note-count pitch-class distribution " << idinfo <<" \",\n"; + } else { + out << " \"title\": \"Duration-weighted pitch-class distribution " << idinfo <<" \",\n"; } + } + out << " \"width\": " << m_width << ",\n"; + out << " \"height\": " << int(m_width * m_ratio) << ",\n"; + out << " \"encoding\": {\n"; + out << " \"y\": {\n"; + if (m_attack) { + out << " \"field\": \"count\",\n"; + out << " \"title\": \"Number of note attacks\",\n"; + } else { + out << " \"field\": \"percent\",\n"; + out << " \"title\": \"Percent of maximum pitch class\",\n"; + } + out << " \"type\": \"quantitative\",\n"; + if (m_attack) { + out << " \"scale\": {\"domain\": [0, " << m_maxpc << "]},\n"; + } else { + out << " \"scale\": {\"domain\": [0, 100]},\n"; + } + out << " \"aggregate\": \"sum\"\n"; + out << " },\n"; + out << " \"x\": {\n"; + out << " \"field\": \"pitch class\",\n"; + out << " \"type\": \"nominal\",\n"; + out << " \"scale\": {\n"; + out << " \"domain\": ["; + printPitchClassList(); + out << "]\n"; + out << " },\n"; + out << " \"axis\": {\n"; + out << " \"labelAngle\": 0\n"; + out << " }\n"; + out << " },\n"; + out << " \"order\": {\"type\": \"quantitative\"},\n"; + out << " \"color\": {\n"; + out << " \"field\": \"voice\",\n"; + out << " \"type\": \"nominal\",\n"; + if (m_counts.size() == 2) { + out << " \"legend\": {\"title\": \"Voice\"},\n"; + } else { + out << " \"legend\": {\"title\": \"Voices\"},\n"; + } + out << " \"scale\": {\n"; + out << " \"domain\": ["; + printVoiceList(); + out << "],\n"; + out << " \"range\": ["; + printColorList(); + out << "],\n"; + out << " }\n"; + out << " }\n"; + out << " },\n"; - // found an "a" and "b" portion of a spine split, now search - // through the target line for a joint of those two sub-spines - for (j=0; jgetSpineInfo() != hre1.getMatch(1)) { - continue; - } - // found a simple binary spine join: emmit a spine manipulator line - printJoinLine(splits, i, 2); + out << " \"layer\": [\n"; + out << " {\"mark\": \"bar\"}"; + + string final = getFinal(infile); + if (m_key && !final.empty()) { + out << ",\n"; + out << " {\n"; + out << " \"mark\": {\"type\":\"text\", \"align\":\"center\", \"fill\":\"black\", \"baseline\":\"bottom\"},\n"; + if (m_attack) { + int count = getCount(final); + out << " \"data\": {\"values\": [ {\"pitch class\":\"" << final << "\", \"count\":" << count << "}]},\n"; + } else { + double percent = getPercent(final); + out << " \"data\": {\"values\": [ {\"pitch class\":\"" << final << "\", \"percent\":" << percent << "}]},\n"; } + out << " \"encoding\": {\"text\": {\"value\":\"final\"}}\n"; + out << " }\n"; } - // handle *x switches, not perfect since ordering might need to be - // handled between manipulators... + out << " ]\n"; + out << "}\n"; + +} + + + +////////////////////////////// +// +// Tool_pccount::getCount -- +// + +int Tool_pccount::getCount(const string& pitchclass) { + int b40 = Convert::kernToBase40(pitchclass); + int index = b40 % 40; + int output = (int)m_counts[0][index]; + return output; +} + + + +////////////////////////////// +// +// Tool_pccount::getPercent -- +// +double Tool_pccount::getPercent(const string& pitchclass) { + setFactorMaximum(); + int b40 = Convert::kernToBase40(pitchclass); + int index = b40 % 40; + double output = m_counts[0][index] / m_factor * 100.0; + return output; } ////////////////////////////// // -// Tool_myank::printJoinLine -- count is currently ignored, but may in the future -// allow for more than two spines to join at the same time. +// Tool_pccount::printColorList -- // -void Tool_myank::printJoinLine(vector& splits, int index, int count) { - int i; - for (i=0; i<(int)splits.size(); i++) { - if (i == index) { - m_humdrum_text << "*v\t*v"; - i+=count-1; +void Tool_pccount::printColorList(void) { + stringstream& out = m_free_text; + for (int i=(int)m_names.size() - 1; i>0; i--) { + string color = m_vcolor[m_names[i]]; + out << "\""; + if (color.empty()) { + out << "black"; } else { - m_humdrum_text << "*"; + out << color; } - if (i<(int)splits.size()-1) { - m_humdrum_text << "\t"; + out << "\""; + if (i > 1) { + out << ", "; } } - m_humdrum_text << "\n"; - - // merge splits by one element - for (i=index+1; i<(int)splits.size()-1; i++) { - splits[i] = splits[i+1]; - } - splits.resize(splits.size()-1); } ////////////////////////////// // -// Tool_myank::reconcileStartingPosition -- merge spines from start of data and -// first measure in output. +// Tool_pccount::printVoiceList -- // -void Tool_myank::reconcileStartingPosition(HumdrumFile& infile, int index2) { - int i; - for (i=0; i0; i--) { + out << "\""; + out << m_names[i]; + out << "\""; + if (i > 1) { + out << ", "; } } } @@ -110548,1130 +116763,933 @@ void Tool_myank::reconcileStartingPosition(HumdrumFile& infile, int index2) { ////////////////////////////// // -// Tool_myank::printStarting -- print header information before start of data. +// Tool_pccount::printReverseVoiceList -- // -void Tool_myank::printStarting(HumdrumFile& infile) { - int i, j; - int exi = -1; - for (i=0; icompare(0, 5, "*part") == 0) { - hasPart = true; - break; - } - } - if (hasPart) { - for (j=0; jcompare(0, 5, "*part") == 0) { - m_humdrum_text << infile.token(i, j); - } else { - m_humdrum_text << "*"; - } - if (j < infile[i].getFieldCount() - 1) { - m_humdrum_text << "\t"; - } - } - m_humdrum_text << "\n"; - } - } - // keep *staff interpretations - bool hasStaff = false; - for (i=exi+1; icompare(0, 6, "*staff") == 0) { - hasStaff = true; - break; - } - } - if (hasStaff) { - for (j=0; jcompare(0, 6, "*staff") == 0) { - m_humdrum_text << infile.token(i, j); - } else { - m_humdrum_text << "*"; - } - if (j < infile[i].getFieldCount() - 1) { - m_humdrum_text << "\t"; - } - } - m_humdrum_text << "\n"; - } - } - int hasI = 0; +////////////////////////////// +// +// Tool_pccount::printPitchClassList -- +// - if (m_instrumentQ) { - // print any tandem interpretations which start with *I found - // at the start of the data before measures, notes, or any - // spine manipulator lines - for (i=exi+1; icompare(0, 2, "*I") == 0) { - hasI = 1; - break; - } - } - if (hasI) { - for (j=0; jcompare(0, 2, "*I") == 0) { - m_humdrum_text << infile.token(i, j); - } else { - m_humdrum_text << "*"; - } - if (j < infile[i].getFieldCount() - 1) { - m_humdrum_text << "\t"; - } - } - m_humdrum_text << "\n"; - } - } - } +void Tool_pccount::printPitchClassList(void) { + stringstream& out = m_free_text; -} + if (m_counts[0][0] > 0.0) { out << "\"C♭♭\", "; } + if (m_counts[0][1] > 0.0) { out << "\"C♭\", "; } + out << "\"C\""; + if (m_counts[0][3] > 0.0) { out << ", \"C♯\""; } + if (m_counts[0][4] > 0.0) { out << ", \"C♯♯\""; } + // 5 is empty + + if (m_counts[0][6] > 0.0) { out << ", \"D♭♭\""; } + if (m_counts[0][7] > 0.0) { out << ", \"D♭\""; } + out << ", \"D\""; + if (m_counts[0][9] > 0.0) { out << ", \"D♯\""; } + if (m_counts[0][10] > 0.0) { out << ", \"D♯♯\""; } + // 11 is empty + + if (m_counts[0][12] > 0.0) { out << ", \"E♭♭\""; } + if (m_counts[0][13] > 0.0) { out << ", \"E♭\""; } + out << ", \"E\""; + if (m_counts[0][15] > 0.0) { out << ", \"E♯\""; } + if (m_counts[0][16] > 0.0) { out << ", \"E♯♯\""; } + + if (m_counts[0][17] > 0.0) { out << ", \"F♭♭\""; } + if (m_counts[0][18] > 0.0) { out << ", \"F♭\""; } + out << ", \"F\""; + if (m_counts[0][20] > 0.0) { out << ", \"F♯\""; } + if (m_counts[0][21] > 0.0) { out << ", \"F♯♯\""; } + // 22 is empty + + if (m_counts[0][23] > 0.0) { out << ", \"G♭♭\""; } + if (m_counts[0][24] > 0.0) { out << ", \"G♭\""; } + out << ", \"G\""; + if (m_counts[0][26] > 0.0) { out << ", \"G♯\""; } + if (m_counts[0][27] > 0.0) { out << ", \"G♯♯\""; } + // 28 is empty + + if (m_counts[0][29] > 0.0) { out << ", \"A♭♭\""; } + if (m_counts[0][30] > 0.0) { out << ", \"A♭\""; } + out << ", \"A\""; + if (m_counts[0][32] > 0.0) { out << ", \"A♯\""; } + if (m_counts[0][33] > 0.0) { out << ", \"A♯♯\""; } + // 34 is empty + + if (m_counts[0][35] > 0.0) { out << ", \"B♭♭\""; } + if (m_counts[0][36] > 0.0) { out << ", \"B♭\""; } + out << ", \"B\""; + if (m_counts[0][38] > 0.0) { out << ", \"B♯\""; } + if (m_counts[0][39] > 0.0) { out << ", \"B♯♯\""; } +} ////////////////////////////// // -// Tool_myank::printEnding -- print the spine terminators and any -// content after the end of the data. +// Tool_pccount::getPitchClassString -- // -void Tool_myank::printEnding(HumdrumFile& infile, int lastline, int adjlin) { - if (m_debugQ) { - m_humdrum_text << "IN printEnding" << endl; - } - int ending = -1; - int marker = -1; - int i; - for (i=infile.getLineCount()-1; i>=0; i--) { - if (infile[i].isInterpretation() && (ending <0) - && (*infile.token(i, 0) == "*-")) { - ending = i; - } - if (infile[i].isData()) { - marker = i+1; - break; - } - if (infile[i].isBarline()) { - marker = i+1; - break; - } +string Tool_pccount::getPitchClassString(int b40) { + switch (b40%40) { + case 0: return "C♭♭"; + case 1: return "C♭"; + case 2: return "C"; + case 3: return "C♯"; + case 4: return "C♯♯"; + // 5 is empty + case 6: return "D♭♭"; + case 7: return "D♭"; + case 8: return "D"; + case 9: return "D♯"; + case 10: return "D♯♯"; + // 11 is empty + case 12: return "E♭♭"; + case 13: return "E♭"; + case 14: return "E"; + case 15: return "E♯"; + case 16: return "E♯♯"; + case 17: return "F♭♭"; + case 18: return "F♭"; + case 19: return "F"; + case 20: return "F♯"; + case 21: return "F♯♯"; + // 22 is empty + case 23: return "G♭♭"; + case 24: return "G♭"; + case 25: return "G"; + case 26: return "G♯"; + case 27: return "G♯♯"; + // 28 is empty + case 29: return "A♭♭"; + case 30: return "A♭"; + case 31: return "A"; + case 32: return "A♯"; + case 33: return "A♯♯"; + // 34 is empty + case 35: return "B♭♭"; + case 36: return "B♭"; + case 37: return "B"; + case 38: return "B♯"; + case 39: return "B♯♯"; } - if (ending >= 0) { - reconcileSpineBoundary(infile, adjlin, ending); + return "?"; +} + + + + + + +///////////////////////////////// +// +// Tool_periodicity::Tool_periodicity -- Set the recognized options for the tool. +// + +Tool_periodicity::Tool_periodicity(void) { + define("m|min=b", "minimum time unit (other than grace notes)"); + define("n|max-rows=i:-1", "maxumum number of rows in svg analysis display"); + define("t|track=i:0", "track to analyze"); + define("attacks=b", "extract attack grid)"); + define("raw=b", "show only raw period data"); + define("s|svg=b", "output svg image"); + define("p|power=d:2.0", "scaling power for visual display"); + define("1|one=b", "composite rhythms are not weighted by attack"); +} + + + +///////////////////////////////// +// +// Tool_periodicity::run -- Primary interfaces to the tool. +// + +bool Tool_periodicity::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; i= 0) { - // capture any comment which occur after the last measure - // line in the data. - startline = marker; + +bool Tool_periodicity::run(const string& indata, ostream& out) { + HumdrumFile infile(indata); + bool status; + status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; } + return status; +} - // reconcileSpineBoundary(infile, lastline, startline); - if (startline >= 0) { - for (i=startline; i ending)) { - if (infile[i].rfind("!!!RDF", 0) == 0 || infile[i].rfind("!!!system-decoration", 0) == 0) { - m_humdrum_text << infile[i] << "\n"; - } - } else { - m_humdrum_text << infile[i] << "\n"; - } - } +bool Tool_periodicity::run(HumdrumFile& infile, ostream& out) { + bool status; + status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; } + return status; +} + +// +// In-place processing of file: +// +bool Tool_periodicity::run(HumdrumFile& infile) { + processFile(infile); + return true; } ////////////////////////////// // -// Tool_myank::getMeasureStartStop -- Get a list of the (numbered) measures in the -// input file, and store the start/stop lines for those measures. -// All data before the first numbered measure is in measure 0. -// although, if the first measure is not labeled, then ... +// Tool_periodicity::processFile -- // -void Tool_myank::getMeasureStartStop(vector& measurelist, HumdrumFile& infile) { - measurelist.reserve(infile.getLineCount()); - measurelist.resize(0); +void Tool_periodicity::processFile(HumdrumFile& infile) { + HumNum minrhy = infile.tpq() * 4; + if (getBoolean("min")) { + m_free_text << minrhy << endl; + return; + } - MeasureInfo current; - int i, ii; - int lastend = -1; - int dataend = -1; - int barnum1 = -1; - int barnum2 = -1; - HumRegex hre; + vector> attackgrids; + attackgrids.resize(infile.getTrackCount()+1); + fillAttackGrids(infile, attackgrids, minrhy); + if (getBoolean("attacks")) { + printAttackGrid(m_free_text, infile, attackgrids, minrhy); + return; + } - insertZerothMeasure(measurelist, infile); + int atrack = getInteger("track"); + vector> analysis; + doPeriodicityAnalysis(analysis, attackgrids[atrack], minrhy); - for (i=0; ic_str(), "=%d", &barnum1)) { - continue; - } - current.clear(); - current.start = i; - current.num = barnum1; - for (ii=i+1; ii=0; i--) { - if ((lastdata < 0) && infile[i].isData()) { - lastdata = i; - } - if ((lastmeasure < 0) && infile[i].isBarline()) { - lastmeasure = i; - } - if ((lastmeasure >= 0) && (lastdata >= 0)) { - break; - } - } - if (lastmeasure < lastdata) { - // no final barline, so set to ignore - lastmeasure = -1; - lastdata = -1; - } - if ((barnum2 >= 0) && (lastend >= 0) && (dataend >= 0)) { - current.clear(); - current.num = barnum2; - current.start = lastend; - current.stop = dataend; - if (lastmeasure > lastdata) { - current.stop = lastmeasure; - } - current.file = &infile; - measurelist.push_back(current); - } +////////////////////////////// +// +// Tool_periodicity::printPeriodicityAnalysis -- +// - // allow "myank -l" when there are no measure numbers - if (getBoolean("lines") && measurelist.size() == 0) { - current.clear(); - current.num = 0; - current.start = 0; - current.stop = dataend; - current.file = &infile; - measurelist.push_back(current); +void Tool_periodicity::printPeriodicityAnalysis(ostream& out, vector>& analysis) { + for (int i=0; i<(int)analysis.size(); i++) { + for (int j=0; j<(int)analysis[i].size(); j++) { + out << analysis[i][j]; + if (j < (int)analysis[i].size() - 1) { + out << "\t"; + } + } + out << "\n"; } - } ////////////////////////////// // -// Tool_myank::getSectionCount -- Count the number of sections in a file according to -// JRP rules: sections are defined by double barlines. There may be some -// corner cases to consider. +// Tool_periodicity::doPeriodicAnalysis -- // -int Tool_myank::getSectionCount(HumdrumFile& infile) { - int i; - int count = 0; - int dataQ = 0; - for (i=0; ifind("||") != string::npos) { - dataQ = 0; - } - } +void Tool_periodicity::doPeriodicityAnalysis(vector> &analysis, vector& grid, HumNum minrhy) { + analysis.resize(minrhy.getNumerator()); + for (int i=0; i<(int)analysis.size(); i++) { + doAnalysis(analysis, i, grid); } - return count; } ////////////////////////////// // -// Tool_myank::getSectionString -- return the measure range of a section. +// Tool_periodicity::doAnalysis -- // -void Tool_myank::getSectionString(string& sstring, HumdrumFile& infile, int sec) { - int i; - int first = -1; - int second = -1; - int barnum = 0; - int count = 0; - int dataQ = 0; - HumRegex hre; - for (i=0; ifind("||") != string::npos) { - dataQ = 0; - } - if (hre.search(infile.token(i, 0), "(\\d+)")) { - barnum = hre.getMatchInt(1); - } +void Tool_periodicity::doAnalysis(vector>& analysis, int level, vector& grid) { + int period = level + 1; + analysis[level].resize(period); + std::fill(analysis[level].begin(), analysis[level].end(), 0.0); + for (int i=0; i>& grids, HumNum minrhy) { + out << "!!!minrhy: " << minrhy << endl; + out << "**all"; + for (int i=1; i<(int)grids.size(); i++) { + out << "\t**track"; + } + out << "\n"; + for (int j=0; j<(int)grids[0].size(); j++) { + for (int i=0; i<(int)grids.size(); i++) { + out << grids[i][j]; + if (i < (int)grids.size() - 1) { + out << "\t"; + } + } + out << "\n"; + } + for (int i=0; i<(int)grids.size(); i++) { + out << "*-"; + if (i < (int)grids.size() - 1) { + out << "\t"; } } + out << "\n"; - return 1; } ////////////////////////////// // -// Tool_myank::insertZerothMeasure -- +// Tool_periodicity::fillAttackGrids -- // -void Tool_myank::insertZerothMeasure(vector& measurelist, - HumdrumFile& infile) { +void Tool_periodicity::fillAttackGrids(HumdrumFile& infile, vector>& grids, HumNum minrhy) { + HumNum elements = minrhy * infile.getScoreDuration() / 4; - HumRegex hre; - int exinterpline = -1; - int startline = -1; - int stopline = -1; - int i; - for (i=0; iisKern()) { + continue; + } + if (token->isNull()) { + continue; + } + if (!token->isNoteAttack()) { + continue; + } + int track = token->getTrack(); + grids.at(track).at(position.getNumerator()) += 1; } } - if (exinterpline < 0) { - // somethind weird happend, just return - return; - } - if (startline < 0) { - // no zeroth measure; - return; - } - if (stopline < 0) { - // strange situation, no measure numbers - // consider what to do later... - return; + bool oneQ = getBoolean("one"); + for (int j=0; j<(int)grids.at(0).size(); j++) { + grids.at(0).at(j) = 0; + for (int i=0; i<(int)grids.size(); i++) { + if (!grids.at(i).at(j)) { + continue; + } + if (oneQ) { + grids.at(0).at(j) = 1; + } else { + grids.at(0).at(j) += grids.at(i).at(j); + } + } } - - MeasureInfo current; - current.clear(); - current.num = 0; - current.start = startline; - // current.start = exinterpline+1; - current.stop = stopline; - current.file = &infile; - measurelist.push_back(current); } ////////////////////////////// // -// Tool_myank::expandMeasureOutList -- read the measure list for the sequence of measures -// to extract. +// Tool_periodicity::printSvgAnalysis -- // -void Tool_myank::expandMeasureOutList(vector& measureout, - vector& measurein, HumdrumFile& infile, - const string& optionstring) { +void Tool_periodicity::printSvgAnalysis(ostream& out, vector>& analysis, HumNum minrhy) { + pugi::xml_document image; + auto declaration = image.prepend_child(pugi::node_declaration); + declaration.append_attribute("version") = "1.0"; + declaration.append_attribute("encoding") = "UTF-8"; + declaration.append_attribute("standalone") = "no"; - HumRegex hre; - // find the largest measure number in the score - int maxmeasure = -1; - int minmeasure = -1; - for (int i=0; i<(int)measurein.size(); i++) { - if (maxmeasure < measurein[i].num) { - maxmeasure = measurein[i].num; - } - if ((minmeasure == -1) || (minmeasure > measurein[i].num)) { - minmeasure = measurein[i].num; - } - } - if (maxmeasure <= 0 && !getBoolean("lines")) { - cerr << "Error: There are no measure numbers present in the data" << endl; - exit(1); - } - if (maxmeasure > 1123123) { - cerr << "Error: ridiculusly large measure number: " << maxmeasure << endl; - exit(1); + auto svgnode = image.append_child("svg"); + svgnode.append_attribute("version") = "1.1"; + svgnode.append_attribute("xmlns") = "http://www.w3.org/2000/svg"; + svgnode.append_attribute("xmlns:xlink") = "http://www.w3.org/1999/xlink"; + svgnode.append_attribute("overflow") = "visible"; + svgnode.append_attribute("viewBox") = "0 0 1000 1000"; + svgnode.append_attribute("width") = "1000px"; + svgnode.append_attribute("height") = "1000px"; + + auto style = svgnode.append_child("style"); + style.text().set(".label { font: 14px sans-serif; alignment-baseline: middle; text-anchor: left; }"); + + auto grid = svgnode.append_child("g"); + grid.append_attribute("id") = "grid"; + + auto labels = svgnode.append_child("g"); + + double hue = 0.0; + double saturation = 100; + double lightness = 75; + + pugi::xml_node crect; + double width; + double height; + + stringstream ss; + stringstream ssl; + //stringstream css; + double x; + double y; + + double imagewidth = 1000.0; + double imageheight = 1000.0; + + int maxrow = getInteger("max-rows"); + if (maxrow <= 0) { + maxrow = (int)analysis.back().size(); } - if (m_maxQ) { - if (measurein.size() == 0) { - m_humdrum_text << 0 << endl; - } else { - m_humdrum_text << maxmeasure << endl; - } - exit(0); - } else if (m_minQ) { - for (int ii=0; ii inmap(maxmeasure+1); - fill(inmap.begin(), inmap.end(), -1); - for (int i=0; i<(int)measurein.size(); i++) { - inmap[measurein[i].num] = i; - } + double power = getDouble("power"); + for (int i=0; i& range = measureout; - range.reserve(10000); - string searchexp = "^([\\d$-]+[^\\d$-]*)"; - value = hre.search(ostring, searchexp); - while (value != 0) { - start += value - 1; - start += (int)hre.getMatch(1).size(); - processFieldEntry(range, hre.getMatch(1), infile, maxmeasure, measurein, inmap); - value = hre.search(ostring, start, searchexp); + getColorMapping(value, hue, saturation, lightness); + ss << "hsl(" << hue << "," << saturation << "%," << lightness << "%)"; + crect = grid.append_child("rect"); + crect.append_attribute("x") = to_string(x).c_str(); + crect.append_attribute("y") = to_string(y).c_str(); + crect.append_attribute("width") = to_string(width*0.99).c_str(); + crect.append_attribute("height") = to_string(height*0.99).c_str(); + crect.append_attribute("fill") = ss.str().c_str(); + //css << "Xm" << getMeasure1(i) << " Ym" << getMeasure2(j); + //css << " X" << getQon1(i) << " Y" << getQon2(j); + //css << " X" << getQoff1(i) << " Y" << getQoff2(j); + //crect.append_attribute("class") = css.str().c_str(); + ss.str(""); + //css.str(""); + } + + pugi::xml_node label = labels.append_child("text"); + label.append_attribute("class") = "label"; + + HumNum rval = (i+1); + rval /= minrhy; + rval *= 4; + + std::string rhythm = Convert::durationToRecip(rval); + rhythm += " (" + to_string(i+1) + ")"; + label.text().set(rhythm.c_str()); + x = (i+1+0.5)/sdur * imageheight; + y = (i+0.5)/sdur * imagewidth; + label.append_attribute("x") = to_string(x).c_str(); + label.append_attribute("y") = to_string(y).c_str(); } + + image.save(out); } ////////////////////////////// // -// Tool_myank::fillGlobalDefaults -- keep track of the clef, key signature, key, etc. +// Tool_periodicity::getColorMapping -- // -void Tool_myank::fillGlobalDefaults(HumdrumFile& infile, vector& measurein, - vector& inmap) { - int i, j; - HumRegex hre; +void Tool_periodicity::getColorMapping(double input, double& hue, + double& saturation, double& lightness) { + double maxhue = 0.75 * 360.0; + hue = input; + if (hue < 0.0) { + hue = 0.0; + } + hue = hue * hue; + if (hue != 1.0) { + hue *= 0.95; + } - int tracks = infile.getMaxTrack(); - // cerr << "MAX TRACKS " << tracks << " ===============================" << endl; + hue = (1.0 - hue) * 360.0; + if (hue == 0.0) { + // avoid -0.0; + hue = 0.0; + } - vector currclef(tracks+1); - vector currkeysig(tracks+1); - vector currkey(tracks+1); - vector currtimesig(tracks+1); - vector currmet(tracks+1); - vector currtempo(tracks+1); + if (hue > maxhue) { + hue = maxhue; + } + if (hue < 0.0) { + hue = maxhue; + } - MyCoord undefMyCoord; - undefMyCoord.clear(); + saturation = 100.0; + lightness = 50.0; - fill(currclef.begin(), currclef.end(), undefMyCoord); - fill(currkeysig.begin(), currkeysig.end(), undefMyCoord); - fill(currkey.begin(), currkey.end(), undefMyCoord); - fill(currtimesig.begin(), currtimesig.end(), undefMyCoord); - fill(currmet.begin(), currmet.end(), undefMyCoord); - fill(currtempo.begin(), currtempo.end(), undefMyCoord); + if (hue > 60) { + lightness = lightness - (hue-60) / (maxhue-60) * lightness / 1.5; + } +} - int currmeasure = -1; - int lastmeasure = -1; - int datafound = 0; - int track; - int thingy = 0; - for (i=0; i= 0) { - measurein[inmap[currmeasure]].eclef = currclef; - measurein[inmap[currmeasure]].ekeysig = currkeysig; - measurein[inmap[currmeasure]].ekey = currkey; - measurein[inmap[currmeasure]].etimesig = currtimesig; - measurein[inmap[currmeasure]].emet = currmet; - measurein[inmap[currmeasure]].etempo = currtempo; - } - lastmeasure = currmeasure; - currmeasure = hre.getMatchInt(1); +///////////////////////////////// +// +// Tool_gridtest::Tool_phrase -- Set the recognized options for the tool. +// - if (currmeasure < (int)inmap.size()) { - // [20120818] Had to compensate for last measure being single - // and un-numbered. - if (inmap[currmeasure] < 0) { - // [20111008] Had to compensate for "==85" barline - datafound = 0; - break; - } -// cerr << "CURRCLEF: "; -// for (int z=0; z<(int)currclef.size(); z++) { -// cerr << "(" << currclef[z].x << "," << currclef[z].y << ") "; -// } -// cerr << endl; - measurein[inmap[currmeasure]].sclef = currclef; - measurein[inmap[currmeasure]].skeysig = currkeysig; - measurein[inmap[currmeasure]].skey = currkey; - measurein[inmap[currmeasure]].stimesig = currtimesig; - // measurein[inmap[currmeasure]].smet = metstates[i]; - measurein[inmap[currmeasure]].smet = currmet; - measurein[inmap[currmeasure]].stempo = currtempo; - } +Tool_phrase::Tool_phrase(void) { + define("A|no-average=b", "do not do average phrase-length analysis"); + define("R|remove2=b", "remove phrase boundaries in data and do not do analysis"); + define("m|mark=b", "mark phrase boundaries based on rests"); + define("r|remove=b", "remove phrase boundaries in data"); + define("c|color=s", "display color of analysis data"); +} - datafound = 0; - continue; - } - if (infile[i].isInterpretation()) { - for (j=0; jisKern()) { - continue; - } - track = infile.token(i, j)->getTrack(); - if ((datafound == 0) && (lastmeasure >= 0)) { - if (infile.token(i, j)->compare(0, 5, "*clef") == 0) { - measurein[inmap[currmeasure]].sclef[track].x = -1; - measurein[inmap[currmeasure]].sclef[track].y = -1; - } else if (hre.search(infile.token(i, j), "^\\*k\\[.*\\]", "")) { - measurein[inmap[currmeasure]].skeysig[track].x = -1; - measurein[inmap[currmeasure]].skeysig[track].y = -1; - } else if (hre.search(infile.token(i, j), "^\\*[A-G][#-]?:", "i")) { - measurein[inmap[currmeasure]].skey[track].x = -1; - measurein[inmap[currmeasure]].skey[track].y = -1; - } else if (hre.search(infile.token(i, j), R"(^\*M\d+/\d+)")) { - measurein[inmap[currmeasure]].stimesig[track].x = -1; - measurein[inmap[currmeasure]].stimesig[track].y = -1; - } else if (hre.search(infile.token(i, j), R"(^\*met\(.*\))")) { - measurein[inmap[currmeasure]].smet[track].x = -1; - measurein[inmap[currmeasure]].smet[track].y = -1; - } else if (hre.search(infile.token(i, j), "^\\*MM\\d+", "i")) { - measurein[inmap[currmeasure]].stempo[track].x = -1; - measurein[inmap[currmeasure]].stempo[track].y = -1; - } - } - if (infile.token(i, j)->compare(0, 5, "*clef") == 0) { - currclef[track].x = i; - currclef[track].y = j; - continue; - } - if (hre.search(infile.token(i, j), R"(^\*k\[.*\])")) { - currkeysig[track].x = i; - currkeysig[track].y = j; - continue; - } - if (hre.search(infile.token(i, j), "^\\*[A-G][#-]?:", "i")) { - currkey[track].x = i; - currkey[track].y = j; - continue; - } - if (hre.search(infile.token(i, j), R"(^\*M\d+/\d+)")) { - currtimesig[track].x = i; - currtimesig[track].y = j; - continue; - } - if (hre.search(infile.token(i, j), R"(^\*met\(.*\))")) { - currmet[track].x = i; - currmet[track].y = j; - continue; - } - if (hre.search(infile.token(i, j), R"(^\*MM[\d.]+)")) { - currtempo[track].x = i; - currtempo[track].y = j; - continue; - } +/////////////////////////////// +// +// Tool_phrase::run -- Primary interfaces to the tool. +// - } - } - if (infile[i].isData()) { - datafound = 1; - } +bool Tool_phrase::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; i= 0) && (currmeasure < (int)inmap.size()) - && (inmap[currmeasure] >= 0)) { - measurein[inmap[currmeasure]].eclef = currclef; - measurein[inmap[currmeasure]].ekeysig = currkeysig; - measurein[inmap[currmeasure]].ekey = currkey; - measurein[inmap[currmeasure]].etimesig = currtimesig; - measurein[inmap[currmeasure]].emet = currmet; - measurein[inmap[currmeasure]].etempo = currtempo; - } - // go through the measure list and clean up start/end states - for (i=0; i<(int)measurein.size()-2; i++) { +bool Tool_phrase::run(const string& indata, ostream& out) { + HumdrumFile infile(indata); + return run(infile, out); +} - if (measurein[i].sclef.size() == 0) { - measurein[i].sclef.resize(tracks+1); - fill(measurein[i].sclef.begin(), measurein[i].sclef.end(), undefMyCoord); - } - if (measurein[i].eclef.size() == 0) { - measurein[i].eclef.resize(tracks+1); - fill(measurein[i].eclef.begin(), measurein[i].eclef.end(), undefMyCoord); - } - if (measurein[i+1].sclef.size() == 0) { - measurein[i+1].sclef.resize(tracks+1); - fill(measurein[i+1].sclef.begin(), measurein[i+1].sclef.end(), undefMyCoord); - } - if (measurein[i+1].eclef.size() == 0) { - measurein[i+1].eclef.resize(tracks+1); - fill(measurein[i+1].eclef.begin(), measurein[i+1].eclef.end(), undefMyCoord); - } - for (j=1; j<(int)measurein[i].sclef.size(); j++) { - if (!measurein[i].eclef[j].isValid()) { - if (measurein[i].sclef[j].isValid()) { - measurein[i].eclef[j] = measurein[i].sclef[j]; - } - } - if (!measurein[i+1].sclef[j].isValid()) { - if (measurein[i].eclef[j].isValid()) { - measurein[i+1].sclef[j] = measurein[i].eclef[j]; - } - } - } - if (measurein[i].skeysig.size() == 0) { - measurein[i].skeysig.resize(tracks+1); - fill(measurein[i].skeysig.begin(), measurein[i].skeysig.end(), undefMyCoord); - } - if (measurein[i].ekeysig.size() == 0) { - measurein[i].ekeysig.resize(tracks+1); - fill(measurein[i].ekeysig.begin(), measurein[i].ekeysig.end(), undefMyCoord); - } - if (measurein[i+1].skeysig.size() == 0) { - measurein[i+1].skeysig.resize(tracks+1); - fill(measurein[i+1].skeysig.begin(), measurein[i+1].skeysig.end(), undefMyCoord); - } - if (measurein[i+1].ekeysig.size() == 0) { - measurein[i+1].ekeysig.resize(tracks+1); - fill(measurein[i+1].ekeysig.begin(), measurein[i+1].ekeysig.end(), undefMyCoord); - } - for (j=1; j<(int)measurein[i].skeysig.size(); j++) { - if (!measurein[i].ekeysig[j].isValid()) { - if (measurein[i].skeysig[j].isValid()) { - measurein[i].ekeysig[j] = measurein[i].skeysig[j]; - } - } - if (!measurein[i+1].skeysig[j].isValid()) { - if (measurein[i].ekeysig[j].isValid()) { - measurein[i+1].skeysig[j] = measurein[i].ekeysig[j]; - } - } - } +bool Tool_phrase::run(HumdrumFile& infile, ostream& out) { + bool status = run(infile); + return status; +} - if (measurein[i].skey.size() == 0) { - measurein[i].skey.resize(tracks+1); - fill(measurein[i].skey.begin(), measurein[i].skey.end(), undefMyCoord); - } - if (measurein[i].ekey.size() == 0) { - measurein[i].ekey.resize(tracks+1); - fill(measurein[i].ekey.begin(), measurein[i].ekey.end(), undefMyCoord); - } - if (measurein[i+1].skey.size() == 0) { - measurein[i+1].skey.resize(tracks+1); - fill(measurein[i+1].skey.begin(), measurein[i+1].skey.end(), undefMyCoord); - } - if (measurein[i+1].ekey.size() == 0) { - measurein[i+1].ekey.resize(tracks+1); - fill(measurein[i+1].ekey.begin(), measurein[i+1].ekey.end(), undefMyCoord); - } - for (j=1; j<(int)measurein[i].skey.size(); j++) { - if (!measurein[i].ekey[j].isValid()) { - if (measurein[i].skey[j].isValid()) { - measurein[i].ekey[j] = measurein[i].skey[j]; - } - } - if (!measurein[i+1].skey[j].isValid()) { - if (measurein[i].ekey[j].isValid()) { - measurein[i+1].skey[j] = measurein[i].ekey[j]; - } - } - } - if (measurein[i].stimesig.size() == 0) { - measurein[i].stimesig.resize(tracks+1); - fill(measurein[i].stimesig.begin(), measurein[i].stimesig.end(), undefMyCoord); - } - if (measurein[i].etimesig.size() == 0) { - measurein[i].etimesig.resize(tracks+1); - fill(measurein[i].etimesig.begin(), measurein[i].etimesig.end(), undefMyCoord); - } - if (measurein[i+1].stimesig.size() == 0) { - measurein[i+1].stimesig.resize(tracks+1); - fill(measurein[i+1].stimesig.begin(), measurein[i+1].stimesig.end(), undefMyCoord); +bool Tool_phrase::run(HumdrumFile& infile) { + initialize(infile); + for (int i=0; i<(int)m_starts.size(); i++) { + if (m_removeQ) { + removePhraseMarks(m_starts[i]); } - if (measurein[i+1].etimesig.size() == 0) { - measurein[i+1].etimesig.resize(tracks+1); - fill(measurein[i+1].etimesig.begin(), measurein[i+1].etimesig.end(), undefMyCoord); + if (m_remove2Q) { + continue; } - for (j=1; j<(int)measurein[i].stimesig.size(); j++) { - if (!measurein[i].etimesig[j].isValid()) { - if (measurein[i].stimesig[j].isValid()) { - measurein[i].etimesig[j] = measurein[i].stimesig[j]; - } - } - if (!measurein[i+1].stimesig[j].isValid()) { - if (measurein[i].etimesig[j].isValid()) { - measurein[i+1].stimesig[j] = measurein[i].etimesig[j]; - } - } + if (hasPhraseMarks(m_starts[i])) { + analyzeSpineByPhrase(i); + } else { + analyzeSpineByRests(i); } + } + if (!m_remove2Q) { + prepareAnalysis(infile); + } + infile.createLinesFromTokens(); + return true; +} - if (measurein[i].smet.size() == 0) { - measurein[i].smet.resize(tracks+1); - fill(measurein[i].smet.begin(), measurein[i].smet.end(), undefMyCoord); - } - if (measurein[i].emet.size() == 0) { - measurein[i].emet.resize(tracks+1); - fill(measurein[i].emet.begin(), measurein[i].emet.end(), undefMyCoord); - } - if (measurein[i+1].smet.size() == 0) { - measurein[i+1].smet.resize(tracks+1); - fill(measurein[i+1].smet.begin(), measurein[i+1].smet.end(), undefMyCoord); - } - if (measurein[i+1].emet.size() == 0) { - measurein[i+1].emet.resize(tracks+1); - fill(measurein[i+1].emet.begin(), measurein[i+1].emet.end(), undefMyCoord); - } - for (j=1; j<(int)measurein[i].smet.size(); j++) { - if (!measurein[i].emet[j].isValid()) { - if (measurein[i].smet[j].isValid()) { - measurein[i].emet[j] = measurein[i].smet[j]; - } - } - if (!measurein[i+1].smet[j].isValid()) { - if (measurein[i].emet[j].isValid()) { - measurein[i+1].smet[j] = measurein[i].emet[j]; - } - } - } - if (measurein[i].stempo.size() == 0) { - measurein[i].stempo.resize(tracks+1); - fill(measurein[i].stempo.begin(), measurein[i].stempo.end(), undefMyCoord); - } - if (measurein[i].etempo.size() == 0) { - measurein[i].etempo.resize(tracks+1); - fill(measurein[i].etempo.begin(), measurein[i].etempo.end(), undefMyCoord); - } - if (measurein[i+1].stempo.size() == 0) { - measurein[i+1].stempo.resize(tracks+1); - fill(measurein[i+1].stempo.begin(), measurein[i+1].stempo.end(), undefMyCoord); - } - if (measurein[i+1].etempo.size() == 0) { - measurein[i+1].etempo.resize(tracks+1); - fill(measurein[i+1].etempo.begin(), measurein[i+1].etempo.end(), undefMyCoord); + +////////////////////////////// +// +// Tool_phrase::prepareAnalysis -- +// + +void Tool_phrase::prepareAnalysis(HumdrumFile& infile) { + string exinterp = "**cdata"; + infile.appendDataSpine(m_results.back(), "", exinterp); + for (int i = (int)m_results.size()-1; i>0; i--) { + int track = m_starts[i]->getTrack(); + infile.insertDataSpineBefore(track, m_results[i-1], "", exinterp); + } + if (m_averageQ) { + addAverageLines(infile); + } + if (!m_color.empty()) { + int insertline = -1; + for (int i=0; i 0) { + stringstream ss; + int fsize = infile[insertline].getFieldCount(); + for (int j=0; jgetDataType(); + if (dt.empty() || (dt == "**cdata")) { + ss << "color:" << m_color; } - } - if (!measurein[i+1].stempo[j].isValid()) { - if (measurein[i].etempo[j].isValid()) { - measurein[i+1].stempo[j] = measurein[i].etempo[j]; + if (j < fsize - 1) { + ss << "\t"; } } + string output = ss.str(); + infile.insertLine(insertline, output); } } } -////////////////////////////// +/////////////////////////////// // -// Tool_myank::processFieldEntry -- -// 3-6 expands to 3 4 5 6 -// $ expands to maximum spine track -// $0 expands to maximum spine track -// $1 expands to maximum spine track minus 1, etc. -// 2-$1 expands to 2 through the maximum minus one. -// 6-3 expands to 6 5 4 3 -// $2-5 expands to the maximum minus 2 down through 5. -// Ignore negative values and values which exceed the maximum value. +// Tool_pharse::addAverageLines -- // -void Tool_myank::processFieldEntry(vector& field, - const string& str, HumdrumFile& infile, int maxmeasure, - vector& inmeasures, vector& inmap) { - - MeasureInfo current; - - HumRegex hre; - string buffer = str; - - // remove any comma left at end of input string (or anywhere else) - hre.replaceDestructive(buffer, "", ",", "g"); +void Tool_phrase::addAverageLines(HumdrumFile& infile) { + vector averages; + averages.resize(m_starts.size()+1); + int tcount = 0; + HumNum tsum = 0; + double average; + stringstream ss; + for (int i=0; i<(int)m_starts.size(); i++) { + if (m_pcount[i] > 0) { + average = m_psum[i].getFloat() / m_pcount[i]; + } else { + average = 0.0; + } + ss.str(""); + ss.clear(); + ss << "!!average-phrase-length-k" << i+1 << ":\t" << average; + averages[i+1] = ss.str(); + tcount += m_pcount[i]; + tsum += m_psum[i]; + } + average = tsum.getFloat() / tcount; + ss.str(""); + ss.clear(); + ss << "!!average-phrase-length:\t" << average; + averages[0] = ss.str(); - string measureStyling = ""; - if (hre.search(buffer, "([|:!=]+)$")) { - measureStyling = hre.getMatch(1); - hre.replaceDestructive(buffer, "", "([|:!=]+)$"); + for (int i=0; i<(int)averages.size(); i++) { + infile.appendLine(averages[i]); } +} - if (hre.search(buffer, "^(\\d+)[a-z]?-(\\d+)[a-z]?$")) { - // processing a measure range - int firstone = hre.getMatchInt(1); - int lastone = hre.getMatchInt(2); - // limit the range to 0 to maxmeasure - if (firstone > maxmeasure) { firstone = maxmeasure; } - if (lastone > maxmeasure) { lastone = maxmeasure; } - if (firstone < 0 ) { firstone = 0 ; } - if (lastone < 0 ) { lastone = 0 ; } - if ((firstone < 1) && (firstone != 0)) { - cerr << "Error: range token: \"" << str << "\"" - << " contains too small a number at start: " << firstone << endl; - cerr << "Minimum number allowed is " << 1 << endl; - exit(1); - } - if ((lastone < 1) && (lastone != 0)) { - cerr << "Error: range token: \"" << str << "\"" - << " contains too small a number at end: " << lastone << endl; - cerr << "Minimum number allowed is " << 1 << endl; - exit(1); - } +/////////////////////////////// +// +// Tool_phrase::initialize -- +// - if (firstone > lastone) { - // reverse the order of the measures - for (int i=firstone; i>=lastone; i--) { - if (inmap[i] >= 0) { - current.clear(); - current.file = &infile; - current.num = i; - current.start = inmeasures[inmap[i]].start; - current.stop = inmeasures[inmap[i]].stop; +void Tool_phrase::initialize(HumdrumFile& infile) { + m_starts = infile.getKernSpineStartList(); + m_results.resize(m_starts.size()); + int lines = infile.getLineCount(); + for (int i=0; i<(int)m_results.size(); i++) { + m_results[i].resize(lines); + } + m_pcount.resize(m_starts.size()); + m_psum.resize(m_starts.size()); + std::fill(m_pcount.begin(), m_pcount.end(), 0); + std::fill(m_psum.begin(), m_psum.end(), 0); + m_markQ = getBoolean("mark"); + m_removeQ = getBoolean("remove"); + m_averageQ = !getBoolean("no-average"); + m_remove2Q = getBoolean("remove2"); + if (getBoolean("color")) { + m_color = getString("color"); + } +} - current.sclef = inmeasures[inmap[i]].sclef; - current.skeysig = inmeasures[inmap[i]].skeysig; - current.skey = inmeasures[inmap[i]].skey; - current.stimesig = inmeasures[inmap[i]].stimesig; - current.smet = inmeasures[inmap[i]].smet; - current.stempo = inmeasures[inmap[i]].stempo; - current.eclef = inmeasures[inmap[i]].eclef; - current.ekeysig = inmeasures[inmap[i]].ekeysig; - current.ekey = inmeasures[inmap[i]].ekey; - current.etimesig = inmeasures[inmap[i]].etimesig; - current.emet = inmeasures[inmap[i]].emet; - current.etempo = inmeasures[inmap[i]].etempo; - field.push_back(current); +/////////////////////////////// +// +// Tool_phrase::analyzeSpineByRests -- +// + +void Tool_phrase::analyzeSpineByRests(int index) { + HTp start = m_starts[index]; + HTp current = start; + HTp lastnote = NULL; // last note to be processed + HTp pstart = NULL; // phrase start; + HumNum dur; + stringstream ss; + while (current) { + if (current->isBarline()) { + if (current->find("||") != std::string::npos) { + if (pstart) { + dur = current->getDurationFromStart() + - pstart->getDurationFromStart(); + ss.str(""); + ss.clear(); + ss << dur.getFloat(); + m_psum[index] += dur; + m_pcount[index]++; + m_results[index][pstart->getLineIndex()] = ss.str(); + pstart = NULL; + if (m_markQ && lastnote) { + lastnote->setText(lastnote->getText() + "}"); + } } } - } else { - // measure range not reversed - for (int i=firstone; i<=lastone; i++) { - if (inmap[i] >= 0) { - current.clear(); - current.file = &infile; - current.num = i; - current.start = inmeasures[inmap[i]].start; - current.stop = inmeasures[inmap[i]].stop; - - current.sclef = inmeasures[inmap[i]].sclef; - current.skeysig = inmeasures[inmap[i]].skeysig; - current.skey = inmeasures[inmap[i]].skey; - current.stimesig = inmeasures[inmap[i]].stimesig; - current.smet = inmeasures[inmap[i]].smet; - current.stempo = inmeasures[inmap[i]].stempo; - - current.eclef = inmeasures[inmap[i]].eclef; - current.ekeysig = inmeasures[inmap[i]].ekeysig; - current.ekey = inmeasures[inmap[i]].ekey; - current.etimesig = inmeasures[inmap[i]].etimesig; - current.emet = inmeasures[inmap[i]].emet; - current.etempo = inmeasures[inmap[i]].etempo; - - field.push_back(current); + } + if (!current->isData()) { + current = current->getNextToken(); + continue; + } + if (current->isNull()) { + current = current->getNextToken(); + continue; + } + if (pstart && current->isRest()) { + if (lastnote) { + dur = current->getDurationFromStart() + - pstart->getDurationFromStart(); + ss.str(""); + ss.clear(); + ss << dur.getFloat(); + m_psum[index] += dur; + m_pcount[index]++; + m_results[index][pstart->getLineIndex()] = ss.str(); + if (m_markQ) { + lastnote->setText(lastnote->getText() + "}"); } } + pstart = NULL; + lastnote = NULL; + current = current->getNextToken(); + continue; } - } else if (hre.search(buffer, "^(\\d+)([a-z]*)")) { - // processing a single measure number - int value = hre.getMatchInt(1); - // do something with letter later... - - if ((value < 1) && (value != 0)) { - cerr << "Error: range token: \"" << str << "\"" - << " contains too small a number at end: " << value << endl; - cerr << "Minimum number allowed is " << 1 << endl; - exit(1); + if (current->isRest()) { + current = current->getNextToken(); + continue; } - if (inmap[value] >= 0) { - current.clear(); - current.file = &infile; - current.num = value; - current.start = inmeasures[inmap[value]].start; - current.stop = inmeasures[inmap[value]].stop; - - current.sclef = inmeasures[inmap[value]].sclef; - current.skeysig = inmeasures[inmap[value]].skeysig; - current.skey = inmeasures[inmap[value]].skey; - current.stimesig = inmeasures[inmap[value]].stimesig; - current.smet = inmeasures[inmap[value]].smet; - current.stempo = inmeasures[inmap[value]].stempo; - - current.eclef = inmeasures[inmap[value]].eclef; - current.ekeysig = inmeasures[inmap[value]].ekeysig; - current.ekey = inmeasures[inmap[value]].ekey; - current.etimesig = inmeasures[inmap[value]].etimesig; - current.emet = inmeasures[inmap[value]].emet; - current.etempo = inmeasures[inmap[value]].etempo; - - field.push_back(current); + if (current->isNote()) { + lastnote = current; + } + if (pstart && current->isNote() && (current->find(";") != std::string::npos)) { + // fermata at end of phrase. + dur = current->getDurationFromStart() + current->getDuration() + - pstart->getDurationFromStart(); + ss.str(""); + ss.clear(); + ss << dur.getFloat(); + m_psum[index] += dur; + m_pcount[index]++; + m_results[index][pstart->getLineIndex()] = ss.str(); + if (m_markQ) { + current->setText(current->getText() + "}"); + } + current = current->getNextToken(); + pstart = NULL; + continue; + } + if (current->isNote() && pstart == NULL) { + pstart = current; + if (m_markQ) { + current->setText("{" + current->getText()); + } + } + current = current->getNextToken(); + } + if (pstart) { + dur = start->getOwner()->getOwner()->getScoreDuration() + - pstart->getDurationFromStart(); + ss.str(""); + ss.clear(); + ss << dur.getFloat(); + m_psum[index] += dur; + m_pcount[index]++; + m_results[index][pstart->getLineIndex()] = ss.str(); + if (m_markQ && lastnote) { + lastnote->setText(lastnote->getText() + "}"); } } - - field.back().stopStyle = measureStyling; - } -////////////////////////////// +/////////////////////////////// // -// Tool_myank::removeDollarsFromString -- substitute $ sign for maximum track count. +// Tool_phrase::analyzeSpineByPhrase -- // -void Tool_myank::removeDollarsFromString(string& buffer, int maxx) { - HumRegex hre; - HumRegex hre2; - string tbuf; - string obuf; - int outval; - int value; - - if (m_debugQ) { - m_free_text << "MEASURE STRING BEFORE DOLLAR REMOVAL: " << buffer << endl; - } - - while (hre.search(buffer, "(\\$\\d*)", "")) { - tbuf = hre.getMatch(1); - if (hre2.search(tbuf, "(\\$\\d+)")) { - sscanf(hre2.getMatch(1).c_str(), "$%d", &value); - outval = maxx - value; - } else { - outval = maxx; +void Tool_phrase::analyzeSpineByPhrase(int index) { + HTp start = m_starts[index]; + HTp current = start; + HTp pstart = NULL; // phrase start; + HumNum dur; + stringstream ss; + while (current) { + if (!current->isData()) { + current = current->getNextToken(); + continue; } - - if (outval < 0) { - outval = 0; + if (current->isNull()) { + current = current->getNextToken(); + continue; } - - tbuf = to_string(outval); - obuf = "\\"; - obuf += hre.getMatch(1); - hre.replaceDestructive(buffer, tbuf, obuf); - } - if (m_debugQ) { - m_free_text << "DOLLAR EXPAND: " << buffer << endl; + if (current->find("{") != std::string::npos) { + pstart = current; + current = current->getNextToken(); + continue; + } + if (current->find("}") != std::string::npos) { + if (pstart) { + dur = current->getDurationFromStart() + current->getDuration() + - pstart->getDurationFromStart(); + ss.str(""); + ss.clear(); + ss << dur.getFloat(); + m_psum[index] += dur; + m_pcount[index]++; + m_results[index][pstart->getLineIndex()] = ss.str(); + } + current = current->getNextToken(); + continue; + } + current = current->getNextToken(); } } - - - ////////////////////////////// // -// Tool_myank::example -- example function calls to the program. +// Tool_phrase::removePhraseMarks -- Remvoe { and } characters from **kern data. // -void Tool_myank::example(void) { - - +void Tool_phrase::removePhraseMarks(HTp start) { + HTp current = start; + HumRegex hre; + while (current) { + if (!current->isData()) { + current = current->getNextToken(); + continue; + } + if (current->isNull()) { + current = current->getNextToken(); + continue; + } + if (current->find("{") != std::string::npos) { + string data = *current; + hre.replaceDestructive(data, "", "\\{", "g"); + current->setText(data); + } + if (current->find("}") != std::string::npos) { + string data = *current; + hre.replaceDestructive(data, "", "\\}", "g"); + current->setText(data); + } + current = current->getNextToken(); + } } ////////////////////////////// // -// Tool_myank::usage -- command-line usage description and brief summary +// Tool_phrase::hasPhraseMarks -- True if **kern data spine (primary layer), has +// "{" (or "}", but this is not checked) characters (phrase markers). // -void Tool_myank::usage(const string& ommand) { - +bool Tool_phrase::hasPhraseMarks(HTp start) { + HTp current = start; + while (current) { + if (!current->isData()) { + current = current->getNextToken(); + continue; + } + if (current->find("{") != std::string::npos) { + return true; + } + current = current->getNextToken(); + } + return false; } + ///////////////////////////////// // -// Tool_nproof::Tool_nproof -- Set the recognized options for the tool. +// Tool_pline::Tool_pline -- Set the recognized options for the tool. // -Tool_nproof::Tool_nproof(void) { - define("B|no-blank|no-blanks=b", "do not check for blank lines.\n"); - define("b|only-blank|only-blanks=b", "only check for blank lines.\n"); - - define("I|no-instrument|no-instruments=b", "do not check instrument interpretations.\n"); - define("i|only-instrument|only-instruments=b", "only check instrument interpretations.\n"); - - define("K|no-key=b", "do not check for !!!key: manual initial key designation.\n"); - define("k|only-key=b", "only check for !!!key: manual initial key designation.\n"); - - define("R|no-reference=b", "do not check for reference records.\n"); - define("r|only-reference=b", "only check for reference records.\n"); - - define("T|no-termination|no-terminations=b", "do not check spine terminations.\n"); - define("t|only-termination|only-terminations=b", "only check spine terminations.\n"); - - define("file|filename=b", "print filename with raw count (if available).\n"); - define("raw=b", "only print error count.\n"); +Tool_pline::Tool_pline(void) { + define("c|color=b", "color poetic lines (currently only by notes)"); + define("o|overlap=b", "do overlap analysis/markup"); } ///////////////////////////////// // -// Tool_nproof::run -- Do the main work of the tool. +// Tool_pline::run -- Do the main work of the tool. // -bool Tool_nproof::run(HumdrumFileSet& infiles) { +bool Tool_pline::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i 0) { - m_humdrum_text << m_errorList; - m_humdrum_text << "!!!TOOL-nproof-error-count: " << m_errorCount << endl; - m_humdrum_text << "!!@@BEGIN: PREHTML\n"; - m_humdrum_text << "!!@TOOL: nproof\n"; - m_humdrum_text << "!!@CONTENT:\n"; - m_humdrum_text << "!!

@{TOOL-nproof-error-count} problem"; - if (m_errorCount != 1) { - m_humdrum_text << "s"; +////////////////////////////// +// +// Tool_pline::markRests -- +// + +void Tool_pline::markRests(HumdrumFile& infile) { + vector spinestops; + infile.getSpineStopList(spinestops); + for (int i=0; i<(int)spinestops.size(); i++) { + if (!spinestops[i]->isKern()) { + continue; } - m_humdrum_text << " detected

\n"; - m_humdrum_text << "!!
    \n"; - m_humdrum_text << m_errorHtml; - m_humdrum_text << "!!
\n"; - m_humdrum_text << "!!@@END: PREHTML\n"; - } else { - m_humdrum_text << "!!@@BEGIN: PREHTML\n"; - m_humdrum_text << "!!@TOOL: nproof\n"; - m_humdrum_text << "!!@CONTENT:\n"; - m_humdrum_text << "!!

No problems detected

\n"; - m_humdrum_text << "!!@@END: PREHTML\n"; + markSpineRests(spinestops[i]); } } - ////////////////////////////// // -// Tool_nproof::checkForBlankLines -- +// Tool_pline::markSpineRests -- // -void Tool_nproof::checkForBlankLines(HumdrumFile& infile) { - vector blanks; - // -1: Not checking for a blank line at the very end of the score. - for (int i=0; igetTrack(); + int lastValue = -1; + HTp current = spineStop->getPreviousToken(); + int line; + int cvalue; + while (current) { + if (!current->isData()) { + current = current->getPreviousToken(); continue; } - HTp token = infile.token(i, 0); - if (*token == "") { - blanks.push_back(i+1); + if (current->isNull()) { + current = current->getPreviousToken(); + continue; } - } - if (blanks.empty()) { - return; - } + line = current->getLineIndex(); + cvalue = m_lineInfo.at(line).at(track); - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Blank lines on row"; - if (blanks.size() != 1) { - m_errorList += "s"; - } - m_errorList += ": "; - for (int i=0; i<(int)blanks.size(); i++) { - m_errorList += to_string(blanks[i]); - if (i < (int)blanks.size() - 1) { - m_errorList += ", "; + if (current->isRest() && (cvalue != lastValue)) { + string text = *current; + text += marker; + current->setText(text); + } else { + lastValue = cvalue; + string text = *current; + text += "@" + to_string(cvalue); + current->setText(text); } + current = current->getPreviousToken(); } - m_errorList += ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; } ////////////////////////////// // -// Tool_nproof::checkForValidInstrumentCode -- +// Tool_pline::fillLineInfo -- // -void Tool_nproof::checkForValidInstrumentCode(HTp token, - vector>& instrumentList) { - - if ((token->find("&") == string::npos) && (token->find("|") == string::npos)) { - string code = token->substr(2); - for (int i=0; i<(int)instrumentList.size(); i++) { - if (instrumentList[i].first == code) { - return; +void Tool_pline::fillLineInfo(HumdrumFile& infile, vector>& lineinfo) { + lineinfo.clear(); + lineinfo.resize(infile.getLineCount()); + int maxtrack = infile.getMaxTrack(); + HumRegex hre; + for (int i=0; igetTrack(); + lineinfo[i][track] = digit; } } + } - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Unknown instrument code \"" + code + "\" on line " + to_string(token->getLineNumber()) + ", field " + to_string(token->getFieldNumber()) + ". See list of codes at https://bit.ly/humdrum-instrument-codes.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - return; + for (int i=1; i<(int)lineinfo.size() - 1; i++) { + for (int j=1; j<=maxtrack; j++) { + if (lineinfo.at(i).at(j)) { + continue; + } else { + lineinfo.at(i).at(j) = lineinfo.at(i-1).at(j); + } + } } - bool found1 = false; - bool found2 = false; - string inst1; - string inst2; + // for (int i=0; i<(int)lineinfo.size() - 1; i++) { + // for (int j=1; j<=maxtrack; j++) { + // cerr << lineinfo[i][j] << "\t"; + // } + // cerr << endl; + // } + +} + + + +////////////////////////////// +// +// Tool_pline::plineToColor -- +// + +void Tool_pline::plineToColor(HumdrumFile& infile, vector& tokens) { HumRegex hre; - if (hre.match(token, "^\\*I(.*)[&|](I.*)")) { - inst1 = hre.getMatch(1); - inst2 = hre.getMatch(2); + markRests(infile); + for (int i=0; i<(int)tokens.size(); i++) { + if (!hre.search(tokens[i], "^\\*pline:\\s*(\\d+)")) { + continue; + } + int lineNum = hre.getMatchInt(1); + int colorIndex = (lineNum - 1) % m_colors.size(); + string color = m_colors.at(colorIndex); + string text = "*color:"; + text += color; + tokens[i]->setText(text); + } +} - for (int i=0; i<(int)instrumentList.size(); i++) { - if (instrumentList[i].first == inst1) { - found1 = true; + + +////////////////////////////// +// +// Tool_pline::getPlineInterpretations -- +// + +void Tool_pline::getPlineInterpretations(HumdrumFile& infile, vector& tokens) { + HumRegex hre; + for (int i=0; iisKern()) { + continue; } - if (instrumentList[i].first == inst2) { - found2 = true; + if (hre.search(token, "^\\*pline:\\s*(\\d+)")) { + tokens.push_back(token); } } } +} - if (!found1) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Unknown instrument code \"" + inst1 + "\" in token " + *token + " on line " + to_string(token->getLineNumber()) + ", field " + to_string(token->getFieldNumber()) + ". See list of codes at https://bit.ly/humdrum-instrument-codes.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; + + + + +///////////////////////////////// +// +// Tool_gridtest::Tool_pnum -- Set the recognized options for the tool. +// + +Tool_pnum::Tool_pnum(void) { + define("b|base=i:midi", "numeric base of pitch to extract"); + define("D|no-duration=b", "do not include duration"); + define("c|pitch-class=b", "give numeric pitch-class rather than pitch"); + define("o|octave=b", "give octave rather than pitch"); + define("r|rest=s:0", "representation string for rests"); + define("R|no-rests=b", "do not include rests in conversion"); + define("x|attacks-only=b", "only mark lines with note attacks"); +} + + + +/////////////////////////////// +// +// Tool_pnum::run -- Primary interfaces to the tool. +// + +bool Tool_pnum::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; igetLineNumber()) + ", field " + to_string(token->getFieldNumber()) + ". See list of codes at https://bit.ly/humdrum-instrument-codes.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; + +bool Tool_pnum::run(const string& indata, ostream& out) { + HumdrumFile infile(indata); + return run(infile, out); +} + + +bool Tool_pnum::run(HumdrumFile& infile, ostream& out) { + bool status = run(infile); + out << infile; + return status; +} + + +bool Tool_pnum::run(HumdrumFile& infile) { + initialize(infile); + processFile(infile); + infile.createLinesFromTokens(); + return true; +} + + + +////////////////////////////// +// +// Tool_pnum::initialize -- +// + +void Tool_pnum::initialize(HumdrumFile& infile) { + m_midiQ = false; + if (getString("base") == "midi") { + m_base = 12; + m_midiQ = true; + } else { + // check base for valid numbers, but for now default to 12 if unknown + m_base = getInteger("base"); } + m_durationQ = !getBoolean("no-duration"); + m_classQ = getBoolean("pitch-class"); + m_octaveQ = getBoolean("octave"); + m_attacksQ = getBoolean("attacks-only"); + m_rest = getString("rest"); + m_restQ = !getBoolean("no-rests"); } ////////////////////////////// // -// Tool_nproof::checkInstrumentInformation -- +// Tool_pnum::processFile -- // -void Tool_nproof::checkInstrumentInformation(HumdrumFile& infile) { - int codeLine = -1; - int classLine = -1; - HumRegex hre; - - vector> instrumentList = Convert::getInstrumentList(); +void Tool_pnum::processFile(HumdrumFile& infile) { + vector kex; for (int i=0; iisKern()) { continue; } - if (token->compare(0, 3, "*IC") == 0) { - if (classLine < 0) { - classLine = i; - } - } else if (hre.search(token, "^\\*I[a-z]")) { - if (codeLine < 0) { - codeLine = i; - } + if (*token == "**kern") { + kex.push_back(token); + continue; } - } - } - - if (codeLine < 0) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": No instrument code line.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } else { - for (int i=0; iisKern()) { - if (!hre.search(token, "^\\*I[a-z]")) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": expected instrument code on line " + to_string(token->getLineNumber()) + ", field " + to_string(token->getFieldNumber()) + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } else { - checkForValidInstrumentCode(token, instrumentList); - } - } else { - if (*token != "*") { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Expected null interpretation on instrument code line " + to_string(token->getLineNumber()) + ", field " + to_string(token->getFieldNumber()) + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } + if (!token->isData()) { + continue; + } + if (token->isNull()) { + continue; } + convertTokenToBase(token); } } - if (classLine < 0) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": No instrument class line.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } else { - for (int i=0; iisKern()) { - if (!hre.search(token, "^\\*IC[a-z]")) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": expected instrument class on line " + to_string(token->getLineNumber()) + ", field " + to_string(token->getFieldNumber()) + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - } else { - if (*token != "*") { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Expected null interpretation on instrument class line " + to_string(token->getLineNumber()) + ", field " + to_string(token->getFieldNumber()) + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - } + string newex; + for (int i=0; i<(int)kex.size(); i++) { + if (m_midiQ) { + newex = "**pmid"; + } else { + newex = "**b" + to_string(m_base); } + kex[i]->setText(newex); } } @@ -111995,336 +118066,237 @@ void Tool_nproof::checkInstrumentInformation(HumdrumFile& infile) { ////////////////////////////// // -// Tool_nproof::checkReferenceRecords -- +// Tool_pnum::convertTokenToBase -- // -void Tool_nproof::checkReferenceRecords(HumdrumFile& infile) { - vector foundENC; // Musescore encoder's name - vector foundEND; // Musescore encdoer's date - vector foundEED; // VHV editor's name - vector foundEEV; // VHV editor's date - - HumRegex hre; - for (int i=0; igetSubtokenCount(); + for (int i=0; igetSubtoken(i); + output += convertSubtokenToBase(subtok); + if (i < scount - 1) { + output += " "; } - string key = infile[i].getReferenceKey(); + } + token->setText(output); +} - if (hre.search(key, "^EED\\d*$")) { - if (key == "EED") { - foundEED.push_back(i); - } - string value = infile[i].getReferenceValue(); - if (hre.search(value, "^\\d\\d\\d\\d")) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": For EED (Electronic EDitor) record on line " + to_string(i+1) + ", found a date rather than a name: " + value + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - } - if (hre.search(key, "^EEV\\d*$")) { - if (key == "EEV") { - foundEEV.push_back(i);; - } - string value = infile[i].getReferenceValue(); - if (!hre.search(value, "^\\d\\d\\d\\d-\\d\\d-\\d\\d")) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": For EEV (ElEctronic Version) record on line " + to_string(i+1) + ", found a name rather than a date (or invalid date): " + value + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - } - if (hre.search(key, "^ENC\\d*(-modern|-iiif)?$")) { - string value = infile[i].getReferenceValue(); - if (hre.search(value, "^\\d\\d\\d\\d-\\d\\d-\\d\\d")) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": For ENC (Electronic eNCoder) record on line " + to_string(i+1) + ", found a date rather than a name: " + value + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - } - if (hre.search(key, "^END\\d*(-modern|-iiif)?$")) { - string value = infile[i].getReferenceValue(); - if (!hre.search(value, "^\\d\\d\\d\\d-\\d\\d-\\d\\d")) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": For END (Electronic eNcoding Date) record on line " + to_string(i+1) + ", found a name rather than a date (or an invalid date): " + value + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - } - if (hre.search(key, "^ENC(\\d*.*)$")) { - if (key == "ENC") { - foundENC.push_back(i); - } - } - if (hre.search(key, "^ENC-(\\d+.*)$")) { - string newvalue = "ENC" + hre.getMatch(1); - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": " + key + " reference record on line " + to_string(i+1) + " should not include a dash and instead be: " + newvalue + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - if (hre.search(key, "END(\\d*.*)")) { - if (key == "END") { - foundEND.push_back(i); - } - } - if (hre.search(key, "^END-(\\d+.*)$")) { - string newvalue = "END" + hre.getMatch(1); - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": " + key + " reference record on line " + to_string(i+1) + " should not include a dash and instead be: " + newvalue + ".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - if (key == "filter-") { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": \"filter-\" reference record on line " + to_string(i+1) + " should probably be \"filter-modern\" instead.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - if (key == "ENC-mod") { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": ENC-mod reference record on line " + to_string(i+1) + " should be ENC-modern instead.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - if (key == "END-mod") { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": END-mod reference record on line " + to_string(i+1) + " should be END-modern instead.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - if (key == "AIN-mod") { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": AIN-mod reference record on line " + to_string(i+1) + " should be AIN-modern instead.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; + + +////////////////////////////// +// +// Tool_pnum::convertSubtokenToBase -- +// + +string Tool_pnum::convertSubtokenToBase(const string& text) { + int pitch = 0; + if (text.find("r") == string::npos) { + switch (m_base) { + case 7: + pitch = Convert::kernToBase7(text); + break; + case 40: + pitch = Convert::kernToBase40(text); + break; + default: + pitch = Convert::kernToBase12(text); } - if (hre.search(key, "^(.*)-ori$")) { - string piece = hre.getMatch(1); - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": " + key + " reference record on line " + to_string(i+1) + " should not be used (either use " + piece + "-mod or don't add -ori qualifier to " + piece + ").\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; + } else if (!m_restQ) { + return "."; + } + string recip; + if (m_durationQ) { + HumRegex hre; + if (hre.search(text, "(\\d+%?\\d*\\.*)")) { + recip = hre.getMatch(1); } } - // vector foundENC; // Musescore encoder's name - // vector foundEND; // Musescore encdoer's date - // vector foundEED; // VHV editor's name - // vector foundEEV; // VHV editor's date + string output; - if (foundENC.empty()) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Missing ENC (initial encoder's name) reference record.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - if (foundEND.empty()) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Missing END (initial encoding date) reference record.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; + int pc = pitch % m_base; + int oct = pitch / m_base; + + if (m_midiQ) { + // MIDI numbers use 5 for middle-C octave. + pitch += 12; } - if (foundEED.empty()) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Missing EED (Humdrum electronic editor's name) reference record.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; + + int tie = 1; + if (text.find("_") != string::npos) { + tie = -1; } - if (foundEEV.empty()) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Missing EEV (Humdrum electronic edition date) reference record.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; + if (text.find("]") != string::npos) { + tie = -1; } - - if ((foundENC.size() == 1) && (foundEED.size() == 1)) { - if (foundENC[0] > foundEED[0]) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": ENC reference record on line " + to_string(foundENC[0]+1) + " should come before EED reference record on line " + to_string(foundEED[0]+1) + "\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } + pitch *= tie; + if (m_attacksQ && pitch < 0) { + return "."; } - if ((foundEND.size() == 1) && (foundEEV.size() == 1)) { - if (foundEND[0] > foundEEV[0]) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": END reference record on line " + to_string(foundEND[0]+1) + " should come before EEV reference record on line " + to_string(foundEEV[0]+1) + "\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } + if (m_durationQ) { + output += recip; + output += "/"; } - - if ((foundENC.size() == 2) && (foundEED.size() == 0)) { - string date1; - string date2; - if (foundEND.size() == 2) { - date1 = infile[foundEND[0]].getReferenceValue(); - date2 = infile[foundEND[1]].getReferenceValue(); - hre.replaceDestructive(date1, "", "-", "g"); - hre.replaceDestructive(date2, "", "-", "g"); - int number1 = 0; - int number2 = 0; - if (hre.search(date1, "^(20\\d{6})$")) { - number1 = hre.getMatchInt(1); + if (text.find("r") != string::npos) { + output += m_rest; + } else { + if (!m_octaveQ && !m_classQ) { + output += to_string(pitch); + } else { + if (m_classQ) { + if (pitch < 0) { + output += "-"; + } + output += to_string(pc); } - if (hre.search(date2, "^(20\\d{6})$")) { - number2 = hre.getMatchInt(1); + if (m_classQ && m_octaveQ) { + output += ":"; } - if ((number1 > 0) && (number2 > 0)) { - if (number1 > number2) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Second ENC reference record on line " + to_string(foundENC[1]+1) + " should probably be changed to EED reference record (and second END reference record on line " + to_string(foundEND[1]+1) + " changed to EEV).\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } + if (m_octaveQ) { + output += to_string(oct); } - } else { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": There are two ENC records on lines " + to_string(foundENC[0]+1) + " and " + to_string(foundENC[1]+1) + ". The Humdrum editor's name should be changed to EED, and the editing date should be changed from END to EEV if necessary.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; } } + return output; } -////////////////////////////// -// -// Tool_nproof::checkKeyInformation -- -// -void Tool_nproof::checkKeyInformation(HumdrumFile& infile) { - int foundKey = -1; - for (int i=0; icompare(0, 7, "!!!key:") == 0) { - foundKey = i; - break; - } - } - if (foundKey < 0) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": No !!!key: reference record.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - return; - } +#define OBJTAB "\t\t\t\t\t\t" +#define SVGTAG "_99%svg%"; - string value = infile[foundKey].getReferenceValue(); - if (value.empty()) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": !!!key: reference record on line " + to_string(foundKey+1) + " should not be empty. If no key, then use \"none\".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - return; - } +#define SVGTEXT(out, text) \ + if (m_defineQ) { \ + out << "SVG "; \ + } else { \ + out << "t 1 1\n"; \ + out << SVGTAG; \ + } \ + printScoreEncodedText((out), (text)); \ + out << "\n"; - HumRegex hre; - if (hre.search(value, "^([a-gA-G][#-n]?):(dor|phr|lyd|mix|aeo|loc|ion)$")) { - string tonic = hre.getMatch(1); - string mode = hre.getMatch(2); - int major = 0; - if ((mode == "lyd") || (mode == "mix") || (mode == "ion")) { - major = 1; - } - int uppercase = isupper(tonic[0]); - if ((major == 1) && (uppercase == 0)) { - tonic[0] = toupper(tonic[0]); - string correct = tonic + ":" + mode; - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": !!!key: reference record on line " + to_string(foundKey + 1) + " should be \"" + correct + "\".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } else if ((major == 0) && (uppercase == 1)) { - tonic[0] = tolower(tonic[0]); - string correct = tonic + ":" + mode; - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": !!!key: reference record on line " + to_string(foundKey + 1) + " should be \"" + correct + "\".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } - } else if (hre.search(value, "([a-gA-G][#-n]?):(.+)")) { - string mode = hre.getMatch(2); - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Unknown mode in !!!key: reference record contents on line " + to_string(foundKey + 1) + ": \"" + mode + "\".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } else if (!hre.search(value, "([a-gA-G][#-n]?):?")) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Unknown key designation in !!!key: reference record contents on line " + to_string(foundKey + 1) + ": \"" + value + "\".\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - } +////////////////////////////// +// +// _VoiceInfo::_VoiceInfo -- +// + +_VoiceInfo::_VoiceInfo(void) { + clear(); } ////////////////////////////// // -// Tool_nproof::checkSpineTerminations -- +// _VoiceInfo::clear -- // -void Tool_nproof::checkSpineTerminations(HumdrumFile& infile) { - int foundTerminal = 0; - for (int i=infile.getLineCount() - 1; i>0; i--) { - if (!infile[i].isInterpretation()) { - continue; - } - HTp token = infile.token(i, 0); - if (*token == "*-") { - foundTerminal = i; - break; - } +void _VoiceInfo::clear(void) { + name = ""; + abbr = ""; + midibins.resize(128); + fill(midibins.begin(), midibins.end(), 0.0); + diatonic.resize(7 * 12); + for (int i=0; i<(int)diatonic.size(); i++) { + diatonic[i].resize(6); + fill(diatonic[i].begin(), diatonic[i].end(), 0.0); } + track = -1; + kernQ = false; + diafinal.clear(); + accfinal.clear(); + namfinal.clear(); + index = -1; +} - if (!foundTerminal) { - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": No spine terminators.\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; - return; - } - bool problem = false; - for (int i=0; igetSpineInfo(); - if (value.find(" ") != string::npos) { - problem = true; - break; - } - } +////////////////////////////// +// +// _VoiceInfo::print -- +// - if (!problem) { - return; +ostream& _VoiceInfo::print(ostream& out) { + out << "==================================" << endl; + out << "track: " << track << endl; + out << " name: " << name << endl; + out << " abbr: " << abbr << endl; + out << " kern: " << kernQ << endl; + out << " final:"; + for (int i=0; i<(int)diafinal.size(); i++) { + out << " " << diafinal.at(i) << "/" << accfinal.at(i); } - - m_errorCount++; - m_errorList += "!!!TOOL-nproof-error-" + to_string(m_errorCount) + ": Incorrect spine merger(s): "; - for (int i=0; igetSpineInfo() + ">"; - if (i < infile[foundTerminal].getFieldCount() - 1) { - m_errorList += " "; + out << endl; + out << " midi: "; + for (int i=0; i<(int)midibins.size(); i++) { + if (midibins.at(i) > 0.0) { + out << " " << i << ":" << midibins.at(i); } } - m_errorList += "\n"; - m_errorHtml += "!!
  • @{TOOL-nproof-error-" + to_string(m_errorCount) + "}
  • \n"; + out << endl; + out << " diat: "; + for (int i=0; i<(int)diatonic.size(); i++) { + if (diatonic.at(i).at(0) > 0.0) { + out << " " << i << ":" << diatonic.at(i).at(0); + } + } + out << endl; + out << "==================================" << endl; + return out; } - - ///////////////////////////////// // -// Tool_ordergps::Tool_ordergps -- Set the recognized options for the tool. +// Tool_prange::Tool_prange -- Set the recognized options for the tool. // -Tool_ordergps::Tool_ordergps(void) { - define("e|empty=b", "list files that have no group/part/staff (used with -p option)."); - define("f|file=b", "list input files only."); - define("l|list=b", "list files that will be changed."); - define("p|problem=b", "list files that have mixed content for *group, *part, *staff info."); - define("r|reverse=b", "order *staff, *part, *group"); - define("s|staff=b", "Add staff line if none present already in score."); - define("t|top=b", "Place group/part/staff lines first after exinterp."); +Tool_prange::Tool_prange(void) { + + define("A|acc|color-accidentals=b", "add color to accidentals in histogram"); + define("D|diatonic=b", "diatonic counts ignore chormatic alteration"); + define("K|no-key=b", "do not display key signature"); + define("N|norm=b", "normalize pitch counts"); + define("S|score=b", "convert range info to SCORE"); + define("T|no-title=b", "do not display a title"); + define("a|all=b", "generate all-voice analysis"); + define("c|range|count=s:60-71", "count notes in a particular MIDI note number range (inclusive)"); + define("debug=b", "trace input parsing"); + define("d|duration=b", "weight pitches by duration"); + define("e|embed=b", "embed SCORE data in input Humdrum data"); + define("fill=b", "change color of fill only"); + define("finalis|final|last=b", "include finalis note by voice"); + define("f|fraction=b", "display histogram fractions"); + define("h|hover=b", "include svg hover capabilities"); + define("i|instrument=b", "categorize multiple inputs by instrument"); + define("j|jrp=b", "set options for JRP style"); + define("l|local|local-maximum|local-maxima=b", "use maximum values by voice rather than all voices"); + define("no-define=b", "do not use defines in output SCORE data"); + define("pitch=b", "display pitch info in **pitch format"); + define("print=b", "count printed notes rather than sounding"); + define("p|percentile=d:0.0", "display the xth percentile pitch"); + define("q|quartile=b", "display quartile notes"); + define("r|reverse=b", "reverse list of notes in analysis from high to low"); + define("x|extrema=b", "highlight extrema notes in each part"); + define("sx|scorexml|score-xml|ScoreXML|scoreXML=b", "output ScoreXML format"); + define("title=s:", "title for SCORE display"); + } - ///////////////////////////////// // -// Tool_ordergps::run -- Do the main work of the tool. +// Tool_prange::run -- Do the main work of the tool. // -bool Tool_ordergps::run(HumdrumFileSet& infiles) { +bool Tool_prange::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i groupIndex; - vector partIndex; - vector staffIndex; - bool foundProblem = false; - - for (int i=0; icompare(0, 6, "*group") == 0) { - hasGroup = true; - } else if (token->compare(0, 5, "*part") == 0) { - hasPart = true; - } else if (token->compare(0, 6, "*staff") == 0) { - hasStaff = true; - } else { - hasOther = true; - } - } - - if (hasOther && hasGroup) { - foundProblem = true; - if (m_problemQ) { - cerr << infile.getFilename() << " HAS MIXED GROUP LINE:" << endl; - cerr << "\t" << infile[i] << endl; - } - } - - if (hasOther && hasPart) { - foundProblem = true; - if (m_problemQ) { - cerr << infile.getFilename() << " HAS MIXED PART LINE:" << endl; - cerr << "\t" << infile[i] << endl; - } - } - - if (hasOther && hasStaff) { - foundProblem = true; - if (m_problemQ) { - cerr << infile.getFilename() << " HAS MIXED STAFF LINE:" << endl; - cerr << "\t" << infile[i] << endl; - } - } - - if (hasOther) { - continue; - } +void Tool_prange::initialize(void) { + m_accQ = getBoolean("color-accidentals"); + m_addFractionQ = getBoolean("fraction"); + m_allQ = getBoolean("all"); + m_debugQ = getBoolean("debug"); + m_defineQ = false; + m_diatonicQ = getBoolean("diatonic"); + m_durationQ = getBoolean("duration"); + m_fillOnlyQ = getBoolean("fill"); + m_finalisQ = getBoolean("finalis"); + m_hoverQ = getBoolean("hover"); + m_instrumentQ = getBoolean("instrument"); + m_keyQ = !getBoolean("no-key"); + m_listQ = false; + m_localQ = getBoolean("local-maximum"); + m_normQ = getBoolean("norm"); + m_notitleQ = getBoolean("no-title"); + m_percentile = getDouble("percentile"); + m_percentileQ = getBoolean("percentile"); + m_pitchQ = getBoolean("pitch"); + m_printQ = getBoolean("print"); + m_quartileQ = getBoolean("quartile"); + m_rangeQ = getBoolean("range"); + m_reverseQ = !getBoolean("reverse"); + m_scoreQ = getBoolean("score"); + m_title = getString("title"); + m_titleQ = getBoolean("title"); + m_embedQ = getBoolean("embed"); + m_extremaQ = getBoolean("extrema"); - if (hasGroup && hasPart) { - foundProblem = true; - if (m_problemQ) { - cerr << infile.getFilename() << " HAS MIXED GROUP AND PART LINE:" << endl; - cerr << "\t" << infile[i] << endl; - } - } + getRange(m_rangeL, m_rangeH, getString("range")); - if (hasGroup && hasStaff) { - foundProblem = true; - if (m_problemQ) { - cerr << infile.getFilename() << " HAS MIXED GROUP AND STAFF LINE:" << endl; - cerr << "\t" << infile[i] << endl; - } - } + if (getBoolean("jrp")) { + // default style settings for JRP range displays: + m_scoreQ = true; + m_allQ = true; + m_hoverQ = true; + m_accQ = true; + m_finalisQ = true; + m_notitleQ = true; + } - if (hasPart && hasStaff) { - foundProblem = true; - if (m_problemQ) { - cerr << infile.getFilename() << " HAS MIXED PART AND STAFF LINE:" << endl; - cerr << "\t" << infile[i] << endl; - } - } + // The percentile is a fraction from 0.0 to 1.0. + // if the percentile is above 1.0, then it is assumed + // to be a percentage, in which case the value will be + // divided by 100 to get it in the range from 0 to 1. + if (m_percentile > 1) { + m_percentile = m_percentile / 100.0; + } - if (hasGroup) { - groupIndex.push_back(i); - } + #ifdef __EMSCRIPTEN__ + // Default styling for JavaScript version of program: + m_accQ = !getBoolean("color-accidentals"); + m_scoreQ = !getBoolean("score"); + m_embedQ = !getBoolean("embed"); + m_hoverQ = !getBoolean("hover"); + m_notitleQ = !getBoolean("no-title"); + #endif +} - if (hasPart) { - partIndex.push_back(i); - } - if (hasStaff) { - staffIndex.push_back(i); - } - } - if (groupIndex.size() > 1) { - foundProblem = true; - if (m_problemQ) { - cerr << infile.getFilename() << " HAS MORE THAN ONE GROUP LINE:" << endl; - for (int i=0; i<(int)groupIndex.size(); i++) { - cerr << "\t" << infile[groupIndex[i]] << endl; - } - } - } +////////////////////////////// +// +// Tool_prange::processFile -- +// - if (partIndex.size() > 1) { - foundProblem = true; - if (m_problemQ) { - cerr << infile.getFilename() << " HAS MORE THAN ONE PART LINE:" << endl; - for (int i=0; i<(int)partIndex.size(); i++) { - cerr << "\t" << infile[partIndex[i]] << endl; - } - } - } +void Tool_prange::processFile(HumdrumFile& infile) { + prepareRefmap(infile); + vector<_VoiceInfo> voiceInfo; + infile.fillMidiInfo(m_trackMidi); + getVoiceInfo(voiceInfo, infile); + fillHistograms(voiceInfo, infile); - if (staffIndex.size() > 1) { - foundProblem = true; - if (m_problemQ) { - cerr << infile.getFilename() << " HAS MORE THAN ONE STAFF LINE:" << endl; - for (int i=0; i<(int)staffIndex.size(); i++) { - cerr << "\t" << infile[staffIndex[i]] << endl; - } + if (m_debugQ) { + for (int i=0; i<(int)voiceInfo.size(); i++) { + voiceInfo[i].print(cerr); } } - if (m_problemQ) { - if (m_emptyQ) { - if (groupIndex.empty() && partIndex.empty() && staffIndex.empty()) { - cerr << infile.getFilename() << " HAS NO GROUP/PART/STAFF INFO" << endl; + if (m_scoreQ) { + stringstream scoreout; + printScoreFile(scoreout, voiceInfo, infile); + if (m_embedQ) { + if (m_extremaQ) { + doExtremaMarkup(infile); } - } - } else { - if (foundProblem) { - // Don try to fix anything, just echo the input: m_humdrum_text << infile; + printEmbeddedScore(m_humdrum_text, scoreout, infile); } else { - if (m_staffQ && groupIndex.empty() && partIndex.empty() && staffIndex.empty()) { - printStaffLine(infile); - } else { - // Process further here - // Check the order of the group/part/staff lines. - int gindex = groupIndex.empty() ? -1 : groupIndex.at(0); - int pindex = partIndex.empty() ? -1 : partIndex.at(0); - int sindex = staffIndex.empty() ? -1 : staffIndex.at(0); - if (m_topQ) { - printFileTop(infile, gindex, pindex, sindex); - } else { - printFile(infile, gindex, pindex, sindex); - } + if (m_extremaQ) { + doExtremaMarkup(infile); } + m_humdrum_text << scoreout.str(); } + } else { + printAnalysis(m_humdrum_text, voiceInfo[0].midibins); } } @@ -112566,111 +118446,56 @@ void Tool_ordergps::processFile(HumdrumFile& infile) { ////////////////////////////// // -// Tool_ordergps::printFileTop -- Print group/part/staff first after exclusive -// interpretations. +// Tool_prange::doExtremaMarkup -- Mark highest and lowest note +// in each **kern spine. +// // -void Tool_ordergps::printFileTop(HumdrumFile& infile, int gindex, int pindex, int sindex) { - for (int i=0; i= 0) { - m_humdrum_text << infile[sindex] << endl; - } - if (pindex >= 0) { - m_humdrum_text << infile[pindex] << endl; - } - if (gindex >= 0) { - m_humdrum_text << infile[gindex] << endl; - } - } else { - if (gindex >= 0) { - m_humdrum_text << infile[gindex] << endl; - } - if (pindex >= 0) { - m_humdrum_text << infile[pindex] << endl; - } - if (sindex >= 0) { - m_humdrum_text << infile[sindex] << endl; - } +void Tool_prange::doExtremaMarkup(HumdrumFile& infile) { + bool highQ = false; + bool lowQ = false; + for (int i=0; i<(int)m_trackMidi.size(); i++) { + int maxindex = -1; + int minindex = -1; + + for (int j=(int)m_trackMidi[i].size()-1; j>=0; j--) { + if (m_trackMidi[i][j].empty()) { + continue; + } + if (maxindex < 0) { + maxindex = j; + break; } - } else { - m_humdrum_text << infile[i] << endl; } - } -} - + for (int j=1; j<(int)m_trackMidi[i].size(); j++) { + if (m_trackMidi[i][j].empty()) { + continue; + } + if (minindex < 0) { + minindex = j; + break; + } + } -////////////////////////////// -// -// Tool_ordergps::printFile -- Check to see if the group/part/staff -// lines need to be adjusted, and the print the file. Lines -// will be ordered group/part/staff, placing the lines where -// the first of group/part/staff is found. -// - -void Tool_ordergps::printFile(HumdrumFile& infile, int gindex, int pindex, int sindex) { - int startIndex = gindex; - if (pindex >= 0) { - if (startIndex < 0) { - startIndex = pindex; - } else if (pindex < startIndex) { - startIndex = pindex; + if ((maxindex < 0) || (minindex < 0)) { + continue; } + applyMarkup(m_trackMidi[i][maxindex], m_highMark); + applyMarkup(m_trackMidi[i][minindex], m_lowMark); + highQ = true; + lowQ = true; } - if (sindex >= 0) { - if (startIndex < 0) { - startIndex = sindex; - } else if (sindex < startIndex) { - startIndex = sindex; - } + if (highQ) { + string highRdf = "!!!RDF**kern: " + m_highMark + " = marked note, color=\"hotpink\", highest note"; + infile.appendLine(highRdf); } - if (startIndex < 0) { - // no group/part/staff lines in file, so just print it: - m_humdrum_text << infile; - return; + if (lowQ) { + string lowRdf = "!!!RDF**kern: " + m_lowMark + " = marked note, color=\"limegreen\", lowest note"; + infile.appendLine(lowRdf); } - - for (int i=0; i= 0) { - m_humdrum_text << infile[sindex] << endl; - } - if (pindex >= 0) { - m_humdrum_text << infile[pindex] << endl; - } - if (gindex >= 0) { - m_humdrum_text << infile[gindex] << endl; - } - } else { - if (gindex >= 0) { - m_humdrum_text << infile[gindex] << endl; - } - if (pindex >= 0) { - m_humdrum_text << infile[pindex] << endl; - } - if (sindex >= 0) { - m_humdrum_text << infile[sindex] << endl; - } - } - } else if (i == gindex) { - continue; - } else if (i == pindex) { - continue; - } else if (i == sindex) { - continue; - } else { - m_humdrum_text << infile[i] << endl; - } + if (highQ || lowQ) { + infile.createLinesFromTokens(); } } @@ -112678,215 +118503,223 @@ void Tool_ordergps::printFile(HumdrumFile& infile, int gindex, int pindex, int s ////////////////////////////// // -// Tool_ordergps::printStaffLine -- Add a *staff at the start of the -// data since none was detected. Does not label staff-like spines -// other than **kern (such as **kernyy, **kern-mod, **mens). +// Tool_prange::applyMarkup -- +// -void Tool_ordergps::printStaffLine(HumdrumFile& infile) { - for (int i=0; i staffLine(infile[i].getFieldCount(), "*"); - int counter = 0; - for (int j=infile[i].getFieldCount() - 1; j>=0; j--) { - HTp token = infile.token(i, j); - if (token->isKern()) { - counter++; - string text = "*staff" + to_string(counter); - staffLine.at(j) = text; - } - } - for (int j=0; j<(int)staffLine.size(); j++) { - m_humdrum_text << staffLine[j]; - if (j < (int)staffLine.size() - 1) { - m_humdrum_text << '\t'; - } +void Tool_prange::applyMarkup(vector>& notelist, const string& mark) { + for (int i=0; i<(int)notelist.size(); i++) { + HTp token = notelist[i].first; + int subtoken = notelist[i].second; + int tokenCount = token->getSubtokenCount(); + if (tokenCount == 1) { + string text = *token; + text += mark; + token->setText(text); + } else { + string stok = token->getSubtoken(subtoken); + stok = mark + stok; + token->replaceSubtoken(subtoken, stok); } - m_humdrum_text << endl; } } - -///////////////////////////////// +////////////////////////////// // -// Tool_gridtest::Tool_pccount -- Set the recognized options for the tool. +// Tool_prange::printEmbeddedScore -- // -Tool_pccount::Tool_pccount(void) { - define("a|attacks=b", "count attacks instead of durations"); - define("d|data|vega-data=b", "display the vega-lite template."); - define("f|full=b", "full count attacks all single sharps and flats."); - define("ff|double-full=b", "full count attacks all double sharps and flats."); - define("h|html=b", "generate vega-lite HTML content"); - define("i|id=s:id", "ID for use as variable and in plot title"); - define("K|no-key|no-final=b", "do not label key tonic or final"); - define("m|maximum=b", "normalize by maximum count"); - define("n|normalize=b", "normalize counts"); - define("p|page=b", "generate vega-lite stand-alone HTML page"); - define("r|ratio|aspect-ratio=d:0.67", "width*ratio=height of vega-lite plot"); - define("s|script|vega-script=b", "generate vega-lite javascript content"); - define("title=s", "title for plot"); - define("t|template|vega-template=b", "display the vega-lite template."); - define("w|width=i:400", "width of vega-lite plot"); -} - - - -/////////////////////////////// -// -// Tool_pccount::run -- Primary interfaces to the tool. -// +void Tool_prange::printEmbeddedScore(ostream& out, stringstream& scoredata, HumdrumFile& infile) { + int id = getPrangeId(infile); -bool Tool_pccount::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i\n"; + out << "!!@@END: PREHTML\n"; + out << "!!@@BEGIN: SCORE\n"; + out << "!!@ID: prange-" << id << "\n"; + out << "!!@OUTPUTFORMAT: svg\n"; + out << "!!@CROP: yes\n"; + out << "!!@PADDING: 10\n"; + out << "!!@SCALING: 1.5\n"; + out << "!!@SVGFORMAT: yes\n"; + out << "!!@TRANSPARENT: yes\n"; + out << "!!@ANTIALIAS: no\n"; + out << "!!@EMBEDPMX: yes\n"; + out << "!!@ANNOTATE: no\n"; + out << "!!@CONTENTS:\n"; + string line; + while(getline(scoredata, line)) { + out << "!!" << line << endl; } - return status; -} - - -bool Tool_pccount::run(const string& indata, ostream& out) { - HumdrumFile infile(indata); - return run(infile, out); + out << "!!@@END: SCORE\n"; } -bool Tool_pccount::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - return status; -} +////////////////////////////// +// +// Tool_prange::getPrangeId -- Find a line in this form +// ^!!@ID: prange-(\d+)$ +// and return $1+1. Searching backwards since the HTML section +// will likely be at the bottom. Assuming that the prange +// SVG images are stored in sequence, with the highest ID last +// in the file if there are more than one. +// -bool Tool_pccount::run(HumdrumFile& infile) { - initialize(infile); - processFile(infile); - return true; +int Tool_prange::getPrangeId(HumdrumFile& infile) { + string search = "!!@ID: prange-"; + int length = (int)search.length(); + for (int i=infile.getLineCount() - 1; i>=0; i--) { + HTp token = infile.token(i, 0); + if (token->compare(0, length, search) == 0) { + HumRegex hre; + if (hre.search(token, "prange-(\\d+)")) { + return hre.getMatchInt(1) + 1; + } + } + } + return 1; } ////////////////////////////// // -// Tool_pccount::initialize -- +// Tool_prange::mergeAllVoiceInfo -- // -void Tool_pccount::initialize(HumdrumFile& infile) { - m_attack = getBoolean("attacks"); - m_full = getBoolean("full"); - m_doublefull = getBoolean("double-full"); - m_normalize = getBoolean("normalize"); - m_maximum = getBoolean("maximum"); - m_template = getBoolean("vega-template"); - m_data = getBoolean("vega-data"); - m_script = getBoolean("vega-script"); - m_width = getInteger("width"); - m_ratio = getDouble("aspect-ratio"); - m_key = !getBoolean("no-key"); - if (getBoolean("title")) { - m_title = getString("title"); - } - m_html = getBoolean("html"); - m_page = getBoolean("page"); - if (getBoolean("id")) { - m_id = getString("id"); - } else { - string filename = infile.getFilename(); - auto pos = filename.rfind("/"); - if (pos != string::npos) { - filename = filename.substr(pos+1); +void Tool_prange::mergeAllVoiceInfo(vector<_VoiceInfo>& voiceInfo) { + voiceInfo.at(0).diafinal.clear(); + voiceInfo.at(0).accfinal.clear(); + + for (int i=1; i<(int)voiceInfo.size(); i++) { + if (!voiceInfo[i].kernQ) { + continue; } - pos = filename.find("-"); - if (pos != string::npos) { - m_id = filename.substr(0, pos); + for (int j=0; j<(int)voiceInfo.at(i).diafinal.size(); j++) { + voiceInfo.at(0).diafinal.push_back(voiceInfo.at(i).diafinal.at(j)); + voiceInfo.at(0).accfinal.push_back(voiceInfo.at(i).accfinal.at(j)); + voiceInfo.at(0).namfinal.push_back(voiceInfo.at(i).name); } - } - m_parttracks.clear(); - m_names.clear(); - m_abbreviations.clear(); - initializePartInfo(infile); - - - // https://encycolorpedia.com/36cd27 - m_vcolor.clear(); - - m_vcolor["Canto"] = "#e49689"; - m_vcolor["Canto (Canto I)"] = "#e49689"; - m_vcolor["Canto I"] = "#e49689"; - m_vcolor["Canto Primo"] = "#e49689"; - 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"; - m_vcolor["Canto II [Sesto]"] = "#d67365"; - m_vcolor["Canto Sec."] = "#d67365"; - m_vcolor["Canto Secondo"] = "#d67365"; - m_vcolor["Canto secondo"] = "#d67365"; - m_vcolor["[Canto 2]"] = "#d67365"; + for (int j=0; j<(int)voiceInfo[i].midibins.size(); j++) { + voiceInfo[0].midibins[j] += voiceInfo[i].midibins[j]; + } - m_vcolor["Canto III [Settimo]"] = "#c54f43"; + for (int j=0; j<(int)voiceInfo.at(i).diatonic.size(); j++) { + for (int k=0; k<(int)voiceInfo.at(i).diatonic.at(k).size(); k++) { + voiceInfo[0].diatonic.at(j).at(k) += voiceInfo.at(i).diatonic.at(j).at(k); + } + } + } +} - m_vcolor["Alto"] = "#f4c6a1"; - m_vcolor["Alti"] = "#f4c6a1"; - m_vcolor["Alto (Canto III)"] = "#f4c6a1"; - m_vcolor["Alto II"] = "#edb383"; - m_vcolor["Tenor"] = "#ecdf7a"; - m_vcolor["Tenore"] = "#ecdf7a"; - m_vcolor["Tenore over Canto"] = "#ecdf7a"; - m_vcolor["[Tenore]"] = "#ecdf7a"; +////////////////////////////// +// +// Tool_prange::getVoiceInfo -- get names and track info for **kern spines. +// - m_vcolor["Sesto"] = "#c8f0bb"; - m_vcolor["Sesto (Canto II)"] = "#c8f0bb"; - m_vcolor["Sesto Canto II"] = "#c8f0bb"; +void Tool_prange::getVoiceInfo(vector<_VoiceInfo>& voiceInfo, HumdrumFile& infile) { + voiceInfo.clear(); + voiceInfo.resize(infile.getMaxTracks() + 1); + for (int i=0; i<(int)voiceInfo.size(); i++) { + voiceInfo.at(i).index = i; + } - m_vcolor["Quinto"] = "#e3f5f8"; - m_vcolor["Qvinto"] = "#e3f5f8"; + vector kstarts = infile.getKernSpineStartList(); - m_vcolor["Ottava parte [Ottavo]"] = "#e0e4f7"; + if (kstarts.size() == 2) { + voiceInfo[0].name = "both"; + voiceInfo[0].abbr = "both"; + voiceInfo[0].track = 0; + } else { + voiceInfo[0].name = "all"; + voiceInfo[0].abbr = "all"; + voiceInfo[0].track = 0; + } - m_vcolor["Nona parte [Nono]"] = "#a39ce5"; - m_vcolor["Basso"] = "#d2aef7"; - m_vcolor["Bass"] = "#d2aef7"; + for (int i=0; igetTrack(); + voiceInfo[track].track = track; + if (token->isKern()) { + voiceInfo[track].kernQ = true; + } + if (!voiceInfo[track].kernQ) { + continue; + } + if (token->isInstrumentName()) { + voiceInfo[track].name = token->getInstrumentName(); + } + if (token->isInstrumentAbbreviation()) { + voiceInfo[track].abbr = token->getInstrumentAbbreviation(); + } + } + } - m_vcolor["Basso II"] = "#c69af5"; - m_vcolor["Basso II [Decimo]"] = "#c69af5"; - m_vcolor["Basso Continuo"] = "#a071ec"; - m_vcolor["Basso continuo"] = "#a071ec"; - m_vcolor["[B. C.]"] = "#a071ec"; - m_vcolor["[Basso Continuo]"] = "#a071ec"; - m_vcolor["[Basso continuo]"] = "#a071ec"; - m_vcolor["B.C."] = "#a071ec"; - m_vcolor["B.c."] = "#a071ec"; + // Check for piano/Grand Staff parts with LH/RH encoding. + if (kstarts.size() == 2) { + string bottomStaff = getHand(kstarts[0]); + string topStaff = getHand(kstarts[1]); + if (!bottomStaff.empty() && !topStaff.empty()) { + int track = kstarts[0]->getTrack(); + voiceInfo[track].name = "left hand"; + track = kstarts[1]->getTrack(); + voiceInfo[track].name = "right hand"; + } + } } ////////////////////////////// // -// Tool_pccount::getFinal -- Extract the last unparenthesed letter from a ref record like this: -// -// !!!final: (A)D +// Tool_prange::getHand -- // -string Tool_pccount::getFinal(HumdrumFile& infile) { - string finalref = infile.getReferenceRecord("final"); - HumRegex hre; - hre.replaceDestructive(finalref, "", "\\(.*?\\)", "g"); - hre.replaceDestructive(finalref, "", "\\s+", "g"); - if (hre.search(finalref, "^[A-G]$", "i")) { - return finalref; +string Tool_prange::getHand(HTp sstart) { + HTp current = sstart->getNextToken(); + HTp target = NULL; + while (current) { + if (current->isData()) { + break; + } + if (*current == "*LH") { + target = current; + break; + } + if (*current == "*RH") { + target = current; + break; + } + current = current->getNextToken(); + } + + if (target) { + if (*current == "*LH") { + return "LH"; + } else if (*current == "*RH") { + return "RH"; + } else { + return ""; + } } else { return ""; } @@ -112896,38 +118729,44 @@ string Tool_pccount::getFinal(HumdrumFile& infile) { ////////////////////////////// // -// Tool_pccount::processFile -- +// Tool_prange::getInstrumentNames -- Find any instrument names which are listed +// before the first data line. Instrument names are in the form: +// +// *I"name // -void Tool_pccount::processFile(HumdrumFile& infile) { - countPitches(infile); - - string datavar; - string target; - string jsonvar; +void Tool_prange::getInstrumentNames(vector& nameByTrack, vector& kernSpines, + HumdrumFile& infile) { + HumRegex hre; - if (m_attack) { - datavar = "data_" + m_id + "_count"; - target = "id_" + m_id + "_count"; - jsonvar = "vega_" + m_id + "_count"; + int track; + string name; + // nameByTrack.resize(kernSpines.size()); + nameByTrack.resize(infile.getMaxTrack() + 1); + fill(nameByTrack.begin(), nameByTrack.end(), ""); + vector kspines = infile.getKernSpineStartList(); + if (kspines.size() == 2) { + nameByTrack.at(0) = "both"; } else { - datavar = "data_" + m_id + "_dur"; - target = "id_" + m_id + "_dur"; - jsonvar = "vega_" + m_id + "_dur"; + nameByTrack.at(0) = "all"; } - if (m_template) { - printVegaLiteJsonTemplate(datavar, infile); - } else if (m_data) { - printVegaLiteJsonData(); - } else if (m_script) { - printVegaLiteScript(jsonvar, target, datavar, infile); - } else if (m_html) { - printVegaLiteHtml(jsonvar, target, datavar, infile); - } else if (m_page) { - printVegaLitePage(jsonvar, target, datavar, infile); - } else { - printHumdrumTable(); + for (int i=0; igetTrack(); + for (int k=0; k<(int)kernSpines.size(); k++) { + if (track == kernSpines[k]) { + nameByTrack[k] = name; + } + } + } + } } } @@ -112935,471 +118774,810 @@ void Tool_pccount::processFile(HumdrumFile& infile) { ////////////////////////////// // -// Tool_pccount::printVegaLitePage -- +// Tool_prange::fillHistograms -- Store notes in score by MIDI note number. // -void Tool_pccount::printVegaLitePage(const string& jsonvar, - const string& target, const string& datavar, HumdrumFile& infile) { - stringstream& out = m_free_text; +void Tool_prange::fillHistograms(vector<_VoiceInfo>& voiceInfo, HumdrumFile& infile) { + // storage for finals info: + vector> diafinal; + vector> accfinal; + diafinal.resize(infile.getMaxTracks() + 1); + accfinal.resize(infile.getMaxTracks() + 1); - out << "\n"; - out << "\n"; - out << " \n"; - out << " Vega-Lite Bar Chart\n"; - out << " \n"; - out << "\n"; - out << " \n"; - out << " \n"; - out << " \n"; - out << "\n"; - out << " \n"; - out << " \n"; - out << " \n"; - out << "

    Pitch-class histogram

    \n"; - printVegaLiteHtml(jsonvar, target, datavar, infile); - out << "\n"; - out << "\n"; + for (int i=0; iisKern()) { + continue; + } + if (token->isNull()) { + continue; + } + int track = token->getTrack(); + + diafinal.at(track).clear(); + accfinal.at(track).clear(); + + vector tokens = token->getSubtokens(); + for (int k=0; k<(int)tokens.size(); k++) { + if (tokens[k].find("r") != string::npos) { + continue; + } + if (tokens[k].find("R") != string::npos) { + // non-pitched note + continue; + } + bool hasPitch = false; + for (int m=0; m<(int)tokens[k].size(); m++) { + char test = tokens[k].at(m); + if (!isalpha(test)) { + continue; + } + test = tolower(test); + if ((test >= 'a') && (test <= 'g')) { + hasPitch = true; + break; + } + } + if (!hasPitch) { + continue; + } + int octave = Convert::kernToOctaveNumber(tokens[k]) + 3; + if (octave < 0) { + cerr << "Note too low: " << tokens[k] << endl; + continue; + } + if (octave >= 12) { + cerr << "Note too high: " << tokens[k] << endl; + continue; + } + int dpc = Convert::kernToDiatonicPC(tokens[k]); + int acc = Convert::kernToAccidentalCount(tokens[k]); + if (acc < -2) { + cerr << "Accidental too flat: " << tokens[k] << endl; + continue; + } + if (acc > +2) { + cerr << "Accidental too sharp: " << tokens[k] << endl; + continue; + } + int diatonic = dpc + 7 * octave; + int realdiatonic = dpc + 7 * (octave-3); + + diafinal.at(track).push_back(realdiatonic); + accfinal.at(track).push_back(acc); + + acc += 3; + int midi = Convert::kernToMidiNoteNumber(tokens[k]); + if (midi < 0) { + cerr << "MIDI pitch too low: " << tokens[k] << endl; + } + if (midi > 127) { + cerr << "MIDI pitch too high: " << tokens[k] << endl; + } + if (m_durationQ) { + double duration = Convert::kernToDuration(tokens[k]).getFloat(); + voiceInfo[track].diatonic.at(diatonic).at(0) += duration; + voiceInfo[track].diatonic.at(diatonic).at(acc) += duration; + voiceInfo[track].midibins.at(midi) += duration; + } else { + if (tokens[k].find("]") != string::npos) { + continue; + } + if (tokens[k].find("_") != string::npos) { + continue; + } + voiceInfo[track].diatonic.at(diatonic).at(0)++; + voiceInfo[track].diatonic.at(diatonic).at(acc)++; + voiceInfo[track].midibins.at(midi)++; + } + } + } + } + + mergeFinals(voiceInfo, diafinal, accfinal); + + // Sum all voices into midibins and diatonic arrays of vector position 0: + mergeAllVoiceInfo(voiceInfo); } ////////////////////////////// // -// Tool_pccount::printVegaLiteHtml -- +// Tool_prange::mergeFinals -- // -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"; +void Tool_prange::mergeFinals(vector<_VoiceInfo>& voiceInfo, vector>& diafinal, + vector>& accfinal) { + for (int i=0; i<(int)voiceInfo.size(); i++) { + voiceInfo.at(i).diafinal = diafinal.at(i); + voiceInfo.at(i).accfinal = accfinal.at(i); + } } ////////////////////////////// // -// Tool_pccount::printVegaLiteScript -- +// Tool_prange::printFilenameBase -- // -void Tool_pccount::printVegaLiteScript(const string& jsonvar, - const string& target, const string& datavar, HumdrumFile& infile) { - stringstream& out = m_free_text; - - out << "var " << datavar << " =\n"; - printVegaLiteJsonData(); - out << ";\n"; - out << "\n"; - out << "var " << jsonvar << " =\n"; - printVegaLiteJsonTemplate(datavar, infile); - out << ";\n"; - out << "vegaEmbed('#" << target << "', " << jsonvar << ");\n"; +void Tool_prange::printFilenameBase(ostream& out, const string& filename) { + HumRegex hre; + if (hre.search(filename, "([^/]+)\\.([^.]*)", "")) { + if (hre.getMatch(1).size() <= 8) { + printXmlEncodedText(out, hre.getMatch(1)); + } else { + // problem with too long a name (MS-DOS will have problems). + // optimize to chop off everything after the dash in the + // name (for Josquin catalog numbers). + string shortname = hre.getMatch(1); + if (hre.search(shortname, "-.*")) { + hre.replaceDestructive(shortname, "", "-.*"); + printXmlEncodedText(out, shortname); + } else { + printXmlEncodedText(out, shortname); + } + } + } } ////////////////////////////// // -// Tool_pccount::printVegaLiteJsonData -- +// Tool_prange::printReferenceRecords -- // -void Tool_pccount::printVegaLiteJsonData(void) { - stringstream& out = m_free_text; - - m_maxpc = 0; - for (int i=0; i<(int)m_counts[0].size(); i++) { - if (m_counts[0][i] > m_maxpc) { - m_maxpc = m_counts[0][i]; - } - } - out << "[\n"; - int commacounter = 0; - double percent = 100.0; - for (int i=1; i<(int)m_counts.size(); i++) { - for (int j=0; j<(int)m_counts[i].size(); j++) { - if (m_counts[i][j] == 0.0) { - continue; - } - if (commacounter > 0) { - out << ",\n\t"; - } else { - out << "\t"; - } - commacounter++; - if (m_attack) { - out << "{\"count\":" << m_counts[i][j] << ", "; - } else { - out << "{\"percent\":" << m_counts[i][j]/m_maxpc*percent << ", "; - } - out << "\"pitch class\":\"" << getPitchClassString(j) << "\", "; - out << "\"voice\":\"" << m_names[i] << "\""; - out << "}"; +void Tool_prange::printReferenceRecords(ostream& out, HumdrumFile& infile) { + for (int i=0; i\n"; } - out << "\n]\n"; } ////////////////////////////// // -// Tool_pccount::setFactorMaximum -- normalize by the maximum pitch-class value. +// Tool_prange::printScoreEncodedText -- print SCORE text string +// See SCORE 3.1 manual additions (page 19) for more. // -void Tool_pccount::setFactorMaximum(void) { - m_factor = 0.0; - for (int i=0; i<(int)m_counts[0].size(); i++) { - if (m_counts[0][i] > m_factor) { - m_factor = m_counts[0][i]; - } - } +void Tool_prange::printScoreEncodedText(ostream& out, const string& strang) { + string newstring = strang; + HumRegex hre; + + hre.replaceDestructive(newstring, "<<$1", "&([aeiou])acute;", "gi"); + hre.replaceDestructive(newstring, "<<$1", "([áéíóú])", "gi"); + + hre.replaceDestructive(newstring, ">>$1", "&([aeiou])grave;", "gi"); + hre.replaceDestructive(newstring, ">>$1", "([àèìòù])", "gi"); + + hre.replaceDestructive(newstring, "%%$1", "&([aeiou])uml;", "gi"); + hre.replaceDestructive(newstring, "%%$1", "([äëïöü])", "gi"); + + hre.replaceDestructive(newstring, "^^$1", "&([aeiou])circ;", "gi"); + hre.replaceDestructive(newstring, "^^$1", "([âêîôû])", "gi"); + + hre.replaceDestructive(newstring, "##c", "ç", "g"); + hre.replaceDestructive(newstring, "##C", "Ç", "g"); + hre.replaceDestructive(newstring, "?\\|", "\\|", "g"); + hre.replaceDestructive(newstring, "?\\", "\\\\", "g"); + hre.replaceDestructive(newstring, "?m", "---", "g"); + hre.replaceDestructive(newstring, "?n", "--", "g"); + hre.replaceDestructive(newstring, "?2", "-sharp", "g"); + hre.replaceDestructive(newstring, "?1", "-flat", "g"); + hre.replaceDestructive(newstring, "?3", "-natural", "g"); + hre.replaceDestructive(newstring, "\\", "/", "g"); + hre.replaceDestructive(newstring, "?[", "\\[", "g"); + hre.replaceDestructive(newstring, "?]", "\\]", "g"); + + out << newstring; } ////////////////////////////// // -// Tool_pccount::setFactorNormalize -- normalize by the sum of all pitch class values. +// Tool_prange::printXmlEncodedText -- convert +// & to & +// " to " +// ' to &spos; +// < to < +// > to > // -void Tool_pccount::setFactorNormalize(void) { - m_factor = 0.0; - for (int i=0; i<(int)m_counts[0].size(); i++) { - m_factor += m_counts[0][i]; - } +void Tool_prange::printXmlEncodedText(ostream& out, const string& strang) { + HumRegex hre; + string astring = strang; + + hre.replaceDestructive(astring, "&", "&", "g"); + hre.replaceDestructive(astring, "'", "'", "g"); + hre.replaceDestructive(astring, "\"", """, "g"); + hre.replaceDestructive(astring, "<", "<", "g"); + hre.replaceDestructive(astring, ">", ">", "g"); + + out << astring; } ////////////////////////////// // -// Tool_pccount::printHumdrumTable -- +// Tool_prange::printScoreFile -- // -void Tool_pccount::printHumdrumTable(void) { +void Tool_prange::printScoreFile(ostream& out, vector<_VoiceInfo>& voiceInfo, HumdrumFile& infile) { + string titlestring = getTitle(); - double factor = 0.0; + if (m_defineQ) { + out << "#define SVG t 1 1 \\n_99%svg%\n"; + } - if (m_maximum) { - setFactorMaximum(); - m_free_text << "!!!MAX: " << m_factor << endl; - } else if (m_normalize) { - setFactorNormalize(); - m_free_text << "!!!TOTAL: " << factor << endl; + string acctext = "g.bar.doubleflat path{color:darkorange;stroke:darkorange;}g.bar.flat path{color:brown;stroke:brown;}g.bar.sharp path{color:royalblue;stroke:royalblue;}g.bar.doublesharp path{color:aquamarine;stroke:aquamarine;}"; + string hovertext = ".bar:hover path{fill:red;color:red;stroke:red !important}"; + string hoverfilltext = hovertext; + + string text1 = ""; + string text2 = text1; - // exclusive interpretation - m_free_text << "**kern"; - m_free_text << "\t**all"; - for (int i=0; i<(int)m_counts.size() - 1; i++) { - m_free_text << "\t**part"; + + // print CSS style information if requested + if (m_hoverQ) { + SVGTEXT(out, text1); } - m_free_text << endl; - // part names - m_free_text << "*"; - for (int i=0; i<(int)m_counts.size(); i++) { - if (i < (int)m_names.size()) { - m_free_text << "\t*I\"" << m_names.at(i); - } else { - m_free_text << "\t*"; + int maxStaffPosition = getMaxStaffPosition(voiceInfo); + + if (!titlestring.empty()) { + // print title + int vpos = 54; + if (maxStaffPosition > 12) { + vpos = maxStaffPosition + 3; } + out << "t 2 10 "; + out << vpos; + out << " 1 1 0 0 0 0 -1.35\n"; + // out << "_03"; + printScoreEncodedText(out, titlestring); + out << "\n"; } - m_free_text << endl; - if (!m_abbreviations.empty()) { + // print duration label if duration weighting is being used + SVGTEXT(out, ""); + if (m_durationQ) { + out << "t 2 185.075 14 1 0.738 0 0 0 0 0\n"; + out << "_00(durations)\n"; + } else { + out << "t 2 185.075 14 1 0.738 0 0 0 0 0\n"; + out << "_00(attacks)\n"; + } + SVGTEXT(out, ""); - // part abbreviation - m_free_text << "*"; - for (int i=0; i<(int)m_counts.size(); i++) { - if (i < (int)m_abbreviations.size()) { - m_free_text << "\t*I\'" << m_abbreviations.at(i); - } else { - m_free_text << "\t*"; - } - } - m_free_text << endl; + // print staff lines + out << "8 1 0 0 0 200\n"; // staff 1 + out << "8 2 0 -6 0 200\n"; // staff 2 + + int keysig = getKeySignature(infile); + // print key signature + if (keysig) { + out << "17 1 10 0 " << keysig << " 101.0"; + printKeySigCompression(out, keysig, 0); + out << endl; + out << "17 2 10 0 " << keysig; + printKeySigCompression(out, keysig, 1); + out << endl; } - for (int i=0; i<(int)m_counts[0].size(); i++) { - if (m_counts[0][i] == 0) { - continue; - } - if ((i == 5) || (i == 11) || (i == 22) || (i == 28) || (i == 34)) { - continue; - } - string pitch = Convert::base40ToKern(i + 4*40); - m_free_text << pitch; - for (int j=0; j<(int)m_counts.size(); j++) { - if (m_normalize) { - m_free_text << "\t" << m_counts[j][i] / m_factor; - } else if (m_maximum) { - m_free_text << "\t" << m_counts[j][i] / m_factor; - } else { - m_free_text << "\t" << m_counts[j][i]; - } + // print barlines + out << "14 1 0 2\n"; // starting barline + out << "14 1 200 2\n"; // ending barline + out << "14 1 0 2 8\n"; // curly brace at start + + // print clefs + out << "3 2 2\n"; // treble clef + out << "3 1 2 0 1\n"; // bass clef + + assignHorizontalPosition(voiceInfo, 25.0, 170.0); + + double maxvalue = 0.0; + for (int i=1; i<(int)voiceInfo.size(); i++) { + double tempvalue = getMaxValue(voiceInfo.at(i).diatonic); + if (tempvalue > maxvalue) { + maxvalue = tempvalue; } - m_free_text << endl; } - - int columns = (int)m_counts.size() + 1; - for (int i=0; i0; i--) { + if (voiceInfo.at(i).kernQ) { + printScoreVoice(out, voiceInfo.at(i), maxvalue); } } - m_free_text << endl; + if (m_allQ) { + printScoreVoice(out, voiceInfo.at(0), maxvalue); + } +} + + +////////////////////////////// +// +// Tool_prange::getMaxStaffPosition(vector<_VoiceInfo>& voiceinfo) { +// + +int Tool_prange::getMaxStaffPosition(vector<_VoiceInfo>& voiceInfo) { + int maxi = getMaxDiatonicIndex(voiceInfo[0].diatonic); + int maxdiatonic = maxi - 3 * 7; + int staffline = maxdiatonic - 27; + return staffline; } + ////////////////////////////// // -// Tool_pccount::countPitches -- +// Tool_prange::printKeySigCompression -- // -void Tool_pccount::countPitches(HumdrumFile& infile) { - if (m_parttracks.size() == 0) { +void Tool_prange::printKeySigCompression(ostream& out, int keysig, int extra) { + double compression = 0.0; + switch (abs(keysig)) { + case 0: compression = 0.0; break; + case 1: compression = 0.0; break; + case 2: compression = 0.0; break; + case 3: compression = 0.0; break; + case 4: compression = 0.9; break; + case 5: compression = 0.8; break; + case 6: compression = 0.7; break; + case 7: compression = 0.6; break; + } + if (compression <= 0.0) { return; } - m_counts.clear(); - m_counts.resize(m_parttracks.size()); - for (int i=0; i<(int)m_parttracks.size(); i++) { - m_counts[i].resize(40); - fill(m_counts[i].begin(), m_counts[i].end(), 0.0); + for (int i=0; i& voiceInfo, int minval, int maxval) { + int count = 0; + for (int i=1; i<(int)voiceInfo.size(); i++) { + if (voiceInfo[i].kernQ) { + count++; + } + } + if (m_allQ) { + count++; } - // fill in sum for all parts - for (int i=0; i<(int)m_counts[0].size(); i++) { - for (int j=1; j<(int)m_counts.size(); j++) { - m_counts[0][i] += m_counts[j][i]; + vector hpos(count, 0); + hpos[0] = maxval; + hpos.back() = minval; + + if (hpos.size() > 2) { + for (int i=1; i<(int)hpos.size()-1; i++) { + int ii = hpos.size() - i - 1; + hpos[i] = (double)ii / (hpos.size()-1) * (maxval - minval) + minval; } } + int position = 0; + if (m_allQ) { + position = 1; + voiceInfo[0].hpos = hpos[0]; + } + for (int i=0; i<(int)voiceInfo.size(); i++) { + if (voiceInfo.at(i).kernQ) { + voiceInfo.at(i).hpos = hpos.at(position++); + } + } } + ////////////////////////////// // -// Tool_pccount::addCounts -- +// Tool_prange::getKeySignature -- find first key signature in file. // -void Tool_pccount::addCounts(HTp sstart, HTp send) { - if (!sstart) { - return; - } - if (!sstart->isKern()) { - return; - } - int track = sstart->getTrack(); - int kindex = m_rkern[track]; - HTp current = sstart; - while (current && (current != send)) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull() || current->isRest()) { - current = current->getNextToken(); +int Tool_prange::getKeySignature(HumdrumFile& infile) { + for (int i=0; i subtokens = current->getSubtokens(); - for (int i=0; i<(int)subtokens.size(); i++) { - if (m_attack) { - // ignore sustained parts of notes when counting attacks - if (subtokens[i].find("_") != string::npos) { - continue; - } - if (subtokens[i].find("]") != string::npos) { - continue; - } - } - int b40 = Convert::kernToBase40(subtokens[i]); - if (m_attack) { - m_counts[kindex][b40%40]++; - } else { - double duration = Convert::recipToDuration(subtokens[i]).getFloat(); - m_counts[kindex][b40%40] += duration; + for (int j=0; jisKeySignature()) { + return Convert::kernKeyToNumber(*token); } } - current = current->getNextToken(); } -} + return 0; // C major key signature +} ////////////////////////////// // -// Tool_pccount::initializePartInfo -- +// Tool_prange::printScoreVoice -- print the range information for a particular voice (in SCORE format). // -void Tool_pccount::initializePartInfo(HumdrumFile& infile) { - m_names.clear(); - m_abbreviations.clear(); - m_parttracks.clear(); - m_rkern.clear(); +void Tool_prange::printScoreVoice(ostream& out, _VoiceInfo& voiceInfo, double maxvalue) { + int mini = getMinDiatonicIndex(voiceInfo.diatonic); + int maxi = getMaxDiatonicIndex(voiceInfo.diatonic); - m_rkern.resize(infile.getTrackCount() + 1); - fill(m_rkern.begin(), m_rkern.end(), -1); + if ((mini < 0) || (maxi < 0)) { + // no data for voice so skip + return; + } - m_parttracks.push_back(-1); - m_names.push_back("all"); - m_abbreviations.push_back("all"); + // int minacci = getMinDiatonicAcc(voiceInfo.diatonic, mini); + // int maxacci = getMaxDiatonicAcc(voiceInfo.diatonic, maxi); + int mindiatonic = mini - 3 * 7; + int maxdiatonic = maxi - 3 * 7; + // int minacc = minacci - 3; + // int maxacc = maxacci - 3; - vector starts = infile.getKernSpineStartList(); + int staff; + double vpos; - int foundpart = false; - int foundabbr = false; + int voicevpos = -3; + staff = getStaffBase7(mindiatonic); + int lowestvpos = getVpos(mindiatonic); + if ((staff == 1) && (lowestvpos <= 0)) { + voicevpos += lowestvpos - 2; + } - int track = 0; - for (int i=0; i<(int)starts.size(); i++) { - track = starts[i]->getTrack(); - m_rkern[track] = i+1; - m_parttracks.push_back(track); - HTp current = starts[i]; - foundpart = false; - foundabbr = false; - if (!current->isKern()) { + if (m_localQ || (voiceInfo.index == 0)) { + double localmaxvalue = getMaxValue(voiceInfo.diatonic); + maxvalue = localmaxvalue; + } + double width; + double hoffset = 2.3333; + double maxhist = 17.6; + int i; + int base7; + + // print histogram bars + for (i=mini; i<=maxi; i++) { + if (voiceInfo.diatonic.at(i).at(0) <= 0.0) { continue; } - while (current) { - if (current->isData()) { - break; + base7 = i - 3 * 7; + staff = getStaffBase7(base7); + vpos = getVpos(base7); + + // staring positions of accidentals: + vector starthpos(6, 0.0); + for (int j=1; j<(int)starthpos.size(); j++) { + double width = maxhist * voiceInfo.diatonic.at(i).at(j)/maxvalue; + starthpos[j] = starthpos[j-1] + width; + } + for (int j=(int)starthpos.size() - 1; j>0; j--) { + starthpos[j] = starthpos[j-1]; + } + + // print chromatic alterations + for (int j=(int)voiceInfo.diatonic.at(i).size()-1; j>0; j--) { + if (voiceInfo.diatonic.at(i).at(j) <= 0.0) { + continue; } - if ((!foundpart) && (current->compare(0, 3, "*I\"") == 0)) { - m_names.emplace_back(current->substr(3)); - foundpart = true; - } else if ((!foundabbr) && (current->compare(0, 3, "*I\'") == 0)) { - m_abbreviations.emplace_back(current->substr(3)); - foundabbr = true; + int acc = 0; + switch (j) { + case 1: acc = -2; break; + case 2: acc = -1; break; + case 3: acc = 0; break; + case 4: acc = +1; break; + case 5: acc = +2; break; + } + + width = maxhist * voiceInfo.diatonic.at(i).at(j)/maxvalue + hoffset; + if (m_hoverQ) { + string title = getNoteTitle((int)voiceInfo.diatonic.at(i).at(j), base7, acc); + SVGTEXT(out, title); + } + out << "1 " << staff << " " << (voiceInfo.hpos + starthpos.at(j) + hoffset) << " " << vpos; + out << " 0 -1 4 0 0 0 99 0 0 "; + out << width << "\n"; + if (m_hoverQ) { + SVGTEXT(out, ""); } - current = current->getNextToken(); } - //if (!foundpart) { - // m_names.emplace_back(""); - //} - //if (!foundabbr) { - // m_names.emplace_back(""); - //} } -} - + string voicestring = voiceInfo.name; + if (voicestring.empty()) { + voicestring = voiceInfo.abbr; + } + if (!voicestring.empty()) { + HumRegex hre; + hre.replaceDestructive(voicestring, "", "(\\\\n)+$"); + vector pieces; + hre.split(pieces, voicestring, "\\\\n"); + + if (pieces.size() > 1) { + voicestring = ""; + for (int i=0; i<(int)pieces.size(); i++) { + voicestring += pieces[i]; + if (i < (int)pieces.size() - 1) { + voicestring += "/"; + } + } + } + + double increment = 4.0; + for (int i=0; i<(int)pieces.size(); i++) { + // print voice name + double tvoffset = -4.0; + out << "t 1 " << voiceInfo.hpos << " " + << (voicevpos - increment * i) + << " 1 1 0 0 0 0 " << tvoffset; + out << "\n"; + + if (pieces[i] == "all") { + out << "_02"; + } else if (pieces[i] == "both") { + out << "_02"; + } else { + out << "_00"; + } + printScoreEncodedText(out, pieces[i]); + out << "\n"; + } + } + + // print the lowest pitch in range + staff = getStaffBase7(mindiatonic); + vpos = getVpos(mindiatonic); + if (m_hoverQ) { + string content = ""; + content += getDiatonicPitchName(mindiatonic, 0); + content += ": lowest note"; + if (!voicestring.empty()) { + content += " of "; + content += voicestring; + content += "'s range"; + } + content += ""; + SVGTEXT(out, content); + } + out << "1 " << staff << " " << voiceInfo.hpos << " " << vpos + << " 0 0 4 0 0 -2\n"; + if (m_hoverQ) { + SVGTEXT(out, ""); + } + + // print the highest pitch in range + staff = getStaffBase7(maxdiatonic); + vpos = getVpos(maxdiatonic); + if (m_hoverQ) { + string content = ""; + content += getDiatonicPitchName(maxdiatonic, 0); + content += ": highest note"; + if (!voicestring.empty()) { + content += " of "; + content += voicestring; + content += "'s range"; + } + content += ""; + SVGTEXT(out, content); + } + out << "1 " << staff << " " << voiceInfo.hpos << " " << vpos + << " 0 0 4 0 0 -2\n"; + if (m_hoverQ) { + SVGTEXT(out, ""); + } -////////////////////////////// -// -// printVegaLiteJsonTemplate -- -// + double goffset = -1.66; + double toffset = 1.5; + double median12 = getMedian12(voiceInfo.midibins); + double median40 = Convert::base12ToBase40(median12); + double median7 = Convert::base40ToDiatonic(median40); + // int acc = Convert::base40ToAccidental(median40); -void Tool_pccount::printVegaLiteJsonTemplate(const string& datavariable, HumdrumFile& infile) { - stringstream& out = m_free_text; + staff = getStaffBase7(median7); + vpos = getVpos(median7); - string idinfo; - if (m_id.empty() || m_id == "id") { - // do nothing - } else { - idinfo = "for " + m_id; + // these offsets are useful when the quartile pitches are not shown... + int vvpos = maxdiatonic - median7 + 1; + int vvpos2 = median7 - mindiatonic + 1; + double offset = goffset; + if (vvpos <= 2) { + offset += toffset; + } else if (vvpos2 <= 2) { + offset -= toffset; } - out << "{\n"; - out << " \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.0-beta.1.json\",\n"; - out << " \"data\": {\"values\": " << datavariable << "},\n"; - if (getBoolean("title")) { - out << " \"title\": \"" << m_title << "\",\n"; - } else { - if (m_attack) { - out << " \"title\": \"Note-count pitch-class distribution " << idinfo <<" \",\n"; - } else { - out << " \"title\": \"Duration-weighted pitch-class distribution " << idinfo <<" \",\n"; + + if (m_hoverQ) { + string content = ""; + content += getDiatonicPitchName(median7, 0); + content += ": median note"; + if (!voicestring.empty()) { + content += " of "; + content += voicestring; + content += "'s range"; } + content += ""; + SVGTEXT(out, content); } - out << " \"width\": " << m_width << ",\n"; - out << " \"height\": " << int(m_width * m_ratio) << ",\n"; - out << " \"encoding\": {\n"; - out << " \"y\": {\n"; - if (m_attack) { - out << " \"field\": \"count\",\n"; - out << " \"title\": \"Number of note attacks\",\n"; + out << "1 " << staff << " " << voiceInfo.hpos << " "; + if (vpos > 0) { + out << vpos + 100; } else { - out << " \"field\": \"percent\",\n"; - out << " \"title\": \"Percent of maximum pitch class\",\n"; + out << vpos - 100; } - out << " \"type\": \"quantitative\",\n"; - if (m_attack) { - out << " \"scale\": {\"domain\": [0, " << m_maxpc << "]},\n"; - } else { - out << " \"scale\": {\"domain\": [0, 100]},\n"; + out << " 0 1 4 0 0 " << offset << "\n"; + if (m_hoverQ) { + SVGTEXT(out, ""); } - out << " \"aggregate\": \"sum\"\n"; - out << " },\n"; - out << " \"x\": {\n"; - out << " \"field\": \"pitch class\",\n"; - out << " \"type\": \"nominal\",\n"; - out << " \"scale\": {\n"; - out << " \"domain\": ["; - printPitchClassList(); - out << "]\n"; - out << " },\n"; - out << " \"axis\": {\n"; - out << " \"labelAngle\": 0\n"; - out << " }\n"; - out << " },\n"; - out << " \"order\": {\"type\": \"quantitative\"},\n"; - out << " \"color\": {\n"; - out << " \"field\": \"voice\",\n"; - out << " \"type\": \"nominal\",\n"; - if (m_counts.size() == 2) { - out << " \"legend\": {\"title\": \"Voice\"},\n"; - } else { - out << " \"legend\": {\"title\": \"Voices\"},\n"; + + if (m_finalisQ) { + for (int f=0; f<(int)voiceInfo.diafinal.size(); f++) { + int diafinalis = voiceInfo.diafinal.at(f); + int accfinalis = voiceInfo.accfinal.at(f); + int staff = getStaffBase7(diafinalis); + int vpos = getVpos(diafinalis); + double goffset = -1.66; + double toffset = 3.5; + + // these offsets are useful when the quartile pitches are not shown... + double offset = goffset; + offset += toffset; + + if (m_hoverQ) { + string content = ""; + content += getDiatonicPitchName(diafinalis, accfinalis); + content += ": last note"; + if (!voicestring.empty()) { + content += " of "; + if (voiceInfo.index == 0) { + content += voiceInfo.namfinal.at(f); + } else { + content += voicestring; + } + } + content += ""; + SVGTEXT(out, content); + } + out << "1 " << staff << " " << voiceInfo.hpos << " "; + if (vpos > 0) { + out << vpos + 100; + } else { + out << vpos - 100; + } + out << " 0 0 4 0 0 " << offset << "\n"; + if (m_hoverQ) { + SVGTEXT(out, ""); + } + } } - out << " \"scale\": {\n"; - out << " \"domain\": ["; - printVoiceList(); - out << "],\n"; - out << " \"range\": ["; - printColorList(); - out << "],\n"; - out << " }\n"; - out << " }\n"; - out << " },\n"; - out << " \"layer\": [\n"; - out << " {\"mark\": \"bar\"}"; + /* Needs fixing + int topquartile; + if (m_quartileQ) { + // print top quartile + topquartile = getTopQuartile(voiceInfo.midibins); + if (m_diatonicQ) { + topquartile = Convert::base7ToBase12(topquartile); + } + staff = getStaffBase7(topquartile); + vpos = getVpos(topquartile); + vvpos = median7 - topquartile + 1; + if (vvpos <= 2) { + offset = goffset + toffset; + } else { + offset = goffset; + } + vvpos = maxdiatonic - topquartile + 1; + if (vvpos <= 2) { + offset = goffset + toffset; + } - string final = getFinal(infile); - if (m_key && !final.empty()) { - out << ",\n"; - out << " {\n"; - out << " \"mark\": {\"type\":\"text\", \"align\":\"center\", \"fill\":\"black\", \"baseline\":\"bottom\"},\n"; - if (m_attack) { - int count = getCount(final); - out << " \"data\": {\"values\": [ {\"pitch class\":\"" << final << "\", \"count\":" << count << "}]},\n"; + if (m_hoverQ) { + if (m_defineQ) { + out << "SVG "; + } else { + out << "t 1 1\n"; + out << SVGTAG; + } + printScoreEncodedText(out, ""); + printDiatonicPitchName(out, topquartile, 0); + out << ": top quartile note"; + if (voicestring.size() > 0) { + out << " of " << voicestring << "\'s range"; + } + printScoreEncodedText(out, "\n"); + } + out << "1 " << staff << " " << voiceInfo.hpos << " "; + if (vpos > 0) { + out << vpos + 100; } else { - double percent = getPercent(final); - out << " \"data\": {\"values\": [ {\"pitch class\":\"" << final << "\", \"percent\":" << percent << "}]},\n"; + out << vpos - 100; + } + out << " 0 0 4 0 0 " << offset << "\n"; + if (m_hoverQ) { + SVGTEXT(out, ""); } - out << " \"encoding\": {\"text\": {\"value\":\"final\"}}\n"; - out << " }\n"; } - out << " ]\n"; - out << "}\n"; + // print bottom quartile + if (m_quartileQ) { + int bottomquartile = getBottomQuartile(voiceInfo.midibins); + if (m_diatonicQ) { + bottomquartile = Convert::base7ToBase12(bottomquartile); + } + staff = getStaffBase7(bottomquartile); + vpos = getVpos(bottomquartile); + vvpos = median7 - bottomquartile + 1; + if (vvpos <= 2) { + offset = goffset + toffset; + } else { + offset = goffset; + } + vvpos = bottomquartile - mindiatonic + 1; + if (vvpos <= 2) { + offset = goffset - toffset; + } + if (m_hoverQ) { + if (m_defineQ) { + out << "SVG "; + } else { + out << "t 1 1\n"; + out << SVGTAG; + } + printScoreEncodedText(out, ""); + printDiatonicPitchName(out, bottomquartile, 0); + out << ": bottom quartile note"; + if (voicestring.size() > 0) { + out << " of " << voicestring << "\'s range"; + } + printScoreEncodedText(out, "\n"); + } + out << "1.0 " << staff << ".0 " << voiceInfo.hpos << " "; + if (vpos > 0) { + out << vpos + 100; + } else { + out << vpos - 100; + } + out << " 0 0 4 0 0 " << offset << "\n"; + if (m_hoverQ) { + SVGTEXT(out, ""); + } + } + */ } @@ -113407,28 +119585,33 @@ void Tool_pccount::printVegaLiteJsonTemplate(const string& datavariable, Humdrum ////////////////////////////// // -// Tool_pccount::getCount -- +// Tool_prange::printDiatonicPitchName -- // -int Tool_pccount::getCount(const string& pitchclass) { - int b40 = Convert::kernToBase40(pitchclass); - int index = b40 % 40; - int output = (int)m_counts[0][index]; - return output; +void Tool_prange::printDiatonicPitchName(ostream& out, int base7, int acc) { + out << getDiatonicPitchName(base7, acc); } ////////////////////////////// // -// Tool_pccount::getPercent -- +// Tool_prange::getDiatonicPitchName -- // -double Tool_pccount::getPercent(const string& pitchclass) { - setFactorMaximum(); - int b40 = Convert::kernToBase40(pitchclass); - int index = b40 % 40; - double output = m_counts[0][index] / m_factor * 100.0; +string Tool_prange::getDiatonicPitchName(int base7, int acc) { + string output; + int dpc = base7 % 7; + char letter = (dpc + 2) % 7 + 'A'; + output += letter; + switch (acc) { + case -1: output += "♭"; break; + case +1: output += "♯"; break; + case -2: output += "𝄫"; break; + case +2: output += "𝄪"; break; + } + int octave = base7 / 7; + output += to_string(octave); return output; } @@ -113436,952 +119619,679 @@ double Tool_pccount::getPercent(const string& pitchclass) { ////////////////////////////// // -// Tool_pccount::printColorList -- +// Tool_prange::printHtmlStringEncodeSimple -- // -void Tool_pccount::printColorList(void) { - stringstream& out = m_free_text; - for (int i=(int)m_names.size() - 1; i>0; i--) { - string color = m_vcolor[m_names[i]]; - out << "\""; - if (color.empty()) { - out << "black"; - } else { - out << color; - } - out << "\""; - if (i > 1) { - out << ", "; - } - } +void Tool_prange::printHtmlStringEncodeSimple(ostream& out, const string& strang) { + string newstring = strang; + HumRegex hre; + hre.replaceDestructive(newstring, "&", "&", "g"); + hre.replaceDestructive(newstring, "<", "<", "g"); + hre.replaceDestructive(newstring, ">", "<", "g"); + out << newstring; } ////////////////////////////// // -// Tool_pccount::printVoiceList -- +// Tool_prange::getNoteTitle -- return the title of the histogram bar. +// value = duration or count of notes +// diatonic = base7 value for note +// acc = accidental for diatonic note. // -void Tool_pccount::printVoiceList(void) { - stringstream& out = m_free_text; - for (int i=(int)m_names.size() - 1; i>0; i--) { - out << "\""; - out << m_names[i]; - out << "\""; - if (i > 1) { - out << ", "; +string Tool_prange::getNoteTitle(double value, int diatonic, int acc) { + stringstream output; + output << ""; + if (m_durationQ) { + output << value / 8.0; + if (value/8.0 == 1.0) { + output << " long on "; + } else { + output << " longs on "; + } + output << getDiatonicPitchName(diatonic, acc); + } else { + output << value; + output << " "; + output << getDiatonicPitchName(diatonic, acc); + if (value != 1.0) { + output << "s"; } } + output << ""; + return output.str(); } ////////////////////////////// // -// Tool_pccount::printReverseVoiceList -- +// Tool_prange::getDiatonicInterval -- // -void Tool_pccount::printReverseVoiceList(void) { - stringstream& out = m_free_text; - for (int i=1; i<(int)m_names.size(); i++) { - out << "\""; - out << m_names[i]; - out << "\""; - if (i < (int)m_names.size() - 1) { - out << ", "; - } - } +int Tool_prange::getDiatonicInterval(int note1, int note2) { + int vpos1 = getVpos(note1); + int vpos2 = getVpos(note2); + return abs(vpos1 - vpos2) + 1; } ////////////////////////////// // -// Tool_pccount::printPitchClassList -- +// Tool_prange::getTopQuartile -- // -void Tool_pccount::printPitchClassList(void) { - stringstream& out = m_free_text; - - if (m_counts[0][0] > 0.0) { out << "\"C♭♭\", "; } - if (m_counts[0][1] > 0.0) { out << "\"C♭\", "; } - out << "\"C\""; - if (m_counts[0][3] > 0.0) { out << ", \"C♯\""; } - if (m_counts[0][4] > 0.0) { out << ", \"C♯♯\""; } - // 5 is empty - - if (m_counts[0][6] > 0.0) { out << ", \"D♭♭\""; } - if (m_counts[0][7] > 0.0) { out << ", \"D♭\""; } - out << ", \"D\""; - if (m_counts[0][9] > 0.0) { out << ", \"D♯\""; } - if (m_counts[0][10] > 0.0) { out << ", \"D♯♯\""; } - // 11 is empty - - if (m_counts[0][12] > 0.0) { out << ", \"E♭♭\""; } - if (m_counts[0][13] > 0.0) { out << ", \"E♭\""; } - out << ", \"E\""; - if (m_counts[0][15] > 0.0) { out << ", \"E♯\""; } - if (m_counts[0][16] > 0.0) { out << ", \"E♯♯\""; } - - if (m_counts[0][17] > 0.0) { out << ", \"F♭♭\""; } - if (m_counts[0][18] > 0.0) { out << ", \"F♭\""; } - out << ", \"F\""; - if (m_counts[0][20] > 0.0) { out << ", \"F♯\""; } - if (m_counts[0][21] > 0.0) { out << ", \"F♯♯\""; } - // 22 is empty - - if (m_counts[0][23] > 0.0) { out << ", \"G♭♭\""; } - if (m_counts[0][24] > 0.0) { out << ", \"G♭\""; } - out << ", \"G\""; - if (m_counts[0][26] > 0.0) { out << ", \"G♯\""; } - if (m_counts[0][27] > 0.0) { out << ", \"G♯♯\""; } - // 28 is empty - - if (m_counts[0][29] > 0.0) { out << ", \"A♭♭\""; } - if (m_counts[0][30] > 0.0) { out << ", \"A♭\""; } - out << ", \"A\""; - if (m_counts[0][32] > 0.0) { out << ", \"A♯\""; } - if (m_counts[0][33] > 0.0) { out << ", \"A♯♯\""; } - // 34 is empty +int Tool_prange::getTopQuartile(vector& midibins) { + double sum = accumulate(midibins.begin(), midibins.end(), 0.0); - if (m_counts[0][35] > 0.0) { out << ", \"B♭♭\""; } - if (m_counts[0][36] > 0.0) { out << ", \"B♭\""; } - out << ", \"B\""; - if (m_counts[0][38] > 0.0) { out << ", \"B♯\""; } - if (m_counts[0][39] > 0.0) { out << ", \"B♯♯\""; } + double cumsum = 0.0; + int i; + for (i=midibins.size()-1; i>=0; i--) { + if (midibins[i] <= 0.0) { + continue; + } + cumsum += midibins[i]/sum; + if (cumsum >= 0.25) { + return i; + } + } + return -1; } + ////////////////////////////// // -// Tool_pccount::getPitchClassString -- +// Tool_prange::getBottomQuartile -- // -string Tool_pccount::getPitchClassString(int b40) { - switch (b40%40) { - case 0: return "C♭♭"; - case 1: return "C♭"; - case 2: return "C"; - case 3: return "C♯"; - case 4: return "C♯♯"; - // 5 is empty - case 6: return "D♭♭"; - case 7: return "D♭"; - case 8: return "D"; - case 9: return "D♯"; - case 10: return "D♯♯"; - // 11 is empty - case 12: return "E♭♭"; - case 13: return "E♭"; - case 14: return "E"; - case 15: return "E♯"; - case 16: return "E♯♯"; - case 17: return "F♭♭"; - case 18: return "F♭"; - case 19: return "F"; - case 20: return "F♯"; - case 21: return "F♯♯"; - // 22 is empty - case 23: return "G♭♭"; - case 24: return "G♭"; - case 25: return "G"; - case 26: return "G♯"; - case 27: return "G♯♯"; - // 28 is empty - case 29: return "A♭♭"; - case 30: return "A♭"; - case 31: return "A"; - case 32: return "A♯"; - case 33: return "A♯♯"; - // 34 is empty - case 35: return "B♭♭"; - case 36: return "B♭"; - case 37: return "B"; - case 38: return "B♯"; - case 39: return "B♯♯"; - } - - return "?"; -} - +int Tool_prange::getBottomQuartile(vector& midibins) { + double sum = accumulate(midibins.begin(), midibins.end(), 0.0); + double cumsum = 0.0; + int i; + for (i=0; i<(int)midibins.size(); i++) { + if (midibins[i] <= 0.0) { + continue; + } + cumsum += midibins[i]/sum; + if (cumsum >= 0.25) { + return i; + } + } + return -1; +} -///////////////////////////////// +////////////////////////////// // -// Tool_periodicity::Tool_periodicity -- Set the recognized options for the tool. +// Tool_prange::getMaxValue -- // -Tool_periodicity::Tool_periodicity(void) { - define("m|min=b", "minimum time unit (other than grace notes)"); - define("n|max-rows=i:-1", "maxumum number of rows in svg analysis display"); - define("t|track=i:0", "track to analyze"); - define("attacks=b", "extract attack grid)"); - define("raw=b", "show only raw period data"); - define("s|svg=b", "output svg image"); - define("p|power=d:2.0", "scaling power for visual display"); - define("1|one=b", "composite rhythms are not weighted by attack"); +double Tool_prange::getMaxValue(vector>& bins) { + double maxi = 0; + for (int i=1; i<(int)bins.size(); i++) { + if (bins.at(i).at(0) > bins.at(maxi).at(0)) { + maxi = i; + } + } + return bins.at(maxi).at(0); } -///////////////////////////////// +////////////////////////////// // -// Tool_periodicity::run -- Primary interfaces to the tool. +// Tool_prange::getVpos == return the position on the staff given the diatonic pitch. +// and the staff. 1=bass, 2=treble. +// 3 = bottom line of clef, 0 = space below first ledger line. // -bool Tool_periodicity::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i> attackgrids; - attackgrids.resize(infile.getTrackCount()+1); - fillAttackGrids(infile, attackgrids, minrhy); - if (getBoolean("attacks")) { - printAttackGrid(m_free_text, infile, attackgrids, minrhy); - return; - } - - int atrack = getInteger("track"); - vector> analysis; - doPeriodicityAnalysis(analysis, attackgrids[atrack], minrhy); - - if (getBoolean("raw")) { - printPeriodicityAnalysis(m_free_text, analysis); - return; +int Tool_prange::getMaxDiatonicIndex(vector>& diatonic) { + for (int i=diatonic.size()-1; i>=0; i--) { + if (diatonic.at(i).at(0) != 0.0) { + return i; + } } - - printSvgAnalysis(m_free_text, analysis, minrhy); + return -1; } ////////////////////////////// // -// Tool_periodicity::printPeriodicityAnalysis -- +// Tool_prange::getMinDiatonicIndex -- return the lowest non-zero content. // -void Tool_periodicity::printPeriodicityAnalysis(ostream& out, vector>& analysis) { - for (int i=0; i<(int)analysis.size(); i++) { - for (int j=0; j<(int)analysis[i].size(); j++) { - out << analysis[i][j]; - if (j < (int)analysis[i].size() - 1) { - out << "\t"; - } +int Tool_prange::getMinDiatonicIndex(vector>& diatonic) { + for (int i=0; i<(int)diatonic.size(); i++) { + if (diatonic.at(i).at(0) != 0.0) { + return i; } - out << "\n"; } + return -1; } ////////////////////////////// // -// Tool_periodicity::doPeriodicAnalysis -- +// Tool_prange::getMinDiatonicAcc -- return the lowest accidental. // -void Tool_periodicity::doPeriodicityAnalysis(vector> &analysis, vector& grid, HumNum minrhy) { - analysis.resize(minrhy.getNumerator()); - for (int i=0; i<(int)analysis.size(); i++) { - doAnalysis(analysis, i, grid); +int Tool_prange::getMinDiatonicAcc(vector>& diatonic, int index) { + for (int i=1; i<(int)diatonic.at(index).size(); i++) { + if (diatonic.at(index).at(i) != 0.0) { + return i; + } } + return -1; } ////////////////////////////// // -// Tool_periodicity::doAnalysis -- +// Tool_prange::getMaxDiatonicAcc -- return the highest accidental. // -void Tool_periodicity::doAnalysis(vector>& analysis, int level, vector& grid) { - int period = level + 1; - analysis[level].resize(period); - std::fill(analysis[level].begin(), analysis[level].end(), 0.0); - for (int i=0; i>& diatonic, int index) { + for (int i=(int)diatonic.at(index).size() - 1; i>0; i--) { + if (diatonic.at(index).at(i) != 0.0) { + return i; } } + return -1; } ////////////////////////////// // -// Tool_periodicity::printAttackGrid -- +// Tool_prange::prepareRefmap -- // -void Tool_periodicity::printAttackGrid(ostream& out, HumdrumFile& infile, vector>& grids, HumNum minrhy) { - out << "!!!minrhy: " << minrhy << endl; - out << "**all"; - for (int i=1; i<(int)grids.size(); i++) { - out << "\t**track"; - } - out << "\n"; - for (int j=0; j<(int)grids[0].size(); j++) { - for (int i=0; i<(int)grids.size(); i++) { - out << grids[i][j]; - if (i < (int)grids.size() - 1) { - out << "\t"; +void Tool_prange::prepareRefmap(HumdrumFile& infile) { + vector refrecords = infile.getGlobalReferenceRecords(); + m_refmap.clear(); + HumRegex hre; + for (int i = (int)refrecords.size()-1; i>=0; i--) { + string key = refrecords[i]->getReferenceKey(); + string value = refrecords[i]->getReferenceValue(); + m_refmap[key] = value; + if (key.find("@") != string::npos) { + // create default value + hre.replaceDestructive(key, "", "@.*"); + if (m_refmap[key].empty()) { + m_refmap[key] = value; } } - out << "\n"; } - for (int i=0; i<(int)grids.size(); i++) { - out << "*-"; - if (i < (int)grids.size() - 1) { - out << "\t"; + // fill in @{} templates (mostly for !!!title:) + int counter = 0; // prevent recursions + for (auto& entry : m_refmap) { + + if (entry.second.find("@") != string::npos) { + while (hre.search(entry.second, "@\\{(.*?)\\}")) { + string key = hre.getMatch(1); + string value = m_refmap[key]; + hre.replaceDestructive(entry.second, value, "@\\{" + key + "\\}", "g"); + counter++; + if (counter > 1000) { + break; + } + } } + } - out << "\n"; + // prepare title + if (m_refmap["title"].empty()) { + m_refmap["title"] = m_refmap["OTL"]; + } } ////////////////////////////// // -// Tool_periodicity::fillAttackGrids -- +// Tool_prange::getTitle -- // -void Tool_periodicity::fillAttackGrids(HumdrumFile& infile, vector>& grids, HumNum minrhy) { - HumNum elements = minrhy * infile.getScoreDuration() / 4; - - for (int t=0; t<(int)grids.size(); t++) { - grids[t].resize(elements.getNumerator()); - } +string Tool_prange::getTitle(void) { + string titlestring = "_00"; + HumRegex hre; + if (m_notitleQ) { + return ""; + } else if (m_titleQ) { + titlestring = m_title; + int counter = 0; - for (int i=0; iisKern()) { - continue; - } - if (token->isNull()) { - continue; - } - if (!token->isNoteAttack()) { - continue; + if (titlestring.find("@") != string::npos) { + while (hre.search(titlestring, "@\\{(.*?)\\}")) { + string key = hre.getMatch(1); + string value = m_refmap[key]; + hre.replaceDestructive(titlestring, value, "@\\{" + key + "\\}", "g"); + counter++; + if (counter > 1000) { + break; + } } - int track = token->getTrack(); - grids.at(track).at(position.getNumerator()) += 1; } - } - - bool oneQ = getBoolean("one"); - for (int j=0; j<(int)grids.at(0).size(); j++) { - grids.at(0).at(j) = 0; - for (int i=0; i<(int)grids.size(); i++) { - if (!grids.at(i).at(j)) { - continue; - } - if (oneQ) { - grids.at(0).at(j) = 1; - } else { - grids.at(0).at(j) += grids.at(i).at(j); - } + if (!titlestring.empty()) { + titlestring = "_00" + titlestring; + } + } else { + titlestring = m_refmap["title"]; + if (!titlestring.empty()) { + titlestring = "_00" + titlestring; } } + return titlestring; } ////////////////////////////// // -// Tool_periodicity::printSvgAnalysis -- +// Tool_prange::clearHistograms -- // -void Tool_periodicity::printSvgAnalysis(ostream& out, vector>& analysis, HumNum minrhy) { - pugi::xml_document image; - auto declaration = image.prepend_child(pugi::node_declaration); - declaration.append_attribute("version") = "1.0"; - declaration.append_attribute("encoding") = "UTF-8"; - declaration.append_attribute("standalone") = "no"; - - auto svgnode = image.append_child("svg"); - svgnode.append_attribute("version") = "1.1"; - svgnode.append_attribute("xmlns") = "http://www.w3.org/2000/svg"; - svgnode.append_attribute("xmlns:xlink") = "http://www.w3.org/1999/xlink"; - svgnode.append_attribute("overflow") = "visible"; - svgnode.append_attribute("viewBox") = "0 0 1000 1000"; - svgnode.append_attribute("width") = "1000px"; - svgnode.append_attribute("height") = "1000px"; +void Tool_prange::clearHistograms(vector >& bins, int start) { + int i; + for (i=start; i<(int)bins.size(); i++) { + bins[i].resize(40*11); + fill(bins[i].begin(), bins[i].end(), 0.0); + // bins[i].allowGrowth(0); + } + for (int i=0; i<(int)bins.size(); i++) { + if (bins[i].size() == 0) { + bins[i].resize(40*11); + fill(bins[i].begin(), bins[i].end(), 0.0); + } + } +} - auto style = svgnode.append_child("style"); - style.text().set(".label { font: 14px sans-serif; alignment-baseline: middle; text-anchor: left; }"); - auto grid = svgnode.append_child("g"); - grid.append_attribute("id") = "grid"; - auto labels = svgnode.append_child("g"); - double hue = 0.0; - double saturation = 100; - double lightness = 75; +////////////////////////////// +// +// Tool_prange::printAnalysis -- +// - pugi::xml_node crect; - double width; - double height; +void Tool_prange::printAnalysis(ostream& out, vector& midibins) { + if (m_percentileQ) { + printPercentile(out, midibins, m_percentile); + return; + } else if (m_rangeQ) { + double notesinrange = countNotesInRange(midibins, m_rangeL, m_rangeH); + out << notesinrange << endl; + return; + } - stringstream ss; - stringstream ssl; - //stringstream css; - double x; - double y; + int i; + double normval = 1.0; - double imagewidth = 1000.0; - double imageheight = 1000.0; + // print the pitch histogram - int maxrow = getInteger("max-rows"); - if (maxrow <= 0) { - maxrow = (int)analysis.back().size(); + double fracL = 0.0; + double fracH = 0.0; + double fracA = 0.0; + double sum = accumulate(midibins.begin(), midibins.end(), 0.0); + if (m_normQ) { + normval = sum; } + double runningtotal = 0.0; - // double sdur = (double)analysis.back().size(); - double sdur = (double)maxrow; - - double maxscore = 0.0; - for (int i=0; i=0; i--) { + if (midibins[i] <= 0.0) { + continue; + } + if (m_diatonicQ) { + base12 = Convert::base7ToBase12(i); + } else { + base12 = i; + } + out << base12 << "\t"; + if (m_pitchQ) { + out << Convert::base12ToPitch(base12); + } else { + out << Convert::base12ToKern(base12); + } + out << "\t"; + out << midibins[i] / normval; + fracL = runningtotal/sum; + runningtotal += midibins[i]; + fracH = runningtotal/sum; + fracA = (fracH + fracL)/2.0; + fracL = (int)(fracL * 10000.0 + 0.5)/10000.0; + fracH = (int)(fracH * 10000.0 + 0.5)/10000.0; + fracA = (int)(fracA * 10000.0 + 0.5)/10000.0; + if (m_addFractionQ) { + out << "\t" << fracL; + out << "\t" << fracA; + out << "\t" << fracH; + } + out << "\n"; } - - pugi::xml_node label = labels.append_child("text"); - label.append_attribute("class") = "label"; - - HumNum rval = (i+1); - rval /= minrhy; - rval *= 4; - - std::string rhythm = Convert::durationToRecip(rval); - rhythm += " (" + to_string(i+1) + ")"; - label.text().set(rhythm.c_str()); - x = (i+1+0.5)/sdur * imageheight; - y = (i+0.5)/sdur * imagewidth; - label.append_attribute("x") = to_string(x).c_str(); - label.append_attribute("y") = to_string(y).c_str(); } - image.save(out); -} - - - -////////////////////////////// -// -// Tool_periodicity::getColorMapping -- -// - -void Tool_periodicity::getColorMapping(double input, double& hue, - double& saturation, double& lightness) { - double maxhue = 0.75 * 360.0; - hue = input; - if (hue < 0.0) { - hue = 0.0; - } - hue = hue * hue; - if (hue != 1.0) { - hue *= 0.95; + out << "*-\t*-\t*-"; + if (m_addFractionQ) { + out << "\t*-"; + out << "\t*-"; + out << "\t*-"; } + out << "\n"; - hue = (1.0 - hue) * 360.0; - if (hue == 0.0) { - // avoid -0.0; - hue = 0.0; - } + out << "!!tessitura:\t" << getTessitura(midibins) << " semitones\n"; - if (hue > maxhue) { - hue = maxhue; + double mean = getMean12(midibins); + if (m_diatonicQ && (mean > 0)) { + mean = Convert::base7ToBase12(mean); } - if (hue < 0.0) { - hue = maxhue; + out << "!!mean:\t\t" << mean; + out << " ("; + if (mean < 0) { + out << "unpitched"; + } else { + out << Convert::base12ToKern(int(mean+0.5)); } + out << ")" << "\n"; - saturation = 100.0; - lightness = 50.0; - - if (hue > 60) { - lightness = lightness - (hue-60) / (maxhue-60) * lightness / 1.5; + int median12 = getMedian12(midibins); + out << "!!median:\t" << median12; + out << " ("; + if (median12 < 0) { + out << "unpitched"; + } else { + out << Convert::base12ToKern(median12); } -} - - - - -///////////////////////////////// -// -// Tool_gridtest::Tool_phrase -- Set the recognized options for the tool. -// + out << ")" << "\n"; -Tool_phrase::Tool_phrase(void) { - define("A|no-average=b", "do not do average phrase-length analysis"); - define("R|remove2=b", "remove phrase boundaries in data and do not do analysis"); - define("m|mark=b", "mark phrase boundaries based on rests"); - define("r|remove=b", "remove phrase boundaries in data"); - define("c|color=s", "display color of analysis data"); } -/////////////////////////////// +////////////////////////////// // -// Tool_phrase::run -- Primary interfaces to the tool. +// Tool_prange::getMedian12 -- return the pitch on which half of pitches are above +// and half are below. // -bool Tool_phrase::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i& midibins) { + double sum = accumulate(midibins.begin(), midibins.end(), 0.0); -bool Tool_phrase::run(HumdrumFile& infile) { - initialize(infile); - for (int i=0; i<(int)m_starts.size(); i++) { - if (m_removeQ) { - removePhraseMarks(m_starts[i]); - } - if (m_remove2Q) { + double cumsum = 0.0; + int i; + for (i=0; i<(int)midibins.size(); i++) { + if (midibins[i] <= 0.0) { continue; } - if (hasPhraseMarks(m_starts[i])) { - analyzeSpineByPhrase(i); - } else { - analyzeSpineByRests(i); + cumsum += midibins[i]/sum; + if (cumsum >= 0.50) { + return i; } } - if (!m_remove2Q) { - prepareAnalysis(infile); - } - infile.createLinesFromTokens(); - return true; + + return -1000; } ////////////////////////////// // -// Tool_phrase::prepareAnalysis -- +// Tool_prange::getMean12 -- return the interval between the highest and lowest +// pitch in terms if semitones. // -void Tool_phrase::prepareAnalysis(HumdrumFile& infile) { - string exinterp = "**cdata"; - infile.appendDataSpine(m_results.back(), "", exinterp); - for (int i = (int)m_results.size()-1; i>0; i--) { - int track = m_starts[i]->getTrack(); - infile.insertDataSpineBefore(track, m_results[i-1], "", exinterp); - } - if (m_averageQ) { - addAverageLines(infile); - } - if (!m_color.empty()) { - int insertline = -1; - for (int i=0; i 0) { - stringstream ss; - int fsize = infile[insertline].getFieldCount(); - for (int j=0; jgetDataType(); - if (dt.empty() || (dt == "**cdata")) { - ss << "color:" << m_color; - } - if (j < fsize - 1) { - ss << "\t"; - } - } - string output = ss.str(); - infile.insertLine(insertline, output); - } - } -} - - - -/////////////////////////////// -// -// Tool_pharse::addAverageLines -- -// +double Tool_prange::getMean12(vector& midibins) { + double top = 0.0; + double bottom = 0.0; -void Tool_phrase::addAverageLines(HumdrumFile& infile) { - vector averages; - averages.resize(m_starts.size()+1); - int tcount = 0; - HumNum tsum = 0; - double average; - stringstream ss; - for (int i=0; i<(int)m_starts.size(); i++) { - if (m_pcount[i] > 0) { - average = m_psum[i].getFloat() / m_pcount[i]; - } else { - average = 0.0; + int i; + for (i=0; i<(int)midibins.size(); i++) { + if (midibins[i] <= 0.0) { + continue; } - ss.str(""); - ss.clear(); - ss << "!!average-phrase-length-k" << i+1 << ":\t" << average; - averages[i+1] = ss.str(); - tcount += m_pcount[i]; - tsum += m_psum[i]; - } - average = tsum.getFloat() / tcount; - ss.str(""); - ss.clear(); - ss << "!!average-phrase-length:\t" << average; - averages[0] = ss.str(); + top += midibins[i] * i; + bottom += midibins[i]; - for (int i=0; i<(int)averages.size(); i++) { - infile.appendLine(averages[i]); } -} - - - -/////////////////////////////// -// -// Tool_phrase::initialize -- -// -void Tool_phrase::initialize(HumdrumFile& infile) { - m_starts = infile.getKernSpineStartList(); - m_results.resize(m_starts.size()); - int lines = infile.getLineCount(); - for (int i=0; i<(int)m_results.size(); i++) { - m_results[i].resize(lines); - } - m_pcount.resize(m_starts.size()); - m_psum.resize(m_starts.size()); - std::fill(m_pcount.begin(), m_pcount.end(), 0); - std::fill(m_psum.begin(), m_psum.end(), 0); - m_markQ = getBoolean("mark"); - m_removeQ = getBoolean("remove"); - m_averageQ = !getBoolean("no-average"); - m_remove2Q = getBoolean("remove2"); - if (getBoolean("color")) { - m_color = getString("color"); + if (bottom == 0) { + return -1000; } + return top / bottom; } -/////////////////////////////// +////////////////////////////// // -// Tool_phrase::analyzeSpineByRests -- +// Tool_prange::getTessitura -- return the interval between the highest and lowest +// pitch in terms if semitones. // -void Tool_phrase::analyzeSpineByRests(int index) { - HTp start = m_starts[index]; - HTp current = start; - HTp lastnote = NULL; // last note to be processed - HTp pstart = NULL; // phrase start; - HumNum dur; - stringstream ss; - while (current) { - if (current->isBarline()) { - if (current->find("||") != std::string::npos) { - if (pstart) { - dur = current->getDurationFromStart() - - pstart->getDurationFromStart(); - ss.str(""); - ss.clear(); - ss << dur.getFloat(); - m_psum[index] += dur; - m_pcount[index]++; - m_results[index][pstart->getLineIndex()] = ss.str(); - pstart = NULL; - if (m_markQ && lastnote) { - lastnote->setText(lastnote->getText() + "}"); - } - } - } - } - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); - continue; - } - if (pstart && current->isRest()) { - if (lastnote) { - dur = current->getDurationFromStart() - - pstart->getDurationFromStart(); - ss.str(""); - ss.clear(); - ss << dur.getFloat(); - m_psum[index] += dur; - m_pcount[index]++; - m_results[index][pstart->getLineIndex()] = ss.str(); - if (m_markQ) { - lastnote->setText(lastnote->getText() + "}"); - } - } - pstart = NULL; - lastnote = NULL; - current = current->getNextToken(); +int Tool_prange::getTessitura(vector& midibins) { + int minn = -1000; + int maxx = -1000; + int i; + + for (i=0; i<(int)midibins.size(); i++) { + if (midibins[i] <= 0.0) { continue; } - if (current->isRest()) { - current = current->getNextToken(); - continue; + if (minn < 0) { + minn = i; } - if (current->isNote()) { - lastnote = current; + if (maxx < 0) { + maxx = i; } - if (pstart && current->isNote() && (current->find(";") != std::string::npos)) { - // fermata at end of phrase. - dur = current->getDurationFromStart() + current->getDuration() - - pstart->getDurationFromStart(); - ss.str(""); - ss.clear(); - ss << dur.getFloat(); - m_psum[index] += dur; - m_pcount[index]++; - m_results[index][pstart->getLineIndex()] = ss.str(); - if (m_markQ) { - current->setText(current->getText() + "}"); - } - current = current->getNextToken(); - pstart = NULL; - continue; + if (minn > i) { + minn = i; } - if (current->isNote() && pstart == NULL) { - pstart = current; - if (m_markQ) { - current->setText("{" + current->getText()); - } + if (maxx < i) { + maxx = i; } - current = current->getNextToken(); } - if (pstart) { - dur = start->getOwner()->getOwner()->getScoreDuration() - - pstart->getDurationFromStart(); - ss.str(""); - ss.clear(); - ss << dur.getFloat(); - m_psum[index] += dur; - m_pcount[index]++; - m_results[index][pstart->getLineIndex()] = ss.str(); - if (m_markQ && lastnote) { - lastnote->setText(lastnote->getText() + "}"); - } + if (m_diatonicQ) { + maxx = Convert::base7ToBase12(maxx); + minn = Convert::base7ToBase12(minn); } + + return maxx - minn + 1; } -/////////////////////////////// +////////////////////////////// // -// Tool_phrase::analyzeSpineByPhrase -- +// Tool_prange::countNotesInRange -- // -void Tool_phrase::analyzeSpineByPhrase(int index) { - HTp start = m_starts[index]; - HTp current = start; - HTp pstart = NULL; // phrase start; - HumNum dur; - stringstream ss; - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); - continue; - } - if (current->find("{") != std::string::npos) { - pstart = current; - current = current->getNextToken(); - continue; - } - if (current->find("}") != std::string::npos) { - if (pstart) { - dur = current->getDurationFromStart() + current->getDuration() - - pstart->getDurationFromStart(); - ss.str(""); - ss.clear(); - ss << dur.getFloat(); - m_psum[index] += dur; - m_pcount[index]++; - m_results[index][pstart->getLineIndex()] = ss.str(); - } - current = current->getNextToken(); - continue; - } - current = current->getNextToken(); +double Tool_prange::countNotesInRange(vector& midibins, int low, int high) { + int i; + double sum = 0; + for (i=low; i<=high; i++) { + sum += midibins[i]; } + return sum; } ////////////////////////////// // -// Tool_phrase::removePhraseMarks -- Remvoe { and } characters from **kern data. +// Tool_prange::printPercentile -- // -void Tool_phrase::removePhraseMarks(HTp start) { - HTp current = start; - HumRegex hre; - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); +void Tool_prange::printPercentile(ostream& out, vector& midibins, double m_percentile) { + double sum = accumulate(midibins.begin(), midibins.end(), 0.0); + double runningtotal = 0.0; + int i; + for (i=0; i<(int)midibins.size(); i++) { + if (midibins[i] <= 0) { continue; } - if (current->find("{") != std::string::npos) { - string data = *current; - hre.replaceDestructive(data, "", "\\{", "g"); - current->setText(data); - } - if (current->find("}") != std::string::npos) { - string data = *current; - hre.replaceDestructive(data, "", "\\}", "g"); - current->setText(data); + runningtotal += midibins[i] / sum; + if (runningtotal >= m_percentile) { + out << i << endl; + return; } - current = current->getNextToken(); } + + out << "unknown" << endl; } ////////////////////////////// // -// Tool_phrase::hasPhraseMarks -- True if **kern data spine (primary layer), has -// "{" (or "}", but this is not checked) characters (phrase markers). +// Tool_prange::getRange -- // -bool Tool_phrase::hasPhraseMarks(HTp start) { - HTp current = start; - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; +void Tool_prange::getRange(int& rangeL, int& rangeH, const string& rangestring) { + rangeL = -1; rangeH = -1; + if (rangestring.empty()) { + return; + } + int length = (int)rangestring.length(); + char* buffer = new char[length+1]; + strcpy(buffer, rangestring.c_str()); + char* ptr; + if (std::isdigit(buffer[0])) { + ptr = strtok(buffer, " \t\n:-"); + sscanf(ptr, "%d", &rangeL); + ptr = strtok(NULL, " \t\n:-"); + if (ptr != NULL) { + sscanf(ptr, "%d", &rangeH); } - if (current->find("{") != std::string::npos) { - return true; + } else { + ptr = strtok(buffer, " :"); + if (ptr != NULL) { + rangeL = Convert::kernToMidiNoteNumber(ptr); + ptr = strtok(NULL, " :"); + if (ptr != NULL) { + rangeH = Convert::kernToMidiNoteNumber(ptr); + } } - current = current->getNextToken(); } - return false; + + if (rangeH < 0) { + rangeH = rangeL; + } + + if (rangeL < 0) { rangeL = 0; } + if (rangeH < 0) { rangeH = 0; } + if (rangeL > 127) { rangeL = 127; } + if (rangeH > 127) { rangeH = 127; } + if (rangeL > rangeH) { + int temp = rangeL; + rangeL = rangeH; + rangeH = temp; + } + } @@ -114390,22 +120300,31 @@ bool Tool_phrase::hasPhraseMarks(HTp start) { ///////////////////////////////// // -// Tool_pline::Tool_pline -- Set the recognized options for the tool. +// Tool_gridtest::Tool_recip -- Set the recognized options for the tool. // -Tool_pline::Tool_pline(void) { - define("c|color=b", "color poetic lines (currently only by notes)"); - define("o|overlap=b", "do overlap analysis/markup"); +Tool_recip::Tool_recip(void) { + define("c|composite=b", "do composite rhythm analysis"); + define("a|append=b", "append composite analysis to input"); + define("p|prepend=b", "prepend composite analysis to input"); + define("r|replace=b", "replace **kern data with **recip data"); + define("x|attacks-only=b", "only mark lines with note attacks"); + define("G|ignore-grace-notes=b", "ignore grace notes"); + define("k|kern-spine=i:1", "analyze only given kern spine"); + define("K|all-spines=b", "analyze each kern spine separately"); + define("e|exinterp=s:**recip", "use the given exinterp for data output"); + define("n|kern-pitch=s:e", "note to add for '-e kern' option"); + define("kern=b", "equivalent to '-e kern' option"); } -///////////////////////////////// +/////////////////////////////// // -// Tool_pline::run -- Do the main work of the tool. +// Tool_recip::run -- Primary interfaces to the tool. // -bool Tool_pline::run(HumdrumFileSet& infiles) { +bool Tool_recip::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i spinestops; - infile.getSpineStopList(spinestops); - for (int i=0; i<(int)spinestops.size(); i++) { - if (!spinestops[i]->isKern()) { +void Tool_recip::insertAnalysisSpines(HumdrumFile& infile, HumdrumFile& cfile) { + for (int i=0; i=0; k--) { + int fcount = infile[i].getFieldCount(); + int ktrack = m_kernspines[k]->getTrack(); + int insertj = -1; + for (int j=fcount-1; j>=0; j--) { + if (!infile.token(i, j)->isKern()) { + continue; + } + int track = infile.token(i, j)->getTrack(); + if (track != ktrack) { + continue; + } + if (insertj < 0) { + insertj = j; + } + infile[i].appendToken(insertj, cfile.token(i, j)->getText()); + // infile.token(i, insertj+1)->setTrack(remapping[k]); + } + } } } + ////////////////////////////// // -// Tool_pline::markSpineRests -- +// Tool_recip::doCompositeAnalysis -- // -void Tool_pline::markSpineRests(HTp spineStop) { - string marker = "😀"; - int track = spineStop->getTrack(); - int lastValue = -1; - HTp current = spineStop->getPreviousToken(); - int line; - int cvalue; - while (current) { - if (!current->isData()) { - current = current->getPreviousToken(); +void Tool_recip::doCompositeAnalysis(HumdrumFile& infile) { + + // Calculate composite rhythm **recip spine: + + vector composite(infile.getLineCount()); + for (int i=0; i<(int)composite.size(); i++) { + composite[i] = infile[i].getDuration(); + } + + int kernQ = false; + if (m_exinterp.find("kern") != std::string::npos) { + kernQ = true; +// cerr << "KERN ON" << endl; + } + + // convert durations to **recip strings + vector recips(composite.size()); + for (int i=0; i<(int)recips.size(); i++) { + if ((!m_graceQ) && (composite[i] == 0)) { continue; } - if (current->isNull()) { - current = current->getPreviousToken(); - continue; + recips[i] = Convert::durationToRecip(composite[i]); + if (kernQ) { + recips[i] += m_kernpitch; +// cerr << "ADDING PITCH " << m_kernpitch << endl; } + } - line = current->getLineIndex(); - cvalue = m_lineInfo.at(line).at(track); - - if (current->isRest() && (cvalue != lastValue)) { - string text = *current; - text += marker; - current->setText(text); - } else { - lastValue = cvalue; - string text = *current; - text += "@" + to_string(cvalue); - current->setText(text); - } - current = current->getPreviousToken(); + if (getBoolean("append")) { + infile.appendDataSpine(recips, "", m_exinterp); + return; + } else if (getBoolean("prepend")) { + infile.prependDataSpine(recips, "", m_exinterp); + return; + } else { + infile.prependDataSpine(recips, "", m_exinterp); + infile.printFieldIndex(0, m_humdrum_text); + infile.clear(); + infile.readString(m_humdrum_text.str()); } } @@ -114563,95 +120462,76 @@ void Tool_pline::markSpineRests(HTp spineStop) { ////////////////////////////// // -// Tool_pline::fillLineInfo -- +// Tool_recip::replaceKernWithRecip -- // -void Tool_pline::fillLineInfo(HumdrumFile& infile, vector>& lineinfo) { - lineinfo.clear(); - lineinfo.resize(infile.getLineCount()); - int maxtrack = infile.getMaxTrack(); +void Tool_recip::replaceKernWithRecip(HumdrumFile& infile) { + vector kspines = infile.getKernSpineStartList(); HumRegex hre; - for (int i=0; iisKern()) { continue; } - for (int j=0; jgetTrack(); - lineinfo[i][track] = digit; + HTp etok = infile.getStrandEnd(i); + HTp tok = stok; + while (tok && (tok != etok)) { + if (!tok->isData()) { + tok = tok->getNextToken(); + continue; } - } - } - - for (int i=1; i<(int)lineinfo.size() - 1; i++) { - for (int j=1; j<=maxtrack; j++) { - if (lineinfo.at(i).at(j)) { + if (tok->isNull()) { + tok = tok->getNextToken(); continue; + } + if (tok->find('q') != string::npos) { + if (m_graceQ) { + tok->setText("q"); + } else { + tok->setText("."); + } } else { - lineinfo.at(i).at(j) = lineinfo.at(i-1).at(j); + hre.replaceDestructive(*tok, "", expression, "g"); } + tok = tok->getNextToken(); } } - // for (int i=0; i<(int)lineinfo.size() - 1; i++) { - // for (int j=1; j<=maxtrack; j++) { - // cerr << lineinfo[i][j] << "\t"; - // } - // cerr << endl; - // } + for (int i=0; i<(int)kspines.size(); i++) { + kspines[i]->setText(m_exinterp); + } } + ////////////////////////////// // -// Tool_pline::plineToColor -- +// Tool_recip::initialize -- // -void Tool_pline::plineToColor(HumdrumFile& infile, vector& tokens) { - HumRegex hre; - markRests(infile); - for (int i=0; i<(int)tokens.size(); i++) { - if (!hre.search(tokens[i], "^\\*pline:\\s*(\\d+)")) { - continue; - } - int lineNum = hre.getMatchInt(1); - int colorIndex = (lineNum - 1) % m_colors.size(); - string color = m_colors.at(colorIndex); - string text = "*color:"; - text += color; - tokens[i]->setText(text); - } -} - +void Tool_recip::initialize(HumdrumFile& infile) { + m_kernspines = infile.getKernSpineStartList(); + m_graceQ = !getBoolean("ignore-grace-notes"); + m_exinterp = getString("exinterp"); + if (m_exinterp.empty()) { + m_exinterp = "**recip"; + } else if (m_exinterp[0] != '*') { + m_exinterp.insert(0, "*"); + } + if (m_exinterp[1] != '*') { + m_exinterp.insert(0, "*"); + } -////////////////////////////// -// -// Tool_pline::getPlineInterpretations -- -// + m_kernpitch = getString("kern-pitch"); -void Tool_pline::getPlineInterpretations(HumdrumFile& infile, vector& tokens) { - HumRegex hre; - for (int i=0; iisKern()) { - continue; - } - if (hre.search(token, "^\\*pline:\\s*(\\d+)")) { - tokens.push_back(token); - } - } + if (getBoolean("kern")) { + m_exinterp = "**kern"; } + } @@ -114660,27 +120540,22 @@ void Tool_pline::getPlineInterpretations(HumdrumFile& infile, vector& token ///////////////////////////////// // -// Tool_gridtest::Tool_pnum -- Set the recognized options for the tool. +// Tool_restfill::Tool_restfill -- Set the recognized options for the tool. // -Tool_pnum::Tool_pnum(void) { - define("b|base=i:midi", "numeric base of pitch to extract"); - define("D|no-duration=b", "do not include duration"); - define("c|pitch-class=b", "give numeric pitch-class rather than pitch"); - define("o|octave=b", "give octave rather than pitch"); - define("r|rest=s:0", "representation string for rests"); - define("R|no-rests=b", "do not include rests in conversion"); - define("x|attacks-only=b", "only mark lines with note attacks"); +Tool_restfill::Tool_restfill(void) { + define("y|hidden-rests=b", "hide inserted rests"); + define("i|exinterp=s:kern", "type of spine to fill with rests"); } -/////////////////////////////// +///////////////////////////////// // -// Tool_pnum::run -- Primary interfaces to the tool. +// Tool_restfill::run -- Do the main work of the tool. // -bool Tool_pnum::run(HumdrumFileSet& infiles) { +bool Tool_restfill::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i kex; +void Tool_restfill::processFile(HumdrumFile& infile) { - for (int i=0; iisKern()) { - continue; - } - if (*token == "**kern") { - kex.push_back(token); - continue; - } - if (!token->isData()) { - continue; - } - if (token->isNull()) { - continue; - } - convertTokenToBase(token); + vector starts; + infile.getSpineStartList(starts, m_exinterp); + vector process(starts.size(), false); + for (int i=0; i<(int)starts.size(); i++) { + process[i] = hasBlankMeasure(starts[i]); + if (process[i]) { + starts[i]->setText("**temp-kern"); } } - - string newex; - for (int i=0; i<(int)kex.size(); i++) { - if (m_midiQ) { - newex = "**pmid"; - } else { - newex = "**b" + to_string(m_base); + infile.analyzeStructure(); + for (int i=0; i<(int)starts.size(); i++) { + if (!process[i]) { + continue; } - kex[i]->setText(newex); + starts[i]->setText("**kern"); + fillInRests(starts[i]); } } @@ -114782,237 +120650,168 @@ void Tool_pnum::processFile(HumdrumFile& infile) { ////////////////////////////// // -// Tool_pnum::convertTokenToBase -- +// Tool_restfill::hasBlankMeasure -- // -void Tool_pnum::convertTokenToBase(HTp token) { - string output; - int scount = token->getSubtokenCount(); - for (int i=0; igetSubtoken(i); - output += convertSubtokenToBase(subtok); - if (i < scount - 1) { - output += " "; +bool Tool_restfill::hasBlankMeasure(HTp start) { + bool foundcontent = false; + HTp current = start; + int founddata = false; + while (current) { + + if (current->isBarline()) { + if (founddata && !foundcontent) { + return true; + } + foundcontent = false; + founddata = false; + current = current->getNextToken(); + continue; + } + if (!current->isData()) { + current = current->getNextToken(); + continue; + } + founddata = true; + if (!current->isNull()) { + foundcontent = true; } + current = current->getNextToken(); + } - token->setText(output); + return false; } ////////////////////////////// // -// Tool_pnum::convertSubtokenToBase -- +// Tool_restfill::fillInRests -- +// Also deal with cases where the last measure does not end in a barline. // -string Tool_pnum::convertSubtokenToBase(const string& text) { - int pitch = 0; - if (text.find("r") == string::npos) { - switch (m_base) { - case 7: - pitch = Convert::kernToBase7(text); - break; - case 40: - pitch = Convert::kernToBase40(text); - break; - default: - pitch = Convert::kernToBase12(text); - } - } else if (!m_restQ) { - return "."; - } - string recip; - if (m_durationQ) { - HumRegex hre; - if (hre.search(text, "(\\d+%?\\d*\\.*)")) { - recip = hre.getMatch(1); - } - } - - string output; - - int pc = pitch % m_base; - int oct = pitch / m_base; - - if (m_midiQ) { - // MIDI numbers use 5 for middle-C octave. - pitch += 12; - } - - int tie = 1; - if (text.find("_") != string::npos) { - tie = -1; - } - if (text.find("]") != string::npos) { - tie = -1; - } - pitch *= tie; - if (m_attacksQ && pitch < 0) { - return "."; - } - - if (m_durationQ) { - output += recip; - output += "/"; - } - - if (text.find("r") != string::npos) { - output += m_rest; - } else { - if (!m_octaveQ && !m_classQ) { - output += to_string(pitch); - } else { - if (m_classQ) { - if (pitch < 0) { - output += "-"; - } - output += to_string(pc); - } - if (m_classQ && m_octaveQ) { - output += ":"; +void Tool_restfill::fillInRests(HTp start) { + HTp current = start; + HTp firstcell = NULL; + int founddata = false; + bool foundcontent = false; + HumNum lasttime = 0; + HumNum currtime = 0; + HumNum duration = 0; + while (current) { + if (current->isBarline()) { + if (firstcell) { + lasttime = firstcell->getDurationFromStart(); } - if (m_octaveQ) { - output += to_string(oct); + currtime = getNextTime(current); + if (firstcell && founddata && !foundcontent) { + duration = currtime - lasttime; + addRest(firstcell, duration); } + firstcell = NULL; + founddata = false; + foundcontent = false; + current = current->getNextToken(); + lasttime = currtime; + continue; + } + if (!current->isData()) { + current = current->getNextToken(); + continue; + } + if (current->getDuration() == 0) { + // grace-note line, so ignore + current = current->getNextToken(); + continue; + } + founddata = true; + if (!current->isNull()) { + foundcontent = true; + } + if (!firstcell) { + firstcell = current; } + current = current->getNextToken(); } - - return output; } - - -#define OBJTAB "\t\t\t\t\t\t" -#define SVGTAG "_99%svg%"; - -#define SVGTEXT(out, text) \ - if (m_defineQ) { \ - out << "SVG "; \ - } else { \ - out << "t 1 1\n"; \ - out << SVGTAG; \ - } \ - printScoreEncodedText((out), (text)); \ - out << "\n"; - - ////////////////////////////// // -// _VoiceInfo::_VoiceInfo -- +// Tool_restfill::addRest -- // -_VoiceInfo::_VoiceInfo(void) { - clear(); +void Tool_restfill::addRest(HTp cell, HumNum duration) { + if (!cell) { + return; + } + string text = Convert::durationToRecip(duration); + text += "r"; + if (m_hiddenQ) { + text += "yy"; + } + cell->setText(text); } ////////////////////////////// // -// _VoiceInfo::clear -- +// Tool_restfill::getNextTime -- // -void _VoiceInfo::clear(void) { - name = ""; - abbr = ""; - midibins.resize(128); - fill(midibins.begin(), midibins.end(), 0.0); - diatonic.resize(7 * 12); - for (int i=0; i<(int)diatonic.size(); i++) { - diatonic[i].resize(6); - fill(diatonic[i].begin(), diatonic[i].end(), 0.0); +HumNum Tool_restfill::getNextTime(HTp token) { + HTp current = token; + while (current) { + if (current->isData()) { + return current->getDurationFromStart(); + } + current = current->getNextToken(); } - track = -1; - kernQ = false; - diafinal.clear(); - accfinal.clear(); - namfinal.clear(); - index = -1; + return token->getOwner()->getOwner()->getScoreDuration(); } -////////////////////////////// -// -// _VoiceInfo::print -- -// -ostream& _VoiceInfo::print(ostream& out) { - out << "==================================" << endl; - out << "track: " << track << endl; - out << " name: " << name << endl; - out << " abbr: " << abbr << endl; - out << " kern: " << kernQ << endl; - out << " final:"; - for (int i=0; i<(int)diafinal.size(); i++) { - out << " " << diafinal.at(i) << "/" << accfinal.at(i); - } - out << endl; - out << " midi: "; - for (int i=0; i<(int)midibins.size(); i++) { - if (midibins.at(i) > 0.0) { - out << " " << i << ":" << midibins.at(i); - } - } - out << endl; - out << " diat: "; - for (int i=0; i<(int)diatonic.size(); i++) { - if (diatonic.at(i).at(0) > 0.0) { - out << " " << i << ":" << diatonic.at(i).at(0); - } - } - out << endl; - out << "==================================" << endl; - return out; -} ///////////////////////////////// // -// Tool_prange::Tool_prange -- Set the recognized options for the tool. +// Tool_rid::Tool_rid -- Set the recognized options for the tool. // -Tool_prange::Tool_prange(void) { - - define("A|acc|color-accidentals=b", "add color to accidentals in histogram"); - define("D|diatonic=b", "diatonic counts ignore chormatic alteration"); - define("K|no-key=b", "do not display key signature"); - define("N|norm=b", "normalize pitch counts"); - define("S|score=b", "convert range info to SCORE"); - define("T|no-title=b", "do not display a title"); - define("a|all=b", "generate all-voice analysis"); - define("c|range|count=s:60-71", "count notes in a particular MIDI note number range (inclusive)"); - define("debug=b", "trace input parsing"); - define("d|duration=b", "weight pitches by duration"); - define("e|embed=b", "embed SCORE data in input Humdrum data"); - define("fill=b", "change color of fill only"); - define("finalis|final|last=b", "include finalis note by voice"); - define("f|fraction=b", "display histogram fractions"); - define("h|hover=b", "include svg hover capabilities"); - define("i|instrument=b", "categorize multiple inputs by instrument"); - define("j|jrp=b", "set options for JRP style"); - define("l|local|local-maximum|local-maxima=b", "use maximum values by voice rather than all voices"); - define("no-define=b", "do not use defines in output SCORE data"); - define("pitch=b", "display pitch info in **pitch format"); - define("print=b", "count printed notes rather than sounding"); - define("p|percentile=d:0.0", "display the xth percentile pitch"); - define("q|quartile=b", "display quartile notes"); - define("r|reverse=b", "reverse list of notes in analysis from high to low"); - define("x|extrema=b", "highlight extrema notes in each part"); - define("sx|scorexml|score-xml|ScoreXML|scoreXML=b", "output ScoreXML format"); - define("title=s:", "title for SCORE display"); +Tool_rid::Tool_rid(void) { + // Humdrum Toolkit classic rid options: + define("D|all-data=b", "remove all data records"); + define("d|null-data=b", "remove null data records"); + define("G|all-global=b", "remove all global comments"); + define("g|null-global=b", "remove null global comments"); + define("I|all-interpretation=b", "remove all interpretation records"); + define("i|null-interpretation=b", "remove null interpretation records"); + define("L|all-local-comment=b", "remove all local comments"); + define("l|1|null-local-comment=b", "remove null local comments"); + define("T|all-tandem-interpretation=b", "remove all tandem interpretations"); + define("U|u=b", "remove unnecessary (duplicate ex. interps."); + define("k|consider-kern-only=b", "for -d, only consider **kern spines."); + define("V=b", "negate filtering effect of program."); + define("H|no-humdrum-syntax=b", "equivalent to -GLIMd."); + // additional options + define("M|all-barlines=b", "remove measure lines"); + define("C|all-comments=b", "remove all comment lines"); + define("c=b", "remove global and local comment lines"); } + ///////////////////////////////// // -// Tool_prange::run -- Do the main work of the tool. +// Tool_rid::run -- Do the main work of the tool. // -bool Tool_prange::run(HumdrumFileSet& infiles) { +bool Tool_rid::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i 1) { - m_percentile = m_percentile / 100.0; - } +void Tool_rid::initialize(void) { + option_D = getBoolean("D"); + option_d = getBoolean("d"); + option_G = getBoolean("G"); + option_g = getBoolean("g"); + option_I = getBoolean("I"); + option_i = getBoolean("i"); + option_L = getBoolean("L"); + option_l = getBoolean("l"); + option_T = getBoolean("T"); + option_U = getBoolean("U"); + option_M = getBoolean("M"); + option_C = getBoolean("C"); + option_c = getBoolean("c"); + option_k = getBoolean("k"); + option_V = getBoolean("V"); - #ifdef __EMSCRIPTEN__ - // Default styling for JavaScript version of program: - m_accQ = !getBoolean("color-accidentals"); - m_scoreQ = !getBoolean("score"); - m_embedQ = !getBoolean("embed"); - m_hoverQ = !getBoolean("hover"); - m_notitleQ = !getBoolean("no-title"); - #endif + if (getBoolean("no-humdrum-syntax")) { + // remove all Humdrum file structure + option_G = option_L = option_I = option_M = option_d = 1; + } } ////////////////////////////// // -// Tool_prange::processFile -- +// Tool_rid::processFile -- // -void Tool_prange::processFile(HumdrumFile& infile) { - prepareRefmap(infile); - vector<_VoiceInfo> voiceInfo; - infile.fillMidiInfo(m_trackMidi); - getVoiceInfo(voiceInfo, infile); - fillHistograms(voiceInfo, infile); - - if (m_debugQ) { - for (int i=0; i<(int)voiceInfo.size(); i++) { - voiceInfo[i].print(cerr); - } - } +void Tool_rid::processFile(HumdrumFile& infile) { + int setcount = 1; // disabled for now. - if (m_scoreQ) { - stringstream scoreout; - printScoreFile(scoreout, voiceInfo, infile); - if (m_embedQ) { - if (m_extremaQ) { - doExtremaMarkup(infile); - } - m_humdrum_text << infile; - printEmbeddedScore(m_humdrum_text, scoreout, infile); - } else { - if (m_extremaQ) { - doExtremaMarkup(infile); - } - m_humdrum_text << scoreout.str(); - } - } else { - printAnalysis(m_humdrum_text, voiceInfo[0].midibins); - } -} + HumRegex hre; + int revQ = option_V; + // if bibliographic/reference records are not suppressed + // print the !!!!SEGMENT: marker if present. + if ((setcount > 1) && (!option_G)) { + infile.printNonemptySegmentLabel(m_humdrum_text); + } + for (int i=0; i=0; j--) { - if (m_trackMidi[i][j].empty()) { - continue; - } - if (maxindex < 0) { - maxindex = j; - break; - } - } + // got past all test, so print the current line: + if (!revQ) { + m_humdrum_text << infile[i] << "\n"; + } + } +} - for (int j=1; j<(int)m_trackMidi[i].size(); j++) { - if (m_trackMidi[i][j].empty()) { - continue; - } - if (minindex < 0) { - minindex = j; - break; - } - } - if ((maxindex < 0) || (minindex < 0)) { - continue; - } - applyMarkup(m_trackMidi[i][maxindex], m_highMark); - applyMarkup(m_trackMidi[i][minindex], m_lowMark); - highQ = true; - lowQ = true; - } - if (highQ) { - string highRdf = "!!!RDF**kern: " + m_highMark + " = marked note, color=\"hotpink\", highest note"; - infile.appendLine(highRdf); - } - if (lowQ) { - string lowRdf = "!!!RDF**kern: " + m_lowMark + " = marked note, color=\"limegreen\", lowest note"; - infile.appendLine(lowRdf); - } - if (highQ || lowQ) { - infile.createLinesFromTokens(); - } -} -////////////////////////////// +///////////////////////////////// // -// Tool_prange::applyMarkup -- +// Tool_rphrase::Tool_rphrase -- Set the recognized options for the tool. // -void Tool_prange::applyMarkup(vector>& notelist, const string& mark) { - for (int i=0; i<(int)notelist.size(); i++) { - HTp token = notelist[i].first; - int subtoken = notelist[i].second; - int tokenCount = token->getSubtokenCount(); - if (tokenCount == 1) { - string text = *token; - text += mark; - token->setText(text); - } else { - string stok = token->getSubtoken(subtoken); - stok = mark + stok; - token->replaceSubtoken(subtoken, stok); - } - } +Tool_rphrase::Tool_rphrase(void) { + define("a|average=b", "calculate average length of rest-phrases by score"); + define("A|all-average=b", "calculate average length of rest-phrases for all scores"); + define("B|no-breath=b", "ignore breath interpretations"); + define("c|composite|collapse=b", "collapse all voices into single part"); + define("d|duration-unit=d:2.0", "duration units, default: 2.0 (minims/half notes)"); + define("f|filename=b", "include filename in output analysis"); + define("F|full-filename=b", "include full filename location in output analysis"); + define("I|no-info=b", "do not display summary info"); + define("l|longa=b", "display minim length of longas"); + define("m|b|measure|barline=b", "include barline numbers in output analysis"); + define("mark=b", "mark starts of phrases in score"); + define("s|sort=b", "sort phrases by short to long length"); + define("S|reverse-sort=b", "sort phrases by long to short length"); + define("u|url-type=s", "URL type (jrp, 1520s) for hyperlink"); + define("z|squeeze=b", "squeeze notation"); + define("close=b", "close details element initially"); } -////////////////////////////// +///////////////////////////////// // -// Tool_prange::printEmbeddedScore -- +// Tool_rphrase::run -- Do the main work of the tool. // -void Tool_prange::printEmbeddedScore(ostream& out, stringstream& scoredata, HumdrumFile& infile) { - int id = getPrangeId(infile); +bool Tool_rphrase::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; i\n"; - out << "!!@@END: PREHTML\n"; - out << "!!@@BEGIN: SCORE\n"; - out << "!!@ID: prange-" << id << "\n"; - out << "!!@OUTPUTFORMAT: svg\n"; - out << "!!@CROP: yes\n"; - out << "!!@PADDING: 10\n"; - out << "!!@SCALING: 1.5\n"; - out << "!!@SVGFORMAT: yes\n"; - out << "!!@TRANSPARENT: yes\n"; - out << "!!@ANTIALIAS: no\n"; - out << "!!@EMBEDPMX: yes\n"; - out << "!!@ANNOTATE: no\n"; - out << "!!@CONTENTS:\n"; - string line; - while(getline(scoredata, line)) { - out << "!!" << line << endl; + +bool Tool_rphrase::run(const string& indata, ostream& out) { + HumdrumFile infile(indata); + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; } - out << "!!@@END: SCORE\n"; + return status; +} + + +bool Tool_rphrase::run(HumdrumFile& infile, ostream& out) { + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; + } + return status; +} + + +bool Tool_rphrase::run(HumdrumFile& infile) { + initialize(); + processFile(infile); + return true; } ////////////////////////////// // -// Tool_prange::getPrangeId -- Find a line in this form -// ^!!@ID: prange-(\d+)$ -// and return $1+1. Searching backwards since the HTML section -// will likely be at the bottom. Assuming that the prange -// SVG images are stored in sequence, with the highest ID last -// in the file if there are more than one. +// Tool_rphrase::finally -- // -int Tool_prange::getPrangeId(HumdrumFile& infile) { - string search = "!!@ID: prange-"; - int length = (int)search.length(); - for (int i=infile.getLineCount() - 1; i>=0; i--) { - HTp token = infile.token(i, 0); - if (token->compare(0, length, search) == 0) { - HumRegex hre; - if (hre.search(token, "prange-(\\d+)")) { - return hre.getMatchInt(1) + 1; +void Tool_rphrase::finally(void) { + if (!m_markQ) { + if (m_allAverageQ) { + if (m_compositeQ) { + double average = m_sumComposite / m_pcountComposite; + m_free_text << "Composite average phrase length: " << average << " minims" << endl; + } else { + double average = m_sum / m_pcount; + m_free_text << "All average phrase length: " << average << " minims" << endl; } } } - return 1; } ////////////////////////////// // -// Tool_prange::mergeAllVoiceInfo -- +// Tool_rphrase::initialize -- // -void Tool_prange::mergeAllVoiceInfo(vector<_VoiceInfo>& voiceInfo) { - voiceInfo.at(0).diafinal.clear(); - voiceInfo.at(0).accfinal.clear(); +void Tool_rphrase::initialize(void) { + m_barlineQ = getBoolean("measure"); + m_allAverageQ = getBoolean("all-average"); + m_breathQ = !getBoolean("no-breath"); + m_compositeQ = getBoolean("collapse"); + m_filenameQ = getBoolean("filename"); + m_fullFilenameQ = getBoolean("full-filename"); + m_urlType = getString("url-type"); + m_longaQ = getBoolean("longa"); + #ifndef __EMSCRIPTEN__ + m_markQ = getBoolean("mark"); + m_averageQ = getBoolean("average"); + #else + m_markQ = !getBoolean("mark"); + m_averageQ = !getBoolean("average"); + #endif + m_sortQ = getBoolean("sort"); + m_reverseSortQ = getBoolean("reverse-sort"); + m_durUnit = getDouble("duration-unit"); + m_infoQ = !getDouble("no-info"); + m_squeezeQ = getBoolean("squeeze"); + m_closeQ = getBoolean("close"); +} - for (int i=1; i<(int)voiceInfo.size(); i++) { - if (!voiceInfo[i].kernQ) { - continue; - } - for (int j=0; j<(int)voiceInfo.at(i).diafinal.size(); j++) { - voiceInfo.at(0).diafinal.push_back(voiceInfo.at(i).diafinal.at(j)); - voiceInfo.at(0).accfinal.push_back(voiceInfo.at(i).accfinal.at(j)); - voiceInfo.at(0).namfinal.push_back(voiceInfo.at(i).name); - } - for (int j=0; j<(int)voiceInfo[i].midibins.size(); j++) { - voiceInfo[0].midibins[j] += voiceInfo[i].midibins[j]; - } - for (int j=0; j<(int)voiceInfo.at(i).diatonic.size(); j++) { - for (int k=0; k<(int)voiceInfo.at(i).diatonic.at(k).size(); k++) { - voiceInfo[0].diatonic.at(j).at(k) += voiceInfo.at(i).diatonic.at(j).at(k); +////////////////////////////// +// +// Tool_rphrase::processFile -- +// + +void Tool_rphrase::processFile(HumdrumFile& infile) { + if (m_filenameQ) { + m_filename = infile.getFilename(); + HumRegex hre; + hre.replaceDestructive(m_filename, "", ".*\\/"); + hre.replaceDestructive(m_filename, "", "\\.krn$"); + } else if (m_fullFilenameQ) { + m_filename = infile.getFilename(); + } + vector kernStarts = infile.getKernSpineStartList(); + vector voiceInfo(kernStarts.size()); + Tool_rphrase::VoiceInfo compositeInfo; + + if (m_compositeQ) { + fillCompositeInfo(compositeInfo, infile); + } else { + fillVoiceInfo(voiceInfo, kernStarts, infile); + } + + if (m_longaQ) { + markLongaDurations(infile); + } + + if ((!m_allAverageQ) && (!m_markQ)) { + if (m_line == 1) { + if (m_compositeQ) { + m_free_text << "Filename"; + if (!m_urlType.empty()) { + m_free_text << "\tVHV"; + } + m_free_text << "\tVoice"; + m_free_text << "\tComp seg count"; + if (m_averageQ) { + m_free_text << "\tAvg comp seg dur"; + } + m_free_text << "\tComposite seg durs"; + m_free_text << endl; + } else { + m_free_text << "Filename"; + if (!m_urlType.empty()) { + m_free_text << "\tVHV"; + } + m_free_text << "\tVoice"; + m_free_text << "\tSounding dur"; + m_free_text << "\tResting dur"; + m_free_text << "\tTotal dur"; + m_free_text << "\tSeg count"; + if (m_averageQ) { + m_free_text << "\tSeg dur average"; + } + m_free_text << "\tSegment durs"; + m_free_text << endl; + } + } + if (m_compositeQ) { + if (m_compositeQ) { + m_line++; } + printVoiceInfo(compositeInfo); + } else { + printVoiceInfo(voiceInfo); + } + } + + if (m_markQ) { + outputMarkedFile(infile, voiceInfo, compositeInfo); + if (m_squeezeQ) { + m_humdrum_text << "!!!verovio: evenNoteSpacing" << endl; } } + } -////////////////////////////// +////////////////////////// // -// Tool_prange::getVoiceInfo -- get names and track info for **kern spines. +// Tool_rphrase::markLongaDuratios -- // -void Tool_prange::getVoiceInfo(vector<_VoiceInfo>& voiceInfo, HumdrumFile& infile) { - voiceInfo.clear(); - voiceInfo.resize(infile.getMaxTracks() + 1); - for (int i=0; i<(int)voiceInfo.size(); i++) { - voiceInfo.at(i).index = i; +void Tool_rphrase::markLongaDurations(HumdrumFile& infile) { + string longrdf; + for (int i=0; i kstarts = infile.getKernSpineStartList(); - - if (kstarts.size() == 2) { - voiceInfo[0].name = "both"; - voiceInfo[0].abbr = "both"; - voiceInfo[0].track = 0; - } else { - voiceInfo[0].name = "all"; - voiceInfo[0].abbr = "all"; - voiceInfo[0].track = 0; + if (longrdf.empty()) { + return; } - for (int i=0; igetTrack(); - voiceInfo[track].track = track; - if (token->isKern()) { - voiceInfo[track].kernQ = true; - } - if (!voiceInfo[track].kernQ) { + if (!token->isKern()) { continue; } - if (token->isInstrumentName()) { - voiceInfo[track].name = token->getInstrumentName(); - } - if (token->isInstrumentAbbreviation()) { - voiceInfo[track].abbr = token->getInstrumentAbbreviation(); + if (token->find(longrdf) != string::npos) { + HumNum duration = token->getTiedDuration(); + stringstream value; + value.str(""); + value << duration.getFloat() / m_durUnit; + token->setValue("auto", "rphrase-longa", value.str()); } } } - - - // Check for piano/Grand Staff parts with LH/RH encoding. - if (kstarts.size() == 2) { - string bottomStaff = getHand(kstarts[0]); - string topStaff = getHand(kstarts[1]); - if (!bottomStaff.empty() && !topStaff.empty()) { - int track = kstarts[0]->getTrack(); - voiceInfo[track].name = "left hand"; - track = kstarts[1]->getTrack(); - voiceInfo[track].name = "right hand"; - } - } } ////////////////////////////// // -// Tool_prange::getHand -- +// Tool_rphrase::outputMarkedFile -- // -string Tool_prange::getHand(HTp sstart) { - HTp current = sstart->getNextToken(); - HTp target = NULL; - while (current) { - if (current->isData()) { - break; - } - if (*current == "*LH") { - target = current; - break; - } - if (*current == "*RH") { - target = current; - break; +void Tool_rphrase::outputMarkedFile(HumdrumFile& infile, vector& voiceInfo, + Tool_rphrase::VoiceInfo& compositeInfo) { + m_free_text.clear(); + m_free_text.str(""); + for (int i=0; igetNextToken(); } - if (target) { - if (*current == "*LH") { - return "LH"; - } else if (*current == "*RH") { - return "RH"; - } else { - return ""; - } - } else { - return ""; + if (m_infoQ) { + printEmbeddedVoiceInfo(voiceInfo, compositeInfo, infile); } } @@ -115445,200 +121303,135 @@ string Tool_prange::getHand(HTp sstart) { ////////////////////////////// // -// Tool_prange::getInstrumentNames -- Find any instrument names which are listed -// before the first data line. Instrument names are in the form: -// -// *I"name +// Tool_rphrase::printDataLine -- // -void Tool_prange::getInstrumentNames(vector& nameByTrack, vector& kernSpines, - HumdrumFile& infile) { - HumRegex hre; +void Tool_rphrase::printDataLine(HumdrumFile& infile, int index) { - int track; - string name; - // nameByTrack.resize(kernSpines.size()); - nameByTrack.resize(infile.getMaxTrack() + 1); - fill(nameByTrack.begin(), nameByTrack.end(), ""); - vector kspines = infile.getKernSpineStartList(); - if (kspines.size() == 2) { - nameByTrack.at(0) = "both"; - } else { - nameByTrack.at(0) = "all"; + bool hasLonga = false; + if (m_longaQ) { + for (int j=0; jisKern()) { + continue; + } + string lotext = token->getValue("auto", "rphrase-longa"); + if (!lotext.empty()) { + hasLonga = true; + break; + } + } } - for (int i=0; iisKern()) { continue; } - for (int j=0; jgetTrack(); - for (int k=0; k<(int)kernSpines.size(); k++) { - if (track == kernSpines[k]) { - nameByTrack[k] = name; - } + string lotext = token->getValue("auto", "rphrase-start"); + if (!lotext.empty()) { + hasLo = true; + break; + } + } + + // search for composite phrase info + bool hasGlo = false; + if (infile[index].isData()) { + string glotext = infile[index].getValue("auto", "rphrase-composite-start"); + if (!glotext.empty()) { + hasGlo = true; + } + } + + if (hasGlo) { + string glotext = infile[index].getValue("auto", "rphrase-composite-start"); + m_humdrum_text << "!!LO:TX:b:B:color=" << m_compositeLengthColor << ":t=" << glotext << endl; + } + + if (hasLonga) { + for (int j=0; jisKern()) { + m_humdrum_text << "!"; + } else { + string value = token->getValue("auto", "rphrase-longa"); + if (value.empty()) { + m_humdrum_text << "!"; + } else { + m_humdrum_text << "!LO:TX:a:B:color=silver:t=" << value; + } + } + if (j < infile[index].getFieldCount() - 1) { + m_humdrum_text << "\t"; + } + } + m_humdrum_text << endl; + } + + if (hasLo) { + for (int j=0; jisKern()) { + m_humdrum_text << "!"; + } else { + string value = token->getValue("auto", "rphrase-start"); + if (value.empty()) { + m_humdrum_text << "!"; + } else { + m_humdrum_text << "!LO:TX:a:B:color=" << m_voiceLengthColor << ":t=" << value; } } + if (j < infile[index].getFieldCount() - 1) { + m_humdrum_text << "\t"; + } } + m_humdrum_text << endl; } + + m_humdrum_text << infile[index] << endl; } ////////////////////////////// // -// Tool_prange::fillHistograms -- Store notes in score by MIDI note number. +// Tool_rphrase::getCompositeStates -- // -void Tool_prange::fillHistograms(vector<_VoiceInfo>& voiceInfo, HumdrumFile& infile) { - // storage for finals info: - vector> diafinal; - vector> accfinal; - diafinal.resize(infile.getMaxTracks() + 1); - accfinal.resize(infile.getMaxTracks() + 1); - +void Tool_rphrase::getCompositeStates(vector& noteStates, HumdrumFile& infile) { + noteStates.resize(infile.getLineCount()); + fill(noteStates.begin(), noteStates.end(), -1); for (int i=0; iisKern()) { continue; } - if (token->isNull()) { + if (token->isRest()) { continue; - } - int track = token->getTrack(); - - diafinal.at(track).clear(); - accfinal.at(track).clear(); - - vector tokens = token->getSubtokens(); - for (int k=0; k<(int)tokens.size(); k++) { - if (tokens[k].find("r") != string::npos) { - continue; - } - if (tokens[k].find("R") != string::npos) { - // non-pitched note - continue; - } - bool hasPitch = false; - for (int m=0; m<(int)tokens[k].size(); m++) { - char test = tokens[k].at(m); - if (!isalpha(test)) { - continue; - } - test = tolower(test); - if ((test >= 'a') && (test <= 'g')) { - hasPitch = true; - break; - } - } - if (!hasPitch) { - continue; - } - int octave = Convert::kernToOctaveNumber(tokens[k]) + 3; - if (octave < 0) { - cerr << "Note too low: " << tokens[k] << endl; - continue; - } - if (octave >= 12) { - cerr << "Note too high: " << tokens[k] << endl; - continue; - } - int dpc = Convert::kernToDiatonicPC(tokens[k]); - int acc = Convert::kernToAccidentalCount(tokens[k]); - if (acc < -2) { - cerr << "Accidental too flat: " << tokens[k] << endl; + } else if (token->isNull()) { + HTp resolve = token->resolveNull(); + if (!resolve) { continue; - } - if (acc > +2) { - cerr << "Accidental too sharp: " << tokens[k] << endl; + } else if (resolve->isRest()) { continue; - } - int diatonic = dpc + 7 * octave; - int realdiatonic = dpc + 7 * (octave-3); - - diafinal.at(track).push_back(realdiatonic); - accfinal.at(track).push_back(acc); - - acc += 3; - int midi = Convert::kernToMidiNoteNumber(tokens[k]); - if (midi < 0) { - cerr << "MIDI pitch too low: " << tokens[k] << endl; - } - if (midi > 127) { - cerr << "MIDI pitch too high: " << tokens[k] << endl; - } - if (m_durationQ) { - double duration = Convert::kernToDuration(tokens[k]).getFloat(); - voiceInfo[track].diatonic.at(diatonic).at(0) += duration; - voiceInfo[track].diatonic.at(diatonic).at(acc) += duration; - voiceInfo[track].midibins.at(midi) += duration; } else { - if (tokens[k].find("]") != string::npos) { - continue; - } - if (tokens[k].find("_") != string::npos) { - continue; - } - voiceInfo[track].diatonic.at(diatonic).at(0)++; - voiceInfo[track].diatonic.at(diatonic).at(acc)++; - voiceInfo[track].midibins.at(midi)++; + value = 1; + break; } - } - } - } - - mergeFinals(voiceInfo, diafinal, accfinal); - - // Sum all voices into midibins and diatonic arrays of vector position 0: - mergeAllVoiceInfo(voiceInfo); -} - - - -////////////////////////////// -// -// Tool_prange::mergeFinals -- -// - -void Tool_prange::mergeFinals(vector<_VoiceInfo>& voiceInfo, vector>& diafinal, - vector>& accfinal) { - for (int i=0; i<(int)voiceInfo.size(); i++) { - voiceInfo.at(i).diafinal = diafinal.at(i); - voiceInfo.at(i).accfinal = accfinal.at(i); - } -} - - - -////////////////////////////// -// -// Tool_prange::printFilenameBase -- -// - -void Tool_prange::printFilenameBase(ostream& out, const string& filename) { - HumRegex hre; - if (hre.search(filename, "([^/]+)\\.([^.]*)", "")) { - if (hre.getMatch(1).size() <= 8) { - printXmlEncodedText(out, hre.getMatch(1)); - } else { - // problem with too long a name (MS-DOS will have problems). - // optimize to chop off everything after the dash in the - // name (for Josquin catalog numbers). - string shortname = hre.getMatch(1); - if (hre.search(shortname, "-.*")) { - hre.replaceDestructive(shortname, "", "-.*"); - printXmlEncodedText(out, shortname); } else { - printXmlEncodedText(out, shortname); + value = 1; + break; } } + noteStates[i] = value; } } @@ -115646,1401 +121439,1767 @@ void Tool_prange::printFilenameBase(ostream& out, const string& filename) { ////////////////////////////// // -// Tool_prange::printReferenceRecords -- +// Tool_rphrase::printVoiceInfo -- // -void Tool_prange::printReferenceRecords(ostream& out, HumdrumFile& infile) { - for (int i=0; i& voiceInfo) { + for (int i=(int)voiceInfo.size() - 1; i>=0; i--) { + if (!m_compositeQ) { + m_line++; } - out << "\t\t\t\t\t\t\n"; + printVoiceInfo(voiceInfo[i]); } } - -////////////////////////////// -// -// Tool_prange::printScoreEncodedText -- print SCORE text string -// See SCORE 3.1 manual additions (page 19) for more. -// - -void Tool_prange::printScoreEncodedText(ostream& out, const string& strang) { - string newstring = strang; - HumRegex hre; - - hre.replaceDestructive(newstring, "<<$1", "&([aeiou])acute;", "gi"); - hre.replaceDestructive(newstring, "<<$1", "([áéíóú])", "gi"); - - hre.replaceDestructive(newstring, ">>$1", "&([aeiou])grave;", "gi"); - hre.replaceDestructive(newstring, ">>$1", "([àèìòù])", "gi"); - - hre.replaceDestructive(newstring, "%%$1", "&([aeiou])uml;", "gi"); - hre.replaceDestructive(newstring, "%%$1", "([äëïöü])", "gi"); - - hre.replaceDestructive(newstring, "^^$1", "&([aeiou])circ;", "gi"); - hre.replaceDestructive(newstring, "^^$1", "([âêîôû])", "gi"); - - hre.replaceDestructive(newstring, "##c", "ç", "g"); - hre.replaceDestructive(newstring, "##C", "Ç", "g"); - hre.replaceDestructive(newstring, "?\\|", "\\|", "g"); - hre.replaceDestructive(newstring, "?\\", "\\\\", "g"); - hre.replaceDestructive(newstring, "?m", "---", "g"); - hre.replaceDestructive(newstring, "?n", "--", "g"); - hre.replaceDestructive(newstring, "?2", "-sharp", "g"); - hre.replaceDestructive(newstring, "?1", "-flat", "g"); - hre.replaceDestructive(newstring, "?3", "-natural", "g"); - hre.replaceDestructive(newstring, "\\", "/", "g"); - hre.replaceDestructive(newstring, "?[", "\\[", "g"); - hre.replaceDestructive(newstring, "?]", "\\]", "g"); - - out << newstring; -} - - - -////////////////////////////// -// -// Tool_prange::printXmlEncodedText -- convert -// & to & -// " to " -// ' to &spos; -// < to < -// > to > -// - -void Tool_prange::printXmlEncodedText(ostream& out, const string& strang) { - HumRegex hre; - string astring = strang; - - hre.replaceDestructive(astring, "&", "&", "g"); - hre.replaceDestructive(astring, "'", "'", "g"); - hre.replaceDestructive(astring, "\"", """, "g"); - hre.replaceDestructive(astring, "<", "<", "g"); - hre.replaceDestructive(astring, ">", ">", "g"); - - out << astring; -} - - - ////////////////////////////// // -// Tool_prange::printScoreFile -- +// Tool_rphrase::printHyperlink -- // -void Tool_prange::printScoreFile(ostream& out, vector<_VoiceInfo>& voiceInfo, HumdrumFile& infile) { - string titlestring = getTitle(); - - if (m_defineQ) { - out << "#define SVG t 1 1 \\n_99%svg%\n"; - } - - string acctext = "g.bar.doubleflat path{color:darkorange;stroke:darkorange;}g.bar.flat path{color:brown;stroke:brown;}g.bar.sharp path{color:royalblue;stroke:royalblue;}g.bar.doublesharp path{color:aquamarine;stroke:aquamarine;}"; - string hovertext = ".bar:hover path{fill:red;color:red;stroke:red !important}"; - string hoverfilltext = hovertext; - - string text1 = ""; - string text2 = text1; - - - // print CSS style information if requested - if (m_hoverQ) { - SVGTEXT(out, text1); + if (!options.empty()) { + command += "%20-"; + command += options; } - int maxStaffPosition = getMaxStaffPosition(voiceInfo); - - if (!titlestring.empty()) { - // print title - int vpos = 54; - if (maxStaffPosition > 12) { - vpos = maxStaffPosition + 3; - } - out << "t 2 10 "; - out << vpos; - out << " 1 1 0 0 0 0 -1.35\n"; - // out << "_03"; - printScoreEncodedText(out, titlestring); - out << "\n"; + if (urlType == "jrp") { + m_free_text << "=HYPERLINK(\"https://verovio.humdrum.org/?file=jrp/\" & "; + m_free_text << "LEFT(A" << m_line << ", 3) & \"/\" & A" << m_line << " & "; + m_free_text << "\".krn&filter=" << command << "&k=ey\", LEFT(A" << m_line; + m_free_text << ", FIND(\"-\", A" << m_line << ") - 1))"; + } else if (urlType == "1520s") { + m_free_text << "=HYPERLINK(\"https://verovio.humdrum.org/?file=1520s/\" & A"; + m_free_text << m_line << " & \".krn&filter=" << command << "&k=ey\", LEFT(A"; + m_free_text << m_line << ", FIND(\"-\", A" << m_line << ") - 1))"; } +} - // print duration label if duration weighting is being used - SVGTEXT(out, ""); - if (m_durationQ) { - out << "t 2 185.075 14 1 0.738 0 0 0 0 0\n"; - out << "_00(durations)\n"; - } else { - out << "t 2 185.075 14 1 0.738 0 0 0 0 0\n"; - out << "_00(attacks)\n"; - } - SVGTEXT(out, ""); - // print staff lines - out << "8 1 0 0 0 200\n"; // staff 1 - out << "8 2 0 -6 0 200\n"; // staff 2 - int keysig = getKeySignature(infile); - // print key signature - if (keysig) { - out << "17 1 10 0 " << keysig << " 101.0"; - printKeySigCompression(out, keysig, 0); - out << endl; - out << "17 2 10 0 " << keysig; - printKeySigCompression(out, keysig, 1); - out << endl; +////////////////////////////// +// +// Tool_rphrase::printVoiceInfo -- +// + +void Tool_rphrase::printVoiceInfo(Tool_rphrase::VoiceInfo& voiceInfo) { + if (m_filenameQ) { + m_free_text << m_filename << "\t"; + } + if (!m_urlType.empty()) { + printHyperlink(m_urlType); + m_free_text << "\t"; } + m_free_text << voiceInfo.name << "\t"; - // print barlines - out << "14 1 0 2\n"; // starting barline - out << "14 1 200 2\n"; // ending barline - out << "14 1 0 2 8\n"; // curly brace at start + if (!m_compositeQ) { + double sounding = 0.0; + double resting = 0.0; + for (int i=0; i<(int)voiceInfo.phraseDurs.size(); i++) { + if (voiceInfo.phraseDurs[i] > 0.0) { + sounding += voiceInfo.phraseDurs[i]; + } + if (voiceInfo.restsBefore[i] > 0.0) { + resting += voiceInfo.restsBefore[i]; + } + } + double total = sounding + resting; + // double sounding_percent = int (sounding/total * 100.0 + 0.5); + // double resting_percent = int (sounding/total * 100.0 + 0.5); - // print clefs - out << "3 2 2\n"; // treble clef - out << "3 1 2 0 1\n"; // bass clef + m_free_text << twoDigitRound(sounding) << "\t"; + m_free_text << twoDigitRound(resting) << "\t"; + m_free_text << twoDigitRound(total) << "\t"; + } - assignHorizontalPosition(voiceInfo, 25.0, 170.0); + m_free_text << voiceInfo.phraseDurs.size() << "\t"; - double maxvalue = 0.0; - for (int i=1; i<(int)voiceInfo.size(); i++) { - double tempvalue = getMaxValue(voiceInfo.at(i).diatonic); - if (tempvalue > maxvalue) { - maxvalue = tempvalue; + if (m_averageQ) { + double sum = 0; + int count = 0; + for (int i=0; i<(int)voiceInfo.phraseDurs.size(); i++) { + count++; + sum += voiceInfo.phraseDurs.at(i); } + m_free_text << int(sum / count * 100.0 + 0.5)/100.0 << "\t"; } - for (int i=(int)voiceInfo.size()-1; i>0; i--) { - if (voiceInfo.at(i).kernQ) { - printScoreVoice(out, voiceInfo.at(i), maxvalue); + + if (m_sortQ || m_reverseSortQ) { + vector> sortList; + for (int i=0; i<(int)voiceInfo.phraseDurs.size(); i++) { + sortList.emplace_back(voiceInfo.phraseDurs[i], i); + } + if (m_sortQ) { + sort(sortList.begin(), sortList.end(), + [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }); + } else if (m_reverseSortQ) { + sort(sortList.begin(), sortList.end(), + [](const std::pair& a, const std::pair& b) { + return a.first > b.first; + }); + } + + for (int i=0; i<(int)sortList.size(); i++) { + int ii = sortList[i].second; + if (m_barlineQ) { + m_free_text << "m" << voiceInfo.barStarts.at(ii) << ":"; + } + m_free_text << twoDigitRound(voiceInfo.phraseDurs.at(ii)); + if (i < (int)sortList.size() - 1) { + m_free_text << " "; + } + } + } else { + for (int i=0; i<(int)voiceInfo.phraseDurs.size(); i++) { + if (voiceInfo.restsBefore.at(i) > 0) { + m_free_text << "r:" << twoDigitRound(voiceInfo.restsBefore.at(i)) << " "; + } else if (i > 0) { + // force display r:0 for section boundaries. + m_free_text << "r:" << twoDigitRound(voiceInfo.restsBefore.at(i)) << " "; + } + if (m_barlineQ) { + m_free_text << "m" << voiceInfo.barStarts.at(i) << ":"; + } + m_free_text << twoDigitRound(voiceInfo.phraseDurs.at(i)); + if (i < (int)voiceInfo.phraseDurs.size() - 1) { + m_free_text << " "; + } } } - if (m_allQ) { - printScoreVoice(out, voiceInfo.at(0), maxvalue); - } + + m_free_text << endl; } + ////////////////////////////// // -// Tool_prange::getMaxStaffPosition(vector<_VoiceInfo>& voiceinfo) { +// Tool_rphrase::printEmbeddedVoiceInfo -- // -int Tool_prange::getMaxStaffPosition(vector<_VoiceInfo>& voiceInfo) { - int maxi = getMaxDiatonicIndex(voiceInfo[0].diatonic); - int maxdiatonic = maxi - 3 * 7; - int staffline = maxdiatonic - 27; - return staffline; -} +void Tool_rphrase::printEmbeddedVoiceInfo(vector& voiceInfo, Tool_rphrase::VoiceInfo& compositeInfo, HumdrumFile& infile) { + m_humdrum_text << "!!@@BEGIN: PREHTML" << endl;; + m_humdrum_text << "!!@SCRIPT:" << endl; + m_humdrum_text << "!! function rphraseGotoMeasure(measure) {" << endl; + m_humdrum_text << "!! let target = `svg .measure.m-${measure}`;" << endl; + m_humdrum_text << "!! let element = document.querySelector(target);" << endl; + m_humdrum_text << "!! if (element) {" << endl; + m_humdrum_text << "!! element.scrollIntoViewIfNeeded({ behavior: 'smooth' });" << endl; + m_humdrum_text << "!! }" << endl; + m_humdrum_text << "!! }" << endl; + m_humdrum_text << "!!@CONTENT:\n"; -////////////////////////////// -// -// Tool_prange::printKeySigCompression -- -// + if (m_compositeQ) { + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + } else { + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; -void Tool_prange::printKeySigCompression(ostream& out, int keysig, int extra) { - double compression = 0.0; - switch (abs(keysig)) { - case 0: compression = 0.0; break; - case 1: compression = 0.0; break; - case 2: compression = 0.0; break; - case 3: compression = 0.0; break; - case 4: compression = 0.9; break; - case 5: compression = 0.8; break; - case 6: compression = 0.7; break; - case 7: compression = 0.6; break; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; } - if (compression <= 0.0) { - return; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + + if (m_compositeQ) { + m_humdrum_text << "!!Composite rest phrasing\n"; + } else { + m_humdrum_text << "!!Voice rest phrasing\n"; } - for (int i=0; i 0) { + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + for (int i=(int)voiceInfo.size() - 1; i>=0; i--) { + printEmbeddedIndividualVoiceInfo(voiceInfo[i], infile); + } + m_humdrum_text << "!!
    VoiceSoundingRestingSegmentsAverageSegment durations
    " << endl; + printEmbeddedVoiceInfoSummary(voiceInfo, infile); + } } - out << " " << compression; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!@@END: PREHTML" << endl; } ////////////////////////////// // -// Tool_prange::assignHorizontalPosition -- +// Tool_rphrase::printEmbeddedCompositeInfo -- // -void Tool_prange::assignHorizontalPosition(vector<_VoiceInfo>& voiceInfo, int minval, int maxval) { - int count = 0; - for (int i=1; i<(int)voiceInfo.size(); i++) { - if (voiceInfo[i].kernQ) { - count++; +void Tool_rphrase::printEmbeddedCompositeInfo(Tool_rphrase::VoiceInfo& compositeInfo, HumdrumFile& infile) { + + m_humdrum_text << "!!
    " << endl; + m_humdrum_text << "!!
      " << endl; + m_humdrum_text << "!!
    • Composite segment count: " << compositeInfo.phraseDurs.size() << "
    • " << endl; + + if (!compositeInfo.phraseDurs.empty()) { + m_humdrum_text << "!!
    • Composite segment duration"; + if (compositeInfo.phraseDurs.size() != 1) { + m_humdrum_text << "s"; } - } - if (m_allQ) { - count++; - } + m_humdrum_text << ": "; + if (m_sortQ || m_reverseSortQ) { + vector> sortList; + for (int i=0; i<(int)compositeInfo.phraseDurs.size(); i++) { + sortList.emplace_back(compositeInfo.phraseDurs[i], i); + } + if (m_sortQ) { + sort(sortList.begin(), sortList.end(), + [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }); + } else if (m_reverseSortQ) { + sort(sortList.begin(), sortList.end(), + [](const std::pair& a, const std::pair& b) { + return a.first > b.first; + }); + } - vector hpos(count, 0); - hpos[0] = maxval; - hpos.back() = minval; + for (int i=0; i<(int)sortList.size(); i++) { + int ii = sortList[i].second; + if (m_barlineQ) { + m_humdrum_text << "m" << compositeInfo.barStarts.at(ii) << ":"; + } + m_humdrum_text << "" << twoDigitRound(compositeInfo.phraseDurs.at(ii)) << ""; + if (i < (int)sortList.size() - 1) { + m_humdrum_text << " "; + } + } + } else { + for (int i=0; i<(int)compositeInfo.phraseDurs.size(); i++) { + if (compositeInfo.restsBefore.at(i) > 0) { + m_humdrum_text << "" << twoDigitRound(compositeInfo.restsBefore.at(i)) << " "; + } else if (i > 0) { + // force display r:0 for section boundaries. + m_humdrum_text << "" << twoDigitRound(compositeInfo.restsBefore.at(i)) << " "; + } + if (m_barlineQ) { + m_humdrum_text << "m" << compositeInfo.barStarts.at(i) << ":"; + } + m_humdrum_text << "" << twoDigitRound(compositeInfo.phraseDurs.at(i)) << ""; + if (i < (int)compositeInfo.phraseDurs.size() - 1) { + m_humdrum_text << " "; + } + } + } + m_humdrum_text << "
    • " << endl; - if (hpos.size() > 2) { - for (int i=1; i<(int)hpos.size()-1; i++) { - int ii = hpos.size() - i - 1; - hpos[i] = (double)ii / (hpos.size()-1) * (maxval - minval) + minval; + if (m_averageQ && (compositeInfo.phraseDurs.size() > 1)) { + double sum = 0; + int count = 0; + for (int i=0; i<(int)compositeInfo.phraseDurs.size(); i++) { + count++; + sum += compositeInfo.phraseDurs.at(i); + } + double average = int(sum / count * 100.0 + 0.5)/100.0; + m_humdrum_text << "!!
    • Average composite segment durations: " << average << "
    • " << endl; } - } - int position = 0; - if (m_allQ) { - position = 1; - voiceInfo[0].hpos = hpos[0]; - } - for (int i=0; i<(int)voiceInfo.size(); i++) { - if (voiceInfo.at(i).kernQ) { - voiceInfo.at(i).hpos = hpos.at(position++); + m_humdrum_text << "!!
    • Voices: " << getVoiceInfo(infile) << "
    • " << endl; + + if (m_durUnit != 2.0) { + m_humdrum_text << "!!
    • Duration unit: " << m_durUnit << "
    • " << endl; } } + + m_humdrum_text << "!!
    " << endl; + m_humdrum_text << "!!
    " << endl; } ////////////////////////////// // -// Tool_prange::getKeySignature -- find first key signature in file. +// Tool_rphrase::getVoiceInfo -- // -int Tool_prange::getKeySignature(HumdrumFile& infile) { +string Tool_rphrase::getVoiceInfo(HumdrumFile& infile) { + vector kspines = infile.getKernSpineStartList(); + string vcount = to_string(kspines.size()); + string ocount; for (int i=0; iisKeySignature()) { - return Convert::kernKeyToNumber(*token); + if (infile[i].isReferenceRecord()) { + string key = infile[i].getReferenceKey(); + if (key == "voices") { + ocount = infile[i].getReferenceValue(); } } } - return 0; // C major key signature + if (ocount.empty()) { + return vcount; + } + + if (ocount != vcount) { + string output = ocount; + output += "("; + output += vcount; + output += ")"; + return output; + } else { + return vcount; + } } ////////////////////////////// // -// Tool_prange::printScoreVoice -- print the range information for a particular voice (in SCORE format). +// Tool_rphrase::printEmbeddedVoiceInfoSummary -- // -void Tool_prange::printScoreVoice(ostream& out, _VoiceInfo& voiceInfo, double maxvalue) { - int mini = getMinDiatonicIndex(voiceInfo.diatonic); - int maxi = getMaxDiatonicIndex(voiceInfo.diatonic); +void Tool_rphrase::printEmbeddedVoiceInfoSummary(vector& voiceInfo, HumdrumFile& infile) { + m_humdrum_text << "!!
      " << endl; - if ((mini < 0) || (maxi < 0)) { - // no data for voice so skip - return; + double total = 0.0; + for (int i=0; i<(int)voiceInfo[0].phraseDurs.size(); i++) { + if (voiceInfo[0].phraseDurs[i] > 0.0) { + total += voiceInfo[0].phraseDurs[i]; + } + if (voiceInfo[0].restsBefore[i] > 0.0) { + total += voiceInfo[0].restsBefore[i]; + } } + m_humdrum_text << "!!
    • Score duration: " << twoDigitRound(total) << "
    • " << endl; - // int minacci = getMinDiatonicAcc(voiceInfo.diatonic, mini); - // int maxacci = getMaxDiatonicAcc(voiceInfo.diatonic, maxi); - int mindiatonic = mini - 3 * 7; - int maxdiatonic = maxi - 3 * 7; - // int minacc = minacci - 3; - // int maxacc = maxacci - 3; + int countSum = 0; + for (int i=0; i<(int)voiceInfo.size(); i++) { + countSum += (int)voiceInfo[i].phraseDurs.size(); + } + m_humdrum_text << "!!
    • Total segments: " << countSum << "
    • " << endl; - int staff; - double vpos; + double averageCount = countSum / (double)voiceInfo.size(); + averageCount = (int)(averageCount * 10 + 0.5) / 10.0; + m_humdrum_text << "!!
    • Average voice segments: " << averageCount << "
    • " << endl; - int voicevpos = -3; - staff = getStaffBase7(mindiatonic); - int lowestvpos = getVpos(mindiatonic); - if ((staff == 1) && (lowestvpos <= 0)) { - voicevpos += lowestvpos - 2; + double durSum = 0.0; + for (int i=0; i<(int)voiceInfo.size(); i++) { + for (int j=0; j<(int)voiceInfo[i].phraseDurs.size(); j++) { + durSum += voiceInfo[i].phraseDurs[j]; + } } + double averageDur = durSum / countSum; + averageDur = (int)(averageDur * 10 + 0.5) / 10.0; + m_humdrum_text << "!!
    • Average segment duration: " << averageDur << "
    • " << endl; - if (m_localQ || (voiceInfo.index == 0)) { - double localmaxvalue = getMaxValue(voiceInfo.diatonic); - maxvalue = localmaxvalue; - } - double width; - double hoffset = 2.3333; - double maxhist = 17.6; - int i; - int base7; + m_humdrum_text << "!!
    • Voices: " << getVoiceInfo(infile) << "
    • " << endl; - // print histogram bars - for (i=mini; i<=maxi; i++) { - if (voiceInfo.diatonic.at(i).at(0) <= 0.0) { - continue; - } - base7 = i - 3 * 7; - staff = getStaffBase7(base7); - vpos = getVpos(base7); + m_humdrum_text << "!!
    " << endl; +} - // staring positions of accidentals: - vector starthpos(6, 0.0); - for (int j=1; j<(int)starthpos.size(); j++) { - double width = maxhist * voiceInfo.diatonic.at(i).at(j)/maxvalue; - starthpos[j] = starthpos[j-1] + width; + + +////////////////////////////// +// +// Tool_rphrase::printEmbeddedIndividualVoiceInfo -- +// + +void Tool_rphrase::printEmbeddedIndividualVoiceInfo(Tool_rphrase::VoiceInfo& voiceInfo, HumdrumFile& infile) { + m_humdrum_text << "!!" << endl; + + m_humdrum_text << "!!" << voiceInfo.name << "" << endl; + + double sounding = 0.0; + double resting = 0.0; + for (int i=0; i<(int)voiceInfo.phraseDurs.size(); i++) { + if (voiceInfo.phraseDurs[i] > 0.0) { + sounding += voiceInfo.phraseDurs[i]; } - for (int j=(int)starthpos.size() - 1; j>0; j--) { - starthpos[j] = starthpos[j-1]; + if (voiceInfo.restsBefore[i] > 0.0) { + resting += voiceInfo.restsBefore[i]; } + } + double total = sounding + resting; + double spercent = int(sounding/total * 100.0 + 0.5); + double rpercent = int(resting/total * 100.0 + 0.5); + m_humdrum_text << "!!" << sounding << "(" << spercent << "%)" << endl; + m_humdrum_text << "!!" << resting << "(" << rpercent << "%)" << endl; - // print chromatic alterations - for (int j=(int)voiceInfo.diatonic.at(i).size()-1; j>0; j--) { - if (voiceInfo.diatonic.at(i).at(j) <= 0.0) { - continue; + // Segment count + m_humdrum_text << "!!"; + m_humdrum_text << voiceInfo.phraseDurs.size(); + m_humdrum_text << "" << endl; + + // Segment duration average + m_humdrum_text << "!!"; + double sum = 0; + int count = 0; + for (int i=0; i<(int)voiceInfo.phraseDurs.size(); i++) { + count++; + sum += voiceInfo.phraseDurs.at(i); + } + double average = int(sum / count * 100.0 + 0.5)/100.0; + m_humdrum_text << average; + m_humdrum_text << "" << endl; + + // Segments + m_humdrum_text << "!!"; + if (m_sortQ || m_reverseSortQ) { + vector> sortList; + for (int i=0; i<(int)voiceInfo.phraseDurs.size(); i++) { + sortList.emplace_back(voiceInfo.phraseDurs[i], i); + } + if (m_sortQ) { + sort(sortList.begin(), sortList.end(), + [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }); + } else if (m_reverseSortQ) { + sort(sortList.begin(), sortList.end(), + [](const std::pair& a, const std::pair& b) { + return a.first > b.first; + }); + } + for (int i=0; i<(int)sortList.size(); i++) { + int ii = sortList[i].second; + if (m_barlineQ) { + m_humdrum_text << "m" << voiceInfo.barStarts.at(ii) << ":"; } - int acc = 0; - switch (j) { - case 1: acc = -2; break; - case 2: acc = -1; break; - case 3: acc = 0; break; - case 4: acc = +1; break; - case 5: acc = +2; break; + m_humdrum_text << "" << twoDigitRound(voiceInfo.phraseDurs.at(ii)) << ""; + if (i < (int)sortList.size() - 1) { + m_humdrum_text << " "; } - - width = maxhist * voiceInfo.diatonic.at(i).at(j)/maxvalue + hoffset; - if (m_hoverQ) { - string title = getNoteTitle((int)voiceInfo.diatonic.at(i).at(j), base7, acc); - SVGTEXT(out, title); + } + } else { + for (int i=0; i<(int)voiceInfo.phraseDurs.size(); i++) { + if (voiceInfo.restsBefore.at(i) > 0) { + m_humdrum_text << "" << twoDigitRound(voiceInfo.restsBefore.at(i)) << " "; + } else if (i > 0) { + // force display r:0 for section boundaries. + m_humdrum_text << "" << twoDigitRound(voiceInfo.restsBefore.at(i)) << " "; } - out << "1 " << staff << " " << (voiceInfo.hpos + starthpos.at(j) + hoffset) << " " << vpos; - out << " 0 -1 4 0 0 0 99 0 0 "; - out << width << "\n"; - if (m_hoverQ) { - SVGTEXT(out, "
    "); + if (m_barlineQ) { + m_humdrum_text << "m" << voiceInfo.barStarts.at(i) << ":"; + } + m_humdrum_text << "" << twoDigitRound(voiceInfo.phraseDurs.at(i)) << ""; + if (i < (int)voiceInfo.phraseDurs.size() - 1) { + m_humdrum_text << " "; } } } - string voicestring = voiceInfo.name; - if (voicestring.empty()) { - voicestring = voiceInfo.abbr; - } - if (!voicestring.empty()) { - HumRegex hre; - hre.replaceDestructive(voicestring, "", "(\\\\n)+$"); - vector pieces; - hre.split(pieces, voicestring, "\\\\n"); + m_humdrum_text << "" << endl; + m_humdrum_text << "!!" << endl; +} + + + +////////////////////////////// +// +// Tool_rphrase::fillCompositeInfo -- +// + +void Tool_rphrase::fillCompositeInfo(Tool_rphrase::VoiceInfo& compositeInfo, HumdrumFile& infile) { + compositeInfo.name = getCompositeLabel(infile); + vector noteStates; + getCompositeStates(noteStates, infile); + + bool inPhraseQ = false; + int currentBarline = 0; + int startBarline = 1; + HumNum startTime = 0; + HumNum restBefore = 0; + HumNum startTimeRest = 0; + HumNum scoreDur = infile.getScoreDuration(); + HTp phraseStartTok = NULL; + + for (int i=0; ifind("||") != string::npos) { + HumNum tdur = token->getDurationFromStart(); + if (tdur != scoreDur) { + // Only process if double barline is not at the end of the score. + + if (inPhraseQ) { + // In phrase, so continue if still notes, otherwise + // if a rest, then record the currently active phrase + // that has ended. + + // ending a phrase + HumNum endTime = infile[i].getDurationFromStart(); + HumNum duration = endTime - startTime; + startTime = -1; + inPhraseQ = false; + double value = duration.getFloat() / m_durUnit; + compositeInfo.phraseDurs.push_back(value); + compositeInfo.barStarts.push_back(startBarline); + compositeInfo.phraseStartToks.push_back(phraseStartTok); + phraseStartTok = NULL; + m_sumComposite += duration.getFloat() / m_durUnit; + m_pcountComposite++; + double rvalue = restBefore.getFloat() / m_durUnit; + compositeInfo.restsBefore.push_back(rvalue); + + // record rest start + startTimeRest = endTime; + } else { + // Not in phrase, so not splitting a rest region. + // This case should be rare (starting a medial cadence + // with rests and potentially starting new section with rests. + } - if (pieces.size() > 1) { - voicestring = ""; - for (int i=0; i<(int)pieces.size(); i++) { - voicestring += pieces[i]; - if (i < (int)pieces.size() - 1) { - voicestring += "/"; } } } - double increment = 4.0; - for (int i=0; i<(int)pieces.size(); i++) { - // print voice name - double tvoffset = -4.0; - out << "t 1 " << voiceInfo.hpos << " " - << (voicevpos - increment * i) - << " 1 1 0 0 0 0 " << tvoffset; - out << "\n"; + if (infile[i].isBarline()) { + HTp token = infile.token(i, 0); + HumRegex hre; + if (hre.search(token, "(\\d+)")) { + currentBarline = hre.getMatchInt(1); + continue; + } + } - if (pieces[i] == "all") { - out << "_02"; - } else if (pieces[i] == "both") { - out << "_02"; + + if (!infile[i].isData()) { + continue; + } + + if (inPhraseQ) { + // In phrase, so continue if still notes, otherwise + // if a rest, then record the currently active phrase + // that has ended. + if (noteStates[i] == 0) { + // ending a phrase + HumNum endTime = infile[i].getDurationFromStart(); + HumNum duration = endTime - startTime; + startTime = -1; + inPhraseQ = false; + double value = duration.getFloat() / m_durUnit; + compositeInfo.phraseDurs.push_back(value); + compositeInfo.barStarts.push_back(startBarline); + compositeInfo.phraseStartToks.push_back(phraseStartTok); + phraseStartTok = NULL; + m_sumComposite += duration.getFloat() / m_durUnit; + m_pcountComposite++; + double rvalue = restBefore.getFloat() / m_durUnit; + compositeInfo.restsBefore.push_back(rvalue); + // record rest start + startTimeRest = endTime; } else { - out << "_00"; + // continuing a phrase, so do nothing + } + } else { + // Not in phrase, so continue if rest; otherwise, + // if a note, then record a phrase start. + if (noteStates[i] == 0) { + // continuing a non-phrase, so do nothing + } else { + // starting a phrase + startTime = infile[i].getDurationFromStart(); + startBarline = currentBarline; + inPhraseQ = true; + // check if there are rests before the phrase + // The rest duration will be stored when the + // end of the next phrase is encountered. + if (startTimeRest >= 0) { + restBefore = startTime - startTimeRest; + } else { + restBefore = 0; + } + phraseStartTok = infile.token(i, 0); } - printScoreEncodedText(out, pieces[i]); - out << "\n"; } + } - // print the lowest pitch in range - staff = getStaffBase7(mindiatonic); - vpos = getVpos(mindiatonic); - if (m_hoverQ) { - string content = ""; - content += getDiatonicPitchName(mindiatonic, 0); - content += ": lowest note"; - if (!voicestring.empty()) { - content += " of "; - content += voicestring; - content += "'s range"; - } - content += ""; - SVGTEXT(out, content); + if (inPhraseQ) { + // process last phrase + HumNum endTime = infile.getScoreDuration(); + HumNum duration = endTime - startTime; + double value = duration.getFloat() / m_durUnit; + compositeInfo.phraseDurs.push_back(value); + compositeInfo.barStarts.push_back(startBarline); + compositeInfo.phraseStartToks.push_back(phraseStartTok); + m_sumComposite += duration.getFloat() / m_durUnit; + m_pcountComposite++; + double rvalue = restBefore.getFloat() / m_durUnit; + compositeInfo.restsBefore.push_back(rvalue); } - out << "1 " << staff << " " << voiceInfo.hpos << " " << vpos - << " 0 0 4 0 0 -2\n"; - if (m_hoverQ) { - SVGTEXT(out, ""); + + if (m_markQ) { + markCompositePhraseStartsInScore(infile, compositeInfo); } +} - // print the highest pitch in range - staff = getStaffBase7(maxdiatonic); - vpos = getVpos(maxdiatonic); - if (m_hoverQ) { - string content = ""; - content += getDiatonicPitchName(maxdiatonic, 0); - content += ": highest note"; - if (!voicestring.empty()) { - content += " of "; - content += voicestring; - content += "'s range"; + + +////////////////////////////// +// +// Tool_rphrase::getCompositeLabel -- +// + +string Tool_rphrase::getCompositeLabel(HumdrumFile& infile) { + string voices; + for (int i=0; i<infile.getLineCount(); i++) { + if (!infile[i].isReferenceRecord()) { + continue; } - content += ""; - SVGTEXT(out, content); + string key = infile[i].getReferenceKey(); + if (key != "voices") { + continue; + } + voices = infile[i].getReferenceValue(); + break; } - out << "1 " << staff << " " << voiceInfo.hpos << " " << vpos - << " 0 0 4 0 0 -2\n"; - if (m_hoverQ) { - SVGTEXT(out, ""); + + if (voices.empty()) { + return "composite"; } - double goffset = -1.66; - double toffset = 1.5; - double median12 = getMedian12(voiceInfo.midibins); - double median40 = Convert::base12ToBase40(median12); - double median7 = Convert::base40ToDiatonic(median40); - // int acc = Convert::base40ToAccidental(median40); + vector kstarts = infile.getKernSpineStartList(); - staff = getStaffBase7(median7); - vpos = getVpos(median7); + string output = "composite "; + output += voices; - // these offsets are useful when the quartile pitches are not shown... - int vvpos = maxdiatonic - median7 + 1; - int vvpos2 = median7 - mindiatonic + 1; - double offset = goffset; - if (vvpos <= 2) { - offset += toffset; - } else if (vvpos2 <= 2) { - offset -= toffset; - } - if (m_hoverQ) { - string content = ""; - content += getDiatonicPitchName(median7, 0); - content += ": median note"; - if (!voicestring.empty()) { - content += " of "; - content += voicestring; - content += "'s range"; + HumRegex hre; + + if (hre.search(voices, "^\\d+$")) { + int vint = stoi(voices); + if (vint != (int)kstarts.size()) { + output += "("; + output += to_string(kstarts.size()); + output += ")"; } - content += ""; - SVGTEXT(out, content); - } - out << "1 " << staff << " " << voiceInfo.hpos << " "; - if (vpos > 0) { - out << vpos + 100; } else { - out << vpos - 100; + output += "("; + output += to_string(kstarts.size()); + output += ")"; } - out << " 0 1 4 0 0 " << offset << "\n"; - if (m_hoverQ) { - SVGTEXT(out, ""); + + return output; +} + + + +////////////////////////////// +// +// Tool_rphrase::fillVoiceInfo -- +// + +void Tool_rphrase::fillVoiceInfo(vector& voiceInfo, + vector& kstarts, HumdrumFile& infile) { + for (int i=0; i<(int)kstarts.size(); i++) { + fillVoiceInfo(voiceInfo.at(i), kstarts.at(i), infile); } +} - if (m_finalisQ) { - for (int f=0; f<(int)voiceInfo.diafinal.size(); f++) { - int diafinalis = voiceInfo.diafinal.at(f); - int accfinalis = voiceInfo.accfinal.at(f); - int staff = getStaffBase7(diafinalis); - int vpos = getVpos(diafinalis); - double goffset = -1.66; - double toffset = 3.5; - // these offsets are useful when the quartile pitches are not shown... - double offset = goffset; - offset += toffset; +void Tool_rphrase::fillVoiceInfo(Tool_rphrase::VoiceInfo& voiceInfo, HTp& kstart, HumdrumFile& infile) { + HTp current = kstart; - if (m_hoverQ) { - string content = ""; - content += getDiatonicPitchName(diafinalis, accfinalis); - content += ": last note"; - if (!voicestring.empty()) { - content += " of "; - if (voiceInfo.index == 0) { - content += voiceInfo.namfinal.at(f); + bool inPhraseQ = false; + int currentBarline = 0; + int startBarline = 1; + HumNum startTime = 0; + + HumNum restBefore = 0; + HumNum startTimeRest = 0; + + HumNum scoreDur = infile.getScoreDuration(); + HTp phraseStartTok = NULL; + + while (current) { + + // Split phrases at double barlines (medial cadences): + if (infile[current->getLineIndex()].isBarline()) { + HTp token = infile.token(current->getLineIndex(), 0); + if (token->find("||") != string::npos) { + HumNum tdur = token->getDurationFromStart(); + if (tdur != scoreDur) { + // Only process if double barline is not at the end of the score. + + if (inPhraseQ) { + // In phrase, so continue if still notes, otherwise + // if a rest, then record the currently active phrase + // that has ended. + + HumNum endTime = current->getDurationFromStart(); + HumNum duration = endTime - startTime; + startTime = -1; + inPhraseQ = false; + double value = duration.getFloat() / m_durUnit; + voiceInfo.phraseDurs.push_back(value); + voiceInfo.barStarts.push_back(startBarline); + voiceInfo.phraseStartToks.push_back(phraseStartTok); + phraseStartTok = NULL; + m_sum += duration.getFloat() / m_durUnit; + m_pcount++; + double rvalue = restBefore.getFloat() / m_durUnit; + voiceInfo.restsBefore.push_back(rvalue); + + // record rest start + startTimeRest = endTime; } else { - content += voicestring; + // Not in phrase, so not splitting a rest region. + // This case should be rare (starting a medial cadence + // with rests and potentially starting new section with rests. } + } - content += ""; - SVGTEXT(out, content); - } - out << "1 " << staff << " " << voiceInfo.hpos << " "; - if (vpos > 0) { - out << vpos + 100; - } else { - out << vpos - 100; - } - out << " 0 0 4 0 0 " << offset << "\n"; - if (m_hoverQ) { - SVGTEXT(out, ""); } } - } - - /* Needs fixing - int topquartile; - if (m_quartileQ) { - // print top quartile - topquartile = getTopQuartile(voiceInfo.midibins); - if (m_diatonicQ) { - topquartile = Convert::base7ToBase12(topquartile); - } - staff = getStaffBase7(topquartile); - vpos = getVpos(topquartile); - vvpos = median7 - topquartile + 1; - if (vvpos <= 2) { - offset = goffset + toffset; - } else { - offset = goffset; - } - vvpos = maxdiatonic - topquartile + 1; - if (vvpos <= 2) { - offset = goffset + toffset; - } - if (m_hoverQ) { - if (m_defineQ) { - out << "SVG "; - } else { - out << "t 1 1\n"; - out << SVGTAG; - } - printScoreEncodedText(out, ""); - printDiatonicPitchName(out, topquartile, 0); - out << ": top quartile note"; - if (voicestring.size() > 0) { - out << " of " << voicestring << "\'s range"; + if (current->isBarline()) { + HumRegex hre; + if (hre.search(current, "(\\d+)")) { + currentBarline = hre.getMatchInt(1); + current = current->getNextToken(); + continue; } - printScoreEncodedText(out, "\n"); - } - out << "1 " << staff << " " << voiceInfo.hpos << " "; - if (vpos > 0) { - out << vpos + 100; - } else { - out << vpos - 100; - } - out << " 0 0 4 0 0 " << offset << "\n"; - if (m_hoverQ) { - SVGTEXT(out, ""); } - } - // print bottom quartile - if (m_quartileQ) { - int bottomquartile = getBottomQuartile(voiceInfo.midibins); - if (m_diatonicQ) { - bottomquartile = Convert::base7ToBase12(bottomquartile); - } - staff = getStaffBase7(bottomquartile); - vpos = getVpos(bottomquartile); - vvpos = median7 - bottomquartile + 1; - if (vvpos <= 2) { - offset = goffset + toffset; - } else { - offset = goffset; + if (current->isInstrumentName()) { + voiceInfo.name = current->substr(3); } - vvpos = bottomquartile - mindiatonic + 1; - if (vvpos <= 2) { - offset = goffset - toffset; + if (!(current->isData() || (m_breathQ && (*current == "*breath")))) { + current = current->getNextToken(); + continue; } - if (m_hoverQ) { - if (m_defineQ) { - out << "SVG "; + if (current->isNull()) { + current = current->getNextToken(); + continue; + } + + if (inPhraseQ) { + // In phrase, so continue if still notes, otherwise + // if a rest, then record the currently active phrase + // that has ended. + if (current->isRest() || (*current == "*breath")) { + // ending a phrase + HumNum endTime = current->getDurationFromStart(); + HumNum duration = endTime - startTime; + startTime = -1; + inPhraseQ = false; + double value = duration.getFloat() / m_durUnit; + voiceInfo.phraseDurs.push_back(value); + voiceInfo.barStarts.push_back(startBarline); + voiceInfo.phraseStartToks.push_back(phraseStartTok); + phraseStartTok = NULL; + m_sum += duration.getFloat() / m_durUnit; + m_pcount++; + double rvalue = restBefore.getFloat() / m_durUnit; + voiceInfo.restsBefore.push_back(rvalue); + // record rest start + startTimeRest = endTime; } else { - out << "t 1 1\n"; - out << SVGTAG; + // continuing a phrase, so do nothing } - printScoreEncodedText(out, ""); - printDiatonicPitchName(out, bottomquartile, 0); - out << ": bottom quartile note"; - if (voicestring.size() > 0) { - out << " of " << voicestring << "\'s range"; + } else { + // Not in phrase, so continue if rest; otherwise, + // if a note, then record a phrase start. + if (current->isRest() || (*current == "*breath")) { + // continuing a non-phrase, so do nothing + } else { + // starting a phrase + startTime = current->getDurationFromStart(); + startBarline = currentBarline; + inPhraseQ = true; + // check if there are rests before the phrase + // The rest duration will be stored when the + // end of the next phrase is encountered. + if (startTimeRest >= 0) { + restBefore = startTime - startTimeRest; + } else { + restBefore = 0; + } + phraseStartTok = current; } - printScoreEncodedText(out, "\n"); } - out << "1.0 " << staff << ".0 " << voiceInfo.hpos << " "; - if (vpos > 0) { - out << vpos + 100; - } else { - out << vpos - 100; + + current = current->getNextToken(); + } + if (inPhraseQ) { + // process last phrase + HumNum endTime = kstart->getLine()->getOwner()->getScoreDuration(); + HumNum duration = endTime - startTime; + double value = duration.getFloat() / m_durUnit; + voiceInfo.phraseDurs.push_back(value); + voiceInfo.barStarts.push_back(startBarline); + voiceInfo.phraseStartToks.push_back(phraseStartTok); + m_sum += duration.getFloat() / m_durUnit; + m_pcount++; + double rvalue = restBefore.getFloat() / m_durUnit; + voiceInfo.restsBefore.push_back(rvalue); + } + + if (m_markQ) { + markPhraseStartsInScore(infile, voiceInfo); + } +} + + + +////////////////////////////// +// +// Tool_rphrase::markCompositePhraseStartsInScore -- +// + +void Tool_rphrase::markCompositePhraseStartsInScore(HumdrumFile& infile, Tool_rphrase::VoiceInfo& compositeInfo) { + stringstream buffer; + for (int i=0; i<(int)compositeInfo.phraseStartToks.size(); i++) { + HTp tok = compositeInfo.phraseStartToks.at(i); + string measure = ""; + if (m_barlineQ) { + measure = to_string(compositeInfo.barStarts.at(i)); } - out << " 0 0 4 0 0 " << offset << "\n"; - if (m_hoverQ) { - SVGTEXT(out, ""); + double duration = compositeInfo.phraseDurs.at(i); + buffer.str(""); + if (!measure.empty()) { + buffer << "m" << measure << ":"; } + buffer << twoDigitRound(duration); + int lineIndex = tok->getLineIndex(); + infile[lineIndex].setValue("auto", "rphrase-composite-start", buffer.str()); } - */ - } ////////////////////////////// // -// Tool_prange::printDiatonicPitchName -- +// Tool_rphrase::twoDigitRound -- // -void Tool_prange::printDiatonicPitchName(ostream& out, int base7, int acc) { - out << getDiatonicPitchName(base7, acc); +double Tool_rphrase::twoDigitRound(double input) { + return int(input * 100.0 + 0.499999) / 100.0; } ////////////////////////////// // -// Tool_prange::getDiatonicPitchName -- +// Tool_rphrase::markPhraseStartsInScore -- // -string Tool_prange::getDiatonicPitchName(int base7, int acc) { - string output; - int dpc = base7 % 7; - char letter = (dpc + 2) % 7 + 'A'; - output += letter; - switch (acc) { - case -1: output += "♭"; break; - case +1: output += "♯"; break; - case -2: output += "𝄫"; break; - case +2: output += "𝄪"; break; +void Tool_rphrase::markPhraseStartsInScore(HumdrumFile& infile, Tool_rphrase::VoiceInfo& voiceInfo) { + stringstream buffer; + for (int i=0; i<(int)voiceInfo.phraseStartToks.size(); i++) { + HTp tok = voiceInfo.phraseStartToks.at(i); + string measure = ""; + if (m_barlineQ) { + measure = to_string(voiceInfo.barStarts.at(i)); + } + double duration = voiceInfo.phraseDurs.at(i); + buffer.str(""); + if (!measure.empty()) { + buffer << "m" << measure << ":"; + } + buffer << duration; + tok->setValue("auto", "rphrase-start", buffer.str()); } - int octave = base7 / 7; - output += to_string(octave); - return output; } -////////////////////////////// + +///////////////////////////////// // -// Tool_prange::printHtmlStringEncodeSimple -- +// Tool_ruthfix::Tool_ruthfix -- Set the recognized options for the tool. // -void Tool_prange::printHtmlStringEncodeSimple(ostream& out, const string& strang) { - string newstring = strang; - HumRegex hre; - hre.replaceDestructive(newstring, "&", "&", "g"); - hre.replaceDestructive(newstring, "<", "<", "g"); - hre.replaceDestructive(newstring, ">", "<", "g"); - out << newstring; +Tool_ruthfix::Tool_ruthfix(void) { + // add options here } -////////////////////////////// +///////////////////////////////// // -// Tool_prange::getNoteTitle -- return the title of the histogram bar. -// value = duration or count of notes -// diatonic = base7 value for note -// acc = accidental for diatonic note. +// Tool_ruthfix::run -- Do the main work of the tool. // -string Tool_prange::getNoteTitle(double value, int diatonic, int acc) { - stringstream output; - output << ""; - if (m_durationQ) { - output << value / 8.0; - if (value/8.0 == 1.0) { - output << " long on "; - } else { - output << " longs on "; - } - output << getDiatonicPitchName(diatonic, acc); + return status; +} + + +bool Tool_ruthfix::run(const string& indata, ostream& out) { + HumdrumFile infile(indata); + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); } else { - output << value; - output << " "; - output << getDiatonicPitchName(diatonic, acc); - if (value != 1.0) { - output << "s"; - } + out << infile; } - output << ""; - return output.str(); + return status; } +bool Tool_ruthfix::run(HumdrumFile& infile, ostream& out) { + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else { + out << infile; + } + return status; +} -////////////////////////////// -// -// Tool_prange::getDiatonicInterval -- -// -int Tool_prange::getDiatonicInterval(int note1, int note2) { - int vpos1 = getVpos(note1); - int vpos2 = getVpos(note2); - return abs(vpos1 - vpos2) + 1; +bool Tool_ruthfix::run(HumdrumFile& infile) { + insertCrossBarTies(infile); + return true; } ////////////////////////////// // -// Tool_prange::getTopQuartile -- +// Tool_ruthfix::insertCrossBarTies -- // -int Tool_prange::getTopQuartile(vector& midibins) { - double sum = accumulate(midibins.begin(), midibins.end(), 0.0); +void Tool_ruthfix::insertCrossBarTies(HumdrumFile& infile) { + int scount = infile.getStrandCount(); + if (scount == 0) { + // The input file was not read from a file but was created + // dynamically. The easiest thing to do is to reload to get the + // spine/strand information. + stringstream ss; + infile.createLinesFromTokens(); + ss << infile; + infile.readString(ss.str()); + } + scount = infile.getStrandCount(); - double cumsum = 0.0; - int i; - for (i=midibins.size()-1; i>=0; i--) { - if (midibins[i] <= 0.0) { + + HTp token; + for (int i=0; iisKern()) { continue; } - cumsum += midibins[i]/sum; - if (cumsum >= 0.25) { - return i; - } + insertCrossBarTies(infile, i); } +} - return -1; + +void Tool_ruthfix::insertCrossBarTies(HumdrumFile& infile, int strand) { + HTp sstart = infile.getStrandStart(strand); + HTp send = infile.getStrandEnd(strand); + HTp s = sstart; + HTp lastnote = NULL; + bool barstart = true; + while (s != send) { + if (s->isBarline()) { + barstart = true; + } else if (s->isNote()) { + if (lastnote && barstart && (s->find("yy") != string::npos)) { + createTiedNote(lastnote, s); + } + barstart = false; + lastnote = s; + } else if (s->isRest()) { + lastnote = NULL; + barstart = false; + } + s = s->getNextToken(); + if (!s) { + break; + } + } } ////////////////////////////// // -// Tool_prange::getBottomQuartile -- +// Tool_ruthfix::createTiedNote -- Does not work for chords. +// change 1E-X TO 2E-Xyy +// to [1E-X TO 2E-X] // -int Tool_prange::getBottomQuartile(vector& midibins) { - double sum = accumulate(midibins.begin(), midibins.end(), 0.0); - - double cumsum = 0.0; - int i; - for (i=0; i<(int)midibins.size(); i++) { - if (midibins[i] <= 0.0) { - continue; - } - cumsum += midibins[i]/sum; - if (cumsum >= 0.25) { - return i; - } +void Tool_ruthfix::createTiedNote(HTp left, HTp right) { + if (left->isChord() || right->isChord()) { + return; + } + auto loc = right->find("yy"); + if (loc != string::npos) { + left->insert(0, 1, '['); + right->replace(loc, 2, "]"); } - - return -1; } -////////////////////////////// + + +///////////////////////////////// // -// Tool_prange::getMaxValue -- +// Tool_sab2gs::Tool_sab2gs -- Set the recognized options for the tool. // -double Tool_prange::getMaxValue(vector>& bins) { - double maxi = 0; - for (int i=1; i<(int)bins.size(); i++) { - if (bins.at(i).at(0) > bins.at(maxi).at(0)) { - maxi = i; - } - } - return bins.at(maxi).at(0); +Tool_sab2gs::Tool_sab2gs(void) { + define("b|below=s:<", "Marker for displaying on next staff below"); + define("d|down=b", "Use only *down/*Xdown interpretations"); } -////////////////////////////// +///////////////////////////////// // -// Tool_prange::getVpos == return the position on the staff given the diatonic pitch. -// and the staff. 1=bass, 2=treble. -// 3 = bottom line of clef, 0 = space below first ledger line. +// Tool_sab2gs::run -- Do the main work of the tool. // -double Tool_prange::getVpos(double base7) { - double output = 0; - if (base7 < 4 * 7) { - // bass clef - output = base7 - (1 + 2*7); // D2 - } else { - // treble clef - output = base7 - (6 + 3*7); // B3 +bool Tool_sab2gs::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; i>& diatonic) { - for (int i=diatonic.size()-1; i>=0; i--) { - if (diatonic.at(i).at(0) != 0.0) { - return i; - } - } - return -1; +void Tool_sab2gs::initialize(void) { + m_belowMarker = getString("below"); + m_downQ = getBoolean("down"); } ////////////////////////////// // -// Tool_prange::getMinDiatonicIndex -- return the lowest non-zero content. +// Tool_sab2gs::processFile -- // -int Tool_prange::getMinDiatonicIndex(vector>& diatonic) { - for (int i=0; i<(int)diatonic.size(); i++) { - if (diatonic.at(i).at(0) != 0.0) { - return i; +void Tool_sab2gs::processFile(HumdrumFile& infile) { + + vector spines; + infile.getSpineStartList(spines); + vector kernSpines; + for (int i=0; i<(int)spines.size(); i++) { + if (spines[i]->isKern()) { + kernSpines.push_back(spines[i]); } } - return -1; + if (kernSpines.size() != 3) { + // Not valid for processing kern spines, so return original: + m_humdrum_text << infile; + return; + } + + string belowMarker = hasBelowMarker(infile); + if (!belowMarker.empty()) { + m_hasBelowMarker = true; + m_belowMarker = belowMarker; + } + + adjustMiddleVoice(kernSpines[1]); + printGrandStaff(infile, kernSpines); } -////////////////////////////// +///////////////////////////// // -// Tool_prange::getMinDiatonicAcc -- return the lowest accidental. +// Tool_sab2gs::hasBelowMarker -- Returns below marker if found; otherwise, +// returns empty string. // -int Tool_prange::getMinDiatonicAcc(vector>& diatonic, int index) { - for (int i=1; i<(int)diatonic.at(index).size(); i++) { - if (diatonic.at(index).at(i) != 0.0) { - return i; +string Tool_sab2gs::hasBelowMarker(HumdrumFile& infile) { + string output; + HumRegex hre; + if (m_hasCrossStaff) { + // Search backwards since if there is a below marker, it will be more + // likely found at the bottom of the score. + for (int i=infile.getLineCount()-1; i<=0; i--) { + if (infile[i].hasSpines()) { + continue; + } + if (hre.search(infile.token(i, 0), "^!!!RDF\\*\\*kern\\s*:\\s*([^\\s=]+)\\s*=\\s*below\\s*$")) { + output = hre.getMatch(1); + break; + } } } - return -1; + return output; } -////////////////////////////// +/////////////////////////////// // -// Tool_prange::getMaxDiatonicAcc -- return the highest accidental. +// Tool_sab2gs::printGrandStaff -- // -int Tool_prange::getMaxDiatonicAcc(vector>& diatonic, int index) { - for (int i=(int)diatonic.at(index).size() - 1; i>0; i--) { - if (diatonic.at(index).at(i) != 0.0) { - return i; +void Tool_sab2gs::printGrandStaff(HumdrumFile& infile, vector& starts) { + bool foundData = false; + + vector ktracks(starts.size()); + for (int i=0; i<(int)starts.size(); i++) { + ktracks.at(i) = starts.at(i)->getTrack(); + } + + for (int i=0; i refrecords = infile.getGlobalReferenceRecords(); - m_refmap.clear(); - HumRegex hre; - for (int i = (int)refrecords.size()-1; i>=0; i--) { - string key = refrecords[i]->getReferenceKey(); - string value = refrecords[i]->getReferenceValue(); - m_refmap[key] = value; - if (key.find("@") != string::npos) { - // create default value - hre.replaceDestructive(key, "", "@.*"); - if (m_refmap[key].empty()) { - m_refmap[key] = value; - } +void Tool_sab2gs::printSpineSplit(HumdrumFile& infile, int index, vector& ktracks) { + // First print all non-kern spines at the start of the line: + int nextIndex = 0; + int fcount = 0; + for (int i=0; iisKern()) { + break; + } + if (fcount > 0) { + m_humdrum_text << "\t"; } + fcount++; + m_humdrum_text << "*"; + nextIndex++; } - // fill in @{} templates (mostly for !!!title:) - int counter = 0; // prevent recursions - for (auto& entry : m_refmap) { - - if (entry.second.find("@") != string::npos) { - while (hre.search(entry.second, "@\\{(.*?)\\}")) { - string key = hre.getMatch(1); - string value = m_refmap[key]; - hre.replaceDestructive(entry.second, value, "@\\{" + key + "\\}", "g"); - counter++; - if (counter > 1000) { - break; - } - } + // Must be on the first **kern spine: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend on line " << index+1 << ": " << infile[index] << endl; + return; + } + // Print the first **kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; + } + fcount++; + m_humdrum_text << "*"; + nextIndex++; + // Next print all non-kern spines after first **kern spine: + for (int i=nextIndex; iisKern()) { + break; + } + if (fcount > 0) { + m_humdrum_text << "\t"; + } + m_humdrum_text << "*"; + nextIndex++; + } + // Second **kern spine must be **kern data: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend B on line " << index+1 << ": " << infile[index] << endl; + return; + } + // Ignore the second kern spine as it does not exist yet in the + // output data. + nextIndex++; + // Then store any non-kern spines between the second and third kern spines to + // append to the end of the data line later. + string postData; + for (int i=nextIndex; iisKern()) { + break; + } + if (!postData.empty()) { + postData += "\t"; + } + nextIndex++; + postData += "*"; + } + // Third kern spine must be **kern data: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend C on line " << index+1 << ": " << infile[index] << endl; + return; + } + // Now print the third kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; + } + fcount++; + nextIndex++; + m_humdrum_text << "*^"; + // Now print the non-kern spines after the third **kern spine (or rather just + // all spines including any other **kern spines, although current requirement + // is that there are only three **kern spines. + for (int i=nextIndex; i 0) { + m_humdrum_text << "\t"; } - + fcount++; + nextIndex++; + m_humdrum_text << "*"; } - - // prepare title - if (m_refmap["title"].empty()) { - m_refmap["title"] = m_refmap["OTL"]; + // Finally print any non-kern spines after the second **kern spine: + if (!postData.empty()) { + if (fcount > 0) { + m_humdrum_text << "\t"; + } + fcount++; + m_humdrum_text << postData; } + m_humdrum_text << endl; } ////////////////////////////// // -// Tool_prange::getTitle -- +// Tool_sab2gs::printSpineMerge -- Merge second and third spines, moving non-kern spines +// after the second one to the end of the line (null interpretations); // -string Tool_prange::getTitle(void) { - string titlestring = "_00"; - HumRegex hre; - if (m_notitleQ) { - return ""; - } else if (m_titleQ) { - titlestring = m_title; - int counter = 0; - - if (titlestring.find("@") != string::npos) { - while (hre.search(titlestring, "@\\{(.*?)\\}")) { - string key = hre.getMatch(1); - string value = m_refmap[key]; - hre.replaceDestructive(titlestring, value, "@\\{" + key + "\\}", "g"); - counter++; - if (counter > 1000) { - break; - } - } +void Tool_sab2gs::printSpineMerge(HumdrumFile& infile, int index, vector& ktracks) { + // First print all non-kern spines at the start of the line: + int nextIndex = 0; + int fcount = 0; + for (int i=0; iisKern()) { + break; } - if (!titlestring.empty()) { - titlestring = "_00" + titlestring; + if (fcount > 0) { + m_humdrum_text << "\t"; } - } else { - titlestring = m_refmap["title"]; - if (!titlestring.empty()) { - titlestring = "_00" + titlestring; + fcount++; + m_humdrum_text << "*"; + nextIndex++; + } + // Must be on the first **kern spine: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend on line " << index+1 << ": " << infile[index] << endl; + return; + } + // Print the first **kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; + } + fcount++; + m_humdrum_text << "*"; + nextIndex++; + // Next print all non-kern spines after first **kern spine: + for (int i=nextIndex; iisKern()) { + break; + } + if (fcount > 0) { + m_humdrum_text << "\t"; } + m_humdrum_text << "*"; + nextIndex++; } - return titlestring; -} - - - -////////////////////////////// -// -// Tool_prange::clearHistograms -- -// - -void Tool_prange::clearHistograms(vector >& bins, int start) { - int i; - for (i=start; i<(int)bins.size(); i++) { - bins[i].resize(40*11); - fill(bins[i].begin(), bins[i].end(), 0.0); - // bins[i].allowGrowth(0); + // Second **kern spine must be **kern data: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend B on line " << index+1 << ": " << infile[index] << endl; + return; } - for (int i=0; i<(int)bins.size(); i++) { - if (bins[i].size() == 0) { - bins[i].resize(40*11); - fill(bins[i].begin(), bins[i].end(), 0.0); + // Save the second kern spine as it does not exist yet in the + // output data. + // HTp savedKernToken = infile.token(index, nextIndex); + nextIndex++; + // Then store any non-kern spines between the second and third kern spines to + // append to the end of the data line later. + string postData; + for (int i=nextIndex; iisKern()) { + break; + } + if (!postData.empty()) { + postData += "\t"; + } + nextIndex++; + postData += "*"; + } + // Third kern spine must be **kern data: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend C on line " << index+1 << ": " << infile[index] << endl; + return; + } + // Now print the third kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; + } + fcount++; + m_humdrum_text << "*v"; + nextIndex++; + // Now printed the saved second **kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; + } + m_humdrum_text << "*v"; + fcount++; + // Now print the non-kern spines after the third **kern spine (or rather just + // all spines including any other **kern spines, although current requirement + // is that there are only three **kern spines. + for (int i=nextIndex; i 0) { + m_humdrum_text << "\t"; + } + fcount++; + nextIndex++; + m_humdrum_text << "*"; + } + // Finally print any non-kern spines after the second **kern spine: + if (!postData.empty()) { + if (fcount > 0) { + m_humdrum_text << "\t"; } + fcount++; + m_humdrum_text << postData; } + m_humdrum_text << endl; } - ////////////////////////////// // -// Tool_prange::printAnalysis -- +// Tool_sab2gs::printSwappedLine -- move the second **kern spine immediately after +// the third one, and move any non-kern spines after then end of the line. // -void Tool_prange::printAnalysis(ostream& out, vector& midibins) { - if (m_percentileQ) { - printPercentile(out, midibins, m_percentile); - return; - } else if (m_rangeQ) { - double notesinrange = countNotesInRange(midibins, m_rangeL, m_rangeH); - out << notesinrange << endl; +void Tool_sab2gs::printSwappedLine(HumdrumFile& infile, int index, vector& ktracks) { + // First print all non-kern spines at the start of the line: + int nextIndex = 0; + int fcount = 0; + for (int i=0; iisKern()) { + break; + } + if (fcount > 0) { + m_humdrum_text << "\t"; + } + fcount++; + m_humdrum_text << token; + nextIndex++; + } + // Must be on the first **kern spine: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend on line " << index+1 << ": " << infile[index] << endl; return; } - - int i; - double normval = 1.0; - - // print the pitch histogram - - double fracL = 0.0; - double fracH = 0.0; - double fracA = 0.0; - double sum = accumulate(midibins.begin(), midibins.end(), 0.0); - if (m_normQ) { - normval = sum; + // Print the first **kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; } - double runningtotal = 0.0; - - - out << "**keyno\t"; - if (m_pitchQ) { - out << "**pitch"; - } else { - out << "**kern"; + fcount++; + m_humdrum_text << infile.token(index, nextIndex); + nextIndex++; + // Next print all non-kern spines after first **kern spine: + for (int i=nextIndex; iisKern()) { + break; + } + if (fcount > 0) { + m_humdrum_text << "\t"; + } + m_humdrum_text << token; + nextIndex++; } - out << "\t**count"; - if (m_addFractionQ) { - out << "\t**fracL"; - out << "\t**fracA"; - out << "\t**fracH"; + // Second **kern spine must be **kern data: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend B on line " << index+1 << ": " << infile[index] << endl; + return; } - out << "\n"; - - - int base12; - - if (!m_reverseQ) { - for (i=0; i<(int)midibins.size(); i++) { - if (midibins[i] <= 0.0) { - continue; - } - if (m_diatonicQ) { - base12 = Convert::base7ToBase12(i); - } else { - base12 = i; - } - out << base12 << "\t"; - if (m_pitchQ) { - out << Convert::base12ToPitch(base12); - } else { - out << Convert::base12ToKern(base12); - } - out << "\t"; - out << midibins[i] / normval; - fracL = runningtotal/sum; - runningtotal += midibins[i]; - fracH = runningtotal/sum; - fracA = (fracH + fracL)/2.0; - fracL = (int)(fracL * 10000.0 + 0.5)/10000.0; - fracH = (int)(fracH * 10000.0 + 0.5)/10000.0; - fracA = (int)(fracA * 10000.0 + 0.5)/10000.0; - if (m_addFractionQ) { - out << "\t" << fracL; - out << "\t" << fracA; - out << "\t" << fracH; - } - out << "\n"; + // Save the second kern spine as it does not exist yet in the + // output data. + HTp savedKernToken = infile.token(index, nextIndex++); + // Then store any non-kern spines between the second and third kern spines to + // append to the end of the data line later. + string postData; + for (int i=nextIndex; iisKern()) { + break; } - } else { - for (i=(int)midibins.size()-1; i>=0; i--) { - if (midibins[i] <= 0.0) { - continue; - } - if (m_diatonicQ) { - base12 = Convert::base7ToBase12(i); - } else { - base12 = i; - } - out << base12 << "\t"; - if (m_pitchQ) { - out << Convert::base12ToPitch(base12); - } else { - out << Convert::base12ToKern(base12); - } - out << "\t"; - out << midibins[i] / normval; - fracL = runningtotal/sum; - runningtotal += midibins[i]; - fracH = runningtotal/sum; - fracA = (fracH + fracL)/2.0; - fracL = (int)(fracL * 10000.0 + 0.5)/10000.0; - fracH = (int)(fracH * 10000.0 + 0.5)/10000.0; - fracA = (int)(fracA * 10000.0 + 0.5)/10000.0; - if (m_addFractionQ) { - out << "\t" << fracL; - out << "\t" << fracA; - out << "\t" << fracH; - } - out << "\n"; + if (!postData.empty()) { + postData += "\t"; } + nextIndex++; + postData += *token; } - - out << "*-\t*-\t*-"; - if (m_addFractionQ) { - out << "\t*-"; - out << "\t*-"; - out << "\t*-"; - } - out << "\n"; - - out << "!!tessitura:\t" << getTessitura(midibins) << " semitones\n"; - - double mean = getMean12(midibins); - if (m_diatonicQ && (mean > 0)) { - mean = Convert::base7ToBase12(mean); + // Third kern spine must be **kern data: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend C on line " << index+1 << ": " << infile[index] << endl; + return; } - out << "!!mean:\t\t" << mean; - out << " ("; - if (mean < 0) { - out << "unpitched"; - } else { - out << Convert::base12ToKern(int(mean+0.5)); + // Now print the third kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; } - out << ")" << "\n"; - - int median12 = getMedian12(midibins); - out << "!!median:\t" << median12; - out << " ("; - if (median12 < 0) { - out << "unpitched"; - } else { - out << Convert::base12ToKern(median12); + fcount++; + m_humdrum_text << infile.token(index, nextIndex++); + // Now printed the saved second **kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; } - out << ")" << "\n"; - -} - - - -////////////////////////////// -// -// Tool_prange::getMedian12 -- return the pitch on which half of pitches are above -// and half are below. -// - -int Tool_prange::getMedian12(vector& midibins) { - double sum = accumulate(midibins.begin(), midibins.end(), 0.0); - - double cumsum = 0.0; - int i; - for (i=0; i<(int)midibins.size(); i++) { - if (midibins[i] <= 0.0) { - continue; + m_humdrum_text << savedKernToken; + fcount++; + // Now print the non-kern spines after the third **kern spine (or rather just + // all spines including any other **kern spines, although current requirement + // is that there are only three **kern spines. + for (int i=nextIndex; i 0) { + m_humdrum_text << "\t"; } - cumsum += midibins[i]/sum; - if (cumsum >= 0.50) { - return i; + fcount++; + nextIndex++; + m_humdrum_text << token; + } + // Finally print any non-kern spines after the second **kern spine: + if (!postData.empty()) { + if (fcount > 0) { + m_humdrum_text << "\t"; } + fcount++; + m_humdrum_text << postData; } - - return -1000; + m_humdrum_text << endl; } ////////////////////////////// // -// Tool_prange::getMean12 -- return the interval between the highest and lowest -// pitch in terms if semitones. +// Tool_sab2gs::printReducedLine -- remove the contents of the second **kern +// spine, and move any non-kernspines after it to become after the third **kern spine // -double Tool_prange::getMean12(vector& midibins) { - double top = 0.0; - double bottom = 0.0; - - int i; - for (i=0; i<(int)midibins.size(); i++) { - if (midibins[i] <= 0.0) { - continue; +void Tool_sab2gs::printReducedLine(HumdrumFile& infile, int index, vector& ktracks) { + // First print all non-kern spines at the start of the line: + int nextIndex = 0; + int fcount = 0; + for (int i=0; iisKern()) { + break; } - top += midibins[i] * i; - bottom += midibins[i]; - + if (fcount > 0) { + m_humdrum_text << "\t"; + } + fcount++; + m_humdrum_text << token; + nextIndex++; } - - if (bottom == 0) { - return -1000; + // Must be on the first **kern spine: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend on line " << index+1 << ": " << infile[index] << endl; + return; } - return top / bottom; -} - - - -////////////////////////////// -// -// Tool_prange::getTessitura -- return the interval between the highest and lowest -// pitch in terms if semitones. -// - -int Tool_prange::getTessitura(vector& midibins) { - int minn = -1000; - int maxx = -1000; - int i; - - for (i=0; i<(int)midibins.size(); i++) { - if (midibins[i] <= 0.0) { - continue; - } - if (minn < 0) { - minn = i; + // Print the first **kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; + } + fcount++; + m_humdrum_text << infile.token(index, nextIndex++); + // Next print all non-kern spines after first **kern spine: + for (int i=nextIndex; iisKern()) { + break; } - if (maxx < 0) { - maxx = i; + if (fcount > 0) { + m_humdrum_text << "\t"; } - if (minn > i) { - minn = i; + m_humdrum_text << token; + nextIndex++; + } + // Second **kern spine must be **kern data: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend B on line " << index+1 << ": " << infile[index] << endl; + return; + } + // Ignore the second kern spine as it does not exist yet in the + // output data. + nextIndex++; + // Then store any non-kern spines between the second and third kern spines to + // append to the end of the data line later. + string postData; + for (int i=nextIndex; iisKern()) { + break; } - if (maxx < i) { - maxx = i; + if (!postData.empty()) { + postData += "\t"; } + nextIndex++; + postData += *token; } - if (m_diatonicQ) { - maxx = Convert::base7ToBase12(maxx); - minn = Convert::base7ToBase12(minn); - } - - return maxx - minn + 1; -} - - - -////////////////////////////// -// -// Tool_prange::countNotesInRange -- -// - -double Tool_prange::countNotesInRange(vector& midibins, int low, int high) { - int i; - double sum = 0; - for (i=low; i<=high; i++) { - sum += midibins[i]; + // Third kern spine must be **kern data: + if (!infile.token(index, nextIndex)->isKern()) { + cerr << "Something strange happend C on line " << index+1 << ": " << infile[index] << endl; + return; } - return sum; -} - - - -////////////////////////////// -// -// Tool_prange::printPercentile -- -// - -void Tool_prange::printPercentile(ostream& out, vector& midibins, double m_percentile) { - double sum = accumulate(midibins.begin(), midibins.end(), 0.0); - double runningtotal = 0.0; - int i; - for (i=0; i<(int)midibins.size(); i++) { - if (midibins[i] <= 0) { - continue; + // Now print the third kern spine: + if (fcount > 0) { + m_humdrum_text << "\t"; + } + fcount++; + m_humdrum_text << infile.token(index, nextIndex++); + // Now print the non-kern spines after the third **kern spine (or rather just + // all spines including any other **kern spines, although current requirement + // is that there are only three **kern spines. + for (int i=nextIndex; i 0) { + m_humdrum_text << "\t"; } - runningtotal += midibins[i] / sum; - if (runningtotal >= m_percentile) { - out << i << endl; - return; + fcount++; + nextIndex++; + m_humdrum_text << token; + } + // Finally print any non-kern spines after the second **kern spine: + if (!postData.empty()) { + if (fcount > 0) { + m_humdrum_text << "\t"; } + fcount++; + m_humdrum_text << postData; } - - out << "unknown" << endl; + m_humdrum_text << endl; } - ////////////////////////////// // -// Tool_prange::getRange -- +// Tool_sab2gs::adjustMiddleVoice -- // -void Tool_prange::getRange(int& rangeL, int& rangeH, const string& rangestring) { - rangeL = -1; rangeH = -1; - if (rangestring.empty()) { - return; - } - int length = (int)rangestring.length(); - char* buffer = new char[length+1]; - strcpy(buffer, rangestring.c_str()); - char* ptr; - if (std::isdigit(buffer[0])) { - ptr = strtok(buffer, " \t\n:-"); - sscanf(ptr, "%d", &rangeL); - ptr = strtok(NULL, " \t\n:-"); - if (ptr != NULL) { - sscanf(ptr, "%d", &rangeH); +void Tool_sab2gs::adjustMiddleVoice(HTp spineStart) { + HTp current = spineStart; + // staff: +1 = top staff, -1 = bottom staff + // when on top staff, force stem down, or on bottom staff, force stem up + // when on bottom staff add "<" marker after pitch (or rest) to move to + // bottom staff. Staff choice is selected by clef: clefG2 is for top staff + // and staffF4 is for bottom staff. Chords are not expected. + int staff = 0; + string replacement = "$1" + m_belowMarker; + HumRegex hre; + while (current) { + if (*current == "*-") { + break; } - } else { - ptr = strtok(buffer, " :"); - if (ptr != NULL) { - rangeL = Convert::kernToMidiNoteNumber(ptr); - ptr = strtok(NULL, " :"); - if (ptr != NULL) { - rangeH = Convert::kernToMidiNoteNumber(ptr); + if (!m_downQ && current->isClef()) { + if (current->substr(0, 7) == "*clefG2") { + staff = 1; + // suppress clef: + string text = "*x" + current->substr(1); + current->setText(text); + } else if (current->substr(0, 7) == "*clefF4") { + staff = -1; + // suppress clef: + string text = "*x" + current->substr(1); + current->setText(text); } - } - } - - if (rangeH < 0) { - rangeH = rangeL; - } + } else if (current->isInterpretation()) { + if (*current == "*down") { + staff = -1; + } else if (*current == "*Xdown") { + staff = 1; + } + } else if ((staff != 0) && current->isData()) { + if (current->isNull()) { + // nothing to do with token + current = current->getNextToken(); + continue; + } + if (staff > 0) { + // force stems down or add stem down to non-rest notes + if (hre.search(current, "[/\\\\]")) { + string value = hre.replaceCopy(current, "\\", "/", "g"); + if (value != *current) { + current->setText(value); + } + current = current->getNextToken(); + continue; + } if (current->isRest()) { + current = current->getNextToken(); + continue; + } else { + string value = *current; + value += "\\"; + current->setText(value); + current = current->getNextToken(); + continue; + } - if (rangeL < 0) { rangeL = 0; } - if (rangeH < 0) { rangeH = 0; } - if (rangeL > 127) { rangeL = 127; } - if (rangeH > 127) { rangeH = 127; } - if (rangeL > rangeH) { - int temp = rangeL; - rangeL = rangeH; - rangeH = temp; + } else if (staff < 0) { + // force stems up or add stem up to non-rest notes + if (hre.search(current, "[/\\\\]")) { + string value = hre.replaceCopy(current, "\\", "/", "g"); + if (value != *current) { + current->setText(value); + } + current = current->getNextToken(); + continue; + } if (current->isRest()) { + // Do not at stem direction to rests + } else { + // Force stem up (assuming not a chord, although it should not matter): + string value = hre.replaceCopy(current, "/", "$"); + if (value != *current) { + current->setText(value); + } + } + // Add < after pitch (and accidental and qualifiers) to display + // on staff below. + m_hasCrossStaff = true; + string output = hre.replaceCopy(current, replacement, "([A-Ga-gr]+[-#nXYxy]*)", "g"); + if (output != *current) { + current->setText(output); + } + } + } + current = current->getNextToken(); } - } - ///////////////////////////////// // -// Tool_gridtest::Tool_recip -- Set the recognized options for the tool. +// Tool_satb2gs::Tool_satb2gs -- Set the recognized options for the tool. // -Tool_recip::Tool_recip(void) { - define("c|composite=b", "do composite rhythm analysis"); - define("a|append=b", "append composite analysis to input"); - define("p|prepend=b", "prepend composite analysis to input"); - define("r|replace=b", "replace **kern data with **recip data"); - define("x|attacks-only=b", "only mark lines with note attacks"); - define("G|ignore-grace-notes=b", "ignore grace notes"); - define("k|kern-spine=i:1", "analyze only given kern spine"); - define("K|all-spines=b", "analyze each kern spine separately"); - define("e|exinterp=s:**recip", "use the given exinterp for data output"); - define("n|kern-pitch=s:e", "note to add for '-e kern' option"); - define("kern=b", "equivalent to '-e kern' option"); +Tool_satb2gs::Tool_satb2gs(void) { + // no options } -/////////////////////////////// +///////////////////////////////// // -// Tool_recip::run -- Primary interfaces to the tool. +// Tool_satb2gs::run -- Do the main work of the tool. // -bool Tool_recip::run(HumdrumFileSet& infiles) { +bool Tool_satb2gs::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i=0; k--) { - int fcount = infile[i].getFieldCount(); - int ktrack = m_kernspines[k]->getTrack(); - int insertj = -1; - for (int j=fcount-1; j>=0; j--) { - if (!infile.token(i, j)->isKern()) { - continue; - } - int track = infile.token(i, j)->getTrack(); - if (track != ktrack) { - continue; - } - if (insertj < 0) { - insertj = j; - } - infile[i].appendToken(insertj, cfile.token(i, j)->getText()); - // infile.token(i, insertj+1)->setTrack(remapping[k]); - } - } - } +void Tool_satb2gs::initialize(void) { + // do nothing } ////////////////////////////// // -// Tool_recip::doCompositeAnalysis -- +// Tool_satb2gs::processFile -- // -void Tool_recip::doCompositeAnalysis(HumdrumFile& infile) { - - // Calculate composite rhythm **recip spine: +void Tool_satb2gs::processFile(HumdrumFile& infile) { + vector> tracks; + getTrackInfo(tracks, infile); - vector composite(infile.getLineCount()); - for (int i=0; i<(int)composite.size(); i++) { - composite[i] = infile[i].getDuration(); + if ((tracks[1].size() != 2) || (tracks[3].size() != 2)) { + cerr << "Warning: not processing data since there must be at least four **kern spines" << endl; + return; } - int kernQ = false; - if (m_exinterp.find("kern") != std::string::npos) { - kernQ = true; -// cerr << "KERN ON" << endl; + bool goodHeader = validateHeader(infile); + if (!goodHeader) { + cerr << "Warning: no spine manipulations allows within header, not processing file" << endl; + return; } - // convert durations to **recip strings - vector recips(composite.size()); - for (int i=0; i<(int)recips.size(); i++) { - if ((!m_graceQ) && (composite[i] == 0)) { + bool dataQ = false; + for (int i=0; i kspines = infile.getKernSpineStartList(); - HumRegex hre; - string expression = "[^q\\d.%\\]\\[]+"; - for (int i=0; iisKern()) { - continue; - } - HTp etok = infile.getStrandEnd(i); - HTp tok = stok; - while (tok && (tok != etok)) { - if (!tok->isData()) { - tok = tok->getNextToken(); - continue; - } - if (tok->isNull()) { - tok = tok->getNextToken(); - continue; - } - if (tok->find('q') != string::npos) { - if (m_graceQ) { - tok->setText("q"); - } else { - tok->setText("."); +void Tool_satb2gs::printRegularLine(HumdrumFile& infile, int line, + vector>& tracks) { + + int spinecount = infile[line].getFieldCount(); + int track; + HTp token; + vector>> tokens; + tokens.resize(5); + for (int i=0; i<(int)tracks.size(); i++) { + tokens[i].resize(tracks[i].size()); + } + + // store tokens in output order: + for (int i=0; i<(int)tracks.size(); i++) { + for (int j=0; j<(int)tracks[i].size(); j++) { + int target = tracks[i][j]; + for (int k=0; kgetTrack(); + if (track != target) { + continue; } - } else { - hre.replaceDestructive(*tok, "", expression, "g"); + tokens[i][j].push_back(token); } - tok = tok->getNextToken(); } } - for (int i=0; i<(int)kspines.size(); i++) { - kspines[i]->setText(m_exinterp); + int counter = 0; + HTp top; + HTp bot; + HTp inner; + HTp outer; + bool suppressQ; + + // now print in output order, but hide fermatas + // in the alto and tenor parts if there are fermatas + // int the soprano and bass parts respectively. + for (int i=0; i<(int)tokens.size(); i++) { + for (int j=0; j<(int)tokens[i].size(); j++) { + switch (i) { + case 0: + case 2: + case 4: + // non-kern spines + for (int k=0; k<(int)tokens[i][j].size(); k++) { + m_humdrum_text << tokens[i][j][k]; + counter++; + if (counter < spinecount) { + m_humdrum_text << "\t"; + } + } + break; + + case 1: + case 3: + top = tokens[i][0][0]; + bot = tokens[i][1][0]; + if (i == 1) { + // tenor: top is inner + inner = top; + outer = bot; + } else { + // alto: bottom is inner + inner = bot; + outer = top; + } + if (inner->hasFermata() && outer->hasFermata()) { + suppressQ = true; + } else { + suppressQ = false; + } + + for (int k=0; k<(int)tokens[i][j].size(); k++) { + token = tokens[i][j][k]; + if (suppressQ && ((void*)token == (void*)inner)) { + string value = *token; + // Make fermata invisible by adding 'y' after it: + for (int m=0; m<(int)value.size(); m++) { + m_humdrum_text << value[m]; + if (value[m] == ';') { + if (m < (int)value.size() - 1) { + if (value.at(m+1) != 'y') { + m_humdrum_text << 'y'; + } + } else { + m_humdrum_text << 'y'; + } + } + } + } else { + m_humdrum_text << token; + } + counter++; + if (counter < spinecount) { + m_humdrum_text << "\t"; + } + } + break; + } + } } + m_humdrum_text << endl; } - ////////////////////////////// // -// Tool_recip::initialize -- +// Tool_satb2gs::printTerminatorLine -- Print the terminator line in the +// output data. // -void Tool_recip::initialize(HumdrumFile& infile) { - m_kernspines = infile.getKernSpineStartList(); - m_graceQ = !getBoolean("ignore-grace-notes"); - - m_exinterp = getString("exinterp"); - if (m_exinterp.empty()) { - m_exinterp = "**recip"; - } else if (m_exinterp[0] != '*') { - m_exinterp.insert(0, "*"); - } - if (m_exinterp[1] != '*') { - m_exinterp.insert(0, "*"); - } - - m_kernpitch = getString("kern-pitch"); - - if (getBoolean("kern")) { - m_exinterp = "**kern"; +void Tool_satb2gs::printTerminatorLine(vector>& tracks) { + int count = getNewTrackCount(tracks); + for (int i=0; i>& tracks) { + int count = getNewTrackCount(tracks); + int counter = 0; + + for (int i=0; i<(int)tracks.size(); i++) { + switch (i) { + case 0: + case 2: + case 4: + for (int j=0; j<(int)tracks[i].size(); j++) { + m_humdrum_text << "*"; + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + } + break; + case 1: + case 3: + m_humdrum_text << "*^"; + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + break; + } + } + m_humdrum_text << endl; } -///////////////////////////////// +////////////////////////////// // -// Tool_restfill::run -- Do the main work of the tool. +// Tool_satb2gs::printSpineMergeLine -- // -bool Tool_restfill::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i>& tracks) { + int count = getNewTrackCount(tracks); + count += 2; + int counter; -bool Tool_restfill::run(const string& indata, ostream& out) { - HumdrumFile infile; - infile.readStringNoRhythm(indata); - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; - } - return status; -} + if (!tracks[2].empty()) { + // do not need to place merges on separate lines since they are + // separated by non-kern spine(s) between bass and soprano subspines. + counter = 0; + for (int i=0; i<(int)tracks.size(); i++) { + switch (i) { + case 0: + case 2: + case 4: + for (int j=0; j<(int)tracks[i].size(); j++) { + m_humdrum_text << "*"; + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + } + break; + case 1: + case 3: + for (int j=0; j<(int)tracks[i].size(); j++) { + m_humdrum_text << "*v"; + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + } + break; + } + } + m_humdrum_text << endl; -bool Tool_restfill::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); } else { - out << infile; - } - return status; -} + // Merges for tenor/bass and soprano/alto need to be placed + // on separate lines. + // First merge tenor/bass (tracks[1]) + counter = 0; + for (int i=0; i<(int)tracks.size(); i++) { + switch (i) { + case 0: + case 2: + case 3: + case 4: + for (int j=0; j<(int)tracks[i].size(); j++) { + m_humdrum_text << "*"; + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + } + break; + case 1: + for (int j=0; j<(int)tracks[i].size(); j++) { + m_humdrum_text << "*v"; + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + } + break; + } + } + m_humdrum_text << endl; -bool Tool_restfill::run(HumdrumFile& infile) { - initialize(); - processFile(infile); - infile.createLinesFromTokens(); - return true; + // Now merge soprano/alto (tracks[3]) + count--; + counter = 0; + for (int i=0; i<(int)tracks.size(); i++) { + switch (i) { + case 0: + case 2: + case 4: + for (int j=0; j<(int)tracks[i].size(); j++) { + m_humdrum_text << "*"; + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + } + break; + case 1: + m_humdrum_text << "*"; + m_humdrum_text << "\t"; + counter++; + break; + case 3: + for (int j=0; j<(int)tracks[i].size(); j++) { + m_humdrum_text << "*v"; + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + } + break; + } + } + m_humdrum_text << endl; + } } ////////////////////////////// // -// Tool_restfill::initialize -- +// Tool_satb2gs::getNewTrackCount -- Return the number of tracks (spines) +// in the output data (not counting subspines). // -void Tool_restfill::initialize(void) { - m_hiddenQ = getBoolean("hidden-rests"); - m_exinterp = getString("exinterp"); - if (m_exinterp.empty()) { - m_exinterp = "**kern"; - } - if (m_exinterp.compare(0, 2, "**") != 0) { - if (m_exinterp.compare(0, 1, "*") != 0) { - m_exinterp = "**" + m_exinterp; - } else { - m_exinterp = "*" + m_exinterp; +int Tool_satb2gs::getNewTrackCount(vector>& tracks) { + int sum = 0; + for (int i=0; i<(int)tracks.size(); i++) { + for (int j=0; j<(int)tracks[i].size(); j++) { + sum++; } } - + // remove two spines that were merged into two others: + sum -= 2; + return sum; } ////////////////////////////// // -// Tool_restfill::processFile -- +// Tool_satb2gs::printHeaderLine -- // -void Tool_restfill::processFile(HumdrumFile& infile) { +void Tool_satb2gs::printHeaderLine(HumdrumFile& infile, int line, + vector>& tracks) { + int count = infile.getMaxTrack() - 2; - vector starts; - infile.getSpineStartList(starts, m_exinterp); - vector process(starts.size(), false); - for (int i=0; i<(int)starts.size(); i++) { - process[i] = hasBlankMeasure(starts[i]); - if (process[i]) { - starts[i]->setText("**temp-kern"); - } - } - infile.analyzeStructure(); - for (int i=0; i<(int)starts.size(); i++) { - if (!process[i]) { - continue; + HTp token; + int counter = 0; + for (int i=0; i<(int)tracks.size(); i++) { + switch (i) { + case 0: + case 2: + case 4: + for (int j=0; j<(int)tracks[i].size(); j++) { + token = infile.token(line, tracks[i][j]-1); + m_humdrum_text << token; + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + } + break; + + case 1: + case 3: + token = infile.token(line, tracks[i][0]-1); + if (token->isInstrumentName()) { + // suppress instrument names, but keep blank name + // to force indent. + m_humdrum_text << "*I\""; + } else if (token->isInstrumentAbbreviation()) { + // suppress instrument abbreviations + m_humdrum_text << "*"; + } else if (token->isInstrumentDesignation()) { + // suppress instrument designations (such as *Itenor) + m_humdrum_text << "*"; + } else if (token->isClef()) { + vector clefs = getClefs(infile, line); + if (i == 1) { + if (clefs.size() == 4) { + m_humdrum_text << clefs[0]; + } else { + m_humdrum_text << "*clefF4"; + } + } else { + if (clefs.size() == 4) { + m_humdrum_text << clefs.back(); + } else { + m_humdrum_text << "*clefG2"; + } + } + } else { + m_humdrum_text << token; + } + counter++; + if (counter < count) { + m_humdrum_text << "\t"; + } + break; } - starts[i]->setText("**kern"); - fillInRests(starts[i]); } + m_humdrum_text << endl; } ////////////////////////////// // -// Tool_restfill::hasBlankMeasure -- +// Tool_satb2gs::getClefs -- get a list of the clefs on the current line. // -bool Tool_restfill::hasBlankMeasure(HTp start) { - bool foundcontent = false; - HTp current = start; - int founddata = false; - while (current) { - - if (current->isBarline()) { - if (founddata && !foundcontent) { - return true; - } - foundcontent = false; - founddata = false; - current = current->getNextToken(); - continue; - } - if (!current->isData()) { - current = current->getNextToken(); +vector Tool_satb2gs::getClefs(HumdrumFile& infile, int line) { + vector output; + for (int i=0; iisKern()) { continue; } - founddata = true; - if (!current->isNull()) { - foundcontent = true; + if (token->isClef()) { + output.push_back(token); } - current = current->getNextToken(); - } - return false; + return output; } ////////////////////////////// // -// Tool_restfill::fillInRests -- -// Also deal with cases where the last measure does not end in a barline. +// Tool_satb2gs::getTrackInfo -- +// tracks 0 = list of spines before bass **kern spine +// tracks 1 = tenor and then bass **kern track numbers +// tracks 2 = aux. spines after after tenor and then after bass +// tracks 3 = soprano and then alto **kern track numbers +// tracks 4 = aux. spines after after soprano and then after alto // -void Tool_restfill::fillInRests(HTp start) { - HTp current = start; - HTp firstcell = NULL; - int founddata = false; - bool foundcontent = false; - HumNum lasttime = 0; - HumNum currtime = 0; - HumNum duration = 0; - while (current) { - if (current->isBarline()) { - if (firstcell) { - lasttime = firstcell->getDurationFromStart(); - } - currtime = getNextTime(current); - if (firstcell && founddata && !foundcontent) { - duration = currtime - lasttime; - addRest(firstcell, duration); - } - firstcell = NULL; - founddata = false; - foundcontent = false; - current = current->getNextToken(); - lasttime = currtime; - continue; +void Tool_satb2gs::getTrackInfo(vector>& tracks, HumdrumFile& infile) { + tracks.resize(5); + for (int i=0; i<(int)tracks.size(); i++) { + tracks[i].clear(); + } + vector sstarts; + infile.getSpineStartList(sstarts); + int track; + + // fill in tracks[0]: spines before first **kern spine + for (int i=0; i<(int)sstarts.size(); i++) { + if (sstarts[i]->isKern()) { + break; } - if (!current->isData()) { - current = current->getNextToken(); - continue; + track = sstarts[i]->getTrack(); + tracks[0].push_back(track); + } + + int kcount = 0; + + kcount = 0; + // Store tracks related to the tenor part: + for (int i=0; i<(int)sstarts.size(); i++) { + if (sstarts[i]->isKern()) { + kcount++; } - if (current->getDuration() == 0) { - // grace-note line, so ignore - current = current->getNextToken(); - continue; + if (kcount > 2) { + break; } - founddata = true; - if (!current->isNull()) { - foundcontent = true; + if (kcount < 2) { + continue; } - if (!firstcell) { - firstcell = current; + track = sstarts[i]->getTrack(); + if (sstarts[i]->isKern()) { + tracks[1].push_back(track); + } else { + tracks[2].push_back(track); } - current = current->getNextToken(); } -} - + kcount = 0; + // Store tracks related to the bass part: + for (int i=0; i<(int)sstarts.size(); i++) { + if (sstarts[i]->isKern()) { + kcount++; + } + if (kcount > 1) { + break; + } + if (kcount < 1) { + continue; + } + track = sstarts[i]->getTrack(); + if (sstarts[i]->isKern()) { + tracks[1].push_back(track); + } else { + tracks[2].push_back(track); + } + } -////////////////////////////// -// -// Tool_restfill::addRest -- -// - -void Tool_restfill::addRest(HTp cell, HumNum duration) { - if (!cell) { - return; + kcount = 0; + // Store tracks related to the soprano part: + for (int i=0; i<(int)sstarts.size(); i++) { + if (sstarts[i]->isKern()) { + kcount++; + } + if (kcount > 4) { + break; + } + if (kcount < 4) { + continue; + } + track = sstarts[i]->getTrack(); + if (sstarts[i]->isKern()) { + tracks[3].push_back(track); + } else { + tracks[4].push_back(track); + } } - string text = Convert::durationToRecip(duration); - text += "r"; - if (m_hiddenQ) { - text += "yy"; + + kcount = 0; + // Store tracks related to the alto part: + for (int i=0; i<(int)sstarts.size(); i++) { + if (sstarts[i]->isKern()) { + kcount++; + } + if (kcount > 3) { + break; + } + if (kcount < 3) { + continue; + } + track = sstarts[i]->getTrack(); + if (sstarts[i]->isKern()) { + tracks[3].push_back(track); + } else { + tracks[4].push_back(track); + } } - cell->setText(text); } ////////////////////////////// // -// Tool_restfill::getNextTime -- +// Tool_satb2gs::validateHeader -- Header cannot contain +// spine manipulators. // -HumNum Tool_restfill::getNextTime(HTp token) { - HTp current = token; - while (current) { - if (current->isData()) { - return current->getDurationFromStart(); +bool Tool_satb2gs::validateHeader(HumdrumFile& infile) { + for (int i=0; iisExclusive()) { + continue; + } + if (infile[i].isManipulator()) { + return false; } - current = current->getNextToken(); } - return token->getOwner()->getOwner()->getScoreDuration(); -} + return true; +} @@ -117495,39 +123836,30 @@ HumNum Tool_restfill::getNextTime(HTp token) { ///////////////////////////////// // -// Tool_rid::Tool_rid -- Set the recognized options for the tool. +// Tool_scordatura::Tool_scordatura -- Set the recognized options for the tool. // -Tool_rid::Tool_rid(void) { - // Humdrum Toolkit classic rid options: - define("D|all-data=b", "remove all data records"); - define("d|null-data=b", "remove null data records"); - define("G|all-global=b", "remove all global comments"); - define("g|null-global=b", "remove null global comments"); - define("I|all-interpretation=b", "remove all interpretation records"); - define("i|null-interpretation=b", "remove null interpretation records"); - define("L|all-local-comment=b", "remove all local comments"); - define("l|1|null-local-comment=b", "remove null local comments"); - define("T|all-tandem-interpretation=b", "remove all tandem interpretations"); - define("U|u=b", "remove unnecessary (duplicate ex. interps."); - define("k|consider-kern-only=b", "for -d, only consider **kern spines."); - define("V=b", "negate filtering effect of program."); - define("H|no-humdrum-syntax=b", "equivalent to -GLIMd."); - - // additional options - define("M|all-barlines=b", "remove measure lines"); - define("C|all-comments=b", "remove all comment lines"); - define("c=b", "remove global and local comment lines"); +Tool_scordatura::Tool_scordatura(void) { + define("s|sounding=b", "generate sounding score"); + define("w|written=b", "generate written score"); + define("m|mark|marker=s:@", "marker to add to score"); + define("p|pitch|pitches=s", "list of pitches to mark"); + define("i|interval=s", "musical interval of marked pitches"); + define("I|is-sounding=s", "musical score is in sounding format for marks"); + define("c|chromatic=i:0", "chromatic interval of marked pitches"); + define("d|diatonic=i:0", "diatonic interval of marked pitches"); + define("color=s", "color marked pitches"); + define("string=s", "string number"); } ///////////////////////////////// // -// Tool_rid::run -- Do the main work of the tool. +// Tool_scordatura::run -- Do the main work of the tool. // -bool Tool_rid::run(HumdrumFileSet& infiles) { +bool Tool_scordatura::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i 28) || (abs(m_chromatic) > 48)) { + m_diatonic = 0; + m_chromatic = 0; + m_cd = false; + } + if (!m_pitches.empty()) { + prepareTranspositionInterval(); + } + m_string = getString("string"); +} - if (getBoolean("no-humdrum-syntax")) { - // remove all Humdrum file structure - option_G = option_L = option_I = option_M = option_d = 1; - } + + +////////////////////////////// +// +// Tool_scordatura::processFile -- +// + +void Tool_scordatura::processFile(HumdrumFile& infile) { + m_modifiedQ = false; + + if (!m_pitches.empty()) { + markPitches(infile); + if (m_modifiedQ) { + addMarkerRdf(infile); + } + } + + if (m_writtenQ || m_soundingQ) { + vector rdfs; + getScordaturaRdfs(rdfs, infile); + if (!rdfs.empty()) { + processScordaturas(infile, rdfs); + } + } + + if (m_modifiedQ) { + infile.createLinesFromTokens(); + } } ////////////////////////////// // -// Tool_rid::processFile -- +// Tool_scordatura::processScoredaturas -- // -void Tool_rid::processFile(HumdrumFile& infile) { - int setcount = 1; // disabled for now. +void Tool_scordatura::processScordaturas(HumdrumFile& infile, vector& rdfs) { + for (int i=0; i<(int)rdfs.size(); i++) { + processScordatura(infile, rdfs[i]); + } +} - HumRegex hre; - int revQ = option_V; - // if bibliographic/reference records are not suppressed - // print the !!!!SEGMENT: marker if present. - if ((setcount > 1) && (!option_G)) { - infile.printNonemptySegmentLabel(m_humdrum_text); - } - for (int i=0; iisKern()) { + continue; + } + HTp sstop = infile.getStrandEnd(i); + transposeStrand(sstart, sstop, marker); + } +} + + + +////////////////////////////// // -// Tool_ruthfix::Tool_ruthfix -- Set the recognized options for the tool. +// Tool_scordatura::transposeStrand -- // -Tool_ruthfix::Tool_ruthfix(void) { - // add options here +void Tool_scordatura::transposeStrand(HTp sstart, HTp sstop, const string& marker) { + HTp current = sstart; + while (current && current != sstop) { + if (!current->isData()) { + current = current->getNextToken(); + continue; + } + if (current->isNull() || current->isRest()) { + current = current->getNextToken(); + continue; + } + if (current->find(marker) != string::npos) { + transposeChord(current, marker); + } + current = current->getNextToken(); + } } -///////////////////////////////// +////////////////////////////// // -// Tool_ruthfix::run -- Do the main work of the tool. +// Tool_scordatura::transposeChord -- // -bool Tool_ruthfix::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; igetSubtokenCount(); + if (scount == 1) { + string inputnote = *token; + string newtoken; + newtoken = transposeNote(inputnote); + token->setText(newtoken); + return; } - return status; + vector subtokens; + subtokens = token->getSubtokens(); + for (int i=0; i<(int)subtokens.size(); i++) { + if (subtokens[i].find(marker) == string::npos) { + continue; + } + string newtoken = transposeNote(subtokens[i]); + subtokens[i] = newtoken; + } + string newchord; + for (int i=0; i<(int)subtokens.size(); i++) { + newchord += subtokens[i]; + if (i<(int)subtokens.size() - 1) { + newchord += ' '; + } + } + token->setText(newchord); } -bool Tool_ruthfix::run(const string& indata, ostream& out) { - HumdrumFile infile(indata); - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; + +////////////////////////////// +// +// Tool_scordatura::transposeNote -- +// + +string Tool_scordatura::transposeNote(const string& note) { + HumRegex hre; + if (!hre.search(note, "(.*?)([A-Ga-g]+[-#]*)(.*)")) { + return note; } - return status; + string pre = hre.getMatch(1); + string pitch = hre.getMatch(2); + string post = hre.getMatch(3); + HumPitch hpitch; + hpitch.setKernPitch(pitch); + m_transposer.transpose(hpitch); + string output; + output = pre; + output += hpitch.getKernPitch(); + output += post; + return output; } -bool Tool_ruthfix::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); + +////////////////////////////// +// +// Tool_scordatura::flipScordaturaInfo -- +// + +void Tool_scordatura::flipScordaturaInfo(HTp reference, int diatonic, int chromatic) { + diatonic *= -1; + chromatic *= -1; + string output; + if (m_writtenQ) { + output = "Trd"; + output += to_string(diatonic); + output += "c"; + output += to_string(chromatic); + } else if (m_soundingQ) { + output = "ITrd"; + output += to_string(diatonic); + output += "c"; + output += to_string(chromatic); } else { - out << infile; + return; + } + HumRegex hre; + string token = *reference; + hre.replaceDestructive(token, output, "I?Trd-?\\dc-?\\d"); + if (token != *reference) { + m_modifiedQ = true; + reference->setText(token); } - return status; } -bool Tool_ruthfix::run(HumdrumFile& infile) { - insertCrossBarTies(infile); - return true; + +////////////////////////////// +// +// Tool_scordatura::getScoredaturaRdfs -- +// + +void Tool_scordatura::getScordaturaRdfs(vector& rdfs, HumdrumFile& infile) { + rdfs.clear(); + HumRegex hre; + for (int i=0; i Tool_scordatura::parsePitches(const string& input) { + HumRegex hre; + string value = input; + hre.replaceDestructive(value, "-", "\\s*-\\s*", "g"); + + vector pieces; + hre.split(pieces, value, "[^A-Ga-g0-9-]+"); + + HumPitch pitcher; + set output; + string p1; + string p2; + int d1; + int d2; + for (int i=0; i<(int)pieces.size(); i++) { + if (hre.search(pieces[i], "(.*)-(.*)")) { + // pitch range + p1 = hre.getMatch(1); + p2 = hre.getMatch(2); + d1 = Convert::kernToBase7(p1); + d2 = Convert::kernToBase7(p2); + if ((d1 < 0) || (d2 < 0) || (d1 > d2) || (d1 > 127) || (d2 > 127)) { + continue; + } + for (int j=d1; j<=d2; j++) { + output.insert(j); + } + } else { + // single pitch + d1 = Convert::kernToBase7(pieces[i]); + if ((d1 < 0) || (d1 > 127)) { + continue; + } + output.insert(d1); + } } - scount = infile.getStrandCount(); + return output; +} - HTp token; - for (int i=0; iisKern()) { + +////////////////////////////// +// +// Tool_scordatura::markPitches -- +// + +void Tool_scordatura::markPitches(HumdrumFile& infile) { + for (int i=0; iisKern()) { continue; } - insertCrossBarTies(infile, i); + HTp sstop = infile.getStrandStop(i); + markPitches(sstart, sstop); } } -void Tool_ruthfix::insertCrossBarTies(HumdrumFile& infile, int strand) { - HTp sstart = infile.getStrandStart(strand); - HTp send = infile.getStrandEnd(strand); - HTp s = sstart; - HTp lastnote = NULL; - bool barstart = true; - while (s != send) { - if (s->isBarline()) { - barstart = true; - } else if (s->isNote()) { - if (lastnote && barstart && (s->find("yy") != string::npos)) { - createTiedNote(lastnote, s); - } - barstart = false; - lastnote = s; - } else if (s->isRest()) { - lastnote = NULL; - barstart = false; +void Tool_scordatura::markPitches(HTp sstart, HTp sstop) { + HTp current = sstart; + while (current && (current != sstop)) { + if (current->isNull() || current->isRest()) { + current = current->getNextToken(); + continue; + } + markPitches(current); + current = current->getNextToken(); + } +} + + +void Tool_scordatura::markPitches(HTp token) { + vector subtokens = token->getSubtokens(); + int counter = 0; + for (int i=0; i<(int)subtokens.size(); i++) { + int dia = Convert::kernToBase7(subtokens[i]); + if (m_pitches.find(dia) != m_pitches.end()) { + counter++; + subtokens[i] += m_marker; } - s = s->getNextToken(); - if (!s) { - break; + } + if (counter == 0) { + return; + } + string newtoken; + for (int i=0; i<(int)subtokens.size(); i++) { + newtoken += subtokens[i]; + if (i < (int)subtokens.size() - 1) { + newtoken += ' '; } } + token->setText(newtoken); + m_modifiedQ = true; } ////////////////////////////// // -// Tool_ruthfix::createTiedNote -- Does not work for chords. -// change 1E-X TO 2E-Xyy -// to [1E-X TO 2E-X] +// Tool_scordatura::addMarkerRdf -- // -void Tool_ruthfix::createTiedNote(HTp left, HTp right) { - if (left->isChord() || right->isChord()) { +void Tool_scordatura::addMarkerRdf(HumdrumFile& infile) { + string line = "!!!RDF**kern: "; + line += m_marker; + line += " = "; + if (!m_string.empty()) { + line += "string="; + line += m_string; + line += " "; + } + line += "scordatura="; + if (m_IQ) { + line += "I"; + } + line += "Tr"; + if (m_transposition.empty()) { + line += "XXX"; + } else { + line += m_transposition; + } + if (!m_color.empty()) { + line += ", color="; + line += m_color; + } + infile.appendLine(line); + m_modifiedQ = true; +} + + + +////////////////////////////// +// +// Tool_scordatura::prepareTranspositionInterval -- +// + +void Tool_scordatura::prepareTranspositionInterval(void) { + m_transposition.clear(); + if (m_cd) { + m_transposition = "d"; + m_transposition += to_string(m_diatonic); + m_transposition += "c"; + m_transposition += to_string(m_chromatic); return; } - auto loc = right->find("yy"); - if (loc != string::npos) { - left->insert(0, 1, '['); - right->replace(loc, 2, "]"); + + if (m_interval.empty()) { + return; } + + HumTransposer trans; + trans.intervalToDiatonicChromatic(m_diatonic, m_chromatic, m_interval); + m_transposition = "d"; + m_transposition += to_string(m_diatonic); + m_transposition += "c"; + m_transposition += to_string(m_chromatic); } @@ -117872,22 +124348,41 @@ void Tool_ruthfix::createTiedNote(HTp left, HTp right) { ///////////////////////////////// // -// Tool_sab2gs::Tool_sab2gs -- Set the recognized options for the tool. +// Tool_semitones::Tool_semitones -- Set the recognized options for the tool. // -Tool_sab2gs::Tool_sab2gs(void) { - define("b|below=s:<", "Marker for displaying on next staff below"); - define("d|down=b", "Use only *down/*Xdown interpretations"); +Tool_semitones::Tool_semitones(void) { + define("1|first=b", "mark only the first note of intervals"); + define("2|second=b", "mark only the second note of intervals"); + define("A|O|no-analysis|no-output=b", "do not print analysis spines"); + define("I|no-input=b", "do not print input data spines"); + define("M|no-mark|no-marks=b", "do not mark notes"); + define("R|no-rests=b", "ignore rests"); + define("T|no-ties=b", "do not mark ties"); + define("X|include|only=s", "include only **kern tokens with given pattern"); + define("color=s:red", "mark color"); + define("c|cdata=b", "store resulting data as **cdata (allowing display in VHV"); + define("d|down=b", "highlight notes that that have a negative semitone interval"); + define("j|jump=i:3", "starting interval defining leaps"); + define("l|leap=b", "highlight notes that have leap motion"); + define("mark=s:@", "mark character"); + define("m|midi=b", "show MIDI note number for pitches"); + define("n|count=b", "output count of intervals being marked"); + define("p|pc=b", "output pitch classes from C=0 instead of MIDI notes for -m option"); + define("r|same|repeat|repeated=b", "highlight notes that are repeated "); + define("s|step=b", "highlight notes that have step-wise motion"); + define("u|up=b", "highlight notes that that have a positive semitone interval"); + define("x|exclude=s", "exclude **kern tokens with given pattern"); } ///////////////////////////////// // -// Tool_sab2gs::run -- Do the main work of the tool. +// Tool_semitones::run -- Do the main work of the tool. // -bool Tool_sab2gs::run(HumdrumFileSet& infiles) { +bool Tool_semitones::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i spines; - infile.getSpineStartList(spines); - vector kernSpines; - for (int i=0; i<(int)spines.size(); i++) { - if (spines[i]->isKern()) { - kernSpines.push_back(spines[i]); +void Tool_semitones::processFile(HumdrumFile& infile) { + m_markCount = 0; + for (int i=0; i 0) && !m_nomarkQ) { + m_humdrum_text << "!!!RDF**kern: "; + m_humdrum_text << m_marker; + m_humdrum_text << " = marked note"; + if (getBoolean("color")) { + m_humdrum_text << ", color=" << m_color; } + m_humdrum_text << '\n'; } - if (kernSpines.size() != 3) { - // Not valid for processing kern spines, so return original: - m_humdrum_text << infile; - return; + if (m_count) { + showCount(); } +} - string belowMarker = hasBelowMarker(infile); - if (!belowMarker.empty()) { - m_hasBelowMarker = true; - m_belowMarker = belowMarker; - } - adjustMiddleVoice(kernSpines[1]); - printGrandStaff(infile, kernSpines); + +////////////////////////////// +// +// Tool_semitones::showCount -- Give a count for the number of +// intervals that were marked. +// + +void Tool_semitones::showCount(void) { + m_humdrum_text << "!!semitone_count: " << m_markCount; + if (m_repeatQ) { + m_humdrum_text << " REPEAT"; + } + if (m_upQ) { + m_humdrum_text << " UP"; + } + if (m_downQ) { + m_humdrum_text << " DOWN"; + } + if (m_stepQ) { + m_humdrum_text << " STEP"; + } + if (m_leapQ) { + m_humdrum_text << " LEAP"; + } + if ((m_stepQ || m_leapQ) && (m_leap != 3)) { + m_humdrum_text << " JUMP:" << m_leap; + } + if (m_marker != "@") { + m_humdrum_text << " MARK:" << m_marker; + } + m_humdrum_text << '\n'; } -///////////////////////////// +////////////////////////////// // -// Tool_sab2gs::hasBelowMarker -- Returns below marker if found; otherwise, -// returns empty string. +// Tool_semitones::analyzeLine -- Append analysis spines after every **kern +// spine. // -string Tool_sab2gs::hasBelowMarker(HumdrumFile& infile) { - string output; - HumRegex hre; - if (m_hasCrossStaff) { - // Search backwards since if there is a below marker, it will be more - // likely found at the bottom of the score. - for (int i=infile.getLineCount()-1; i<=0; i--) { - if (infile[i].hasSpines()) { +void Tool_semitones::analyzeLine(HumdrumFile& infile, int line) { + int group = 0; + if (!infile[line].hasSpines()) { + m_humdrum_text << infile[line] << "\n"; + return; + } + for (int i=0; iisKern()) { + m_humdrum_text << token; + if (i < infile[line].getFieldCount() - 1) { + m_humdrum_text << '\t'; + } continue; } - if (hre.search(infile.token(i, 0), "^!!!RDF\\*\\*kern\\s*:\\s*([^\\s=]+)\\s*=\\s*below\\s*$")) { - output = hre.getMatch(1); - break; + } + i = processKernSpines(infile, line, i, group++); + if (!m_noinputQ) { + if (i < infile[line].getFieldCount() - 1) { + m_humdrum_text << '\t'; } } } - return output; + m_humdrum_text << '\n'; } -/////////////////////////////// +////////////////////////////// // -// Tool_sab2gs::printGrandStaff -- +// Tool_semitones::processKernSpine -- // -void Tool_sab2gs::printGrandStaff(HumdrumFile& infile, vector& starts) { - bool foundData = false; +int Tool_semitones::processKernSpines(HumdrumFile& infile, int line, int start, int kspine) { + HTp token = infile.token(line, start); + if (!token->isKern()) { + return start; + } + int track = token->getTrack(); + vector toks; + toks.push_back(token); + for (int i=start+1; igetTrack(); + if (newtrack == track) { + toks.push_back(newtok); + continue; + } + break; + } - vector ktracks(starts.size()); - for (int i=0; i<(int)starts.size(); i++) { - ktracks.at(i) = starts.at(i)->getTrack(); + int toksize = (int)toks.size(); + + // calculate intervals/MIDI note numbers if appropriate + bool allQ = m_stepQ || m_leapQ || m_upQ || m_downQ || m_repeatQ; + bool dirQ = m_upQ || m_downQ; + bool typeQ = m_stepQ || m_leapQ; + vector intervals(toksize); + if (infile[line].isData()) { + for (int i=0; i 0) && (value < m_leap)) { + markInterval(toks[i]); + } else if (m_downQ && m_stepQ && (value < 0) && (value > -m_leap)) { + markInterval(toks[i]); + } else if (!dirQ && m_stepQ && (value != 0) && (abs(value) < m_leap)) { + markInterval(toks[i]); + + } else if (m_upQ && m_leapQ && (value > 0) && (value >= m_leap)) { + markInterval(toks[i]); + } else if (m_downQ && m_leapQ && (value < 0) && (value <= -m_leap)) { + markInterval(toks[i]); + } else if (!dirQ && m_leapQ && (value != 0) && (abs(value) >= m_leap)) { + markInterval(toks[i]); + + } else if (m_repeatQ && (value == 0)) { + markInterval(toks[i]); + } else if (!typeQ && m_upQ && (value > 0)) { + markInterval(toks[i]); + } else if (!typeQ && m_downQ && (value < 0)) { + markInterval(toks[i]); + } + } + } } - for (int i=0; icompare(0, 2, "**") == 0) { + if (m_cdataQ) { + printTokens("**cdata", toksize); + } else if (m_midiQ) { + if (m_pcQ) { + printTokens("**mpc", toksize); + } else { + printTokens("**mnn", toksize); + } + } else { + printTokens("**tti", toksize); + } + } else { + for (int i=0; i& ktracks) { - // First print all non-kern spines at the start of the line: - int nextIndex = 0; - int fcount = 0; - for (int i=0; iisKern()) { - break; - } - if (fcount > 0) { - m_humdrum_text << "\t"; - } - fcount++; - m_humdrum_text << "*"; - nextIndex++; +void Tool_semitones::markInterval(HTp token) { + if (!token->isData()) { + return; } - // Must be on the first **kern spine: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend on line " << index+1 << ": " << infile[index] << endl; + if (!token->isKern()) { return; } - // Print the first **kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; + if (token->isNull()) { + return; } - fcount++; - m_humdrum_text << "*"; - nextIndex++; - // Next print all non-kern spines after first **kern spine: - for (int i=nextIndex; iisKern()) { - break; - } - if (fcount > 0) { - m_humdrum_text << "\t"; - } - m_humdrum_text << "*"; - nextIndex++; + if (token->isRest()) { + return; } - // Second **kern spine must be **kern data: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend B on line " << index+1 << ": " << infile[index] << endl; + if (token->isUnpitched()) { return; } - // Ignore the second kern spine as it does not exist yet in the - // output data. - nextIndex++; - // Then store any non-kern spines between the second and third kern spines to - // append to the end of the data line later. - string postData; - for (int i=nextIndex; iisKern()) { - break; + m_markCount++; + token = markNote(token, m_firstQ); + if (m_firstQ && !m_secondQ) { + return; + } + // find next note + HTp current = token->getNextToken(); + while (current) { + if (!current->isData()) { + current = current->getNextToken(); + continue; } - if (!postData.empty()) { - postData += "\t"; + if (current->isNull()) { + current = current->getNextToken(); + continue; } - nextIndex++; - postData += "*"; + markNote(current, m_secondQ); + break; } - // Third kern spine must be **kern data: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend C on line " << index+1 << ": " << infile[index] << endl; - return; +} + + + +////////////////////////////// +// +// Tool_semitones::markNote -- make note and any tied notes after it. +// Return the last note of a tied note (or the note if no tied notes +// after it). +// + +HTp Tool_semitones::markNote(HTp token, bool markQ) { + string subtok = token->getSubtoken(0); + bool hasTieEnd = false; + if (subtok.find('_') != string::npos) { + hasTieEnd = true; + } else if (subtok.find(']') != string::npos) { + hasTieEnd = true; + } + + if (!(hasTieEnd && m_notiesQ)) { + if (markQ) { + addMarker(token); + } + } + + bool hasTie = false; + if (subtok.find('[') != string::npos) { + hasTie = true; + } else if (subtok.find('_') != string::npos) { + hasTie = true; } - // Now print the third kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; + + if (!hasTie) { + return token; } - fcount++; - nextIndex++; - m_humdrum_text << "*^"; - // Now print the non-kern spines after the third **kern spine (or rather just - // all spines including any other **kern spines, although current requirement - // is that there are only three **kern spines. - for (int i=nextIndex; i 0) { - m_humdrum_text << "\t"; + HTp current = token->getNextToken(); + while (current) { + if (!current->isData()) { + current = current->getNextToken(); + continue; } - fcount++; - nextIndex++; - m_humdrum_text << "*"; - } - // Finally print any non-kern spines after the second **kern spine: - if (!postData.empty()) { - if (fcount > 0) { - m_humdrum_text << "\t"; + if (current->isNull()) { + current = current->getNextToken(); + continue; } - fcount++; - m_humdrum_text << postData; + subtok = current->getSubtoken(0); + bool hasTie = false; + if (subtok.find('[') != string::npos) { + hasTie = true; + } else if (subtok.find('_') != string::npos) { + hasTie = true; + } + if (!hasTie) { + if (subtok.find(']') != string::npos) { + markNote(current, markQ); + } + return current; + } else { + return markNote(current, markQ); + } + break; } - m_humdrum_text << endl; + return NULL; } ////////////////////////////// // -// Tool_sab2gs::printSpineMerge -- Merge second and third spines, moving non-kern spines -// after the second one to the end of the line (null interpretations); +// Tool_semitones::addMarker -- // -void Tool_sab2gs::printSpineMerge(HumdrumFile& infile, int index, vector& ktracks) { - // First print all non-kern spines at the start of the line: - int nextIndex = 0; - int fcount = 0; - for (int i=0; iisKern()) { - break; - } - if (fcount > 0) { - m_humdrum_text << "\t"; - } - fcount++; - m_humdrum_text << "*"; - nextIndex++; - } - // Must be on the first **kern spine: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend on line " << index+1 << ": " << infile[index] << endl; - return; - } - // Print the first **kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; +void Tool_semitones::addMarker(HTp token) { + if (!m_nomarkQ) { + string contents = m_marker; + contents += token->getText(); + token->setText(contents); } - fcount++; - m_humdrum_text << "*"; - nextIndex++; - // Next print all non-kern spines after first **kern spine: - for (int i=nextIndex; iisKern()) { - break; - } - if (fcount > 0) { - m_humdrum_text << "\t"; +} + + + +////////////////////////////// +// +// Tool_semitones::printTokens -- +// + +void Tool_semitones::printTokens(const string& value, int count) { + for (int i=0; iisKern()) { - cerr << "Something strange happend B on line " << index+1 << ": " << infile[index] << endl; - return; +} + + + +/////////////////////////////// +// +// Tool_semitones::getTwelveToneIntervalString -- +// + +string Tool_semitones::getTwelveToneIntervalString(HTp token) { + if (token->isNull()) { + return "."; } - // Save the second kern spine as it does not exist yet in the - // output data. - // HTp savedKernToken = infile.token(index, nextIndex); - nextIndex++; - // Then store any non-kern spines between the second and third kern spines to - // append to the end of the data line later. - string postData; - for (int i=nextIndex; iisKern()) { - break; + if (token->isRest()) { + if (m_midiQ) { + return "r"; + } else { + return "."; } - if (!postData.empty()) { - postData += "\t"; + } + if (token->isUnpitched()) { + if (m_midiQ) { + return "R"; + } else { + return "."; } - nextIndex++; - postData += "*"; } - // Third kern spine must be **kern data: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend C on line " << index+1 << ": " << infile[index] << endl; - return; + if ((m_include.size() > 0) || (m_exclude.size() > 0)) { + int status = filterData(token); + if (status == 0) { + return "."; + } else if (status < 0) { + return "x"; // excluded note + } } - // Now print the third kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; + string tok = token->getSubtoken(0); + if (tok.find(']') != string::npos) { + return "."; } - fcount++; - m_humdrum_text << "*v"; - nextIndex++; - // Now printed the saved second **kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; + if (tok.find('_') != string::npos) { + return "."; } - m_humdrum_text << "*v"; - fcount++; - // Now print the non-kern spines after the third **kern spine (or rather just - // all spines including any other **kern spines, although current requirement - // is that there are only three **kern spines. - for (int i=nextIndex; i 0) { - m_humdrum_text << "\t"; + int value = Convert::kernToMidiNoteNumber(tok); + + if (m_midiQ) { + string output; + if (m_pcQ) { + value = value % 12; } - fcount++; - nextIndex++; - m_humdrum_text << "*"; + output = to_string(value); + return output; } - // Finally print any non-kern spines after the second **kern spine: - if (!postData.empty()) { - if (fcount > 0) { - m_humdrum_text << "\t"; - } - fcount++; - m_humdrum_text << postData; + + string nexttok = getNextNoteAttack(token); + if (nexttok.empty()) { + return "."; } - m_humdrum_text << endl; + if (nexttok.find('r') != string::npos) { + // no interval since next note is a rest + return "r"; + } + int value2 = Convert::kernToMidiNoteNumber(nexttok); + int interval = value2 - value; + string output = to_string(interval); + return output; } -////////////////////////////// +/////////////////////////////// // -// Tool_sab2gs::printSwappedLine -- move the second **kern spine immediately after -// the third one, and move any non-kern spines after then end of the line. +// Tool_semitones::getNextNoteAttack -- Or rest. // -void Tool_sab2gs::printSwappedLine(HumdrumFile& infile, int index, vector& ktracks) { - // First print all non-kern spines at the start of the line: - int nextIndex = 0; - int fcount = 0; - for (int i=0; iisKern()) { - break; +string Tool_semitones::getNextNoteAttack(HTp token) { + HTp current = token; + current = current->getNextToken(); + string tok; + while (current) { + if (!current->isData()) { + current = current->getNextToken(); + continue; } - if (fcount > 0) { - m_humdrum_text << "\t"; + if (current->isNull()) { + current = current->getNextToken(); + continue; } - fcount++; - m_humdrum_text << token; - nextIndex++; - } - // Must be on the first **kern spine: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend on line " << index+1 << ": " << infile[index] << endl; - return; - } - // Print the first **kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; - } - fcount++; - m_humdrum_text << infile.token(index, nextIndex); - nextIndex++; - // Next print all non-kern spines after first **kern spine: - for (int i=nextIndex; iisKern()) { - break; + if (current->isRest()) { + if (!m_norestsQ) { + return "r"; + } else { + current = current->getNextToken(); + continue; + } } - if (fcount > 0) { - m_humdrum_text << "\t"; + if (current->isUnpitched()) { + return "R"; } - m_humdrum_text << token; - nextIndex++; - } - // Second **kern spine must be **kern data: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend B on line " << index+1 << ": " << infile[index] << endl; - return; - } - // Save the second kern spine as it does not exist yet in the - // output data. - HTp savedKernToken = infile.token(index, nextIndex++); - // Then store any non-kern spines between the second and third kern spines to - // append to the end of the data line later. - string postData; - for (int i=nextIndex; iisKern()) { - break; + string tok = current->getSubtoken(0); + if (tok.find(']') != string::npos) { + current = current->getNextToken(); + continue; } - if (!postData.empty()) { - postData += "\t"; + if (tok.find('_') != string::npos) { + current = current->getNextToken(); + continue; } - nextIndex++; - postData += *token; - } - // Third kern spine must be **kern data: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend C on line " << index+1 << ": " << infile[index] << endl; - return; - } - // Now print the third kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; - } - fcount++; - m_humdrum_text << infile.token(index, nextIndex++); - // Now printed the saved second **kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; + return tok; } - m_humdrum_text << savedKernToken; - fcount++; - // Now print the non-kern spines after the third **kern spine (or rather just - // all spines including any other **kern spines, although current requirement - // is that there are only three **kern spines. - for (int i=nextIndex; i 0) { - m_humdrum_text << "\t"; - } - fcount++; - nextIndex++; - m_humdrum_text << token; + + if (!current) { + return ""; } - // Finally print any non-kern spines after the second **kern spine: - if (!postData.empty()) { - if (fcount > 0) { - m_humdrum_text << "\t"; - } - fcount++; - m_humdrum_text << postData; + if (!current->isData()) { + return ""; } - m_humdrum_text << endl; + // Some other strange problem. + return "."; } ////////////////////////////// // -// Tool_sab2gs::printReducedLine -- remove the contents of the second **kern -// spine, and move any non-kernspines after it to become after the third **kern spine +// Tool_semitones::filterData -- select or deselect an interval based +// on regular expression pattern. Return true if the note should +// be kept; otherwise, return false. // -void Tool_sab2gs::printReducedLine(HumdrumFile& infile, int index, vector& ktracks) { - // First print all non-kern spines at the start of the line: - int nextIndex = 0; - int fcount = 0; - for (int i=0; iisKern()) { - break; +int Tool_semitones::filterData(HTp token) { + vector toks = getTieGroup(token); + HumRegex hre; + if (!m_exclude.empty()) { + for (int i=0; i<(int)toks.size(); i++) { + if (hre.search(toks[i], m_exclude)) { + return -1; + } } - if (fcount > 0) { - m_humdrum_text << "\t"; + return 1; + } else if (!m_include.empty()) { + for (int i=0; i<(int)toks.size(); i++) { + if (hre.search(toks[i], m_include)) { + return 1; + } } - fcount++; - m_humdrum_text << token; - nextIndex++; + return 0; } - // Must be on the first **kern spine: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend on line " << index+1 << ": " << infile[index] << endl; - return; + return 0; +} + + + +////////////////////////////// +// +// Tool_semitones::getTieGroup -- +// + +vector Tool_semitones::getTieGroup(HTp token) { + vector output; + if (!token) { + return output; } - // Print the first **kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; + if (token->isNull()) { + return output; } - fcount++; - m_humdrum_text << infile.token(index, nextIndex++); - // Next print all non-kern spines after first **kern spine: - for (int i=nextIndex; iisKern()) { - break; - } - if (fcount > 0) { - m_humdrum_text << "\t"; - } - m_humdrum_text << token; - nextIndex++; + if (!token->isData()) { + return output; } - // Second **kern spine must be **kern data: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend B on line " << index+1 << ": " << infile[index] << endl; - return; + output.push_back(token); + if (token->isRest()) { + return output; } - // Ignore the second kern spine as it does not exist yet in the - // output data. - nextIndex++; - // Then store any non-kern spines between the second and third kern spines to - // append to the end of the data line later. - string postData; - for (int i=nextIndex; iisKern()) { + string subtok = token->getSubtoken(0); + bool continues = hasTieContinue(subtok); + HTp current = token; + while (continues) { + current = getNextNote(current); + if (!current) { break; } - if (!postData.empty()) { - postData += "\t"; - } - nextIndex++; - postData += *token; - } - // Third kern spine must be **kern data: - if (!infile.token(index, nextIndex)->isKern()) { - cerr << "Something strange happend C on line " << index+1 << ": " << infile[index] << endl; - return; - } - // Now print the third kern spine: - if (fcount > 0) { - m_humdrum_text << "\t"; - } - fcount++; - m_humdrum_text << infile.token(index, nextIndex++); - // Now print the non-kern spines after the third **kern spine (or rather just - // all spines including any other **kern spines, although current requirement - // is that there are only three **kern spines. - for (int i=nextIndex; i 0) { - m_humdrum_text << "\t"; - } - fcount++; - nextIndex++; - m_humdrum_text << token; - } - // Finally print any non-kern spines after the second **kern spine: - if (!postData.empty()) { - if (fcount > 0) { - m_humdrum_text << "\t"; + string subtok = current->getSubtoken(0); + if (subtok.find(']') != string::npos) { + output.push_back(current); + break; } - fcount++; - m_humdrum_text << postData; + continues = hasTieContinue(subtok); } - m_humdrum_text << endl; + return output; } + ////////////////////////////// // -// Tool_sab2gs::adjustMiddleVoice -- +// Tool_semitones::hasTieContinue -- // -void Tool_sab2gs::adjustMiddleVoice(HTp spineStart) { - HTp current = spineStart; - // staff: +1 = top staff, -1 = bottom staff - // when on top staff, force stem down, or on bottom staff, force stem up - // when on bottom staff add "<" marker after pitch (or rest) to move to - // bottom staff. Staff choice is selected by clef: clefG2 is for top staff - // and staffF4 is for bottom staff. Chords are not expected. - int staff = 0; - string replacement = "$1" + m_belowMarker; - HumRegex hre; +bool Tool_semitones::hasTieContinue(const string& value) { + if (value.find('_') != string::npos) { + return true; + } + if (value.find('[') != string::npos) { + return true; + } + return false; +} + + + +////////////////////////////// +// +// getNextNote -- +// + +HTp Tool_semitones::getNextNote(HTp token) { + HTp current = token->getNextToken(); while (current) { - if (*current == "*-") { - break; + if (!current->isData()) { + current = current->getNextToken(); + continue; } - if (!m_downQ && current->isClef()) { - if (current->substr(0, 7) == "*clefG2") { - staff = 1; - // suppress clef: - string text = "*x" + current->substr(1); - current->setText(text); - } else if (current->substr(0, 7) == "*clefF4") { - staff = -1; - // suppress clef: - string text = "*x" + current->substr(1); - current->setText(text); - } - } else if (current->isInterpretation()) { - if (*current == "*down") { - staff = -1; - } else if (*current == "*Xdown") { - staff = 1; - } - } else if ((staff != 0) && current->isData()) { - if (current->isNull()) { - // nothing to do with token - current = current->getNextToken(); - continue; - } - if (staff > 0) { - // force stems down or add stem down to non-rest notes - if (hre.search(current, "[/\\\\]")) { - string value = hre.replaceCopy(current, "\\", "/", "g"); - if (value != *current) { - current->setText(value); - } - current = current->getNextToken(); - continue; - } if (current->isRest()) { - current = current->getNextToken(); - continue; - } else { - string value = *current; - value += "\\"; - current->setText(value); - current = current->getNextToken(); - continue; - } - - } else if (staff < 0) { - // force stems up or add stem up to non-rest notes - if (hre.search(current, "[/\\\\]")) { - string value = hre.replaceCopy(current, "\\", "/", "g"); - if (value != *current) { - current->setText(value); - } - current = current->getNextToken(); - continue; - } if (current->isRest()) { - // Do not at stem direction to rests - } else { - // Force stem up (assuming not a chord, although it should not matter): - string value = hre.replaceCopy(current, "/", "$"); - if (value != *current) { - current->setText(value); - } - } - // Add < after pitch (and accidental and qualifiers) to display - // on staff below. - m_hasCrossStaff = true; - string output = hre.replaceCopy(current, replacement, "([A-Ga-gr]+[-#nXYxy]*)", "g"); - if (output != *current) { - current->setText(output); - } - } + if (current->isNull()) { + current = current->getNextToken(); + continue; } - current = current->getNextToken(); + break; } + return current; } + ///////////////////////////////// // -// Tool_satb2gs::Tool_satb2gs -- Set the recognized options for the tool. +// Tool_shed::Tool_shed -- Set the recognized options for the tool. // -Tool_satb2gs::Tool_satb2gs(void) { - // no options +Tool_shed::Tool_shed(void) { + define("s|spine|spines=s", "list of spines to process"); + define("e|expression=s", "regular expression"); + define("E|exclusion-expression=s", "regular expression to skip"); + define("x|exclusive-interpretations=s", "apply only to spine types in list"); + define("k|kern=b", "apply only to **kern data"); + define("X=s", "defineable exclusive interpretation x"); + define("Y=s", "defineable exclusive interpretation y"); + define("Z=s", "defineable exclusive interpretation z"); } ///////////////////////////////// // -// Tool_satb2gs::run -- Do the main work of the tool. +// Tool_shed::run -- Do the main work of the tool. // -bool Tool_satb2gs::run(HumdrumFileSet& infiles) { +bool Tool_shed::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; i extra = addToExInterpList(); + for (int i=0; i<(int)extra.size(); i++) { + m_exinterps.push_back(extra[i]); + } + } -void Tool_satb2gs::processFile(HumdrumFile& infile) { - vector> tracks; - getTrackInfo(tracks, infile); + m_search = m_searches.at(index); + m_replace = m_replaces.at(index); + m_option = m_options.at(index); - if ((tracks[1].size() != 2) || (tracks[3].size() != 2)) { - cerr << "Warning: not processing data since there must be at least four **kern spines" << endl; - return; + m_grepoptions = ""; + if (m_option.find("i") != std::string::npos) { + m_grepoptions += "i"; } - - bool goodHeader = validateHeader(infile); - if (!goodHeader) { - cerr << "Warning: no spine manipulations allows within header, not processing file" << endl; - return; + if (m_option.find("g") != std::string::npos) { + m_grepoptions += "g"; } - bool dataQ = false; - for (int i=0; i>& tracks) { + m_data = true; // process data + m_barline = false; // process barline + m_exinterp = false; // process exclusive interpretations + m_interpretation = false; // process interpretations (other than exinterp + // and spine manipulators). - int spinecount = infile[line].getFieldCount(); - int track; - HTp token; - vector>> tokens; - tokens.resize(5); - for (int i=0; i<(int)tracks.size(); i++) { - tokens[i].resize(tracks[i].size()); + if (m_option.find("I") != std::string::npos) { + m_interpretation = true; + m_data = false; } - - // store tokens in output order: - for (int i=0; i<(int)tracks.size(); i++) { - for (int j=0; j<(int)tracks[i].size(); j++) { - int target = tracks[i][j]; - for (int k=0; kgetTrack(); - if (track != target) { - continue; - } - tokens[i][j].push_back(token); - } - } + if (m_option.find("X") != std::string::npos) { + m_exinterp = true; + m_data = false; } - - int counter = 0; - HTp top; - HTp bot; - HTp inner; - HTp outer; - bool suppressQ; - - // now print in output order, but hide fermatas - // in the alto and tenor parts if there are fermatas - // int the soprano and bass parts respectively. - for (int i=0; i<(int)tokens.size(); i++) { - for (int j=0; j<(int)tokens[i].size(); j++) { - switch (i) { - case 0: - case 2: - case 4: - // non-kern spines - for (int k=0; k<(int)tokens[i][j].size(); k++) { - m_humdrum_text << tokens[i][j][k]; - counter++; - if (counter < spinecount) { - m_humdrum_text << "\t"; - } - } - break; - - case 1: - case 3: - top = tokens[i][0][0]; - bot = tokens[i][1][0]; - if (i == 1) { - // tenor: top is inner - inner = top; - outer = bot; - } else { - // alto: bottom is inner - inner = bot; - outer = top; - } - if (inner->hasFermata() && outer->hasFermata()) { - suppressQ = true; - } else { - suppressQ = false; - } - - for (int k=0; k<(int)tokens[i][j].size(); k++) { - token = tokens[i][j][k]; - if (suppressQ && ((void*)token == (void*)inner)) { - string value = *token; - // Make fermata invisible by adding 'y' after it: - for (int m=0; m<(int)value.size(); m++) { - m_humdrum_text << value[m]; - if (value[m] == ';') { - if (m < (int)value.size() - 1) { - if (value.at(m+1) != 'y') { - m_humdrum_text << 'y'; - } - } else { - m_humdrum_text << 'y'; - } - } - } - } else { - m_humdrum_text << token; - } - counter++; - if (counter < spinecount) { - m_humdrum_text << "\t"; - } - } - break; - } - } + if (m_option.find("B") != std::string::npos) { + m_barline = true; + m_data = false; + } + if (m_option.find("M") != std::string::npos) { + // measure is an alias for barline + m_barline = true; + m_data = false; + } + if (m_option.find("L") != std::string::npos) { + m_localcomment = true; + m_data = false; + } + if (m_option.find("G") != std::string::npos) { + m_globalcomment = true; + m_data = false; + } + if (m_option.find("K") != std::string::npos) { + m_referencekey = true; + m_data = false; + } + if (m_option.find("V") != std::string::npos) { + m_referencevalue = true; + m_data = false; + } + if (m_option.find("R") != std::string::npos) { + m_reference = true; + m_referencekey = false; + m_referencevalue = false; + m_data = false; + } + if (m_option.find("D") != std::string::npos) { + m_data = true; } - m_humdrum_text << endl; } ////////////////////////////// // -// Tool_satb2gs::printTerminatorLine -- Print the terminator line in the -// output data. +// Tool_shed::initialize -- Initializations that only have to be done once +// for all HumdrumFile segments. // -void Tool_satb2gs::printTerminatorLine(vector>& tracks) { - int count = getNewTrackCount(tracks); - for (int i=0; i>& tracks) { - int count = getNewTrackCount(tracks); - int counter = 0; - - for (int i=0; i<(int)tracks.size(); i++) { - switch (i) { - case 0: - case 2: - case 4: - for (int j=0; j<(int)tracks[i].size(); j++) { - m_humdrum_text << "*"; - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - } - break; - case 1: - case 3: - m_humdrum_text << "*^"; - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - break; - } +string Tool_shed::getExInterp(const string& value) { + if (value == "") { + return "**"; } - m_humdrum_text << endl; + if (value == "*") { + return "**"; + } + if (value.compare(0, 2, "**") == 0) { + return value; + } + if (value.compare(0, 1, "*") == 0) { + return "*" + value; + } + return "**" + value; } ////////////////////////////// // -// Tool_satb2gs::printSpineMergeLine -- +// Tool_shed::parseExpression -- +// Form of string: +// s/search/replace/options; s/search2/replace2/options2 +// // -void Tool_satb2gs::printSpineMergeLine(vector>& tracks) { - int count = getNewTrackCount(tracks); - count += 2; - int counter; - - if (!tracks[2].empty()) { - // do not need to place merges on separate lines since they are - // separated by non-kern spine(s) between bass and soprano subspines. +void Tool_shed::parseExpression(const string& expression) { + int state = 0; - counter = 0; - for (int i=0; i<(int)tracks.size(); i++) { - switch (i) { - case 0: - case 2: - case 4: - for (int j=0; j<(int)tracks[i].size(); j++) { - m_humdrum_text << "*"; - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - } - break; - case 1: - case 3: - for (int j=0; j<(int)tracks[i].size(); j++) { - m_humdrum_text << "*v"; - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - } - break; - } - } - m_humdrum_text << endl; + m_searches.clear(); + m_replaces.clear(); + m_options.clear(); - } else { - // Merges for tenor/bass and soprano/alto need to be placed - // on separate lines. + char divchar = '/'; - // First merge tenor/bass (tracks[1]) - counter = 0; - for (int i=0; i<(int)tracks.size(); i++) { - switch (i) { - case 0: - case 2: - case 3: - case 4: - for (int j=0; j<(int)tracks[i].size(); j++) { - m_humdrum_text << "*"; - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - } - break; - case 1: - for (int j=0; j<(int)tracks[i].size(); j++) { - m_humdrum_text << "*v"; - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - } - break; + for (int i=0; i<(int)expression.size(); i++) { + if (state == 0) { // start of expression + if (isspace(expression[i])) { + continue; + } else if (expression[i] == 's') { + if (i >= (int)expression.size() - 1) { + cerr << "Error: spurious s at end of expression: " + << expression << endl; + return; + } else { + divchar = expression[i+1]; + i++; + state++; + m_searches.push_back(""); + } + } else { + cerr << "Error at position " << i + << " in expression: " << expression << endl; + return; + } + } else if (state == 1) { // search string + if (expression[i] == divchar) { + state++; + m_replaces.push_back(""); + continue; + } if (expression[i] == '\\') { + if (i >= (int)expression.size() - 1) { + cerr << "Error: expression ends too soon: " + << expression << endl; + return; + } else { + m_searches.back() += '\\'; + m_searches.back() += expression[i+1]; + i++; + } + } else { + m_searches.back() += expression[i]; } - } - m_humdrum_text << endl; - - // Now merge soprano/alto (tracks[3]) - count--; - counter = 0; - for (int i=0; i<(int)tracks.size(); i++) { - switch (i) { - case 0: - case 2: - case 4: - for (int j=0; j<(int)tracks[i].size(); j++) { - m_humdrum_text << "*"; - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - } - break; - case 1: - m_humdrum_text << "*"; - m_humdrum_text << "\t"; - counter++; - break; - case 3: - for (int j=0; j<(int)tracks[i].size(); j++) { - m_humdrum_text << "*v"; - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - } - break; + } else if (state == 2) { // replace string + if (expression[i] == divchar) { + state++; + m_options.push_back(""); + continue; + } if (expression[i] == '\\') { + if (i >= (int)expression.size() - 1) { + cerr << "Error: expression ends too soon: " + << expression << endl; + return; + } else { + m_replaces.back() += '\\'; + m_replaces.back() += expression[i+1]; + i++; + } + } else { + m_replaces.back() += expression[i]; + } + } else if (state == 3) { // regular expression options + if (expression[i] == ';') { + state++; + } else if (isspace(expression[i])) { + state++; + } else { + m_options.back() += expression[i]; } } - m_humdrum_text << endl; + if (state == 4) { + state = 0; + } } } @@ -118966,222 +125373,96 @@ void Tool_satb2gs::printSpineMergeLine(vector>& tracks) { ////////////////////////////// // -// Tool_satb2gs::getNewTrackCount -- Return the number of tracks (spines) -// in the output data (not counting subspines). +// Tool_shed::initializeSegment -- Recalculate variables for each Humdrum +// input segment. // -int Tool_satb2gs::getNewTrackCount(vector>& tracks) { - int sum = 0; - for (int i=0; i<(int)tracks.size(); i++) { - for (int j=0; j<(int)tracks[i].size(); j++) { - sum++; - } +void Tool_shed::initializeSegment(HumdrumFile& infile) { + m_spines.clear(); + if (getBoolean("spines")) { + int maxtrack = infile.getMaxTrack(); + Convert::makeBooleanTrackList(m_spines, getString("spines"), maxtrack); } - // remove two spines that were merged into two others: - sum -= 2; - return sum; } ////////////////////////////// // -// Tool_satb2gs::printHeaderLine -- +// Tool_shed::addToExInterpList -- // -void Tool_satb2gs::printHeaderLine(HumdrumFile& infile, int line, - vector>& tracks) { - int count = infile.getMaxTrack() - 2; - - HTp token; - int counter = 0; - for (int i=0; i<(int)tracks.size(); i++) { - switch (i) { - case 0: - case 2: - case 4: - for (int j=0; j<(int)tracks[i].size(); j++) { - token = infile.token(line, tracks[i][j]-1); - m_humdrum_text << token; - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - } - break; +vector Tool_shed::addToExInterpList(void) { + string elist = getString("exclusive-interpretations"); + elist = Convert::trimWhiteSpace(elist); + HumRegex hre; + hre.replaceDestructive(elist, "", "^[,;\\s*]+"); + hre.replaceDestructive(elist, "", "[,;\\s*]+$"); + vector pieces; + hre.split(pieces, elist, "[,;\\s*]+"); - case 1: - case 3: - token = infile.token(line, tracks[i][0]-1); - if (token->isInstrumentName()) { - // suppress instrument names, but keep blank name - // to force indent. - m_humdrum_text << "*I\""; - } else if (token->isInstrumentAbbreviation()) { - // suppress instrument abbreviations - m_humdrum_text << "*"; - } else if (token->isInstrumentDesignation()) { - // suppress instrument designations (such as *Itenor) - m_humdrum_text << "*"; - } else if (token->isClef()) { - vector clefs = getClefs(infile, line); - if (i == 1) { - if (clefs.size() == 4) { - m_humdrum_text << clefs[0]; - } else { - m_humdrum_text << "*clefF4"; - } - } else { - if (clefs.size() == 4) { - m_humdrum_text << clefs.back(); - } else { - m_humdrum_text << "*clefG2"; - } - } - } else { - m_humdrum_text << token; - } - counter++; - if (counter < count) { - m_humdrum_text << "\t"; - } - break; + vector output; + for (int i=0; i<(int)pieces.size(); i++) { + if (pieces[i].empty()) { + continue; } + output.push_back("**" + pieces[i]); } - m_humdrum_text << endl; + return output; } ////////////////////////////// // -// Tool_satb2gs::getClefs -- get a list of the clefs on the current line. +// Tool_shed::processFile -- // -vector Tool_satb2gs::getClefs(HumdrumFile& infile, int line) { - vector output; - for (int i=0; iisKern()) { - continue; - } - if (token->isClef()) { - output.push_back(token); - } +void Tool_shed::processFile(HumdrumFile& infile) { + if (m_search == "") { + // nothing to do + return; } - return output; -} + m_modified = false; + if (m_interpretation) { + searchAndReplaceInterpretation(infile); + } + if (m_localcomment) { + searchAndReplaceLocalComment(infile); + } -////////////////////////////// -// -// Tool_satb2gs::getTrackInfo -- -// tracks 0 = list of spines before bass **kern spine -// tracks 1 = tenor and then bass **kern track numbers -// tracks 2 = aux. spines after after tenor and then after bass -// tracks 3 = soprano and then alto **kern track numbers -// tracks 4 = aux. spines after after soprano and then after alto -// + if (m_globalcomment) { + searchAndReplaceGlobalComment(infile); + } -void Tool_satb2gs::getTrackInfo(vector>& tracks, HumdrumFile& infile) { - tracks.resize(5); - for (int i=0; i<(int)tracks.size(); i++) { - tracks[i].clear(); + if (m_reference) { + searchAndReplaceReferenceRecords(infile); } - vector sstarts; - infile.getSpineStartList(sstarts); - int track; - // fill in tracks[0]: spines before first **kern spine - for (int i=0; i<(int)sstarts.size(); i++) { - if (sstarts[i]->isKern()) { - break; - } - track = sstarts[i]->getTrack(); - tracks[0].push_back(track); + if (m_referencekey) { + searchAndReplaceReferenceKeys(infile); } - int kcount = 0; + if (m_referencevalue) { + searchAndReplaceReferenceValues(infile); + } - kcount = 0; - // Store tracks related to the tenor part: - for (int i=0; i<(int)sstarts.size(); i++) { - if (sstarts[i]->isKern()) { - kcount++; - } - if (kcount > 2) { - break; - } - if (kcount < 2) { - continue; - } - track = sstarts[i]->getTrack(); - if (sstarts[i]->isKern()) { - tracks[1].push_back(track); - } else { - tracks[2].push_back(track); - } + if (m_exinterp) { + searchAndReplaceExinterp(infile); } - kcount = 0; - // Store tracks related to the bass part: - for (int i=0; i<(int)sstarts.size(); i++) { - if (sstarts[i]->isKern()) { - kcount++; - } - if (kcount > 1) { - break; - } - if (kcount < 1) { - continue; - } - track = sstarts[i]->getTrack(); - if (sstarts[i]->isKern()) { - tracks[1].push_back(track); - } else { - tracks[2].push_back(track); - } + if (m_barline) { + searchAndReplaceBarline(infile); } - kcount = 0; - // Store tracks related to the soprano part: - for (int i=0; i<(int)sstarts.size(); i++) { - if (sstarts[i]->isKern()) { - kcount++; - } - if (kcount > 4) { - break; - } - if (kcount < 4) { - continue; - } - track = sstarts[i]->getTrack(); - if (sstarts[i]->isKern()) { - tracks[3].push_back(track); - } else { - tracks[4].push_back(track); - } + if (m_data) { + searchAndReplaceData(infile); } - kcount = 0; - // Store tracks related to the alto part: - for (int i=0; i<(int)sstarts.size(); i++) { - if (sstarts[i]->isKern()) { - kcount++; - } - if (kcount > 3) { - break; - } - if (kcount < 3) { - continue; - } - track = sstarts[i]->getTrack(); - if (sstarts[i]->isKern()) { - tracks[3].push_back(track); - } else { - tracks[4].push_back(track); - } + if (m_modified) { + infile.createLinesFromTokens(); } } @@ -119189,163 +125470,189 @@ void Tool_satb2gs::getTrackInfo(vector>& tracks, HumdrumFile& infile ////////////////////////////// // -// Tool_satb2gs::validateHeader -- Header cannot contain -// spine manipulators. +// Tool_shed::searchAndReplaceBarline -- // -bool Tool_satb2gs::validateHeader(HumdrumFile& infile) { +void Tool_shed::searchAndReplaceBarline(HumdrumFile& infile) { + string isearch; + if (m_search[0] == '^') { + isearch = "^=" + m_search.substr(1); + } else { + isearch = "^=.*" + m_search; + } + HumRegex hre; for (int i=0; iisExclusive()) { + if (!infile[i].isBarline()) { continue; } - if (infile[i].isManipulator()) { - return false; + for (int j=0; jisNull()) { + // Don't mess with null interpretations + continue; + } + if (!isValid(token)) { + continue; + } + if (hre.search(token, isearch, m_grepoptions)) { + string text = token->getText().substr(1); + hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); + hre.replaceDestructive(text, "", "^=+"); + text = "=" + text; + token->setText(text); + m_modified = true; + } } } - - return true; } - - -///////////////////////////////// +////////////////////////////// // -// Tool_scordatura::Tool_scordatura -- Set the recognized options for the tool. +// Tool_shed::searchAndReplaceInterpretation -- // -Tool_scordatura::Tool_scordatura(void) { - define("s|sounding=b", "generate sounding score"); - define("w|written=b", "generate written score"); - define("m|mark|marker=s:@", "marker to add to score"); - define("p|pitch|pitches=s", "list of pitches to mark"); - define("i|interval=s", "musical interval of marked pitches"); - define("I|is-sounding=s", "musical score is in sounding format for marks"); - define("c|chromatic=i:0", "chromatic interval of marked pitches"); - define("d|diatonic=i:0", "diatonic interval of marked pitches"); - define("color=s", "color marked pitches"); - define("string=s", "string number"); +void Tool_shed::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().substr(1); + hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); + hre.replaceDestructive(text, "", "^\\*+"); + text = "*" + text; + token->setText(text); + m_modified = true; + } + } + } } -///////////////////////////////// +////////////////////////////// // -// Tool_scordatura::run -- Do the main work of the tool. +// Tool_shed::searchAndReplaceLocalComment -- // -bool Tool_scordatura::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().substr(1); + hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); + hre.replaceDestructive(text, "", "^!+"); + text = "!" + text; + token->setText(text); + m_modified = true; + } + } } - return status; -} - - -bool Tool_scordatura::run(HumdrumFile& infile) { - initialize(); - processFile(infile); - return true; } ////////////////////////////// // -// Tool_scordatura::initialize -- Initializations that only have to be done once -// for all HumdrumFile segments. +// Tool_shed::searchAndReplaceGlobalComment -- // -void Tool_scordatura::initialize(void) { - m_writtenQ = getBoolean("written"); - m_soundingQ = getBoolean("sounding"); - m_pitches.clear(); - m_marker = getString("mark"); - m_IQ = getBoolean("I"); - m_color = getString("color"); - if (getBoolean("pitches")) { - m_pitches = parsePitches(getString("pitches")); - } - m_cd = getBoolean("diatonic") && getBoolean("chromatic"); - m_interval.clear(); - if (m_cd) { - m_diatonic = getInteger("diatonic"); - m_chromatic = getInteger("chromatic"); +void Tool_shed::searchAndReplaceGlobalComment(HumdrumFile& infile) { + string isearch; + if (m_search[0] == '^') { + isearch = "^!!" + m_search.substr(1); } else { - if (getBoolean("interval")) { - m_interval = getString("interval"); - } - } - if ((abs(m_diatonic) > 28) || (abs(m_chromatic) > 48)) { - m_diatonic = 0; - m_chromatic = 0; - m_cd = false; + isearch = "^!!.*" + m_search; } - if (!m_pitches.empty()) { - prepareTranspositionInterval(); + HumRegex hre; + for (int i=0; isize() < 3) { + // Don't mess with null comments + continue; + } + if (hre.search(token, isearch, m_grepoptions)) { + string text = token->getText().substr(2); + hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); + hre.replaceDestructive(text, "", "^!+"); + text = "!!" + text; + token->setText(text); + m_modified = true; + } } - m_string = getString("string"); } ////////////////////////////// // -// Tool_scordatura::processFile -- +// Tool_shed::searchAndReplaceReferenceRecords -- // -void Tool_scordatura::processFile(HumdrumFile& infile) { - m_modifiedQ = false; - - if (!m_pitches.empty()) { - markPitches(infile); - if (m_modifiedQ) { - addMarkerRdf(infile); - } +void Tool_shed::searchAndReplaceReferenceRecords(HumdrumFile& infile) { + string isearch; + if (m_search[0] == '^') { + isearch = "^!!!" + m_search.substr(1); + } else { + isearch = "^!!!.*" + m_search; } - - if (m_writtenQ || m_soundingQ) { - vector rdfs; - getScordaturaRdfs(rdfs, infile); - if (!rdfs.empty()) { - processScordaturas(infile, rdfs); + HumRegex hre; + for (int i=0; igetText().substr(1); + hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); + hre.replaceDestructive(text, "", "^!+"); + text = "!!!" + text; + token->setText(text); + m_modified = true; } - } - - if (m_modifiedQ) { - infile.createLinesFromTokens(); } } @@ -119353,12 +125660,27 @@ void Tool_scordatura::processFile(HumdrumFile& infile) { ////////////////////////////// // -// Tool_scordatura::processScoredaturas -- +// Tool_shed::searchAndReplaceReferenceKeys -- // -void Tool_scordatura::processScordaturas(HumdrumFile& infile, vector& rdfs) { - for (int i=0; i<(int)rdfs.size(); i++) { - processScordatura(infile, rdfs[i]); +void Tool_shed::searchAndReplaceReferenceKeys(HumdrumFile& infile) { + string isearch = m_search; + HumRegex hre; + for (int i=0; isetText(text); + m_modified = true; + } } } @@ -119366,52 +125688,70 @@ void Tool_scordatura::processScordaturas(HumdrumFile& infile, vector& rdfs) ////////////////////////////// // -// Tool_scordatura::processScordatura -- +// Tool_shed::searchAndReplaceReferenceValues -- // -void Tool_scordatura::processScordatura(HumdrumFile& infile, HTp reference) { +void Tool_shed::searchAndReplaceReferenceValues(HumdrumFile& infile) { + string isearch = m_search; HumRegex hre; - - if (m_writtenQ) { - if (!hre.search(reference, "^!!!RDF\\*\\*kern\\s*:\\s*([^\\s]+)\\s*=.*\\bscordatura\\s*=\\s*[\"']?\\s*ITrd(-?\\d+)c(-?\\d+)\\b")) { - return; + for (int i=0; isetText(text); + m_modified = true; } } - - string marker = hre.getMatch(1); - int diatonic = hre.getMatchInt(2); - int chromatic = hre.getMatchInt(3); - - if (diatonic == 0 && chromatic == 0) { - // nothing to do - return; - } - - flipScordaturaInfo(reference, diatonic, chromatic); - transposeMarker(infile, marker, diatonic, chromatic); } ////////////////////////////// // -// Tool_scordatura::transposeMarker -- +// Tool_shed::searchAndReplaceExinterp -- // - -void Tool_scordatura::transposeMarker(HumdrumFile& infile, const string& marker, int diatonic, int chromatic) { - m_transposer.setTranspositionDC(diatonic, chromatic); - for (int i=0; iisKern()) { +void Tool_shed::searchAndReplaceExinterp(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().substr(2); + hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); + hre.replaceDestructive(text, "", "^\\*+"); + text = "**" + text; + token->setText(text); + m_modified = true; + } + } } } @@ -119419,24 +125759,36 @@ void Tool_scordatura::transposeMarker(HumdrumFile& infile, const string& marker, ////////////////////////////// // -// Tool_scordatura::transposeStrand -- +// Tool_shed::searchAndReplaceData -- // -void Tool_scordatura::transposeStrand(HTp sstart, HTp sstop, const string& marker) { - HTp current = sstart; - while (current && current != sstop) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull() || current->isRest()) { - current = current->getNextToken(); +void Tool_shed::searchAndReplaceData(HumdrumFile& infile) { + string dsearch = m_search; + + HumRegex hre; + for (int i=0; ifind(marker) != string::npos) { - transposeChord(current, marker); + for (int j=0; jisNull()) { + // 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; + } } - current = current->getNextToken(); } } @@ -119444,221 +125796,211 @@ void Tool_scordatura::transposeStrand(HTp sstart, HTp sstop, const string& marke ////////////////////////////// // -// Tool_scordatura::transposeChord -- +// Tool_shed::isValidDataType -- usar with -x and -k options. // -void Tool_scordatura::transposeChord(HTp token, const string& marker) { - int scount = token->getSubtokenCount(); - if (scount == 1) { - string inputnote = *token; - string newtoken; - newtoken = transposeNote(inputnote); - token->setText(newtoken); - return; - } - vector subtokens; - subtokens = token->getSubtokens(); - for (int i=0; i<(int)subtokens.size(); i++) { - if (subtokens[i].find(marker) == string::npos) { - continue; - } - string newtoken = transposeNote(subtokens[i]); - subtokens[i] = newtoken; +bool Tool_shed::isValidDataType(HTp token) { + if (m_exinterps.empty()) { + return true; } - string newchord; - for (int i=0; i<(int)subtokens.size(); i++) { - newchord += subtokens[i]; - if (i<(int)subtokens.size() - 1) { - newchord += ' '; + string datatype = token->getDataType(); + for (int i=0; i<(int)m_exinterps.size(); i++) { + if (datatype == m_exinterps[i]) { + return true; } } - token->setText(newchord); + return false; } ////////////////////////////// // -// Tool_scordatura::transposeNote -- +// Tool_shed::isValidSpine -- used with -s option. // -string Tool_scordatura::transposeNote(const string& note) { - HumRegex hre; - if (!hre.search(note, "(.*?)([A-Ga-g]+[-#]*)(.*)")) { - return note; +bool Tool_shed::isValidSpine(HTp token) { + if (m_spines.empty()) { + return true; } - string pre = hre.getMatch(1); - string pitch = hre.getMatch(2); - string post = hre.getMatch(3); - HumPitch hpitch; - hpitch.setKernPitch(pitch); - m_transposer.transpose(hpitch); - string output; - output = pre; - output += hpitch.getKernPitch(); - output += post; - return output; + int track = token->getTrack(); + return m_spines.at(track); } ////////////////////////////// // -// Tool_scordatura::flipScordaturaInfo -- +// Tool_shed::isValid -- // -void Tool_scordatura::flipScordaturaInfo(HTp reference, int diatonic, int chromatic) { - diatonic *= -1; - chromatic *= -1; - string output; - if (m_writtenQ) { - output = "Trd"; - output += to_string(diatonic); - output += "c"; - output += to_string(chromatic); - } else if (m_soundingQ) { - output = "ITrd"; - output += to_string(diatonic); - output += "c"; - output += to_string(chromatic); - } else { - return; +bool Tool_shed::isValid(HTp token) { + if (!m_exclusion.empty()) { + HumRegex hre; + if (hre.search(token, m_exclusion)) { + return false; + } } - HumRegex hre; - string token = *reference; - hre.replaceDestructive(token, output, "I?Trd-?\\dc-?\\d"); - if (token != *reference) { - m_modifiedQ = true; - reference->setText(token); + if (isValidDataType(token) && isValidSpine(token)) { + return true; } + return false; } -////////////////////////////// + + +///////////////////////////////// // -// Tool_scordatura::getScoredaturaRdfs -- +// Tool_sic::Tool_sic -- Set the recognized options for the tool. // -void Tool_scordatura::getScordaturaRdfs(vector& rdfs, HumdrumFile& infile) { - rdfs.clear(); - HumRegex hre; - for (int i=0; i Tool_scordatura::parsePitches(const string& input) { - HumRegex hre; - string value = input; - hre.replaceDestructive(value, "-", "\\s*-\\s*", "g"); +void Tool_sic::initialize(void) { + m_substituteQ = getBoolean("substitution"); + m_originalQ = getBoolean("original"); + m_removeQ = getBoolean("remove"); + m_verboseQ = getBoolean("verbose"); + m_quietQ = getBoolean("quiet"); +} - vector pieces; - hre.split(pieces, value, "[^A-Ga-g0-9-]+"); - HumPitch pitcher; - set output; - string p1; - string p2; - int d1; - int d2; - for (int i=0; i<(int)pieces.size(); i++) { - if (hre.search(pieces[i], "(.*)-(.*)")) { - // pitch range - p1 = hre.getMatch(1); - p2 = hre.getMatch(2); - d1 = Convert::kernToBase7(p1); - d2 = Convert::kernToBase7(p2); - if ((d1 < 0) || (d2 < 0) || (d1 > d2) || (d1 > 127) || (d2 > 127)) { + +////////////////////////////// +// +// Tool_sic::processFile -- +// + +void Tool_sic::processFile(HumdrumFile& infile) { + for (int i=0; icompare(0, 8, "!LO:SIC:") != 0) { continue; } - for (int j=d1; j<=d2; j++) { - output.insert(j); + if (m_verboseQ) { + addVerboseParameter(token); + } else if (m_quietQ) { + removeVerboseParameter(token); } - } else { - // single pitch - d1 = Convert::kernToBase7(pieces[i]); - if ((d1 < 0) || (d1 > 127)) { - continue; + if (m_removeQ) { + token->setText("!"); + m_modifiedQ = true; + } else if (m_substituteQ) { + insertSubstitutionToken(token); + } else if (m_originalQ) { + insertOriginalToken(token); } - output.insert(d1); } } - return output; + if (m_modifiedQ) { + infile.createLinesFromTokens(); + } + m_humdrum_text << infile; } ////////////////////////////// // -// Tool_scordatura::markPitches -- +// Tool_sic::addVerboseParameter -- // -void Tool_scordatura::markPitches(HumdrumFile& infile) { - for (int i=0; iisKern()) { - continue; - } - HTp sstop = infile.getStrandStop(i); - markPitches(sstart, sstop); +void Tool_sic::addVerboseParameter(HTp token) { + HumRegex hre; + string value = token->getText(); + if (hre.search(value, "(:v:)|(:v$)")) { + return; } + string newvalue = value + ":v"; + token->setText(newvalue); + m_modifiedQ = true; } -void Tool_scordatura::markPitches(HTp sstart, HTp sstop) { - HTp current = sstart; - while (current && (current != sstop)) { - if (current->isNull() || current->isRest()) { - current = current->getNextToken(); - continue; - } - markPitches(current); - current = current->getNextToken(); - } -} +////////////////////////////// +// +// Tool_sic::removeVerboseParameter -- +// -void Tool_scordatura::markPitches(HTp token) { - vector subtokens = token->getSubtokens(); - int counter = 0; - for (int i=0; i<(int)subtokens.size(); i++) { - int dia = Convert::kernToBase7(subtokens[i]); - if (m_pitches.find(dia) != m_pitches.end()) { - counter++; - subtokens[i] += m_marker; - } - } - if (counter == 0) { +void Tool_sic::removeVerboseParameter(HTp token) { + HumRegex hre; + string value = token->getText(); + string newvalue = value; + hre.replaceDestructive(newvalue, ":", ":v:", "g"); + hre.replaceDestructive(newvalue, "", ":v$", ""); + if (value == newvalue) { return; } - string newtoken; - for (int i=0; i<(int)subtokens.size(); i++) { - newtoken += subtokens[i]; - if (i < (int)subtokens.size() - 1) { - newtoken += ' '; - } - } - token->setText(newtoken); + token->setText(newvalue); m_modifiedQ = true; } @@ -119666,1568 +126008,1173 @@ void Tool_scordatura::markPitches(HTp token) { ////////////////////////////// // -// Tool_scordatura::addMarkerRdf -- +// Tool_sic::getTargetToken -- Get the token that the layout command +// applies to. // -void Tool_scordatura::addMarkerRdf(HumdrumFile& infile) { - string line = "!!!RDF**kern: "; - line += m_marker; - line += " = "; - if (!m_string.empty()) { - line += "string="; - line += m_string; - line += " "; - } - line += "scordatura="; - if (m_IQ) { - line += "I"; - } - line += "Tr"; - if (m_transposition.empty()) { - line += "XXX"; - } else { - line += m_transposition; +HTp Tool_sic::getTargetToken(HTp stok) { + HTp current = stok->getNextToken(); + while (current) { + if (current->isNull()) { + current = current->getNextToken(); + continue; + } + if (current->isManipulator()) { + // Layout commands should not apply to manipulators nor be split + // from their associated token. + current = NULL; + break; + } + if (current->isCommentLocal()) { + current = current->getNextToken(); + continue; + } + break; } - if (!m_color.empty()) { - line += ", color="; - line += m_color; + if (!current) { + return NULL; } - infile.appendLine(line); - m_modifiedQ = true; + return current; } ////////////////////////////// // -// Tool_scordatura::prepareTranspositionInterval -- +// Tool_sic::insertSubstitutionToken -- // -void Tool_scordatura::prepareTranspositionInterval(void) { - m_transposition.clear(); - if (m_cd) { - m_transposition = "d"; - m_transposition += to_string(m_diatonic); - m_transposition += "c"; - m_transposition += to_string(m_chromatic); +void Tool_sic::insertSubstitutionToken(HTp sictok) { + HTp target = getTargetToken(sictok); + if (!target) { return; } - - if (m_interval.empty()) { + HumRegex hre; + vector pieces; + hre.split(pieces, *sictok, ":"); + string tstring = target->getText(); + string sstring; + for (int i=2; i<(int)pieces.size(); i++) { + if (pieces[i].compare(0, 2, "s=") == 0) { + sstring = pieces[i].substr(2); + } + } + if (sstring.empty()) { return; } - - HumTransposer trans; - trans.intervalToDiatonicChromatic(m_diatonic, m_chromatic, m_interval); - m_transposition = "d"; - m_transposition += to_string(m_diatonic); - m_transposition += "c"; - m_transposition += to_string(m_chromatic); + target->setText(sstring); + m_modifiedQ = true; + string newsic = "!LO:SIC"; + for (int i=2; i<(int)pieces.size(); i++) { + if (pieces[i].compare(0, 2, "s=") == 0) { + newsic += ":o=" + tstring; + } else { + newsic += ":" + pieces[i]; + } + } + sictok->setText(newsic); + m_modifiedQ = true; } - - -///////////////////////////////// +////////////////////////////// // -// Tool_semitones::Tool_semitones -- Set the recognized options for the tool. +// Tool_sic::insertOriginalToken -- // -Tool_semitones::Tool_semitones(void) { - define("1|first=b", "mark only the first note of intervals"); - define("2|second=b", "mark only the second note of intervals"); - define("A|O|no-analysis|no-output=b", "do not print analysis spines"); - define("I|no-input=b", "do not print input data spines"); - define("M|no-mark|no-marks=b", "do not mark notes"); - define("R|no-rests=b", "ignore rests"); - define("T|no-ties=b", "do not mark ties"); - define("X|include|only=s", "include only **kern tokens with given pattern"); - define("color=s:red", "mark color"); - define("c|cdata=b", "store resulting data as **cdata (allowing display in VHV"); - define("d|down=b", "highlight notes that that have a negative semitone interval"); - define("j|jump=i:3", "starting interval defining leaps"); - define("l|leap=b", "highlight notes that have leap motion"); - define("mark=s:@", "mark character"); - define("m|midi=b", "show MIDI note number for pitches"); - define("n|count=b", "output count of intervals being marked"); - define("p|pc=b", "output pitch classes from C=0 instead of MIDI notes for -m option"); - define("r|same|repeat|repeated=b", "highlight notes that are repeated "); - define("s|step=b", "highlight notes that have step-wise motion"); - define("u|up=b", "highlight notes that that have a positive semitone interval"); - define("x|exclude=s", "exclude **kern tokens with given pattern"); +void Tool_sic::insertOriginalToken(HTp sictok) { + HTp target = getTargetToken(sictok); + if (!target) { + return; + } + HumRegex hre; + vector pieces; + hre.split(pieces, *sictok, ":"); + string tstring = target->getText(); + string sstring; + for (int i=2; i<(int)pieces.size(); i++) { + if (pieces[i].compare(0, 2, "o=") == 0) { + sstring = pieces[i].substr(2); + } + } + if (sstring.empty()) { + return; + } + target->setText(sstring); + m_modifiedQ = true; + string newsic = "!LO:SIC"; + for (int i=2; i<(int)pieces.size(); i++) { + if (pieces[i].compare(0, 2, "o=") == 0) { + newsic += ":s=" + tstring; + } else { + newsic += ":" + pieces[i]; + } + } + sictok->setText(newsic); + m_modifiedQ = true; } -///////////////////////////////// + + +////////////////////////////// // -// Tool_semitones::run -- Do the main work of the tool. +// MeasureData::MeasureData -- // -bool Tool_semitones::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i 0) && !m_nomarkQ) { - m_humdrum_text << "!!!RDF**kern: "; - m_humdrum_text << m_marker; - m_humdrum_text << " = marked note"; - if (getBoolean("color")) { - m_humdrum_text << ", color=" << m_color; - } - m_humdrum_text << '\n'; - } - if (m_count) { - showCount(); - } +void MeasureData::setStartLine(int startline) { + m_startline = startline; } ////////////////////////////// // -// Tool_semitones::showCount -- Give a count for the number of -// intervals that were marked. +// MeasureData::setStopLine -- // -void Tool_semitones::showCount(void) { - m_humdrum_text << "!!semitone_count: " << m_markCount; - if (m_repeatQ) { - m_humdrum_text << " REPEAT"; - } - if (m_upQ) { - m_humdrum_text << " UP"; - } - if (m_downQ) { - m_humdrum_text << " DOWN"; - } - if (m_stepQ) { - m_humdrum_text << " STEP"; - } - if (m_leapQ) { - m_humdrum_text << " LEAP"; - } - if ((m_stepQ || m_leapQ) && (m_leap != 3)) { - m_humdrum_text << " JUMP:" << m_leap; - } - if (m_marker != "@") { - m_humdrum_text << " MARK:" << m_marker; - } - m_humdrum_text << '\n'; +void MeasureData::setStopLine(int stopline) { + m_stopline = stopline; } ////////////////////////////// // -// Tool_semitones::analyzeLine -- Append analysis spines after every **kern -// spine. +// MeasureData::getStartLine -- // -void Tool_semitones::analyzeLine(HumdrumFile& infile, int line) { - int group = 0; - if (!infile[line].hasSpines()) { - m_humdrum_text << infile[line] << "\n"; - return; - } - for (int i=0; iisKern()) { - m_humdrum_text << token; - if (i < infile[line].getFieldCount() - 1) { - m_humdrum_text << '\t'; - } - continue; - } - } - i = processKernSpines(infile, line, i, group++); - if (!m_noinputQ) { - if (i < infile[line].getFieldCount() - 1) { - m_humdrum_text << '\t'; - } - } - } - m_humdrum_text << '\n'; +int MeasureData::getStartLine(void) { + return m_startline; } ////////////////////////////// // -// Tool_semitones::processKernSpine -- +// MeasureData::getStopLine -- // -int Tool_semitones::processKernSpines(HumdrumFile& infile, int line, int start, int kspine) { - HTp token = infile.token(line, start); - if (!token->isKern()) { - return start; - } - int track = token->getTrack(); - vector toks; - toks.push_back(token); - for (int i=start+1; igetTrack(); - if (newtrack == track) { - toks.push_back(newtok); - continue; - } - break; - } - - int toksize = (int)toks.size(); - - // calculate intervals/MIDI note numbers if appropriate - bool allQ = m_stepQ || m_leapQ || m_upQ || m_downQ || m_repeatQ; - bool dirQ = m_upQ || m_downQ; - bool typeQ = m_stepQ || m_leapQ; - vector intervals(toksize); - if (infile[line].isData()) { - for (int i=0; i 0) && (value < m_leap)) { - markInterval(toks[i]); - } else if (m_downQ && m_stepQ && (value < 0) && (value > -m_leap)) { - markInterval(toks[i]); - } else if (!dirQ && m_stepQ && (value != 0) && (abs(value) < m_leap)) { - markInterval(toks[i]); - - } else if (m_upQ && m_leapQ && (value > 0) && (value >= m_leap)) { - markInterval(toks[i]); - } else if (m_downQ && m_leapQ && (value < 0) && (value <= -m_leap)) { - markInterval(toks[i]); - } else if (!dirQ && m_leapQ && (value != 0) && (abs(value) >= m_leap)) { - markInterval(toks[i]); - - } else if (m_repeatQ && (value == 0)) { - markInterval(toks[i]); - } else if (!typeQ && m_upQ && (value > 0)) { - markInterval(toks[i]); - } else if (!typeQ && m_downQ && (value < 0)) { - markInterval(toks[i]); - } - } - } - } - - // print the **kern fields - if (!m_noinputQ) { - for (int i=0; icompare(0, 2, "**") == 0) { - if (m_cdataQ) { - printTokens("**cdata", toksize); - } else if (m_midiQ) { - if (m_pcQ) { - printTokens("**mpc", toksize); - } else { - printTokens("**mnn", toksize); - } - } else { - printTokens("**tti", toksize); - } - } else { - for (int i=0; iisData()) { - return; - } - if (!token->isKern()) { - return; - } - if (token->isNull()) { - return; - } - if (token->isRest()) { - return; - } - if (token->isUnpitched()) { - return; - } - m_markCount++; - token = markNote(token, m_firstQ); - if (m_firstQ && !m_secondQ) { - return; +double MeasureData::getStartTime(void) { + if (m_owner == NULL) { + return 0.0; } - // find next note - HTp current = token->getNextToken(); - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); - continue; - } - markNote(current, m_secondQ); - break; + if (getStartLine() < 0) { + return 0.0; } + return (*m_owner)[getStartLine()].getDurationFromStart().getFloat(); } ////////////////////////////// // -// Tool_semitones::markNote -- make note and any tied notes after it. -// Return the last note of a tied note (or the note if no tied notes -// after it). +// MeasureData::getMeasure -- return the measure number of the measure. +// return -1 if no measure number. // -HTp Tool_semitones::markNote(HTp token, bool markQ) { - string subtok = token->getSubtoken(0); - bool hasTieEnd = false; - if (subtok.find('_') != string::npos) { - hasTieEnd = true; - } else if (subtok.find(']') != string::npos) { - hasTieEnd = true; - } - - if (!(hasTieEnd && m_notiesQ)) { - if (markQ) { - addMarker(token); - } +int MeasureData::getMeasure(void) { + if (m_owner == NULL) { + return -1; } - - bool hasTie = false; - if (subtok.find('[') != string::npos) { - hasTie = true; - } else if (subtok.find('_') != string::npos) { - hasTie = true; + if (getStartLine() < 0) { + return -1; } - - if (!hasTie) { - return token; + HumdrumFile& infile = *m_owner; + if (!infile[getStartLine()].isBarline()) { + return -1; } - HTp current = token->getNextToken(); - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); - continue; - } - subtok = current->getSubtoken(0); - bool hasTie = false; - if (subtok.find('[') != string::npos) { - hasTie = true; - } else if (subtok.find('_') != string::npos) { - hasTie = true; - } - if (!hasTie) { - if (subtok.find(']') != string::npos) { - markNote(current, markQ); - } - return current; - } else { - return markNote(current, markQ); - } - break; + HumRegex hre; + if (hre.search(infile.token(getStartLine(), 0), "(\\d+)")) { + return hre.getMatchInt(1); + } else { + return -1; } - return NULL; } ////////////////////////////// // -// Tool_semitones::addMarker -- +// MeasureData::getQon -- return the start time class id of the measure. // -void Tool_semitones::addMarker(HTp token) { - if (!m_nomarkQ) { - string contents = m_marker; - contents += token->getText(); - token->setText(contents); +std::string MeasureData::getQon(void) { + if (m_owner == NULL) { + return ""; } + if (getStartLine() < 0) { + return ""; + } + HumdrumFile& infile = *m_owner; + HumNum ts = infile[getStartLine()].getDurationFromStart(); + string output = "qon" + to_string(ts.getNumerator()); + if (ts.getDenominator() != 1) { + output += "-" + to_string(ts.getDenominator()); + } + return output; } ////////////////////////////// // -// Tool_semitones::printTokens -- +// MeasureData::getQoff -- return the end time class id of the measure. // -void Tool_semitones::printTokens(const string& value, int count) { - for (int i=0; iisNull()) { - return "."; - } - if (token->isRest()) { - if (m_midiQ) { - return "r"; - } else { - return "."; - } - } - if (token->isUnpitched()) { - if (m_midiQ) { - return "R"; - } else { - return "."; - } - } - if ((m_include.size() > 0) || (m_exclude.size() > 0)) { - int status = filterData(token); - if (status == 0) { - return "."; - } else if (status < 0) { - return "x"; // excluded note - } - } - string tok = token->getSubtoken(0); - if (tok.find(']') != string::npos) { - return "."; +double MeasureData::getStopTime(void) { + if (m_owner == NULL) { + return 0.0; } - if (tok.find('_') != string::npos) { - return "."; + if (getStopLine() < 0) { + return 0.0; } - int value = Convert::kernToMidiNoteNumber(tok); + return (*m_owner)[getStopLine()].getDurationFromStart().getFloat(); +} - if (m_midiQ) { - string output; - if (m_pcQ) { - value = value % 12; - } - output = to_string(value); - return output; - } - string nexttok = getNextNoteAttack(token); - if (nexttok.empty()) { - return "."; - } - if (nexttok.find('r') != string::npos) { - // no interval since next note is a rest - return "r"; - } - int value2 = Convert::kernToMidiNoteNumber(nexttok); - int interval = value2 - value; - string output = to_string(interval); - return output; + +////////////////////////////// +// +// MeasureData::getDuration -- return the duration of the measure +// int quarter notes +// + +double MeasureData::getDuration(void) { + return getStopTime() - getStartTime(); } -/////////////////////////////// +////////////////////////////// // -// Tool_semitones::getNextNoteAttack -- Or rest. +// MeasureData::getScoreDuration -- // -string Tool_semitones::getNextNoteAttack(HTp token) { - HTp current = token; - current = current->getNextToken(); - string tok; - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); - continue; - } - if (current->isRest()) { - if (!m_norestsQ) { - return "r"; - } else { - current = current->getNextToken(); - continue; - } - } - if (current->isUnpitched()) { - return "R"; - } - string tok = current->getSubtoken(0); - if (tok.find(']') != string::npos) { - current = current->getNextToken(); - continue; - } - if (tok.find('_') != string::npos) { - current = current->getNextToken(); - continue; - } - return tok; +double MeasureData::getScoreDuration(void) { + if (m_owner == NULL) { + return 0.0; } + return m_owner->getScoreDuration().getFloat(); +} - if (!current) { - return ""; - } - if (!current->isData()) { - return ""; - } - // Some other strange problem. - return "."; + + +////////////////////////////// +// +// MeasureData::clear -- +// + +void MeasureData::clear(void) { + m_owner = NULL; + m_owner = NULL; + m_startline = -1; + m_startline = -1; + m_hist7pc.resize(7); + std::fill(m_hist7pc.begin(), m_hist7pc.end(), 0.0); + m_sum7pc = 0.0; } ////////////////////////////// // -// Tool_semitones::filterData -- select or deselect an interval based -// on regular expression pattern. Return true if the note should -// be kept; otherwise, return false. +// MeasureData::getHistogram7pc -- // -int Tool_semitones::filterData(HTp token) { - vector toks = getTieGroup(token); - HumRegex hre; - if (!m_exclude.empty()) { - for (int i=0; i<(int)toks.size(); i++) { - if (hre.search(toks[i], m_exclude)) { - return -1; - } - } - return 1; - } else if (!m_include.empty()) { - for (int i=0; i<(int)toks.size(); i++) { - if (hre.search(toks[i], m_include)) { - return 1; - } - } - return 0; - } - return 0; +std::vector& MeasureData::getHistogram7pc(void) { + return m_hist7pc; +} + + +////////////////////////////// +// +// MeasureData::getSum7pc -- +// + +double MeasureData::getSum7pc(void) { + return m_sum7pc; } ////////////////////////////// // -// Tool_semitones::getTieGroup -- +// MeasureData::generateNoteHistogram -- // -vector Tool_semitones::getTieGroup(HTp token) { - vector output; - if (!token) { - return output; - } - if (token->isNull()) { - return output; +void MeasureData::generateNoteHistogram(void) { + m_hist7pc.resize(7); + std::fill(m_hist7pc.begin(), m_hist7pc.end(), 0.0); + m_sum7pc = 0; + if (m_owner == NULL) { + return; } - if (!token->isData()) { - return output; + if (m_startline < 0) { + return; } - output.push_back(token); - if (token->isRest()) { - return output; + if (m_stopline < 0) { + return; } - string subtok = token->getSubtoken(0); - bool continues = hasTieContinue(subtok); - HTp current = token; - while (continues) { - current = getNextNote(current); - if (!current) { - break; + + HumdrumFile& infile = *m_owner; + for (int i=m_startline; igetSubtoken(0); - if (subtok.find(']') != string::npos) { - output.push_back(current); - break; + for (int j=0; jisKern()) { + continue; + } + if (token->isNull()) { + continue; + } + if (token->isRest()) { + continue; + } + double duration = token->getDuration().getFloat(); + int subtokcount = token->getSubtokenCount(); + for (int k=0; kgetSubtoken(k); + int pc = Convert::kernToBase7PC(subtok); + if (pc < 0) { + continue; + } + m_hist7pc.at(pc) += duration; + } } - continues = hasTieContinue(subtok); } - return output; + m_sum7pc = 0.0; + for (int i=0; i<(int)m_hist7pc.size(); i++) { + m_sum7pc += m_hist7pc[i]; + } } +/////////////////////////////////////////////////////////////////////////// + ////////////////////////////// // -// Tool_semitones::hasTieContinue -- +// MeasureDataSet::MeasureDataSet -- // -bool Tool_semitones::hasTieContinue(const string& value) { - if (value.find('_') != string::npos) { - return true; - } - if (value.find('[') != string::npos) { - return true; - } - return false; +MeasureDataSet::MeasureDataSet(void) { + m_data.reserve(1000); } ////////////////////////////// // -// getNextNote -- +// MeasureDataSet::MeasureDataSet -- // -HTp Tool_semitones::getNextNote(HTp token) { - HTp current = token->getNextToken(); - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); - continue; - } - break; - } - return current; +MeasureDataSet::MeasureDataSet(HumdrumFile& infile) { + parse(infile); } - - -///////////////////////////////// +////////////////////////////// // -// Tool_shed::Tool_shed -- Set the recognized options for the tool. +// MeasureDataSet::~MeasureDataSet -- // -Tool_shed::Tool_shed(void) { - define("s|spine|spines=s", "list of spines to process"); - define("e|expression=s", "regular expression"); - define("E|exclusion-expression=s", "regular expression to skip"); - define("x|exclusive-interpretations=s", "apply only to spine types in list"); - define("k|kern=b", "apply only to **kern data"); - define("X=s", "defineable exclusive interpretation x"); - define("Y=s", "defineable exclusive interpretation y"); - define("Z=s", "defineable exclusive interpretation z"); +MeasureDataSet::~MeasureDataSet() { + clear(); } -///////////////////////////////// +////////////////////////////// // -// Tool_shed::run -- Do the main work of the tool. +// MeasureDataSet::clear -- // -bool Tool_shed::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; igenerateNoteHistogram(); + m_data.push_back(info); + lastbar = i; } - return status; + MeasureData* info = new MeasureData(infile, lastbar, infile.getLineCount() - 1); + m_data.push_back(info); + return 1; } -bool Tool_shed::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - if (hasAnyText()) { - getAllText(out); - } else { - out << infile; - } - return status; + +////////////////////////////// +// +// MeasureDataSet::operator[] -- +// + +MeasureData& MeasureDataSet::operator[](int index) { + return *m_data[index]; } -bool Tool_shed::run(HumdrumFile& infile) { - initialize(); - initializeSegment(infile); - if (m_options.empty()) { - cerr << "Error: -e option is required" << endl; - return false; - } - for (int i=0; i<(int)m_options.size(); i++) { - prepareSearch(i); - processFile(infile); + +////////////////////////////// +// +// MeasureDataSet::getScoreDuration -- +// + +double MeasureDataSet::getScoreDuration(void) { + if (m_data.empty()) { + return 0.0; } - return true; + return m_data[0]->getScoreDuration(); + } +/////////////////////////////////////////////////////////////////////////// + ////////////////////////////// // -// Tool_shed::prepareSearch -- +// MeasureComparison::MeasureComparison -- // -void Tool_shed::prepareSearch(int index) { - // deal with command-line options (seprately for each search): - m_exinterps.clear(); - - if (getBoolean("kern")) { - m_exinterps.push_back("**kern"); - } else if (getBoolean("exclusive-interpretations")) { - vector extra = addToExInterpList(); - for (int i=0; i<(int)extra.size(); i++) { - m_exinterps.push_back(extra[i]); - } - } +MeasureComparison::MeasureComparison() { + // do nothing +} - m_search = m_searches.at(index); - m_replace = m_replaces.at(index); - m_option = m_options.at(index); - m_grepoptions = ""; - if (m_option.find("i") != std::string::npos) { - m_grepoptions += "i"; - } - if (m_option.find("g") != std::string::npos) { - m_grepoptions += "g"; - } +MeasureComparison::MeasureComparison(MeasureData& data1, MeasureData& data2) { + compare(data1, data2); +} - if (m_option.find("X") != std::string::npos) { - if (m_xInterp != "") { - m_exinterps.push_back(m_xInterp); - } - } - if (m_option.find("Y") != std::string::npos) { - if (m_yInterp != "") { - m_exinterps.push_back(m_yInterp); - } - } - if (m_option.find("Z") != std::string::npos) { - if (m_zInterp != "") { - m_exinterps.push_back(m_zInterp); - } - } - m_data = true; // process data - m_barline = false; // process barline - m_exinterp = false; // process exclusive interpretations - m_interpretation = false; // process interpretations (other than exinterp - // and spine manipulators). +MeasureComparison::MeasureComparison(MeasureData* data1, MeasureData* data2) { + compare(data1, data2); +} - if (m_option.find("I") != std::string::npos) { - m_interpretation = true; - m_data = false; - } - if (m_option.find("X") != std::string::npos) { - m_exinterp = true; - m_data = false; - } - if (m_option.find("B") != std::string::npos) { - m_barline = true; - m_data = false; - } - if (m_option.find("M") != std::string::npos) { - // measure is an alias for barline - m_barline = true; - m_data = false; - } - if (m_option.find("L") != std::string::npos) { - m_localcomment = true; - m_data = false; - } - if (m_option.find("G") != std::string::npos) { - m_globalcomment = true; - m_data = false; - } - if (m_option.find("K") != std::string::npos) { - m_referencekey = true; - m_data = false; - } - if (m_option.find("V") != std::string::npos) { - m_referencevalue = true; - m_data = false; - } - if (m_option.find("R") != std::string::npos) { - m_reference = true; - m_referencekey = false; - m_referencevalue = false; - m_data = false; - } - if (m_option.find("D") != std::string::npos) { - m_data = true; - } + +////////////////////////////// +// +// MeasureComparison::~MeasureComparison -- +// + +MeasureComparison::~MeasureComparison() { + clear(); } ////////////////////////////// // -// Tool_shed::initialize -- Initializations that only have to be done once -// for all HumdrumFile segments. +// MeasureComparison::clear -- // -void Tool_shed::initialize(void) { - if (getBoolean("expression")) { - string value = getString("expression"); - parseExpression(value); - } - m_exclusion = getString("exclusion-expression"); - - if (getBoolean("X")) { - m_xInterp = getExInterp(getString("X")); - } - if (getBoolean("Y")) { - m_yInterp = getExInterp(getString("Y")); - } - if (getBoolean("Z")) { - m_zInterp = getExInterp(getString("Z")); - } +void MeasureComparison::clear(void) { + correlation7pc = 0.0; } ////////////////////////////// // -// Tool_shed::getExInterp -- +// MeasureComparison::compare -- // -string Tool_shed::getExInterp(const string& value) { - if (value == "") { - return "**"; +void MeasureComparison::compare(MeasureData& data1, MeasureData& data2) { + compare(&data1, &data2); +} + + +void MeasureComparison::compare(MeasureData* data1, MeasureData* data2) { + double sum1 = data1->getSum7pc(); + double sum2 = data2->getSum7pc(); + if ((sum1 == sum2) && (sum1 == 0.0)) { + correlation7pc = 1.0; + return; } - if (value == "*") { - return "**"; + if (sum1 == 0.0) { + correlation7pc = 0.0; + return; } - if (value.compare(0, 2, "**") == 0) { - return value; + if (sum2 == 0.0) { + correlation7pc = 0.0; + return; } - if (value.compare(0, 1, "*") == 0) { - return "*" + value; + correlation7pc = Convert::pearsonCorrelation(data1->getHistogram7pc(), data2->getHistogram7pc()); + if (fabs(correlation7pc - 1.0) < 0.00000001) { + correlation7pc = 1.0; } - return "**" + value; } ////////////////////////////// // -// Tool_shed::parseExpression -- -// Form of string: -// s/search/replace/options; s/search2/replace2/options2 +// MeasureComparison::getCorrelation7pc -- +// + +double MeasureComparison::getCorrelation7pc(void) { + return correlation7pc; +} + +////////////////////////////////////////////////////////////////////////// + +////////////////////////////// // +// MeasureComparisonGrid::MeasureComparisonGrid -- // -void Tool_shed::parseExpression(const string& expression) { - int state = 0; +MeasureComparisonGrid::MeasureComparisonGrid(void) { + // do nothing +} - m_searches.clear(); - m_replaces.clear(); - m_options.clear(); - char divchar = '/'; +MeasureComparisonGrid::MeasureComparisonGrid(MeasureDataSet& set1, MeasureDataSet& set2) { + analyze(set1, set2); +} - for (int i=0; i<(int)expression.size(); i++) { - if (state == 0) { // start of expression - if (isspace(expression[i])) { - continue; - } else if (expression[i] == 's') { - if (i >= (int)expression.size() - 1) { - cerr << "Error: spurious s at end of expression: " - << expression << endl; - return; - } else { - divchar = expression[i+1]; - i++; - state++; - m_searches.push_back(""); - } - } else { - cerr << "Error at position " << i - << " in expression: " << expression << endl; - return; - } - } else if (state == 1) { // search string - if (expression[i] == divchar) { - state++; - m_replaces.push_back(""); - continue; - } if (expression[i] == '\\') { - if (i >= (int)expression.size() - 1) { - cerr << "Error: expression ends too soon: " - << expression << endl; - return; - } else { - m_searches.back() += '\\'; - m_searches.back() += expression[i+1]; - i++; - } - } else { - m_searches.back() += expression[i]; - } - } else if (state == 2) { // replace string - if (expression[i] == divchar) { - state++; - m_options.push_back(""); - continue; - } if (expression[i] == '\\') { - if (i >= (int)expression.size() - 1) { - cerr << "Error: expression ends too soon: " - << expression << endl; - return; - } else { - m_replaces.back() += '\\'; - m_replaces.back() += expression[i+1]; - i++; - } - } else { - m_replaces.back() += expression[i]; - } - } else if (state == 3) { // regular expression options - if (expression[i] == ';') { - state++; - } else if (isspace(expression[i])) { - state++; - } else { - m_options.back() += expression[i]; - } - } - if (state == 4) { - state = 0; - } - } + +MeasureComparisonGrid::MeasureComparisonGrid(MeasureDataSet* set1, MeasureDataSet* set2) { + analyze(set1, set2); } ////////////////////////////// // -// Tool_shed::initializeSegment -- Recalculate variables for each Humdrum -// input segment. +// MeasureComparisonGrid::~MeasureComparisonGrid -- // -void Tool_shed::initializeSegment(HumdrumFile& infile) { - m_spines.clear(); - if (getBoolean("spines")) { - int maxtrack = infile.getMaxTrack(); - Convert::makeBooleanTrackList(m_spines, getString("spines"), maxtrack); - } +MeasureComparisonGrid::~MeasureComparisonGrid() { + // do nothing } ////////////////////////////// // -// Tool_shed::addToExInterpList -- +// MeasureComparisonGrid::clear -- // -vector Tool_shed::addToExInterpList(void) { - string elist = getString("exclusive-interpretations"); - elist = Convert::trimWhiteSpace(elist); - HumRegex hre; - hre.replaceDestructive(elist, "", "^[,;\\s*]+"); - hre.replaceDestructive(elist, "", "[,;\\s*]+$"); - vector pieces; - hre.split(pieces, elist, "[,;\\s*]+"); +void MeasureComparisonGrid::clear(void) { + m_grid.clear(); +} - vector output; - for (int i=0; i<(int)pieces.size(); i++) { - if (pieces[i].empty()) { - continue; + + +////////////////////////////// +// +// MeasureComparisonGrid::analyze -- +// + +void MeasureComparisonGrid::analyze(MeasureDataSet* set1, MeasureDataSet* set2) { + analyze(*set1, *set2); +} + +void MeasureComparisonGrid::analyze(MeasureDataSet& set1, MeasureDataSet& set2) { + m_grid.resize(set1.size()); + for (int i=0; i<(int)m_grid.size(); i++) { + m_grid[i].resize(set2.size()); + } + for (int i=0; i<(int)m_grid.size(); i++) { + for (int j=0; j<(int)m_grid[i].size(); j++) { + m_grid[i][j].compare(set1[i], set2[j]); } - output.push_back("**" + pieces[i]); } - return output; + m_set1 = &set1; + m_set2 = &set2; } ////////////////////////////// // -// Tool_shed::processFile -- +// MeasureComparisonGrid::printCorrelationGrid -- +// default value: out = std::cout // -void Tool_shed::processFile(HumdrumFile& infile) { - if (m_search == "") { - // nothing to do - return; +ostream& MeasureComparisonGrid::printCorrelationGrid(ostream& out) { + for (int i=0; i<(int)m_grid.size(); i++) { + for (int j=0; j<(int)m_grid[i].size(); j++) { + double correl = m_grid[i][j].getCorrelation7pc(); + if (correl > 0.0) { + out << int(correl * 100.0 + 0.5)/100.0; + } else { + out << -int(-correl * 100.0 + 0.5)/100.0; + } + if (j < (int)m_grid[i].size() - 1) { + out << '\t'; + } + } + out << endl; } - m_modified = false; + return out; +} - if (m_interpretation) { - searchAndReplaceInterpretation(infile); - } - if (m_localcomment) { - searchAndReplaceLocalComment(infile); - } - if (m_globalcomment) { - searchAndReplaceGlobalComment(infile); - } +////////////////////////////// +// +// MeasureComparisonGrid::printCorrelationDiagonal -- Assuming a square grid for now. +// default value: out = std::cout +// - if (m_reference) { - searchAndReplaceReferenceRecords(infile); +ostream& MeasureComparisonGrid::printCorrelationDiagonal(ostream& out) { + for (int i=0; i<(int)m_grid.size(); i++) { + for (int j=0; j<(int)m_grid[i].size(); j++) { + if (i != j) { + continue; + } + double correl = m_grid[i][j].getCorrelation7pc(); + if (correl > 0.0) { + out << int(correl * 100.0 + 0.5)/100.0; + } else { + out << -int(-correl * 100.0 + 0.5)/100.0; + } + if (j < (int)m_grid[i].size() - 1) { + out << '\t'; + } + } + out << endl; } + return out; +} - if (m_referencekey) { - searchAndReplaceReferenceKeys(infile); - } - if (m_referencevalue) { - searchAndReplaceReferenceValues(infile); + +////////////////////////////// +// +// MeasureComparisonGrid::getColorMapping -- +// + +void MeasureComparisonGrid::getColorMapping(double input, double& hue, + double& saturation, double& lightness) { + double maxhue = 0.75 * 360.0; + hue = input; + if (hue < 0.0) { + hue = 0.0; + } + hue = hue * hue; + if (hue != 1.0) { + hue *= 0.95; } - if (m_exinterp) { - searchAndReplaceExinterp(infile); + hue = (1.0 - hue) * 360.0; + if (hue == 0.0) { + // avoid -0.0; + hue = 0.0; } - if (m_barline) { - searchAndReplaceBarline(infile); + if (hue > maxhue) { + hue = maxhue; + } + if (hue < 0.0) { + hue = maxhue; } - if (m_data) { - searchAndReplaceData(infile); + saturation = 100.0; + lightness = 50.0; + + if (hue > 60) { + lightness = lightness - (hue-60) / (maxhue-60) * lightness / 1.5; } +} - if (m_modified) { - infile.createLinesFromTokens(); + + +////////////////////////////// +// +// MeasureComparisonGrid::getQoff1 -- return the end time class ID of the +// current grid cell (for the first piece being compared). +// + +std::string MeasureComparisonGrid::getQoff1(int index) { + if (m_set1 == NULL) { + return ""; } + return (*m_set1)[index].getQoff(); } ////////////////////////////// // -// Tool_shed::searchAndReplaceBarline -- +// MeasureComparisonGrid::getQoff2 -- return the end time class ID of the +// current grid cell (for the first piece being compared). // -void Tool_shed::searchAndReplaceBarline(HumdrumFile& infile) { - string isearch; - if (m_search[0] == '^') { - isearch = "^=" + m_search.substr(1); - } else { - isearch = "^=.*" + m_search; +std::string MeasureComparisonGrid::getQoff2(int index) { + if (m_set2 == NULL) { + return ""; } - 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().substr(1); - hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); - hre.replaceDestructive(text, "", "^=+"); - text = "=" + text; - token->setText(text); - m_modified = true; - } - } + return (*m_set2)[index].getQoff(); +} + + + +////////////////////////////// +// +// MeasureComparisonGrid::getQon1 -- return the start time class ID of the +// current grid cell (for the first piece being compared). +// + +string MeasureComparisonGrid::getQon1(int index) { + if (m_set1 == NULL) { + return ""; } + return (*m_set1)[index].getQon(); } ////////////////////////////// // -// Tool_shed::searchAndReplaceInterpretation -- +// MeasureComparisonGrid::getQon2 -- return the start time class ID of the +// current grid cell (for the second piece being compared). // -void Tool_shed::searchAndReplaceInterpretation(HumdrumFile& infile) { - string isearch; - if (m_search[0] == '^') { - isearch = "^\\*" + m_search.substr(1); - } else { - isearch = "^\\*.*" + m_search; +string MeasureComparisonGrid::getQon2(int index) { + if (m_set2 == NULL) { + return ""; } - 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().substr(1); - hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); - hre.replaceDestructive(text, "", "^\\*+"); - text = "*" + text; - token->setText(text); - m_modified = true; - } - } + return (*m_set2)[index].getQon(); +} + + + +////////////////////////////// +// +// MeasureComparisonGrid::getMeasure1 -- return the measure of the +// current grid cell (for the first piece being compared). +// + +int MeasureComparisonGrid::getMeasure1(int index) { + if (m_set1 == NULL) { + return 0.0; } + return (*m_set1)[index].getMeasure(); } ////////////////////////////// // -// Tool_shed::searchAndReplaceLocalComment -- +// MeasureComparisonGrid::getMeasure2 -- return the measure of the +// current grid cell (for the second piece being compared). // -void Tool_shed::searchAndReplaceLocalComment(HumdrumFile& infile) { - string isearch; - if (m_search[0] == '^') { - isearch = "^!" + m_search.substr(1); - } else { - isearch = "^!.*" + m_search; +int MeasureComparisonGrid::getMeasure2(int index) { + if (m_set2 == NULL) { + return 0.0; } - 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().substr(1); - hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); - hre.replaceDestructive(text, "", "^!+"); - text = "!" + text; - token->setText(text); - m_modified = true; - } - } + return (*m_set2)[index].getMeasure(); +} + + + +////////////////////////////// +// +// MeasureComparisonGrid::getStartTime1 -- return the start time of the +// measure at index position in the first compared score. +// + +double MeasureComparisonGrid::getStartTime1(int index) { + if (m_set1 == NULL) { + return 0.0; } + return (*m_set1)[index].getStartTime(); } ////////////////////////////// // -// Tool_shed::searchAndReplaceGlobalComment -- +// MeasureComparisonGrid::getScoreDuration1 -- // -void Tool_shed::searchAndReplaceGlobalComment(HumdrumFile& infile) { - string isearch; - if (m_search[0] == '^') { - isearch = "^!!" + m_search.substr(1); - } else { - isearch = "^!!.*" + m_search; +double MeasureComparisonGrid::getScoreDuration1(void) { + if (m_set1 == NULL) { + return 0.0; } - HumRegex hre; - for (int i=0; isize() < 3) { - // Don't mess with null comments - continue; - } - if (hre.search(token, isearch, m_grepoptions)) { - string text = token->getText().substr(2); - hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); - hre.replaceDestructive(text, "", "^!+"); - text = "!!" + text; - token->setText(text); - m_modified = true; - } + return m_set1->getScoreDuration(); +} + + + +////////////////////////////// +// +// MeasureComparisonGrid::getStartTime2 -- +// + +double MeasureComparisonGrid::getStartTime2(int index) { + if (m_set2 == NULL) { + return 0.0; } + return (*m_set2)[index].getStartTime(); } ////////////////////////////// // -// Tool_shed::searchAndReplaceReferenceRecords -- +// MeasureComparisonGrid::getStopTime1 -- // -void Tool_shed::searchAndReplaceReferenceRecords(HumdrumFile& infile) { - string isearch; - if (m_search[0] == '^') { - isearch = "^!!!" + m_search.substr(1); - } else { - isearch = "^!!!.*" + m_search; - } - HumRegex hre; - for (int i=0; igetText().substr(1); - hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); - hre.replaceDestructive(text, "", "^!+"); - text = "!!!" + text; - token->setText(text); - m_modified = true; - } +double MeasureComparisonGrid::getStopTime1(int index) { + if (m_set1 == NULL) { + return 0.0; } + return (*m_set1)[index].getStopTime(); } ////////////////////////////// // -// Tool_shed::searchAndReplaceReferenceKeys -- +// MeasureComparisonGrid::getStopTime2 -- // -void Tool_shed::searchAndReplaceReferenceKeys(HumdrumFile& infile) { - string isearch = m_search; - HumRegex hre; - for (int i=0; isetText(text); - m_modified = true; - } +double MeasureComparisonGrid::getStopTime2(int index) { + if (m_set2 == NULL) { + return 0.0; } + return (*m_set2)[index].getStopTime(); } ////////////////////////////// // -// Tool_shed::searchAndReplaceReferenceValues -- +// MeasureComparisonGrid::getDuration1 -- // -void Tool_shed::searchAndReplaceReferenceValues(HumdrumFile& infile) { - string isearch = m_search; - HumRegex hre; - for (int i=0; isetText(text); - m_modified = true; - } +double MeasureComparisonGrid::getDuration1(int index) { + if (m_set1 == NULL) { + return 0.0; } + return (*m_set1)[index].getDuration(); } ////////////////////////////// // -// Tool_shed::searchAndReplaceExinterp -- +// MeasureComparisonGrid::getDuration2 -- // -void Tool_shed::searchAndReplaceExinterp(HumdrumFile& infile) { - string isearch; - if (m_search[0] == '^') { - isearch = "^\\*\\*" + m_search.substr(1); - } else { - isearch = "^\\*\\*.*" + m_search; +double MeasureComparisonGrid::getDuration2(int index) { + if (m_set2 == NULL) { + return 0.0; } - 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().substr(2); - hre.replaceDestructive(text, m_replace, m_search, m_grepoptions); - hre.replaceDestructive(text, "", "^\\*+"); - text = "**" + text; - token->setText(text); - m_modified = true; - } - } + return (*m_set2)[index].getDuration(); +} + + + +////////////////////////////// +// +// MeasureComparisonGrid::getScoreDuration2 -- +// + +double MeasureComparisonGrid::getScoreDuration2(void) { + if (m_set2 == NULL) { + return 0.0; } + return m_set2->getScoreDuration(); } ////////////////////////////// // -// Tool_shed::searchAndReplaceData -- +// MeasureComparisonGrid::printSvgGrid -- +// default value: out = std::cout // -void Tool_shed::searchAndReplaceData(HumdrumFile& infile) { - string dsearch = m_search; +ostream& MeasureComparisonGrid::printSvgGrid(ostream& out) { + pugi::xml_document image; + auto declaration = image.prepend_child(pugi::node_declaration); + declaration.append_attribute("version") = "1.0"; + declaration.append_attribute("encoding") = "UTF-8"; + declaration.append_attribute("standalone") = "no"; - 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; - } + auto svgnode = image.append_child("svg"); + svgnode.append_attribute("version") = "1.1"; + svgnode.append_attribute("xmlns") = "http://www.w3.org/2000/svg"; + svgnode.append_attribute("xmlns:xlink") = "http://www.w3.org/1999/xlink"; + svgnode.append_attribute("overflow") = "visible"; + svgnode.append_attribute("viewBox") = "0 0 1000 1000"; + svgnode.append_attribute("width") = "1000px"; + svgnode.append_attribute("height") = "1000px"; + + auto grid = svgnode.append_child("g"); + grid.append_attribute("id") = "grid"; + + double hue = 0.0; + double saturation = 100; + double lightness = 75; + + pugi::xml_node crect; + double width; + double height; + + stringstream ss; + stringstream css; + double x; + double y; + + double imagewidth = 1000.0; + double imageheight = 1000.0; + + double sdur1 = getScoreDuration1(); + double sdur2 = getScoreDuration2(); + + for (int i=0; i<(int)m_grid.size(); i++) { + for (int j=0; j<(int)m_grid[i].size(); j++) { + width = getDuration2(j) / sdur2 * imagewidth; + height = getDuration1(i) / sdur1 * imageheight; + + x = getStartTime2(j)/sdur2 * imageheight; + y = getStartTime1(i)/sdur1 * imagewidth; + + getColorMapping(m_grid[i][j].getCorrelation7pc(), hue, saturation, lightness); + ss << "hsl(" << hue << "," << saturation << "%," << lightness << "%)"; + crect = grid.append_child("rect"); + crect.append_attribute("x") = to_string(x).c_str(); + crect.append_attribute("y") = to_string(y).c_str(); + crect.append_attribute("width") = to_string(width*0.99).c_str(); + crect.append_attribute("height") = to_string(height*0.99).c_str(); + crect.append_attribute("fill") = ss.str().c_str(); + css << "Xm" << getMeasure1(i) << " Ym" << getMeasure2(j); + css << " X" << getQon1(i) << " Y" << getQon2(j); + css << " X" << getQoff1(i) << " Y" << getQoff2(j); + crect.append_attribute("class") = css.str().c_str(); + ss.str(""); + css.str(""); } } + + image.save(out); + return out; } +/////////////////////////////////////////////////////////////////////////// -////////////////////////////// + +///////////////////////////////// // -// Tool_shed::isValidDataType -- usar with -x and -k options. +// Tool_simat::Tool_simat -- Set the recognized options for the tool. // -bool Tool_shed::isValidDataType(HTp token) { - if (m_exinterps.empty()) { - return true; +Tool_simat::Tool_simat(void) { + define("r|raw=b", "output raw correlation matrix"); + define("d|diagonal=b", "output diagonal of correlation matrix"); +} + + + +///////////////////////////////// +// +// Tool_simat::run -- Primary interfaces to the tool. +// + +bool Tool_simat::run(HumdrumFileSet& infiles) { + bool status = true; + if (infiles.getCount() == 1) { + status = run(infiles[0], infiles[0]); + } else if (infiles.getCount() > 1) { + status = run(infiles[0], infiles[1]); + } else { + status = false; } - string datatype = token->getDataType(); - for (int i=0; i<(int)m_exinterps.size(); i++) { - if (datatype == m_exinterps[i]) { - return true; - } + return status; +} + + +bool Tool_simat::run(const string& indata1, const string& indata2, ostream& out) { + HumdrumFile infile1(indata1); + HumdrumFile infile2; + bool status; + if (indata2.empty()) { + infile2.read(indata2); + status = run(infile1, infile2); + } else { + status = run(infile1, infile1); } - return false; + if (hasAnyText()) { + getAllText(out); + } else { + out << infile1; + out << infile2; + } + return status; } +bool Tool_simat::run(HumdrumFile& infile1, HumdrumFile& infile2, ostream& out) { + bool status; + if (infile2.getLineCount() == 0) { + status = run(infile1, infile1); + } else { + status = run(infile1, infile2); + } + if (hasAnyText()) { + getAllText(out); + } else { + out << infile1; + out << infile2; + } + return status; +} -////////////////////////////// // -// Tool_shed::isValidSpine -- used with -s option. +// In-place processing of file: // -bool Tool_shed::isValidSpine(HTp token) { - if (m_spines.empty()) { - return true; +bool Tool_simat::run(HumdrumFile& infile1, HumdrumFile& infile2) { + if (infile2.getLineCount() == 0) { + processFile(infile1, infile1); + } else { + processFile(infile1, infile2); } - int track = token->getTrack(); - return m_spines.at(track); + + return true; } ////////////////////////////// // -// Tool_shed::isValid -- +// Tool_simat::processFile -- // -bool Tool_shed::isValid(HTp token) { - if (!m_exclusion.empty()) { - HumRegex hre; - if (hre.search(token, m_exclusion)) { - return false; - } - } - if (isValidDataType(token) && isValidSpine(token)) { - return true; +void Tool_simat::processFile(HumdrumFile& infile1, HumdrumFile& infile2) { + m_data1.parse(infile1); + m_data2.parse(infile2); + m_grid.analyze(m_data1, m_data2); + if (getBoolean("raw")) { + m_grid.printCorrelationGrid(m_free_text); + suppressHumdrumFileOutput(); + } else if (getBoolean("diagonal")) { + m_grid.printCorrelationDiagonal(m_free_text); + suppressHumdrumFileOutput(); + } else { + m_grid.printSvgGrid(m_free_text); + suppressHumdrumFileOutput(); } - return false; } @@ -121236,25 +127183,25 @@ bool Tool_shed::isValid(HTp token) { ///////////////////////////////// // -// Tool_sic::Tool_sic -- Set the recognized options for the tool. +// Tool_slurcheck::Tool_slurcheck -- Set the recognized options for the tool. // -Tool_sic::Tool_sic(void) { - define("s|substitution=b", "insert substitutions into music"); - define("o|original=b", "insert originals into music"); - define("r|remove=b", "remove sic layout tokens"); - define("v|verbose=b", "add verbose parameter"); - define("q|quiet=b", "remove verbose parameter"); +Tool_slurcheck::Tool_slurcheck(void) { + // add options here + define("l|list=b", "list locations of unclosed slur endings"); + define("c|count=b", "count unclosed slur endings"); + define("Z|no-zeros=b", "do not list files that have zero unclosed slurs in counts"); + define("f|filename=b", "print filename for list and count options"); } ///////////////////////////////// // -// Tool_sic::run -- Do the main work of the tool. +// Tool_slurcheck::run -- Do the main work of the tool. // -bool Tool_sic::run(HumdrumFileSet& infiles) { +bool Tool_slurcheck::run(HumdrumFileSet& infiles) { bool status = true; for (int i=0; iisKern()) { continue; } - for (int j=0; jcompare(0, 8, "!LO:SIC:") != 0) { + HTp etok = infile.getStrandEnd(i); + HTp tok = stok; + while (tok && (tok != etok)) { + if (!tok->isData()) { + tok = tok->getNextToken(); continue; } - if (m_verboseQ) { - addVerboseParameter(token); - } else if (m_quietQ) { - removeVerboseParameter(token); + if (tok->isNull()) { + tok = tok->getNextToken(); + continue; } - if (m_removeQ) { - token->setText("!"); - m_modifiedQ = true; - } else if (m_substituteQ) { - insertSubstitutionToken(token); - } else if (m_originalQ) { - insertOriginalToken(token); + string value = tok->getValue("auto", "hangingSlur"); + if (value == "true") { + string side = tok->getValue("auto", "slurSide"); + if (side == "start") { + opencount++; + if (listQ) { + if (filenameQ) { + m_free_text << infile.getFilename() << ":\t"; + } + m_free_text << "UNCLOSED SLUR\tline:" << tok->getLineIndex()+1 + << "\tfield:" << tok->getFieldIndex()+1 << "\ttoken:" << tok << endl; + } else if (!countQ) { + string data = *tok; + data += "i"; + tok->setText(data); + } + } else if (side == "stop") { + closecount++; + if (listQ) { + if (filenameQ) { + m_free_text << infile.getFilename() << ":\t"; + } + m_free_text << "UNOPENED SLUR\tline:" << tok->getLineIndex()+1 + << "\tfield:" << tok->getFieldIndex()+1 << "\ttoken:" << tok << endl; + } else if (!countQ) { + string data = *tok; + data += "j"; + tok->setText(data); + } + } } + tok = tok->getNextToken(); } } - if (m_modifiedQ) { - infile.createLinesFromTokens(); + + if (countQ) { + int sum = opencount + closecount; + if ((!zeroQ) && (sum == 0)) { + return; + } + if (filenameQ) { + m_free_text << infile.getFilename() << ":\t"; + } + m_free_text << (opencount + closecount) << "\t(:" << opencount << "\t):" << closecount << endl; } - m_humdrum_text << infile; + + if (countQ || listQ) { + return; + } + + if (opencount + closecount == 0) { + return; + } + + if (opencount) { + infile.appendLine("!!!RDF**kern: i = marked note, color=\"hotpink\", text=\"extra(\""); + } + + if (closecount) { + infile.appendLine("!!!RDF**kern: j = marked note, color=\"magenta\", text=\"extra)\""); + } + + infile.createLinesFromTokens(); +} + + + + + +///////////////////////////////// +// +// Tool_gridtest::Tool_spinetrace -- Set the recognized options for the tool. +// + +Tool_spinetrace::Tool_spinetrace(void) { + define("a|append=b", "append analysis to input data lines"); + define("p|prepend=b", "prepend analysis to input data lines"); +} + + + +/////////////////////////////// +// +// Tool_spinetrace::run -- Primary interfaces to the tool. +// + +bool Tool_spinetrace::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; igetText(); - if (hre.search(value, "(:v:)|(:v$)")) { - return; - } - string newvalue = value + ":v"; - token->setText(newvalue); - m_modifiedQ = true; +bool Tool_spinetrace::run(HumdrumFile& infile) { + initialize(infile); + processFile(infile); + return true; } ////////////////////////////// // -// Tool_sic::removeVerboseParameter -- +// Tool_spinetrace::initialize -- // -void Tool_sic::removeVerboseParameter(HTp token) { - HumRegex hre; - string value = token->getText(); - string newvalue = value; - hre.replaceDestructive(newvalue, ":", ":v:", "g"); - hre.replaceDestructive(newvalue, "", ":v$", ""); - if (value == newvalue) { - return; - } - token->setText(newvalue); - m_modifiedQ = true; +void Tool_spinetrace::initialize(HumdrumFile& infile) { + // do nothing for now } ////////////////////////////// // -// Tool_sic::getTargetToken -- Get the token that the layout command -// applies to. +// Tool_spinetrace::processFile -- // -HTp Tool_sic::getTargetToken(HTp stok) { - HTp current = stok->getNextToken(); - while (current) { - if (current->isNull()) { - current = current->getNextToken(); +void Tool_spinetrace::processFile(HumdrumFile& infile) { + bool appendQ = getBoolean("append"); + bool prependQ = getBoolean("prepend"); + + int linecount = infile.getLineCount(); + for (int i=0; iisManipulator()) { - // Layout commands should not apply to manipulators nor be split - // from their associated token. - current = NULL; - break; + if (appendQ) { + m_humdrum_text << infile[i] << "\t"; } - if (current->isCommentLocal()) { - current = current->getNextToken(); - continue; + + if (!infile[i].isData()) { + if (infile[i].isInterpretation()) { + int fieldcount = infile[i].getFieldCount(); + for (int j=0; jcompare(0, 2, "**") == 0) { + m_humdrum_text << "**spine"; + } else { + m_humdrum_text << token; + } + if (j < fieldcount - 1) { + m_humdrum_text << "\t"; + } + } + } else { + m_humdrum_text << infile[i]; + } + } else { + int fieldcount = infile[i].getFieldCount(); + for (int j=0; jgetSpineInfo(); + if (j < fieldcount - 1) { + m_humdrum_text << '\t'; + } + } } - break; - } - if (!current) { - return NULL; + + if (prependQ) { + m_humdrum_text << "\t" << infile[i]; + } + m_humdrum_text << "\n"; } - return current; } -////////////////////////////// + +///////////////////////////////// // -// Tool_sic::insertSubstitutionToken -- +// Tool_strophe::Tool_strophe -- Set the recognized options for the tool. // -void Tool_sic::insertSubstitutionToken(HTp sictok) { - HTp target = getTargetToken(sictok); - if (!target) { - return; - } - HumRegex hre; - vector pieces; - hre.split(pieces, *sictok, ":"); - string tstring = target->getText(); - string sstring; - for (int i=2; i<(int)pieces.size(); i++) { - if (pieces[i].compare(0, 2, "s=") == 0) { - sstring = pieces[i].substr(2); - } - } - if (sstring.empty()) { - return; - } - target->setText(sstring); - m_modifiedQ = true; - string newsic = "!LO:SIC"; - for (int i=2; i<(int)pieces.size(); i++) { - if (pieces[i].compare(0, 2, "s=") == 0) { - newsic += ":o=" + tstring; - } else { - newsic += ":" + pieces[i]; - } - } - sictok->setText(newsic); - m_modifiedQ = true; +Tool_strophe::Tool_strophe(void) { + define("l|list=b", "list all possible variants"); + define("m=b", "mark strophe music"); + define("mark|marker=s:@", "character to mark with"); + define("c|color=s:red", "character to mark with"); } -////////////////////////////// +///////////////////////////////// // -// Tool_sic::insertOriginalToken -- +// Tool_strophe::run -- Do the main work of the tool. // -void Tool_sic::insertOriginalToken(HTp sictok) { - HTp target = getTargetToken(sictok); - if (!target) { - return; - } - HumRegex hre; - vector pieces; - hre.split(pieces, *sictok, ":"); - string tstring = target->getText(); - string sstring; - for (int i=2; i<(int)pieces.size(); i++) { - if (pieces[i].compare(0, 2, "o=") == 0) { - sstring = pieces[i].substr(2); - } - } - if (sstring.empty()) { - return; +bool Tool_strophe::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; isetText(sstring); - m_modifiedQ = true; - string newsic = "!LO:SIC"; - for (int i=2; i<(int)pieces.size(); i++) { - if (pieces[i].compare(0, 2, "o=") == 0) { - newsic += ":s=" + tstring; - } else { - newsic += ":" + pieces[i]; - } + for (auto it = m_variants.begin(); it != m_variants.end(); ++it) { + m_free_text << *it << endl; } - sictok->setText(newsic); - m_modifiedQ = true; + return status; } - - - -////////////////////////////// -// -// MeasureData::MeasureData -- -// - -MeasureData::MeasureData(void) { - m_hist7pc.resize(7); - std::fill(m_hist7pc.begin(), m_hist7pc.end(), 0.0); +bool Tool_strophe::run(const string& indata, ostream& out) { + HumdrumFile infile(indata); + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else if (!m_listQ) { + out << infile; + } + return status; } -MeasureData::MeasureData(HumdrumFile& infile, int startline, int stopline) { - setStartLine(startline); - setStopLine(stopline); - setOwner(infile); +bool Tool_strophe::run(HumdrumFile& infile, ostream& out) { + bool status = run(infile); + if (hasAnyText()) { + getAllText(out); + } else if (!m_listQ) { + out << infile; + } + return status; } -MeasureData::MeasureData(HumdrumFile* infile, int startline, int stopline) { - setStartLine(startline); - setStopLine(stopline); - setOwner(infile); +bool Tool_strophe::run(HumdrumFile& infile) { + initialize(); + processFile(infile); + return true; } ////////////////////////////// // -// MeasureData::~MeasureData -- +// Tool_strophe::initialize -- Initializations that only have to be done once +// for all HumdrumFile segments. // -MeasureData::~MeasureData() { - clear(); +void Tool_strophe::initialize(void) { + m_listQ = getBoolean("list"); + m_markQ = getBoolean("m"); + m_marker = getString("marker"); + m_color = getString("color"); } ////////////////////////////// // -// MeasureData::setOwner -- +// Tool_strophe::processFile -- // -void MeasureData::setOwner(HumdrumFile* infile) { - m_owner = infile; -} - - -void MeasureData::setOwner(HumdrumFile& infile) { - m_owner = &infile; +void Tool_strophe::processFile(HumdrumFile& infile) { + infile.analyzeStrophes(); + if (m_listQ) { + displayStropheVariants(infile); + } else { + markWithColor(infile); + } } ////////////////////////////// // -// MeasureData::setStartLine -- +// Tool_strophe::markWithColor -- Maybe give different colors +// to different variants. Currently only marking the primary +// strophe. // -void MeasureData::setStartLine(int startline) { - m_startline = startline; +void Tool_strophe::markWithColor(HumdrumFile& infile) { + int counter = 0; + for (int i=0; iisData() && !current->isNull()) { + // Think about multiple marking for individual notes in chords. + string value = current->getText(); + value += m_marker; + current->setText(value); + output++; + } + current = current->getNextToken(); + } + return output; } ////////////////////////////// // -// MeasureData::getStartLine -- +// displayStropheVariants -- // -int MeasureData::getStartLine(void) { - return m_startline; +void Tool_strophe::displayStropheVariants(HumdrumFile& infile) { + for (int i=0; icompare(0, 3, "*S/") != 0) { + continue; + } + string variant = token->substr(3); + m_variants.insert(variant); + } + } } -////////////////////////////// -// -// MeasureData::getStopLine -- -// -int MeasureData::getStopLine(void) { - return m_stopline; -} -////////////////////////////// +///////////////////////////////// // -// MeasureData::getStartTime -- return the start time in -// quarter notes +// Tool_synco::Tool_synco -- Set the recognized options for the tool. // -double MeasureData::getStartTime(void) { - if (m_owner == NULL) { - return 0.0; - } - if (getStartLine() < 0) { - return 0.0; - } - return (*m_owner)[getStartLine()].getDurationFromStart().getFloat(); +Tool_synco::Tool_synco(void) { + define("c|color=s:skyblue", "SVG color to highlight syncopation notes"); + define("i|info=b", "display only statistics info"); + define("f|filename=b", "add filename to statistics info"); + define("a|all=b", "average all statistics info"); } -////////////////////////////// +///////////////////////////////// // -// MeasureData::getMeasure -- return the measure number of the measure. -// return -1 if no measure number. +// Tool_synco::run -- Do the main work of the tool. // -int MeasureData::getMeasure(void) { - if (m_owner == NULL) { - return -1; - } - if (getStartLine() < 0) { - return -1; - } - HumdrumFile& infile = *m_owner; - if (!infile[getStartLine()].isBarline()) { - return -1; +bool Tool_synco::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; iisKern()) { + continue; + } + HTp etok = infile.getStrandEnd(i); + processStrand(stok, etok); } - return m_owner->getScoreDuration().getFloat(); } ////////////////////////////// // -// MeasureData::clear -- +// Tool_synco::processStrand -- // -void MeasureData::clear(void) { - m_owner = NULL; - m_owner = NULL; - m_startline = -1; - m_startline = -1; - m_hist7pc.resize(7); - std::fill(m_hist7pc.begin(), m_hist7pc.end(), 0.0); - m_sum7pc = 0.0; +void Tool_synco::processStrand(HTp stok, HTp etok) { + HTp current = stok; + while (current && (current != etok)) { + if (!current->isData()) { + current = current->getNextToken(); + continue; + } + if (current->isNull()) { + current = current->getNextToken(); + continue; + } + if (current->isRest()) { + current = current->getNextToken(); + continue; + } + if (current->isSecondaryTiedNote()) { + current = current->getNextToken(); + continue; + } + if (isSyncopated(current)) { + m_hasSyncoQ = true; + m_scount++; + markNote(current); + } + current = current->getNextToken(); + } } ////////////////////////////// // -// MeasureData::getHistogram7pc -- +// Tool_synco::isSyncopated -- // -std::vector& MeasureData::getHistogram7pc(void) { - return m_hist7pc; +bool Tool_synco::isSyncopated(HTp token) { + double metlev = getMetricLevel(token); + HumNum duration = token->getTiedDuration(); + double logDur = log2(duration.getFloat()); + if (metlev == 2) { + return false; + } + if (logDur > metlev) { + return true; + } else { + return false; + } } + ////////////////////////////// // -// MeasureData::getSum7pc -- +// Tool_synco::getMetricLevel -- Assuming whole-note beats for now. // -double MeasureData::getSum7pc(void) { - return m_sum7pc; +double Tool_synco::getMetricLevel(HTp token) { + HumNum durbar = token->getDurationFromBarline(); + if (!durbar.isInteger()) { + return -1.0; + } + if (durbar.getNumerator() % 4 == 0) { + return 2.0; + } + if (durbar.getNumerator() % 2 == 0) { + return 1.0; + } + return 0.0; } ////////////////////////////// // -// MeasureData::generateNoteHistogram -- +// Tool_synco::markNote -- Currently ignoring chords. // -void MeasureData::generateNoteHistogram(void) { - m_hist7pc.resize(7); - std::fill(m_hist7pc.begin(), m_hist7pc.end(), 0.0); - m_sum7pc = 0; - if (m_owner == NULL) { - return; - } - if (m_startline < 0) { - return; - } - if (m_stopline < 0) { - return; - } - - HumdrumFile& infile = *m_owner; - for (int i=m_startline; iisKern()) { +void Tool_synco::markNote(HTp token) { + token->setText(token->getText() + "|"); + if ((token->find('[') != string::npos) || (token->find('_') != string::npos)) { + HTp current = token->getNextToken(); + while (current) { + if (!current->isData()) { + current = current->getNextToken(); continue; } - if (token->isNull()) { + if (current->isNull()) { + current = current->getNextToken(); continue; } - if (token->isRest()) { - continue; + if (current->isRest()) { + break; } - double duration = token->getDuration().getFloat(); - int subtokcount = token->getSubtokenCount(); - for (int k=0; kgetSubtoken(k); - int pc = Convert::kernToBase7PC(subtok); - if (pc < 0) { - continue; - } - m_hist7pc.at(pc) += duration; + if (current->find("_") != string::npos) { + current->setText(current->getText() + "|"); + } else if (current->find("]") != string::npos) { + current->setText(current->getText() + "|"); + break; } + current = current->getNextToken(); } } - m_sum7pc = 0.0; - for (int i=0; i<(int)m_hist7pc.size(); i++) { - m_sum7pc += m_hist7pc[i]; - } } -/////////////////////////////////////////////////////////////////////////// -////////////////////////////// + +///////////////////////////////// // -// MeasureDataSet::MeasureDataSet -- +// Tool_gridtest::Tool_tabber -- Set the recognized options for the tool. // -MeasureDataSet::MeasureDataSet(void) { - m_data.reserve(1000); +Tool_tabber::Tool_tabber(void) { + // do nothing for now. + define("r|remove=b", "remove any extra tabs"); } -////////////////////////////// +/////////////////////////////// // -// MeasureDataSet::MeasureDataSet -- +// Tool_tabber::run -- Primary interfaces to the tool. // -MeasureDataSet::MeasureDataSet(HumdrumFile& infile) { - parse(infile); +bool Tool_tabber::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; igenerateNoteHistogram(); - m_data.push_back(info); - lastbar = i; - } - MeasureData* info = new MeasureData(infile, lastbar, infile.getLineCount() - 1); - m_data.push_back(info); - return 1; +void Tool_tabber::initialize(HumdrumFile& infile) { + // do nothing for now } ////////////////////////////// // -// MeasureDataSet::operator[] -- +// Tool_tabber::processFile -- // -MeasureData& MeasureDataSet::operator[](int index) { - return *m_data[index]; +void Tool_tabber::processFile(HumdrumFile& infile) { + if (getBoolean("remove")) { + infile.removeExtraTabs(); + } else { + infile.addExtraTabs(); + } + infile.createLinesFromTokens(); } -////////////////////////////// + +///////////////////////////////// // -// MeasureDataSet::getScoreDuration -- +// Tool_tandeminfo::Tool_tandeminfo -- Set the recognized options for the tool. // -double MeasureDataSet::getScoreDuration(void) { - if (m_data.empty()) { - return 0.0; - } - return m_data[0]->getScoreDuration(); +Tool_tandeminfo::Tool_tandeminfo(void) { -} + define("c|count=b", "show only unique list of interpretations with counts"); + define("D|no-description|M|no-meaning=b", "do not include descriptions of tandem interpretations in output"); + define("f|filename=b", "show filename"); + define("h|header-only=b", "only process interpretations before first data line"); + define("H|body-only=b", "only process interpretations after first data line"); + define("l|location=b", "show location of interpretation in file (row, column)"); + define("n|sort-by-count=b", "sort entries by unique counts from low to high (when -c is used)"); + define("N|sort-by-reverse-count=b", "sort entries by unique counts from high to low (when -c is used)"); + define("s|sort=b", "sort entries alphabetically by tandem interpretation"); + define("t|table=b", "embed analysis withing input data"); + define("u|unknown-tandem-interpretations-only=b", "only list unknown interpretations"); + define("x|exclusive-interpretations=b", "show exclusive interpretation context"); + define("z|zero-indexed-locations=b", "locations are 0-indexed"); + define("close=b", "close
    by default in HTML output"); + define("humdrum|hmd=b", "textual output formatted with Humdrum syntax"); + m_entries.reserve(1000); +} -/////////////////////////////////////////////////////////////////////////// -////////////////////////////// + +///////////////////////////////// // -// MeasureComparison::MeasureComparison -- +// Tool_tandeminfo::run -- Do the main work of the tool. // -MeasureComparison::MeasureComparison() { - // do nothing +bool Tool_tandeminfo::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; igetSum7pc(); - double sum2 = data2->getSum7pc(); - if ((sum1 == sum2) && (sum1 == 0.0)) { - correlation7pc = 1.0; - return; - } - if (sum1 == 0.0) { - correlation7pc = 0.0; - return; - } - if (sum2 == 0.0) { - correlation7pc = 0.0; - return; - } - correlation7pc = Convert::pearsonCorrelation(data1->getHistogram7pc(), data2->getHistogram7pc()); - if (fabs(correlation7pc - 1.0) < 0.00000001) { - correlation7pc = 1.0; + bool foundDataQ = false; + for (int i=0; igetText(); + string bb = b.token->getText(); + std::transform(aa.begin(), aa.end(), aa.begin(), ::tolower); + std::transform(bb.begin(), bb.end(), bb.begin(), ::tolower); + return (aa < bb); + }); + } + if (m_countQ) { + doCountAnalysis(); + } -////////////////////////////// -// -// MeasureComparisonGrid::MeasureComparisonGrid -- -// + if (m_sortByCountQ) { -MeasureComparisonGrid::MeasureComparisonGrid(void) { - // do nothing -} + sort(m_entries.begin(), m_entries.end(), [](const Entry &a, const Entry &b) { + int anum = a.count; + int bnum = b.count; + if (anum != bnum) { + return anum < bnum; + } + string aa = a.token->getText(); + string bb = b.token->getText(); + std::transform(aa.begin(), aa.end(), aa.begin(), ::tolower); + std::transform(bb.begin(), bb.end(), bb.begin(), ::tolower); + return (aa < bb); + }); + } else if (m_sortByReverseCountQ) { -MeasureComparisonGrid::MeasureComparisonGrid(MeasureDataSet& set1, MeasureDataSet& set2) { - analyze(set1, set2); -} + sort(m_entries.begin(), m_entries.end(), [](const Entry &a, const Entry &b) { + int anum = a.count; + int bnum = b.count; + if (anum != bnum) { + return anum > bnum; + } + string aa = a.token->getText(); + string bb = b.token->getText(); + std::transform(aa.begin(), aa.end(), aa.begin(), ::tolower); + std::transform(bb.begin(), bb.end(), bb.begin(), ::tolower); + return (aa < bb); + }); + } -MeasureComparisonGrid::MeasureComparisonGrid(MeasureDataSet* set1, MeasureDataSet* set2) { - analyze(set1, set2); + if (m_tableQ) { + printEntriesHtml(infile); + } else { + printEntriesText(infile); + } } ////////////////////////////// // -// MeasureComparisonGrid::~MeasureComparisonGrid -- +// Tool_tandeminfo::doCountAnalysis -- // -MeasureComparisonGrid::~MeasureComparisonGrid() { - // do nothing +void Tool_tandeminfo::doCountAnalysis(void) { + m_count.clear(); + for (int i=0; i<(int)m_entries.size(); i++) { + m_count[m_entries[i].token->getText()] += 1; + } + + // store counts in entries: + for (int i=0; i<(int)m_entries.size(); i++) { + m_entries[i].count = m_count[m_entries[i].token->getText()]; + } } ////////////////////////////// // -// MeasureComparisonGrid::clear -- +// Tool_tandeminfo::printEntiesHtml -- Print as embedded HTML code at end of +// input score. // -void MeasureComparisonGrid::clear(void) { - m_grid.clear(); -} +void Tool_tandeminfo::printEntriesHtml(HumdrumFile& infile) { + map processed; // used for -c option + m_humdrum_text << infile; + m_humdrum_text << "!!@@BEGIN: PREHTML" << endl; -////////////////////////////// -// -// MeasureComparisonGrid::analyze -- -// + m_humdrum_text << "!!@SCRIPT:" << endl; + m_humdrum_text << "!!function gotoEditorCoordinate(row, col) {" << endl; + m_humdrum_text << "!! if ((typeof EDITOR == 'undefined') || !EDITOR) {" << endl; + m_humdrum_text << "!! return;" << endl; + m_humdrum_text << "!! }" << endl; + m_humdrum_text << "!! gotoLineFieldInEditor(row, col);" << endl; + m_humdrum_text << "!!}" << endl; + m_humdrum_text << "!!@CONTENT:" << endl; + + m_humdrum_text << "!!" << endl; + + m_humdrum_text << "!!
    " << endl;; + m_humdrum_text << "!!Tandem interpretation information" << endl;; + if (!m_entries.empty()) { + m_humdrum_text << "!!" << endl; + + // print table header + m_humdrum_text << "!!" << endl; + if (m_locationQ) { + m_humdrum_text << "!!" << endl; + } else if (m_countQ) { + m_humdrum_text << "!!" << endl; + } + if (m_exclusiveQ) { + m_humdrum_text << "!!" << endl; + } + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << endl; + + // print table entries + for (int i=0; i<(int)m_entries.size(); i++) { + HTp token = m_entries[i].token; + if (m_countQ && processed[token->getText()]) { + continue; + } + processed[token->getText()] = true; + m_humdrum_text << "!!" << endl; + + if (m_locationQ) { + m_humdrum_text << "!!" << endl; + } else if (m_countQ) { + m_humdrum_text << "!!" << endl; + } + + if (m_exclusiveQ) { + m_humdrum_text << "!!" << endl; + } + + m_humdrum_text << "!!" << endl; -void MeasureComparisonGrid::analyze(MeasureDataSet* set1, MeasureDataSet* set2) { - analyze(*set1, *set2); -} + HumRegex hre; + m_humdrum_text << "!!" << endl; -void MeasureComparisonGrid::analyze(MeasureDataSet& set1, MeasureDataSet& set2) { - m_grid.resize(set1.size()); - for (int i=0; i<(int)m_grid.size(); i++) { - m_grid[i].resize(set2.size()); + m_humdrum_text << "!!" << endl; + } + + m_humdrum_text << "!!
    LocationCountExclusiveTandemDescription
    " << endl; + m_humdrum_text << "!!(" << token->getLineNumber() << ", " << token->getFieldNumber() << ")" << endl; + m_humdrum_text << "!!"; + m_humdrum_text << m_count[token->getText()]; + m_humdrum_text << "" << endl; + m_humdrum_text << "!!" << token->getDataType() << endl; + m_humdrum_text << "!!" << endl; + m_humdrum_text << "!!" << m_entries[i].token << endl; + m_humdrum_text << "!!" << endl; + string description = m_entries[i].description; + // hre.replaceDestructive(description, "<", "<", "g"); + // hre.replaceDestructive(description, ">", ">", "g"); + hre.replaceDestructive(description, "unknown", "unknown"); + hre.replaceDestructive(description, "non-standard", "non-standard"); + m_humdrum_text << "!!" << description << endl; + m_humdrum_text << "!!
    " << endl; } - for (int i=0; i<(int)m_grid.size(); i++) { - for (int j=0; j<(int)m_grid[i].size(); j++) { - m_grid[i][j].compare(set1[i], set2[j]); + + // print relevant settings + vector settings; + if (m_headerOnlyQ) { + settings.push_back("Only processing header interpretations"); + } + if (m_bodyOnlyQ) { + settings.push_back("Only processing body interpretations"); + } + if (m_unknownQ) { + settings.push_back("Only processing unknown interpretations"); + } + if (m_entries.empty()) { + settings.push_back("No interpretations found"); + } + if (!m_entries.empty()) { + if ((!m_countQ) && m_sortQ && !m_entries.empty()) { + settings.push_back("List sorted alphabetically by interpretation"); + } else if (m_countQ && m_sortByCountQ) { + settings.push_back("List sorted low to high by count"); + } else if (m_countQ && m_sortByReverseCountQ) { + settings.push_back("List sorted high to low by count"); } } - m_set1 = &set1; - m_set2 = &set2; + if (!settings.empty()) { + m_humdrum_text << "!!
      " << endl; + for (int i=0; i<(int)settings.size(); i++) { + m_humdrum_text << "!!
    • "; + m_humdrum_text << settings[i]; + m_humdrum_text << "
    • " << endl; + } + m_humdrum_text << "!!
    " << endl; + } + + m_humdrum_text << "!!
    " << endl; + m_humdrum_text << "!!@@END: PREHTML" << endl; } ////////////////////////////// // -// MeasureComparisonGrid::printCorrelationGrid -- -// default value: out = std::cout +// Tool_tandeminfo::printEntiesText -- // -ostream& MeasureComparisonGrid::printCorrelationGrid(ostream& out) { - for (int i=0; i<(int)m_grid.size(); i++) { - for (int j=0; j<(int)m_grid[i].size(); j++) { - double correl = m_grid[i][j].getCorrelation7pc(); - if (correl > 0.0) { - out << int(correl * 100.0 + 0.5)/100.0; - } else { - out << -int(-correl * 100.0 + 0.5)/100.0; - } - if (j < (int)m_grid[i].size() - 1) { - out << '\t'; - } +void Tool_tandeminfo::printEntriesText(HumdrumFile& infile) { + map processed; // used for -c option + + if (m_humdrumQ) { + if (m_locationQ) { + m_free_text << "**loc" << "\t"; + } else if (m_countQ) { + m_free_text << "**count" << "\t"; } - out << endl; + if (m_filenameQ) { + m_free_text << "**file" << "\t"; + } + if (m_exclusiveQ) { + m_free_text << "**exinterp" << "\t"; + } + m_free_text << "**tandem"; + if (m_descriptionQ) { + m_free_text << "\t" << "**info"; + } + m_free_text << endl; } - return out; -} - + for (int i=0; i<(int)m_entries.size(); i++) { + HTp token = m_entries[i].token; + if (m_countQ && processed[token->getText()]) { + continue; + } + processed[token->getText()] = true; -////////////////////////////// -// -// MeasureComparisonGrid::printCorrelationDiagonal -- Assuming a square grid for now. -// default value: out = std::cout -// - -ostream& MeasureComparisonGrid::printCorrelationDiagonal(ostream& out) { - for (int i=0; i<(int)m_grid.size(); i++) { - for (int j=0; j<(int)m_grid[i].size(); j++) { - if (i != j) { - continue; - } - double correl = m_grid[i][j].getCorrelation7pc(); - if (correl > 0.0) { - out << int(correl * 100.0 + 0.5)/100.0; + string description = m_entries[i].description; + HumRegex hre; + hre.replaceDestructive(description, "", "", "g"); + if (m_filenameQ) { + m_free_text << infile.getFilename() << "\t"; + } + if (m_locationQ) { + if (m_zeroQ) { + int row = token->getLineIndex(); + int col = token->getFieldIndex(); + m_free_text << "(" << row << ", " << col << ")" << "\t"; } else { - out << -int(-correl * 100.0 + 0.5)/100.0; + int row = token->getLineNumber(); + int col = token->getFieldNumber(); + m_free_text << "(" << row << ", " << col << ")" << "\t"; } - if (j < (int)m_grid[i].size() - 1) { - out << '\t'; + } else if (m_countQ) { + m_free_text << m_count[token->getText()] << "\t"; + } + if (m_exclusiveQ) { + string exinterp = token->getDataType(); + if (m_humdrumQ) { + exinterp = exinterp.substr(2); } + m_free_text << exinterp << "\t"; } - out << endl; + if (m_humdrumQ) { + string text = token->getText(); + text = text.substr(1); + m_free_text << text; + } else { + m_free_text << token; + } + if (m_descriptionQ) { + m_free_text << "\t" << description; + } + m_free_text << endl; + } + + if (m_humdrumQ) { + if (m_locationQ) { + m_free_text << "*-" << "\t"; + } else if (m_countQ) { + m_free_text << "*-" << "\t"; + } + if (m_filenameQ) { + m_free_text << "*-" << "\t"; + } + if (m_exclusiveQ) { + m_free_text << "*-" << "\t"; + } + m_free_text << "*-"; + if (m_descriptionQ) { + m_free_text << "\t" << "*-"; + } + m_free_text << endl; } - return out; } ////////////////////////////// // -// MeasureComparisonGrid::getColorMapping -- +// Tool_tandeminfo::getDescription -- Return description of the input token; otherwise, return m_unknown. // -void MeasureComparisonGrid::getColorMapping(double input, double& hue, - double& saturation, double& lightness) { - double maxhue = 0.75 * 360.0; - hue = input; - if (hue < 0.0) { - hue = 0.0; +string Tool_tandeminfo::getDescription(HTp token) { + string tok = token->substr(1); + string description; + + description = checkForKeySignature(tok); + if (description != m_unknown) { + return description; } - hue = hue * hue; - if (hue != 1.0) { - hue *= 0.95; + + description = checkForKeyDesignation(tok); + if (description != m_unknown) { + return description; } - hue = (1.0 - hue) * 360.0; - if (hue == 0.0) { - // avoid -0.0; - hue = 0.0; + description = checkForInstrumentInfo(tok); + if (description != m_unknown) { + return description; } - if (hue > maxhue) { - hue = maxhue; + description = checkForLabelInfo(tok); + if (description != m_unknown) { + return description; } - if (hue < 0.0) { - hue = maxhue; + + description = checkForTimeSignature(tok); + if (description != m_unknown) { + return description; } - saturation = 100.0; - lightness = 50.0; + description = checkForMeter(tok); + if (description != m_unknown) { + return description; + } - if (hue > 60) { - lightness = lightness - (hue-60) / (maxhue-60) * lightness / 1.5; + description = checkForTempoMarking(tok); + if (description != m_unknown) { + return description; } -} + description = checkForClef(tok); + if (description != m_unknown) { + return description; + } + description = checkForStaffPartGroup(tok); + if (description != m_unknown) { + return description; + } -////////////////////////////// -// -// MeasureComparisonGrid::getQoff1 -- return the end time class ID of the -// current grid cell (for the first piece being compared). -// + description = checkForTuplet(tok); + if (description != m_unknown) { + return description; + } -std::string MeasureComparisonGrid::getQoff1(int index) { - if (m_set1 == NULL) { - return ""; + description = checkForHands(tok); + if (description != m_unknown) { + return description; } - return (*m_set1)[index].getQoff(); -} + description = checkForPosition(tok); + if (description != m_unknown) { + return description; + } + description = checkForCue(tok); + if (description != m_unknown) { + return description; + } -////////////////////////////// -// -// MeasureComparisonGrid::getQoff2 -- return the end time class ID of the -// current grid cell (for the first piece being compared). -// + description = checkForFlip(tok); + if (description != m_unknown) { + return description; + } -std::string MeasureComparisonGrid::getQoff2(int index) { - if (m_set2 == NULL) { - return ""; + description = checkForTremolo(tok); + if (description != m_unknown) { + return description; } - return (*m_set2)[index].getQoff(); -} + description = checkForOttava(tok); + if (description != m_unknown) { + return description; + } + description = checkForPedal(tok); + if (description != m_unknown) { + return description; + } -////////////////////////////// -// -// MeasureComparisonGrid::getQon1 -- return the start time class ID of the -// current grid cell (for the first piece being compared). -// + description = checkForBracket(tok); + if (description != m_unknown) { + return description; + } -string MeasureComparisonGrid::getQon1(int index) { - if (m_set1 == NULL) { - return ""; + description = checkForRscale(tok); + if (description != m_unknown) { + return description; } - return (*m_set1)[index].getQon(); -} + description = checkForTimebase(tok); + if (description != m_unknown) { + return description; + } + description = checkForTransposition(tok); + if (description != m_unknown) { + return description; + } -////////////////////////////// -// -// MeasureComparisonGrid::getQon2 -- return the start time class ID of the -// current grid cell (for the second piece being compared). -// + description = checkForGrp(tok); + if (description != m_unknown) { + return description; + } -string MeasureComparisonGrid::getQon2(int index) { - if (m_set2 == NULL) { - return ""; + description = checkForStria(tok); + if (description != m_unknown) { + return description; + } + + description = checkForFont(tok); + if (description != m_unknown) { + return description; } - return (*m_set2)[index].getQon(); -} + description = checkForVerseLabels(tok); + if (description != m_unknown) { + return description; + } + description = checkForLanguage(tok); + if (description != m_unknown) { + return description; + } -////////////////////////////// -// -// MeasureComparisonGrid::getMeasure1 -- return the measure of the -// current grid cell (for the first piece being compared). -// + description = checkForStemInfo(tok); + if (description != m_unknown) { + return description; + } -int MeasureComparisonGrid::getMeasure1(int index) { - if (m_set1 == NULL) { - return 0.0; + description = checkForXywh(tok); + if (description != m_unknown) { + return description; } - return (*m_set1)[index].getMeasure(); -} + description = checkForCustos(tok); + if (description != m_unknown) { + return description; + } + description = checkForTextInterps(tok); + if (description != m_unknown) { + return description; + } -////////////////////////////// -// -// MeasureComparisonGrid::getMeasure2 -- return the measure of the -// current grid cell (for the second piece being compared). -// + description = checkForRep(tok); + if (description != m_unknown) { + return description; + } -int MeasureComparisonGrid::getMeasure2(int index) { - if (m_set2 == NULL) { - return 0.0; + description = checkForPline(tok); + if (description != m_unknown) { + return description; } - return (*m_set2)[index].getMeasure(); -} + description = checkForTacet(tok); + if (description != m_unknown) { + return description; + } + description = checkForFb(tok); + if (description != m_unknown) { + return description; + } -////////////////////////////// -// -// MeasureComparisonGrid::getStartTime1 -- return the start time of the -// measure at index position in the first compared score. -// + description = checkForColor(tok); + if (description != m_unknown) { + return description; + } -double MeasureComparisonGrid::getStartTime1(int index) { - if (m_set1 == NULL) { - return 0.0; + description = checkForThru(tok); + if (description != m_unknown) { + return description; + } + + HumRegex hre; + if (hre.search(token, "\\s+$")) { + return "unknown (space at end of interpretation may be the problem)"; + } else { + return m_unknown; } - return (*m_set1)[index].getStartTime(); } ////////////////////////////// // -// MeasureComparisonGrid::getScoreDuration1 -- +// Tool_tandeminfo::checkForThru -- Humdrum Toolkit interpretations related to thru command. // -double MeasureComparisonGrid::getScoreDuration1(void) { - if (m_set1 == NULL) { - return 0.0; +string Tool_tandeminfo::checkForThru(const string& tok) { + if (tok == "thru") { + return "data processed by thru command (expansion lists processed)"; } - return m_set1->getScoreDuration(); + + return m_unknown; } ////////////////////////////// // -// MeasureComparisonGrid::getStartTime2 -- +// Tool_tandeminfo::checkForColor -- Extended interprerations for coloring notes in **kern data. +// Used in verovio. // -double MeasureComparisonGrid::getStartTime2(int index) { - if (m_set2 == NULL) { - return 0.0; +string Tool_tandeminfo::checkForColor(const string& tok) { + HumRegex hre; + if (hre.search(tok, "^color:(.*)")) { + string color = hre.getMatch(1); + string output; + if (hre.search(tok, "^#[0-9A-Fa-f]{3}$")) { + output = "3-digit hex "; + } else if (hre.search(tok, "^#[0-9A-Fa-f]{6}$")) { + output = "6-digit hex "; + } else if (hre.search(tok, "^#[0-9A-Fa-f]{8}$")) { + output = "8-digit hex (RGB + transparency)"; + } else if (hre.search(tok, "^rgb(\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+)$")) { + output = "RGB integer"; + } else if (hre.search(tok, "^rgb(\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*,[\\d.]+)$")) { + output = "RGB integer with alpha"; + } else if (hre.search(tok, "^hsl(\\d+\\s*,\\s*\\d+%\\s*,\\s*\\d+%)$")) { + output = "HSL"; + } else if (hre.search(tok, "^hsl(\\d+\\s*,\\s*\\d+%\\s*,\\s*\\d+%,\\s*[\\d.]+)$")) { + output = "HSL with alpha"; + } else if (hre.search(tok, "^[a-z]+$")) { + output = "named "; + } + output += " color"; + return output; } - return (*m_set2)[index].getStartTime(); + + return m_unknown; } ////////////////////////////// // -// MeasureComparisonGrid::getStopTime1 -- +// Tool_tandeminfo::checkForFb -- Extended interprerations especially for **fb (**fa) exclusive +// interpretations. // -double MeasureComparisonGrid::getStopTime1(int index) { - if (m_set1 == NULL) { - return 0.0; +string Tool_tandeminfo::checkForFb(const string& tok) { + if (tok == "reverse") { + return "reverse order of accidental and number in figured bass"; } - return (*m_set1)[index].getStopTime(); + if (tok == "Xreverse") { + return "stop reversing order of accidental and number in figured bass"; + } + + return m_unknown; } ////////////////////////////// // -// MeasureComparisonGrid::getStopTime2 -- +// Tool_tandeminfo::checkForTacet -- Extended interprerations for marking parts that are not +// playing (rests only) in a movement/movement subsection. // -double MeasureComparisonGrid::getStopTime2(int index) { - if (m_set2 == NULL) { - return 0.0; +string Tool_tandeminfo::checkForTacet(const string& tok) { + if (tok == "tacet") { + return "part is tacet in movement/section"; } - return (*m_set2)[index].getStopTime(); + if (tok == "Xtacet") { + return "end of part tacet"; + } + + return m_unknown; } ////////////////////////////// // -// MeasureComparisonGrid::getDuration1 -- +// Tool_tandeminfo::checkForRep -- Extended interprerations for poetic line analysis related to pline tool. // -double MeasureComparisonGrid::getDuration1(int index) { - if (m_set1 == NULL) { - return 0.0; +string Tool_tandeminfo::checkForPline(const string& tok) { + HumRegex hre; + if (hre.search(tok, "^pline:(\\d+)([abcr]*)$")) { + string number = hre.getMatch(1); + string info = hre.getMatch(2); + string output = "poetic line markup: " + number + info; + return output; } - return (*m_set1)[index].getDuration(); + + return m_unknown; } ////////////////////////////// // -// MeasureComparisonGrid::getDuration2 -- +// Tool_tandeminfo::checkForRep -- Extended interprerations for adding repeat sign shorthand for +// repeated music. // -double MeasureComparisonGrid::getDuration2(int index) { - if (m_set2 == NULL) { - return 0.0; +string Tool_tandeminfo::checkForRep(const string& tok) { + if (tok == "rep") { + return "start of repeat sign replacing notes/rests"; } - return (*m_set2)[index].getDuration(); + if (tok == "Xrep") { + return "end of repeat sign replacing notes/rests"; + } + + return m_unknown; } ////////////////////////////// // -// MeasureComparisonGrid::getScoreDuration2 -- +// Tool_tandeminfo::checkForTextInterps -- Extended interprerations for **text and **silbe // -double MeasureComparisonGrid::getScoreDuration2(void) { - if (m_set2 == NULL) { - return 0.0; +string Tool_tandeminfo::checkForTextInterps(const string& tok) { + if (tok == "ij") { + return "start of text repeat region"; } - return m_set2->getScoreDuration(); + if (tok == "Xij") { + return "end of text repeat region"; + } + if (tok == "edit") { + return "start of editorial text region"; + } + if (tok == "Xedit") { + return "end of editorial text region"; + } + + return m_unknown; } ////////////////////////////// // -// MeasureComparisonGrid::printSvgGrid -- -// default value: out = std::cout +// Tool_tandeminfo::checkForCustos -- Extended interprerations for marker +// at end of system for next note in part. // -ostream& MeasureComparisonGrid::printSvgGrid(ostream& out) { - pugi::xml_document image; - auto declaration = image.prepend_child(pugi::node_declaration); - declaration.append_attribute("version") = "1.0"; - declaration.append_attribute("encoding") = "UTF-8"; - declaration.append_attribute("standalone") = "no"; - - auto svgnode = image.append_child("svg"); - svgnode.append_attribute("version") = "1.1"; - svgnode.append_attribute("xmlns") = "http://www.w3.org/2000/svg"; - svgnode.append_attribute("xmlns:xlink") = "http://www.w3.org/1999/xlink"; - svgnode.append_attribute("overflow") = "visible"; - svgnode.append_attribute("viewBox") = "0 0 1000 1000"; - svgnode.append_attribute("width") = "1000px"; - svgnode.append_attribute("height") = "1000px"; - - auto grid = svgnode.append_child("g"); - grid.append_attribute("id") = "grid"; - - double hue = 0.0; - double saturation = 100; - double lightness = 75; - - pugi::xml_node crect; - double width; - double height; - - stringstream ss; - stringstream css; - double x; - double y; - - double imagewidth = 1000.0; - double imageheight = 1000.0; - - double sdur1 = getScoreDuration1(); - double sdur2 = getScoreDuration2(); +string Tool_tandeminfo::checkForCustos(const string& tok) { + HumRegex hre; - for (int i=0; i<(int)m_grid.size(); i++) { - for (int j=0; j<(int)m_grid[i].size(); j++) { - width = getDuration2(j) / sdur2 * imagewidth; - height = getDuration1(i) / sdur1 * imageheight; + if (tok == "custos") { + return "custos, pitch unspecified"; + } - x = getStartTime2(j)/sdur2 * imageheight; - y = getStartTime1(i)/sdur1 * imagewidth; + if (tok == "custos:") { + return "custos, pitch unspecified"; + } - getColorMapping(m_grid[i][j].getCorrelation7pc(), hue, saturation, lightness); - ss << "hsl(" << hue << "," << saturation << "%," << lightness << "%)"; - crect = grid.append_child("rect"); - crect.append_attribute("x") = to_string(x).c_str(); - crect.append_attribute("y") = to_string(y).c_str(); - crect.append_attribute("width") = to_string(width*0.99).c_str(); - crect.append_attribute("height") = to_string(height*0.99).c_str(); - crect.append_attribute("fill") = ss.str().c_str(); - css << "Xm" << getMeasure1(i) << " Ym" << getMeasure2(j); - css << " X" << getQon1(i) << " Y" << getQon2(j); - css << " X" << getQoff1(i) << " Y" << getQoff2(j); - crect.append_attribute("class") = css.str().c_str(); - ss.str(""); - css.str(""); - } + if (hre.search(tok, "^custos:([A-G]+|[a-g]+)(#+|-+|n)?$")) { + // also deal with chord custos + string pitch = hre.getMatch(1); + string accid = hre.getMatch(2); + string output = "custos on pitch " + pitch + accid; + return output; } - image.save(out); - return out; + return m_unknown; } -/////////////////////////////////////////////////////////////////////////// - -///////////////////////////////// +////////////////////////////// // -// Tool_simat::Tool_simat -- Set the recognized options for the tool. +// Tool_tandeminfo::checkForStemInfo -- Extended interprerations +// for visual display of stems (on left or right side of notes). // -Tool_simat::Tool_simat(void) { - define("r|raw=b", "output raw correlation matrix"); - define("d|diagonal=b", "output diagonal of correlation matrix"); +string Tool_tandeminfo::checkForXywh(const string& tok) { + HumRegex hre; + if (hre.search(tok, "^xywh-([^:\\s]+):(\\d+),(\\d+),(\\d+),(\\d+)$")) { + string page = hre.getMatch(1); + string x = hre.getMatch(2); + string y = hre.getMatch(3); + string w = hre.getMatch(4); + string h = hre.getMatch(5); + string output = "IIIF bounding box, page="; + output += page; + output += ", x=" + x; + output += ", y=" + y; + output += ", w=" + w; + output += ", h=" + h; + return output; + } + + return m_unknown; } -///////////////////////////////// +////////////////////////////// // -// Tool_simat::run -- Primary interfaces to the tool. +// Tool_tandeminfo::checkForStemInfo -- Extended interprerations +// for visual display of stems (on left or right side of notes). // -bool Tool_simat::run(HumdrumFileSet& infiles) { - bool status = true; - if (infiles.getCount() == 1) { - status = run(infiles[0], infiles[0]); - } else if (infiles.getCount() > 1) { - status = run(infiles[0], infiles[1]); - } else { - status = false; - } - return status; -} - +string Tool_tandeminfo::checkForStemInfo(const string& tok) { + HumRegex hre; -bool Tool_simat::run(const string& indata1, const string& indata2, ostream& out) { - HumdrumFile infile1(indata1); - HumdrumFile infile2; - bool status; - if (indata2.empty()) { - infile2.read(indata2); - status = run(infile1, infile2); - } else { - status = run(infile1, infile1); - } - if (hasAnyText()) { - getAllText(out); - } else { - out << infile1; - out << infile2; + if (hre.search(tok, "^(\\d+)/left$")) { + string rhythm = hre.getMatch(1); + string output = rhythm + "-rhythm notes always have stem up on the left"; + return output; } - return status; -} - -bool Tool_simat::run(HumdrumFile& infile1, HumdrumFile& infile2, ostream& out) { - bool status; - if (infile2.getLineCount() == 0) { - status = run(infile1, infile1); - } else { - status = run(infile1, infile2); - } - if (hasAnyText()) { - getAllText(out); - } else { - out << infile1; - out << infile2; + if (hre.search(tok, "^(\\d+)\\\\left$")) { + string rhythm = hre.getMatch(1); + string output = rhythm + "-rhythm notes always have stem down on the left"; + return output; } - return status; -} - -// -// In-place processing of file: -// -bool Tool_simat::run(HumdrumFile& infile1, HumdrumFile& infile2) { - if (infile2.getLineCount() == 0) { - processFile(infile1, infile1); - } else { - processFile(infile1, infile2); + if (hre.search(tok, "^(\\d+)/right$")) { + string rhythm = hre.getMatch(1); + string output = rhythm + "-rhythm notes always have stem up on the right"; + return output; } - return true; -} - - - -////////////////////////////// -// -// Tool_simat::processFile -- -// + if (hre.search(tok, "^(\\d+)\\\\right$")) { + string rhythm = hre.getMatch(1); + string output = rhythm + "-rhythm notes always have stem down on the right"; + return output; + } -void Tool_simat::processFile(HumdrumFile& infile1, HumdrumFile& infile2) { - m_data1.parse(infile1); - m_data2.parse(infile2); - m_grid.analyze(m_data1, m_data2); - if (getBoolean("raw")) { - m_grid.printCorrelationGrid(m_free_text); - suppressHumdrumFileOutput(); - } else if (getBoolean("diagonal")) { - m_grid.printCorrelationDiagonal(m_free_text); - suppressHumdrumFileOutput(); - } else { - m_grid.printSvgGrid(m_free_text); - suppressHumdrumFileOutput(); + if (tok == "all/right") { + string output = "all notes always have stem up on the right"; + return output; } -} + if (tok == "all\\right") { + string output = "all notes always have stem down on the right"; + return output; + } + if (tok == "all/left") { + string output = "all notes always have stem up on the left"; + return output; + } + if (tok == "all\\left") { + string output = "all notes always have stem down on the left"; + return output; + } + if (tok == "all/center") { + string output = "all notes always have stem up on notehead center"; + return output; + } -///////////////////////////////// -// -// Tool_slurcheck::Tool_slurcheck -- Set the recognized options for the tool. -// + if (tok == "all\\center") { + string output = "all notes always have stem down on notehead center"; + return output; + } + // there is also "middle" which is the same as "center"; -Tool_slurcheck::Tool_slurcheck(void) { - // add options here - define("l|list=b", "list locations of unclosed slur endings"); - define("c|count=b", "count unclosed slur endings"); - define("Z|no-zeros=b", "do not list files that have zero unclosed slurs in counts"); - define("f|filename=b", "print filename for list and count options"); + return m_unknown; } -///////////////////////////////// +////////////////////////////// // -// Tool_slurcheck::run -- Do the main work of the tool. +// Tool_tandeminfo::checkForLanguage -- Humdrum Toolkit and extended interprerations +// for langauages (for **text and **silbe). // -bool Tool_slurcheck::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i=unknown"; + return output; + } + string output = "language code"; + if (code.size() == 2) { + output = "ISO 639-3 two-letter language code: "; + } else if (code.size() == 3) { + output = "ISO 639-3 three-letter language code: "; + } + output += ""; + output += code; + output += "=\""; + output += name; + output += "\""; + return output; } - return status; -} - -bool Tool_slurcheck::run(HumdrumFile& infile) { - initialize(); - processFile(infile); - infile.createLinesFromTokens(); - return true; + return m_unknown; } ////////////////////////////// // -// Tool_slurcheck::initialize -- +// Tool_tandeminfo::checkForVerseLabels -- Extended tandem interpretations (used by verovio +// for visual rendeing of notation). // -void Tool_slurcheck::initialize(void) { +string Tool_tandeminfo::checkForVerseLabels(const string& tok) { + HumRegex hre; + if (hre.search(tok, "^v:(.*)$")) { + string output = "verse label \"" + hre.getMatch(1) + "\""; + return output; + } + if (hre.search(tok, "^vv:(.*)$")) { + string output = "verse label \"" + hre.getMatch(1) + "\", repeated after each system break"; + return output; + } + + return m_unknown; } ////////////////////////////// // -// Tool_slurcheck::processFile -- +// Tool_tandeminfo::checkForFont -- Extended interprtations for styling **text and **silbe. // -void Tool_slurcheck::processFile(HumdrumFile& infile) { - infile.analyzeSlurs(); - int opencount = 0; - int closecount = 0; - int listQ = getBoolean("list"); - int countQ = getBoolean("count"); - int zeroQ = !getBoolean("no-zeros"); - int filenameQ = getBoolean("filename"); - if (listQ || countQ) { - suppressHumdrumFileOutput(); - } - for (int i=0; iisKern()) { - continue; - } - HTp etok = infile.getStrandEnd(i); - HTp tok = stok; - while (tok && (tok != etok)) { - if (!tok->isData()) { - tok = tok->getNextToken(); - continue; - } - if (tok->isNull()) { - tok = tok->getNextToken(); - continue; - } - string value = tok->getValue("auto", "hangingSlur"); - if (value == "true") { - string side = tok->getValue("auto", "slurSide"); - if (side == "start") { - opencount++; - if (listQ) { - if (filenameQ) { - m_free_text << infile.getFilename() << ":\t"; - } - m_free_text << "UNCLOSED SLUR\tline:" << tok->getLineIndex()+1 - << "\tfield:" << tok->getFieldIndex()+1 << "\ttoken:" << tok << endl; - } else if (!countQ) { - string data = *tok; - data += "i"; - tok->setText(data); - } - } else if (side == "stop") { - closecount++; - if (listQ) { - if (filenameQ) { - m_free_text << infile.getFilename() << ":\t"; - } - m_free_text << "UNOPENED SLUR\tline:" << tok->getLineIndex()+1 - << "\tfield:" << tok->getFieldIndex()+1 << "\ttoken:" << tok << endl; - } else if (!countQ) { - string data = *tok; - data += "j"; - tok->setText(data); - } - } - } - tok = tok->getNextToken(); - } - } - - if (countQ) { - int sum = opencount + closecount; - if ((!zeroQ) && (sum == 0)) { - return; - } - if (filenameQ) { - m_free_text << infile.getFilename() << ":\t"; - } - m_free_text << (opencount + closecount) << "\t(:" << opencount << "\t):" << closecount << endl; - } - - if (countQ || listQ) { - return; +string Tool_tandeminfo::checkForFont(const string& tok) { + if (tok == "italic") { + return "use italic font style"; } - - if (opencount + closecount == 0) { - return; + if (tok == "Xitalic") { + return "stop using italic font style"; } - - if (opencount) { - infile.appendLine("!!!RDF**kern: i = marked note, color=\"hotpink\", text=\"extra(\""); + if (tok == "bold") { + return "use bold font style"; } - - if (closecount) { - infile.appendLine("!!!RDF**kern: j = marked note, color=\"magenta\", text=\"extra)\""); + if (tok == "Xbold") { + return "stop using bold font style"; } - infile.createLinesFromTokens(); + return m_unknown; } - - -///////////////////////////////// +////////////////////////////// // -// Tool_gridtest::Tool_spinetrace -- Set the recognized options for the tool. +// Tool_tandeminfo::checkForStria -- Humdrum Toolkit interpretation. // -Tool_spinetrace::Tool_spinetrace(void) { - define("a|append=b", "append analysis to input data lines"); - define("p|prepend=b", "prepend analysis to input data lines"); +string Tool_tandeminfo::checkForStria(const string& tok) { + HumRegex hre; + if (hre.search(tok, "^stria(\\d+)$")) { + string output = "number of staff lines:" + hre.getMatch(1); + return output; + } + + return m_unknown; } -/////////////////////////////// +////////////////////////////// // -// Tool_spinetrace::run -- Primary interfaces to the tool. +// Tool_tandeminfo::checkForGrp -- Polyrhythm project interpretations for +// polyrhythm group assignments. Related to humlib composite tool. // -bool Tool_spinetrace::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; icompare(0, 2, "**") == 0) { - m_humdrum_text << "**spine"; - } else { - m_humdrum_text << token; - } - if (j < fieldcount - 1) { - m_humdrum_text << "\t"; - } - } - } else { - m_humdrum_text << infile[i]; - } - } else { - int fieldcount = infile[i].getFieldCount(); - for (int j=0; jgetSpineInfo(); - if (j < fieldcount - 1) { - m_humdrum_text << '\t'; - } - } - } - - if (prependQ) { - m_humdrum_text << "\t" << infile[i]; - } - m_humdrum_text << "\n"; +string Tool_tandeminfo::checkForTimebase(const string& tok) { + HumRegex hre; + if (hre.search(tok, "^tb(\\d+)$")) { + string number = hre.getMatch(1); + string output = "timebase: all data lines (should) have a duration of " + number; + return output; } -} + return m_unknown; +} -///////////////////////////////// +////////////////////////////// // -// Tool_strophe::Tool_strophe -- Set the recognized options for the tool. +// Tool_tandeminfo::checkForRscale -- Extended interpretation for adjusting the visual +// display of note durations when they do not match the logical +// note durations (such as show a quarter note as if it were a +// half note, which would be indicated by "*rscale:2". Or a +// half note as if it were a quarter note with "*rscale:1/2". +// Also related to the rscale tool from Humdrum Extras and humlib. +// Used in verovio. // -Tool_strophe::Tool_strophe(void) { - define("l|list=b", "list all possible variants"); - define("m=b", "mark strophe music"); - define("mark|marker=s:@", "character to mark with"); - define("c|color=s:red", "character to mark with"); +string Tool_tandeminfo::checkForRscale(const string& tok) { + HumRegex hre; + if (hre.search(tok, "^rscale:(\\d+)(/\\d+)?$")) { + string fraction = hre.getMatch(1) + hre.getMatch(2); + string output = "visual rhythmic scaling factor " + fraction; + return output; + } + + return m_unknown; } -///////////////////////////////// +////////////////////////////// // -// Tool_strophe::run -- Do the main work of the tool. +// Tool_tandeminfo::checkForBracket -- Extended interpretations for displaying +// various bracket lines in visual music notation. // -bool Tool_strophe::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; iisData() && !current->isNull()) { - // Think about multiple marking for individual notes in chords. - string value = current->getText(); - value += m_marker; - current->setText(value); - output++; - } - current = current->getNextToken(); +string Tool_tandeminfo::checkForFlip(const string& tok) { + if (tok == "flip") { + return "switch order of subspines, specific to flipper tool"; } - return output; + if (tok == "Xflip") { + return "cancel flipping of subspine, specific to flipper tool"; + } + return m_unknown; } ////////////////////////////// // -// displayStropheVariants -- +// Tool_tandeminfo::checkForCue -- Extended interpretations for visual rendering +// *cue means display as cue-sized notes. Probably change +// this so that *cue means following notes are cue notes +// and add *cuesz for cue-sized notes (that are not cues +// from other instruments). // -void Tool_strophe::displayStropheVariants(HumdrumFile& infile) { - for (int i=0; icompare(0, 3, "*S/") != 0) { - continue; - } - string variant = token->substr(3); - m_variants.insert(variant); - } +string Tool_tandeminfo::checkForCue(const string& tok) { + if (tok == "cue") { + return "cue-sized notation follows"; } + if (tok == "Xcue") { + return "cancel cue-sized notation"; + } + return m_unknown; } +////////////////////////////// +// +// Tool_tandeminfo::checkForPosition -- Extended interpretations for visual rendering +// data above/below staff. Useful in particular for **dynam. +// Staff number in part (relative to top staff) can be given +// as a number following a colon after the placement. +// +string Tool_tandeminfo::checkForPosition(const string& tok) { + if (tok == "above") { + return "place items above staff"; + } + if (tok == "above:1") { + return "place items above first staff of part"; + } + if (tok == "above:2") { + return "place items above second staff of part"; + } + if (tok == "below") { + return "place items below staff"; + } + if (tok == "below:1") { + return "place items below first staff of part"; + } + if (tok == "below:2") { + return "place items below second staff of part"; + } + if (tok == "center") { + return "centered items between two staves"; + } + return m_unknown; +} -///////////////////////////////// +////////////////////////////// // -// Tool_synco::Tool_synco -- Set the recognized options for the tool. +// Tool_tandeminfo::checkForHands -- Extended interpretations to indicate which +// hand is playing the notes (for grand-staff keyboard in particular). // -Tool_synco::Tool_synco(void) { - define("c|color=s:skyblue", "SVG color to highlight syncopation notes"); - define("i|info=b", "display only statistics info"); - define("f|filename=b", "add filename to statistics info"); - define("a|all=b", "average all statistics info"); +string Tool_tandeminfo::checkForHands(const string& tok) { + if (tok == "LH") { + return "notes played by left hand"; + } + if (tok == "RH") { + return "notes played by right hand"; + } + return m_unknown; } -///////////////////////////////// +////////////////////////////// // -// Tool_synco::run -- Do the main work of the tool. +// Tool_tandeminfo::checkForTuplet -- Extended interpretations for **kern data to control +// visual stylings of tuplet numbers and brackets. // -bool Tool_synco::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; iisKern()) { - continue; +string Tool_tandeminfo::checkForTimeSignature(const string& tok) { + HumRegex hre; + if (tok == "MX") { + return "unmeasured music time signature"; + } + if (hre.search(tok, "^MX/(\\d+)(%\\d+)?(yy)?")) { + string output = "unmeasured music with beat " + hre.getMatch(1) + hre.getMatch(2); + if (hre.getMatch(3) == "yy") { + output += ", invisible"; + return output; } - HTp etok = infile.getStrandEnd(i); - processStrand(stok, etok); } + if (hre.search(tok, "^M(\\d+)/(\\d+)(%\\d+)?(yy)?$")) { + string top = hre.getMatch(1); + string bot = hre.getMatch(2) + hre.getMatch(3); + string invisible = hre.getMatch(4); + string output = "time signature: top="; + output += ""; + output += top; + output += ""; + output += ", bottom="; + output += ""; + output += bot; + output += ""; + if (bot == "3%2") { + output += " (triplet semibreve)"; + } else if (bot == "3%4") { + output += " (triplet breve)"; + } + if (invisible == "yy") { + output += ", invisible"; + } + return output; + } + + return m_unknown; } ////////////////////////////// // -// Tool_synco::processStrand -- +// Tool_tandeminfo::checkForMeter -- Humdrum Toolkit interpretations. Extended for use +// with mensural signs. // -void Tool_synco::processStrand(HTp stok, HTp etok) { - HTp current = stok; - while (current && (current != etok)) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); - continue; - } - if (current->isRest()) { - current = current->getNextToken(); - continue; - } - if (current->isSecondaryTiedNote()) { - current = current->getNextToken(); - continue; - } - if (isSyncopated(current)) { - m_hasSyncoQ = true; - m_scount++; - markNote(current); +string Tool_tandeminfo::checkForMeter(const string& tok) { + HumRegex hre; + if (hre.search(tok, "^(m|o)?met\\((.*?)\\)$")) { + string modori = hre.getMatch(1); + string meter = hre.getMatch(2); + if (meter == "c") { + return "meter: common time"; + } + if (meter == "c|") { + return "meter: cut time"; + } + if (meter == "") { + return "meter: empty"; + } + string output = "mensuration sign: "; + if (meter == "O") { + output += "circle"; + } else if (meter == "O|") { + output += "cut-circle"; + } else if (meter == "C") { + output += "c"; + } else if (meter == "C|") { + output += "cut-c"; + } else if (meter == "Cr") { + output += "reverse-c"; + } else if (meter == "C.") { + output += "c-dot"; + } else if (meter == "O.") { + output += "circle-dot"; + } else { + output += meter; } - current = current->getNextToken(); + return output; } + + return m_unknown; } ////////////////////////////// // -// Tool_synco::isSyncopated -- +// Tool_tandeminfo::checkForTempoMarking -- Humdrum Toolit interpretations. // -bool Tool_synco::isSyncopated(HTp token) { - double metlev = getMetricLevel(token); - HumNum duration = token->getTiedDuration(); - double logDur = log2(duration.getFloat()); - if (metlev == 2) { - return false; +string Tool_tandeminfo::checkForTempoMarking(const string& tok) { + HumRegex hre; + if (hre.search(tok, "^MM(\\d+)(\\.\\d*)?$")) { + string tempo = hre.getMatch(1) + hre.getMatch(2); + string output = "tempo: " + tempo + " quarter notes per minute"; + return output; } - if (logDur > metlev) { - return true; - } else { - return false; + + if (hre.search(tok, "^MM\\[(.*?)\\]$")) { + string text = hre.getMatch(1); + string output = "text-based tempo: " + text; + return output; } + + return m_unknown; } ////////////////////////////// // -// Tool_synco::getMetricLevel -- Assuming whole-note beats for now. +// Tool_tandeminfo::checkForLabelInfo -- Humdrum Toolkit interpretations. +// Used by the thru command. // -double Tool_synco::getMetricLevel(HTp token) { - HumNum durbar = token->getDurationFromBarline(); - if (!durbar.isInteger()) { - return -1.0; +string Tool_tandeminfo::checkForLabelInfo(const string& tok) { + HumRegex hre; + if (!hre.search(tok, "^>")) { + return m_unknown; } - if (durbar.getNumerator() % 4 == 0) { - return 2.0; + + if (hre.search(tok, "^>(\\[.*\\]$)")) { + string list = hre.getMatch(1); + string output = "default expansion list: "; + output += ""; + output += list; + output += ""; + return output; } - if (durbar.getNumerator() % 2 == 0) { - return 1.0; + + if (hre.search(tok, "^>([^[\\[\\]]+)(\\[.*\\]$)")) { + string expansionName = hre.getMatch(1); + string list = hre.getMatch(2); + string output = "alternate expansion list: label="; + output += "" + expansionName + ""; + if (expansionName == "norep") { + output += " (meaning: no repeats, i.e., take only second endings)"; + } + output += ", expansion list: " + list; + return output; } - return 0.0; + + if (hre.search(tok, "^>([^\\[\\]]+)$")) { + string label = hre.getMatch(1); + string output = "expansion label: "; + output += ""; + output += label; + output += ""; + return output; + } + + return m_unknown; + } ////////////////////////////// // -// Tool_synco::markNote -- Currently ignoring chords. +// Tool_tandeminfo::checkForInstrumentInfo -- Humdrum Toolkit and extended interpretations. +// Humdrum Tookit: +// instrument group *IG +// instrument class *IC +// instrument code *I +// Extended: +// instrument name *I" +// instrument number *I# +// instrument abbreviation *I' +// +// modori tool extensions: +// *mI == modernized +// *oI == original // -void Tool_synco::markNote(HTp token) { - token->setText(token->getText() + "|"); - if ((token->find('[') != string::npos) || (token->find('_') != string::npos)) { - HTp current = token->getNextToken(); - while (current) { - if (!current->isData()) { - current = current->getNextToken(); - continue; - } - if (current->isNull()) { - current = current->getNextToken(); - continue; - } - if (current->isRest()) { - break; - } - if (current->find("_") != string::npos) { - current->setText(current->getText() + "|"); - } else if (current->find("]") != string::npos) { - current->setText(current->getText() + "|"); - break; - } - current = current->getNextToken(); +string Tool_tandeminfo::checkForInstrumentInfo(const string& tok) { + HumRegex hre; + + if (hre.search(tok, "^(m|o)?I\"(.*)$")) { + string modori = hre.getMatch(1); + string name = hre.getMatch(2); + string output = "text to display in fromt of staff on first system (usually instrument name): \""; + output += name; + output += "\""; + if (modori == "o") { + output += " (original)"; + } else if (modori == "m") { + output += " (modern)"; + } + if (hre.search(tok, "\\\\n")) { + output += ", \"\\n\" means a line break"; + } + return output; + } + + if (hre.search(tok, "^(m|o)?I'(.*)$")) { + string modori = hre.getMatch(1); + string abbr = hre.getMatch(2); + string output = "text to display in front of staff on secondary systems (usually instrument abbreviation): \""; + output += abbr; + output += "\""; + if (modori == "o") { + output += " (original)"; + } else if (modori == "m") { + output += " (modern)"; } + if (hre.search(tok, "\\\\n")) { + output += ", \"\\n\" means a line break"; + } + return output; + } + + + if (hre.search(tok, "^(m|o)?IC([^\\s]*)$")) { + string modori = hre.getMatch(1); + string iclass = hre.getMatch(2); + bool andy = false; + bool ory = false; + vector iclasses; + string tok2 = tok; + hre.replaceDestructive(tok2, "", "IC", "g"); + if (hre.search(tok2, "&")) { + hre.split(iclasses, tok2, "&+"); + andy = true; + } else if (hre.search(tok2, "\\|")) { + hre.split(iclasses, tok2, "\\++"); + ory = true; + } else { + iclasses.push_back(tok2); + } + string output; + if (modori == "o") { + output += "(original) "; + } else if (modori == "m") { + output += "(modern) "; + } + output += "instrument class"; + if (iclasses.size() != 1) { + output += "es"; + } + output += ":"; + for (int i=0; i<(int)iclasses.size(); i++) { + output += " "; + output += ""; + output += iclasses[i]; + output += ""; + HumInstrument inst; + inst.setHumdrum(iclasses[i]); + string name; + if (iclasses[i] == "bras") { + name = "brass"; + } else if (iclasses[i] == "idio") { + name = "percussion"; + } else if (iclasses[i] == "klav") { + name = "keyboards"; + } else if (iclasses[i] == "str") { + name = "strings"; + } else if (iclasses[i] == "vox") { + name = "voices"; + } else if (iclasses[i] == "ww") { + name = "woodwinds"; + } else if (iclasses[i] != "") { + name = "unknown"; + } + if (!name.empty()) { + output += "=\"" + name + "\""; + } + if (i < (int)iclasses.size() - 1) { + if (andy) { + output += " and "; + } else if (ory) { + output += " or "; + } + } + } + if (modori == "o") { + output += " (original)"; + } else if (modori == "m") { + output += " (modern)"; + } + return output; } -} + if (hre.search(tok, "^(m|o)?IG([^\\s]*)$")) { + string modori = hre.getMatch(1); + string group = hre.getMatch(2); + bool andy = false; + bool ory = false; + vector groups; + string tok2 = tok; + hre.replaceDestructive(tok2, "", "IG", "g"); + if (hre.search(tok2, "&")) { + hre.split(groups, tok2, "&+"); + andy = true; + } else if (hre.search(tok2, "\\|")) { + hre.split(groups, tok2, "\\++"); + ory = true; + } else { + groups.push_back(tok2); + } + string output; + if (modori == "o") { + output += "(original) "; + } else if (modori == "m") { + output += "(modern) "; + } + output += "instrument group"; + if (groups.size() != 1) { + output += "s"; + } + output += ":"; + for (int i=0; i<(int)groups.size(); i++) { + output += " "; + output += groups[i]; + HumInstrument inst; + inst.setHumdrum(groups[i]); + string name; + if (groups[i] == "acmp") { + name = "=accompaniment"; + } else if (groups[i] == "solo") { + name = "=solo"; + } else if (groups[i] == "cont") { + name = "=basso-continuo"; + } else if (groups[i] == "ripn") { + name = "=ripieno"; + } else if (groups[i] == "conc") { + name = "=concertino"; + } else if (groups[i] != "") { + name = "=unknown"; + } + if (!name.empty()) { + output += "=\"" + name + "\""; + } + if (i < (int)groups.size() - 1) { + if (andy) { + output += " and "; + } else if (ory) { + output += " or "; + } + } + } + if (modori == "o") { + output += " (original)"; + } else if (modori == "m") { + output += " (modern)"; + } + return output; + } + if (hre.search(tok, "^(m|o)?I#(\\d+)$")) { + string modori = hre.getMatch(1); + string number = hre.getMatch(2); + string output = "sub-instrument number: "; + output += number; + if (modori == "o") { + output += " (original)"; + } else if (modori == "m") { + output += " (modern)"; + } + return output; + } + if (hre.search(tok, "^(m|o)?I([a-z][a-zA-Z0-9_|&-]+)$")) { + string modori = hre.getMatch(1); + string code = hre.getMatch(2); + bool andy = false; + bool ory = false; + vector codes; + string tok2 = tok; + hre.replaceDestructive(tok2, "", "I", "g"); + if (hre.search(tok2, "&")) { + hre.split(codes, tok2, "&+"); + andy = true; + } else if (hre.search(tok2, "\\|")) { + hre.split(codes, tok2, "\\++"); + ory = true; + } else { + codes.push_back(tok2); + } + string output; + if (modori == "o") { + output += "(original) "; + } else if (modori == "m") { + output += "(modern) "; + } + output += "instrument code"; + if (codes.size() != 1) { + output += "s"; + } + output += ":"; + for (int i=0; i<(int)codes.size(); i++) { + output += " "; + output += codes[i]; + output += ""; + HumInstrument inst; + inst.setHumdrum(codes[i]); + string text = inst.getName(); + if (!text.empty()) { + output += "= \"" + text + "\""; + } else { + output += "= unknown code"; + } + output += ""; + if (i < (int)codes.size() - 1) { + if (andy) { + output += " and "; + } else if (ory) { + output += " or "; + } + } + } -///////////////////////////////// -// -// Tool_gridtest::Tool_tabber -- Set the recognized options for the tool. -// + return output; + } -Tool_tabber::Tool_tabber(void) { - // do nothing for now. - define("r|remove=b", "remove any extra tabs"); + return m_unknown; } -/////////////////////////////// +////////////////////////////// // -// Tool_tabber::run -- Primary interfaces to the tool. +// Tool_tandeminfo::checkForKeySignature -- Standard Humdrum Toolkit interpretations. +// Extended key signatures are possible (and detected by this function), +// but typically the standard ones are in circle-of-fifths orderings. +// This function also allows double sharps/flats in the key signature +// which are very uncommon in real music. Standard key signatures: +// +// *k[f#c#g#d#a#e#b#] +// *k[f#c#g#d#a#e#] +// *k[f#c#g#d#a#] +// *k[f#c#g#d#] +// *k[f#c#g#] +// *k[f#c#] +// *k[f#] +// *k[] +// *k[b-] +// *k[b-e-] +// *k[b-e-a-] +// *k[b-e-a-d-] +// *k[b-e-a-d-g-] +// *k[b-e-a-d-g-c-] +// *k[b-e-a-d-g-c-f-] // -bool Tool_tabber::run(HumdrumFileSet& infiles) { - bool status = true; - for (int i=0; i accidentals; + hre.split(accidentals, pcs, "[a-gA-G]"); + for (int i=0; i<(int)accidentals.size(); i++) { + if (accidentals[i] == "##") { + doublesharps++; + } else if (accidentals[i] == "--") { + doubleflats++; + } else if (accidentals[i] == "#") { + sharps++; + } else if (accidentals[i] == "-") { + flats++; + } else if (accidentals[i] == "n") { + naturals++; + } + } -bool Tool_tabber::run(HumdrumFile& infile, ostream& out) { - bool status = run(infile); - out << m_free_text.str(); - return status; -} + bool foundQ = false; + if (sharps) { + if (foundQ) { + output += ", "; + } + foundQ = true; + if (sharps == 1) { + output += "1 sharp"; + } else { + output += to_string(sharps) + " sharps"; + } + } + if (flats) { + if (foundQ) { + output += ", "; + } + foundQ = true; + if (flats == 1) { + output += "1 flat"; + } else { + output += to_string(flats) + " flats"; + } + } -bool Tool_tabber::run(HumdrumFile& infile) { - initialize(infile); - processFile(infile); - return true; + if (naturals) { + if (foundQ) { + output += ", "; + } + foundQ = true; + if (naturals == 1) { + output += "1 natural"; + } else { + output += to_string(naturals) + " naturals"; + } + } + + if (doublesharps) { + if (foundQ) { + output += ", "; + } + foundQ = true; + if (doublesharps == 1) { + output += "1 double sharp"; + } else { + output += to_string(doublesharps) + " double sharps"; + } + } + + if (doubleflats) { + if (foundQ) { + output += ", "; + } + foundQ = true; + if (doubleflats == 1) { + output += "1 double flat"; + } else { + output += to_string(doubleflats) + " double flats"; + } + } + + return output; + } + return m_unknown; } ////////////////////////////// // -// Tool_tabber::initialize -- +// Tool_tandeminfo::checkForKeyDesignation -- Standard Humdrum Toolkit interpretations, plus +// modal extensions by Brett Arden. Typically only used in **kern data. // -void Tool_tabber::initialize(HumdrumFile& infile) { - // do nothing for now -} +string Tool_tandeminfo::checkForKeyDesignation(const string& tok) { + HumRegex hre; + if (tok == "?:") { + return "key designation, unknown/unassigned key"; + } + if (hre.search(tok, "^([a-gA-G])([-#]*):(ion|dor|phr|lyd|mix|aeo|loc)?(-hypo|-auth)?$")) { + string tonic = hre.getMatch(1); + string accid = hre.getMatch(2); + string mode = hre.getMatch(3); + string older = hre.getMatch(4); + bool isUpper = isupper(tonic[0]); + string output = "key designation: "; + if (mode.empty()) { + output += toupper(tonic[0]); + + if (accid == "") { + // do nothing + } else if (accid == "#") { + output += "-sharp"; + } else if (accid == "-") { + output += "-flat"; + } else if (accid == "##") { + output += "-double-sharp"; + } else if (accid == "--") { + output += "-double-flat"; + } else if (accid == "###") { + output += "-triple-sharp"; + } else if (accid == "---") { + output += "-triple-flat"; + } else { + return m_unknown; + } + if (isUpper) { + output += " major"; + } else { + output += " minor"; + } + return output; + } else { + // Modal key + if (isUpper && ((mode == "dor") || (mode == "phr") || (mode == "aeo") || (mode == "loc"))) { + // need a lower-case letter for these modes (minor third above tonic) + return m_unknown; + } + if ((!isUpper) && ((mode == "ion") || (mode == "lyd") || (mode == "mix"))) { + // need an upper-case letter for these modes (major third above tonic) + return m_unknown; + } + output += toupper(tonic[0]); + if (accid == "") { + // do nothing + } else if (accid == "#") { + output += "-sharp"; + } else if (accid == "-") { + output += "-flat"; + } else if (accid == "##") { + output += "-double-sharp"; + } else if (accid == "--") { + output += "-double-flat"; + } else if (accid == "###") { + output += "-triple-sharp"; + } else if (accid == "---") { + output += "-triple-flat"; + } else { + return m_unknown; + } + + if (mode == "ion") { + output += " ionian"; + } else if (mode == "dor") { + output += " dorian"; + } else if (mode == "phr") { + output += " phrygian"; + } else if (mode == "lyd") { + output += " lydian"; + } else if (mode == "mix") { + output += " mixolydian"; + } else if (mode == "aeo") { + output += " aeolian"; + } else if (mode == "loc") { + output += " locrian"; + } else { + return m_unknown; + } -////////////////////////////// -// -// Tool_tabber::processFile -- -// + if (!older.empty()) { + if (older == "-plag") { + output += " (plagal)"; + } else if (older == "-auth") { + output += " (authentic)"; + } else { + output += " (unknown mode type: should be -auth (authentic), or -plag (plagal) for hypo modes)"; + } + } -void Tool_tabber::processFile(HumdrumFile& infile) { - if (getBoolean("remove")) { - infile.removeExtraTabs(); - } else { - infile.addExtraTabs(); + return output; + } } - infile.createLinesFromTokens(); + + return m_unknown; } diff --git a/src/iohumdrum.cpp b/src/iohumdrum.cpp index 2c833b4a887..5feedb4b297 100644 --- a/src/iohumdrum.cpp +++ b/src/iohumdrum.cpp @@ -499,46 +499,46 @@ namespace humaux { ostream &StaffStateVariables::print(ostream &out, const std::string &prefix) { - out << prefix << "ADDRESS ================== " << (long long)this << endl; - out << prefix << "verse = " << verse << endl; - out << prefix << "suppress_tuplet_number = " << suppress_tuplet_number << endl; - out << prefix << "suppress_tuplet_bracket = " << suppress_tuplet_bracket << endl; - out << prefix << "suppress_articulations = " << suppress_articulations << endl; - out << prefix << "tremolo = " << tremolo << endl; + out << prefix << "ADDRESS ================== " << (long long)this << std::endl; + out << prefix << "verse = " << verse << std::endl; + out << prefix << "suppress_tuplet_number = " << suppress_tuplet_number << std::endl; + out << prefix << "suppress_tuplet_bracket = " << suppress_tuplet_bracket << std::endl; + out << prefix << "suppress_articulations = " << suppress_articulations << std::endl; + out << prefix << "tremolo = " << tremolo << std::endl; // std::vector cue_size; // std::vector stem_type; // std::vector stem_visible; - out << prefix << "ligature_recta = " << ligature_recta << endl; - out << prefix << "ligature_obliqua = " << ligature_obliqua << endl; - out << prefix << "last_clef = " << last_clef << endl; - out << prefix << "acclev = " << acclev << endl; - out << prefix << "righthalfstem = " << righthalfstem << endl; + out << prefix << "ligature_recta = " << ligature_recta << std::endl; + out << prefix << "ligature_obliqua = " << ligature_obliqua << std::endl; + out << prefix << "last_clef = " << last_clef << std::endl; + out << prefix << "acclev = " << acclev << std::endl; + out << prefix << "righthalfstem = " << righthalfstem << std::endl; // Note *ottavanotestart; // Note *ottavanoteend; - out << prefix << "ottavaendtimestamp = " << ottavaendtimestamp << endl; + out << prefix << "ottavaendtimestamp = " << ottavaendtimestamp << std::endl; // Measure *ottavameasure; // Note *ottavadownnotestart; // Note *ottavadownnoteend; - out << prefix << "ottavadownendtimestamp = " << ottavadownendtimestamp << endl; + out << prefix << "ottavadownendtimestamp = " << ottavadownendtimestamp << std::endl; // Measure *ottavadownmeasure; // Note *ottava2notestart; // Note *ottava2noteend; - out << prefix << "ottava2endtimestamp = " << ottava2endtimestamp << endl; + out << prefix << "ottava2endtimestamp = " << ottava2endtimestamp << std::endl; // Measure *ottava2measure; // Note *ottava2downnotestart; // Note *ottava2downnoteend; - out << prefix << "ottava2downendtimestamp = " << ottava2downendtimestamp << endl; + out << prefix << "ottava2downendtimestamp = " << ottava2downendtimestamp << std::endl; // Measure *ottava2downmeasure; - out << prefix << "meter_top = " << meter_top << endl; - out << prefix << "meter_bottom = " << meter_bottom << endl; + out << prefix << "meter_top = " << meter_top << std::endl; + out << prefix << "meter_bottom = " << meter_bottom << std::endl; // std::list ties; - out << prefix << "m_dynampos = " << m_dynampos << endl; - out << prefix << "m_dynamstaffadj = " << m_dynamstaffadj << endl; - out << prefix << "m_dynamposdefined = " << m_dynamposdefined << endl; - out << prefix << "auto_custos = " << auto_custos << endl; - out << prefix << "suppress_manual_custos = " << suppress_manual_custos << endl; - out << prefix << "mensuration_type = " << mensuration_type << endl; - out << prefix << "join = " << join << endl; + out << prefix << "m_dynampos = " << m_dynampos << std::endl; + out << prefix << "m_dynamstaffadj = " << m_dynamstaffadj << std::endl; + out << prefix << "m_dynamposdefined = " << m_dynamposdefined << std::endl; + out << prefix << "auto_custos = " << auto_custos << std::endl; + out << prefix << "suppress_manual_custos = " << suppress_manual_custos << std::endl; + out << prefix << "mensuration_type = " << mensuration_type << std::endl; + out << prefix << "join = " << join << std::endl; return out; } @@ -982,7 +982,7 @@ bool HumdrumInput::convertHumdrum() processMeiOptions(infile); if (m_debug) { - cout << GetMeiString(); + std::cout << GetMeiString(); } // If the document has and elements you can call: @@ -2011,7 +2011,7 @@ void HumdrumInput::processHangingTieStart(humaux::HumdrumTie &tieinfo) int subindex = tieinfo.getStartSubindex(); Measure *measure = tieinfo.getStartMeasure(); if (measure == NULL) { - cerr << "Problem with start measure being NULL" << endl; + LogWarning("In HumdrumInput::processHangingTieStart: Start measure is NULL"); return; } // int metercount = tieinfo.getMeterTop(); @@ -2668,7 +2668,9 @@ void HumdrumInput::parseEmbeddedOptions(Doc *doc) continue; } if (value.empty()) { - cerr << "Warning: value is empty for parameter " << key << endl; + std::stringstream warning; + warning << "In HumdrumInput::parseEmbeddedOptions: value is empty for parameter " << key; + LogWarning(warning.str().c_str()); continue; } inputparameters[pkey] = pvalue; @@ -2687,7 +2689,9 @@ void HumdrumInput::parseEmbeddedOptions(Doc *doc) std::string pkey = hre.getMatch(1); std::string pvalue = hre.getMatch(2); if (value.empty()) { - cerr << "Warning: value is empty for parameter " << key << endl; + std::stringstream warning; + warning << "In HumdrumInput::parseEmbeddedOptions: Value is empty for parameter " << key; + LogWarning(warning.str().c_str()); continue; } inputparameters[pkey] = pvalue; @@ -2700,7 +2704,10 @@ void HumdrumInput::parseEmbeddedOptions(Doc *doc) for (auto inputoption : inputparameters) { auto entry = optionlist->find(inputoption.first); if (entry == optionlist->end()) { - cerr << "Warning: option " << inputoption.first << " is not recognized" << endl; + std::stringstream warning; + warning << "In HumdrumInput::parseEmbeddedOptions: option "; + warning << inputoption.first << " is not recognized"; + LogWarning(warning.str().c_str()); continue; } @@ -4954,8 +4961,13 @@ void HumdrumInput::createHumdrumVerbatimExtMeta(pugi::xml_node meiHead) pugi::xml_parse_result result = tmpdoc.load_string(xmldata.str().c_str()); if (!result) { // some sort of error, so give up; - cerr << "ExtMeta parse error: " << result.description() << endl; - cerr << xmldata.str(); + std::stringstream warning; + warning << "In HumdrumInput::createHumdrumVerbatimExtMeta: ExtMeta parse error: "; + warning << result.description(); + LogWarning(warning.str().c_str()); + std::stringstream warning2; + warning2 << " xmldata string is: " << xmldata.str(); + LogWarning(warning2.str().c_str()); return; } @@ -5879,8 +5891,8 @@ bool HumdrumInput::processStaffDecoration(const std::string &decoration) return false; } if ((0)) { - cerr << "INPUT DECORATION: " << decoration << endl; - cerr << " PROCESSED: " << d << endl; + std::cerr << "INPUT DECORATION: " << decoration << std::endl; + std::cerr << " PROCESSED: " << d << std::endl; } // Remove any staff numbers that are no longer present (or invalid): @@ -6010,7 +6022,7 @@ bool HumdrumInput::processStaffDecoration(const std::string &decoration) if ((0)) { // print analysis: for (int i = 0; i < (int)d.size(); ++i) { - cerr << "D[" << i << "] =\t" << d[i] << " pairing: " << pairing[i] << endl; + std::cerr << "D[" << i << "] =\t" << d[i] << " pairing: " << pairing[i] << std::endl; } } @@ -6238,13 +6250,13 @@ bool HumdrumInput::processStaffDecoration(const std::string &decoration) } if ((0)) { - cerr << "BAR GROUPS" << endl; + std::cerr << "BAR GROUPS" << std::endl; for (int i = 0; i < (int)bargroups.size(); ++i) { - cerr << "\tgroup_style=" << groupstyle[i] << "\tgroup = " << i << ":\t"; + std::cerr << "\tgroup_style=" << groupstyle[i] << "\tgroup = " << i << ":\t"; for (int j = 0; j < (int)bargroups[i].size(); j++) { - cerr << " " << bargroups[i][j]; + std::cerr << " " << bargroups[i][j]; } - cerr << endl; + std::cerr << std::endl; } } @@ -6275,7 +6287,7 @@ bool HumdrumInput::processStaffDecoration(const std::string &decoration) for (int i = 0; i < (int)found.size(); ++i) { if (found[i] != 1) { - cerr << "I:" << i << "\t=\t" << found[i] << endl; + std::cerr << "I:" << i << "\t=\t" << found[i] << std::endl; validQ = false; break; } @@ -6283,9 +6295,13 @@ bool HumdrumInput::processStaffDecoration(const std::string &decoration) } if (!validQ) { - cerr << "DECORATION IS INVALID " << decoration << endl; + std::stringstream warning; + warning << "In HumdrumInput::processStaffDecoration: Decoration is invalid: " << decoration; + LogWarning(warning.str().c_str()); if (d != decoration) { - cerr << "\tSTAFF VERSION: " << d << endl; + std::stringstream warning; + warning << "In HumdrumInput::processStaffDecoration: Staff version: " << d; + LogWarning(warning.str().c_str()); } StaffGrp *sg = new StaffGrp(); setGroupSymbol(sg, staffGroupingSym_SYMBOL_bracket); @@ -6890,7 +6906,7 @@ bool HumdrumInput::prepareFooter( } Object *detached = pgfoot->GetParent()->DetachChild(index); if (detached != pgfoot) { - std::cerr << "Detached element is not the pgHead" << std::endl; + LogWarning("In HumdrumInput::prepareFooter: Detached element is not the pgHead."); if (detached) { delete detached; } @@ -6911,7 +6927,7 @@ bool HumdrumInput::prepareFooter( } detached = pgfoot2->GetParent()->DetachChild(index); if (detached != pgfoot2) { - std::cerr << "Detached element is not a pgFoot element" << std::endl; + LogWarning("In HumdrumInput::prepareFooter: Detached element is not a pgFoot element"); if (detached) { delete detached; } @@ -7065,7 +7081,7 @@ bool HumdrumInput::prepareHeader( } Object *detached = pghead->GetParent()->DetachChild(index); if (detached != pghead) { - std::cerr << "Detached element is not the pgHead" << std::endl; + LogWarning("In HumdrumInput::prepareHeader: Detached element is not the pgHead"); if (detached) { delete detached; } @@ -8070,76 +8086,238 @@ bool HumdrumInput::isBlackNotation(hum::HTp starting) std::string HumdrumInput::getLabelFromInstrumentCode(hum::HTp icode, const std::string &transpose) { - std::string output; - std::string name = icode->substr(2); - if (name == "piano") { - output = "Piano"; - } - else if (name == "flt") { - output = "Flute"; - } - else if (name == "picco") { - output = "Piccolo"; - } - else if (name == "oboe") { - output = "Oboe"; - } - else if (name == "clars") { - output = "Clarinet"; - } - else if (name == "clara") { - output = "Alto Clarinet"; - } - else if (name == "clarb") { - output = "Bass Clarinet"; - } - else if (name == "fagot") { - output = "Bassoon"; - } - else if (name == "fagot") { - output = "Bassoon"; - } - else if (name == "tromp") { - output = "Trumpet"; - } - else if (name == "tromb") { - output = "Trombone"; - } - else if (name == "violin") { - // Deal with Violin 1 versus Violin 2, but need more info to do that. - output = "Violin"; - } - else if (name == "viola") { - output = "Viola"; - } - else if (name == "cello") { - output = "Violoncello"; - } - else if (name == "cemba") { - output = "Harpsichord"; - } - else if (name == "organ") { - output = "Organ"; - } - else if (name == "clavi") { - output = "Clavichord"; - } - else if (name == "forte") { - output = "Fortepiano"; - } - else if (name == "guitr") { - output = "Guitar"; - } - else if (name == "cbass") { - output = "Contrabass"; - } - else if (name == "koto") { - output = "Koto"; - } + static std::map codeToLabel; + if (codeToLabel.empty()) { + codeToLabel["piano"] = "Piano"; + codeToLabel["accor"] = "Accordion"; + codeToLabel["alto"] = "Alto"; + codeToLabel["anvil"] = "Anvil"; + codeToLabel["archl"] = "Archlute"; + codeToLabel["armon"] = "Harmonic"; + codeToLabel["arpa"] = "Harp"; + codeToLabel["bagpI"] = "Irish bagpipe"; + codeToLabel["bagpS"] = "Scottish bagpipe"; + codeToLabel["banjo"] = "Banjo"; + codeToLabel["bansu"] = "Bansuri"; + codeToLabel["barit"] = "Baritone"; + codeToLabel["mbari"] = "High baritone"; + codeToLabel["baset"] = "Bassett horn"; + codeToLabel["bass"] = "Bass"; + codeToLabel["bdrum"] = "Bass drum"; + codeToLabel["bongo"] = "Bongo"; + codeToLabel["bguit"] = "Bass guitar"; + codeToLabel["biwa"] = "Biwa"; + codeToLabel["bscan"] = "Singing bass"; + codeToLabel["bspro"] = "Basso profondo"; + codeToLabel["brush"] = "Brush"; + codeToLabel["calam"] = "Chalumeau"; + codeToLabel["calpe"] = "Calliope"; + codeToLabel["calto"] = "Contralto"; + codeToLabel["campn"] = "Bells"; + codeToLabel["cangl"] = "English horn"; + codeToLabel["canto"] = "Canto"; + codeToLabel["caril"] = "Carillon"; + codeToLabel["castr"] = "Castrato"; + codeToLabel["casts"] = "Castanets"; + codeToLabel["cbass"] = "Contrabass"; + codeToLabel["cello"] = "Violoncello"; + codeToLabel["cemba"] = "Harpsichord"; + codeToLabel["cetra"] = "Cittern"; + codeToLabel["chain"] = "Chain"; + codeToLabel["chime"] = "Tubular bells"; + codeToLabel["chcym"] = "China cymbal"; + codeToLabel["chlma"] = "Soprano shawm"; + codeToLabel["chlmt"] = "Tenor shawm"; + codeToLabel["clap"] = "Hand clap"; + codeToLabel["clara"] = "Alto Clarinet"; + codeToLabel["clarb"] = "Bass Clarinet"; + codeToLabel["claro"] = "Sopranino clarinet"; + codeToLabel["clarp"] = "Piccolo clarinet"; + codeToLabel["clars"] = "Clarinet"; + codeToLabel["clave"] = "Claves"; + codeToLabel["clavi"] = "Clavichord"; + codeToLabel["clest"] = "Celesta"; + codeToLabel["clrno"] = "Clarino"; + codeToLabel["colsp"] = "Coloratura soprano"; + codeToLabel["conga"] = "Conga"; + codeToLabel["cor"] = "Horn"; + codeToLabel["cornm"] = "Cornemuse"; + codeToLabel["corno"] = "Cornett"; + codeToLabel["cornt"] = "Cornet"; + codeToLabel["coro"] = "Chorus"; + codeToLabel["crshc"] = "Crash cymbal"; + codeToLabel["ctenor"] = "Contratenor"; + codeToLabel["ctina"] = "concertina"; + codeToLabel["drmsp"] = "Dramatic soprano"; + codeToLabel["drum"] = "Drum"; + codeToLabel["drumP"] = "Small drum"; + codeToLabel["dulc"] = "Dulcimer"; + codeToLabel["eguit"] = "Electric guitar"; + codeToLabel["fag_c"] = "Contrabassoon"; + codeToLabel["fagot"] = "Bassoon"; + codeToLabel["false"] = "Falsetto"; + codeToLabel["feme"] = "Female voice"; + codeToLabel["fife"] = "Fife"; + codeToLabel["fingc"] = "Finger Cymbals"; + codeToLabel["flex"] = "Flexatone"; + codeToLabel["flt"] = "Flute"; + codeToLabel["flt_a"] = "Alto flute"; + codeToLabel["flt_b"] = "Bass flute"; + codeToLabel["fltda"] = "Alto recorder"; + codeToLabel["fltdb"] = "Bass recorder"; + codeToLabel["fltdn"] = "Sopranino recorder"; + codeToLabel["fltds"] = "Soprano recorder"; + codeToLabel["fltdt"] = "Tenor recorder"; + codeToLabel["flugh"] = "Flugelhorn"; + codeToLabel["forte"] = "Fortepiano"; + codeToLabel["glock"] = "Glockenspiel"; + codeToLabel["gen"] = "Generic instrument"; + codeToLabel["genT"] = "Generic treble instrument"; + codeToLabel["genB"] = "Generic bass instrument"; + codeToLabel["gong"] = "Gong"; + codeToLabel["guitr"] = "Guitar"; + codeToLabel["hammd"] = "Hammond eletronic organ"; + codeToLabel["hbell"] = "Hand bells"; + codeToLabel["heck"] = "Heckelphone"; + codeToLabel["heltn"] = "Heroic tenor"; + codeToLabel["hichi"] = "Hichiriki"; + codeToLabel["hurdy"] = "Hurdy-gurdy"; + codeToLabel["kitv"] = "Kit violin"; + codeToLabel["klav"] = "Generic keyboard"; + codeToLabel["kokyu"] = "Kokyu"; + codeToLabel["komun"] = "Koumngo"; + codeToLabel["koto"] = "Koto"; + codeToLabel["kruma"] = "Alto crumhorn"; + codeToLabel["krumb"] = "Bass crumhorn"; + codeToLabel["krums"] = "Crumhorn"; + codeToLabel["krumt"] = "Tenor crumhorn"; + codeToLabel["lion"] = "Lion's roar"; + codeToLabel["liuto"] = "Lute"; + codeToLabel["lyrsp"] = "Lyric soprano"; + codeToLabel["lyrtn"] = "Lyric tenor"; + codeToLabel["male"] = "Male voice"; + codeToLabel["mando"] = "Mandolin"; + codeToLabel["marac"] = "Maracas"; + codeToLabel["marim"] = "Marimba"; + codeToLabel["mezzo"] = "Mezzo soprano"; + codeToLabel["nfant"] = "Child's voice"; + codeToLabel["nokan"] = "Nokan"; + codeToLabel["oboe"] = "Oboe"; + codeToLabel["oboeD"] = "Oboe d'amore"; + codeToLabel["ocari"] = "Ocarina"; + codeToLabel["ondes"] = "Ondes Martenot"; + codeToLabel["ophic"] = "Ophicleide"; + codeToLabel["organ"] = "Organ"; + codeToLabel["oud"] = "Oud"; + codeToLabel["panpi"] = "Panpipes"; + codeToLabel["paila"] = "Timbales"; + codeToLabel["pbell"] = "Bell plate"; + codeToLabel["pguit"] = "Portuguese guitar"; + codeToLabel["physh"] = "Physharmonica"; + codeToLabel["piano"] = "Piano"; + codeToLabel["piatt"] = "Cymbales"; + codeToLabel["picco"] = "Piccolo"; + codeToLabel["pipa"] = "Pipa"; + codeToLabel["piri"] = "Piri"; + codeToLabel["porta"] = "Portative organ"; + codeToLabel["psalt"] = "Psaltery"; + codeToLabel["qin"] = "Qin"; + codeToLabel["quinto"] = "Quinto"; + codeToLabel["quitr"] = "Gittern"; + codeToLabel["rackt"] = "Rackett"; + codeToLabel["ratch"] = "Ratchet"; + codeToLabel["ratl"] = "Rattle"; + codeToLabel["rebec"] = "Rebec"; + codeToLabel["recit"] = "Recitativo"; + codeToLabel["reedo"] = "Reed organ"; + codeToLabel["rhode"] = "Rhodes piano"; + codeToLabel["ridec"] = "Ride cymbal"; + codeToLabel["sarod"] = "Sarod"; + codeToLabel["sarus"] = "Sarrusophone"; + codeToLabel["saxA"] = "Alto saxophone"; + codeToLabel["saxB"] = "Bass saxophone"; + codeToLabel["saxN"] = "Sopranino saxophone"; + codeToLabel["saxR"] = "Baritone saxophone"; + codeToLabel["saxS"] = "Saxophone"; + codeToLabel["saxT"] = "Tenor saxophone"; + codeToLabel["sbell"] = "Sleigh bells"; + codeToLabel["sdrum"] = "Snare drum"; + codeToLabel["serp"] = "Serpent"; + codeToLabel["sesto"] = "Sesto"; + codeToLabel["shaku"] = "Shakuhachi"; + codeToLabel["shami"] = "Shamisen"; + codeToLabel["sheng"] = "Sheng"; + codeToLabel["sho"] = "Sho"; + codeToLabel["siren"] = "Siren"; + codeToLabel["sitar"] = "Sitar"; + codeToLabel["slap"] = "Slapstick"; + codeToLabel["soprn"] = "Soprano"; + codeToLabel["spshc"] = "Splash cymbal"; + codeToLabel["spok"] = "Spoken voice"; + codeToLabel["spokF"] = "Female spoken voice"; + codeToLabel["spokM"] = "Male spoken voice"; + codeToLabel["steel"] = "Steel drum"; + codeToLabel["stim"] = "Sprechstimme"; + codeToLabel["stimS"] = "Soprano Sprechstimme"; + codeToLabel["stimA"] = "Alto Sprechstimme"; + codeToLabel["stimC"] = "Contralto Sprechstimme"; + codeToLabel["stimR"] = "Baritone Sprechstimme"; + codeToLabel["stimB"] = "Bass Sprechstimme"; + codeToLabel["strdr"] = "String drum"; + codeToLabel["sxhA"] = "Alto saxhorn"; + codeToLabel["sxhB"] = "Bass saxhorn"; + codeToLabel["sxhC"] = "Contrabass saxhorn"; + codeToLabel["sxhR"] = "Baritons saxhorn"; + codeToLabel["sxhS"] = "Saxhorn"; + codeToLabel["sxhT"] = "Tenor saxhorn"; + codeToLabel["synth"] = "Synthesizer"; + codeToLabel["tabla"] = "Tabla"; + codeToLabel["tambn"] = "Tambourine"; + codeToLabel["tambu"] = "Tambura"; + codeToLabel["tambr"] = "Tambur"; + codeToLabel["tblok"] = "Temple blocks"; + codeToLabel["tdrum"] = "Tenor drum"; + codeToLabel["tenor"] = "Tenor"; + codeToLabel["timpa"] = "Timpani"; + codeToLabel["tiorb"] = "Theorbo"; + codeToLabel["tom"] = "Tom-tom"; + codeToLabel["trngl"] = "Triangle"; + codeToLabel["troma"] = "Alto trombone"; + codeToLabel["tromb"] = "Bass trombone"; + codeToLabel["tromp"] = "Trumpet"; + codeToLabel["tromP"] = "Piccolo trumpet"; + codeToLabel["tromB"] = "Bass trumpet"; + codeToLabel["tromt"] = "Trombone"; + codeToLabel["tuba"] = "Tuba"; + codeToLabel["tubaB"] = "Bass Tuba"; + codeToLabel["tubaC"] = "Contrabass Tuba"; + codeToLabel["tubaT"] = "Tenor Tuba"; + codeToLabel["tubaU"] = "Subcontrabass Tuba"; + codeToLabel["ukule"] = "Ukulele"; + codeToLabel["vibra"] = "Vibraphone"; + codeToLabel["vina"] = "Vina"; + codeToLabel["viola"] = "Viola"; + codeToLabel["violb"] = "Bass viola da gamba"; + codeToLabel["viold"] = "Viola d'amore"; + codeToLabel["viole"] = "violone"; + codeToLabel["violn"] = "Violin"; + codeToLabel["violp"] = "Piccolo violin"; + codeToLabel["viols"] = "Viola da gamba"; + codeToLabel["violt"] = "Tenor viola da gamba"; + codeToLabel["vox"] = "Voice"; + codeToLabel["wblok"] = "Woodblock"; + codeToLabel["xylo"] = "Xylophone"; + codeToLabel["zithr"] = "Zither"; + codeToLabel["zurna"] = "Zurna"; + } + + std::string code = icode->substr(2); + + std::string output = codeToLabel[code]; if (output.empty()) { - // could not find an automatic name for the instrument. + // Could not find an automatic name for the instrument. return output; } @@ -8158,13 +8336,44 @@ std::string HumdrumInput::getLabelFromInstrumentCode(hum::HTp icode, const std:: else if (transpose == "*ITrd-5c-9") { output += " in E-flat"; } + // Add other keys here. + + // Add instrument number + string number = getInstrumentNumber(icode); + if (!number.empty()) { + output += " "; + output += number; + } return output; } ////////////////////////////// // -// hasIndent -- true if *indent tandem interpretation before first data token. +// HumdrumInput::getInstrumentNumber -- search until data has been found +// for an interpretation in the form *I#4 for instrument 4. +// + +std::string HumdrumInput::getInstrumentNumber(hum::HTp icode) +{ + hum::HTp current = icode->getNextToken(); + while (current && !current->isData()) { + if (!current->isInterpretation()) { + current = current->getNextToken(); + continue; + } + hum::HumRegex hre; + if (hre.search(current, "^\\*I#(\\d+)")) { + return hre.getMatch(1); + } + current = current->getNextToken(); + } + return ""; +} + +////////////////////////////// +// +// HumdrumInput::hasIndent -- true if *indent tandem interpretation before first data token. // bool HumdrumInput::hasIndent(hum::HTp tok) @@ -8681,7 +8890,10 @@ void HumdrumInput::setMensurationSymbol( } } else { - std::cerr << "Warning: do not understand mensuration " << metdata << std::endl; + std::stringstream warning; + warning << "In HumdrumInput::setMensurationSymbol: Problem parsing mensuration: "; + warning << metdata; + LogWarning(warning.str().c_str()); return; } @@ -8736,41 +8948,63 @@ void HumdrumInput::setMensurationSymbol( prolatio = stoi(num4); } + std::stringstream warning; switch (prolatio) { case 2: vrvmensur->SetProlatio(PROLATIO_2); break; case 3: vrvmensur->SetProlatio(PROLATIO_3); break; case 0: break; - default: cerr << "Warning: unknown prolation " << prolatio << " in " << mensurtok << endl; + default: + warning.str(""); + warning << "In HumdrumInput::setMensurationSymbol: Unknown prolation "; + warning << prolatio << " in " << mensurtok; + LogWarning(warning.str().c_str()); } switch (tempus) { case 2: vrvmensur->SetTempus(TEMPUS_2); break; case 3: vrvmensur->SetTempus(TEMPUS_3); break; case 0: break; - default: cerr << "Warning: unknown tempus " << tempus << " in " << mensurtok << endl; + default: + warning.str(""); + warning << "In HumdrumInput::setMensurationSymbol: Unknown tempus "; + warning << tempus << " in " << mensurtok; + LogWarning(warning.str().c_str()); } switch (modus) { case 2: vrvmensur->SetModusminor(MODUSMINOR_2); break; case 3: vrvmensur->SetModusminor(MODUSMINOR_3); break; case 0: break; - default: cerr << "Warning: unknown modus " << modus << " in " << mensurtok << endl; + default: + warning.str(""); + warning << "In HumdrumInput::setMensurationSymbol: Unknown modus "; + warning << modus << " in " << mensurtok; + LogWarning(warning.str().c_str()); } switch (maximodus) { case 2: vrvmensur->SetModusmaior(MODUSMAIOR_2); break; case 3: vrvmensur->SetModusmaior(MODUSMAIOR_3); break; case 0: break; - default: cerr << "Warning: unknown maximodus " << maximodus << " in " << mensurtok << endl; + default: + warning.str(""); + warning << "In HumdrumInput::setMensurationSymbol: Unknown maximodus "; + warning << maximodus << " in " << mensurtok; + LogWarning(warning.str().c_str()); } } std::vector &ss = m_staffstates; if (staffindex < 0) { - cerr << "Initialization problem, not setting mensuration information" << endl; - cerr << "STAFF INDEX = " << staffindex << endl; + std::stringstream warning; + warning << "In HumdrumInput::setMensrationSymbol: "; + warning << "Initialization problem, not setting mensuration information"; + LogWarning(warning.str().c_str()); + std::stringstream warning2; + warning2 << " Staff index = " << staffindex; + LogWarning(warning2.str().c_str()); return; } if (staffindex >= (int)ss.size()) { - cerr << "Problem with staff indexing in mensuration processing" << endl; + LogWarning("InHumdrumInput::setMensurationSymbol: Problem with staff indexing in mensuration processing"); return; } @@ -9954,7 +10188,10 @@ void HumdrumInput::storeStaffLayerTokensForMeasure(int startline, int endline) } staffindex = rkern[track]; if (staffindex < 0) { - cerr << "STAFF INDEX PROBLEM FOR TRACK " << track << endl; + std::stringstream warning; + warning << "In HumdrumInput::storeStaffLayerTokensForMeasure:"; + warning << "Staff inex problem for track " << track; + LogWarning(warning.str().c_str()); } if ((int)lt[staffindex].size() < layerindex + 1) { lt[staffindex].resize(lt[staffindex].size() + 1); @@ -10912,10 +11149,7 @@ void HumdrumInput::addHarmFloatsForMeasure(int startline, int endline) place = "above"; } else { - int belowQ = token->getValueInt("auto", "below"); - if (belowQ) { - place = "below"; - } + place = "below"; } if (place.size() > 0) { setPlaceRelStaff(harm, place, false); @@ -10963,7 +11197,10 @@ void HumdrumInput::addHarmFloatsForMeasure(int startline, int endline) text->SetText(UTF8to32(*token)); } else { - cerr << "Unknown type of harm data: " << datatype << endl; + std::stringstream warning; + warning << "In HumdrumInput::addHarmFloatsForMeasure: "; + warning << "Unknown type of harm data " << datatype; + LogWarning(warning.str().c_str()); continue; } } @@ -12341,7 +12578,10 @@ void HumdrumInput::setMxHarmContent(Rend *rend, const std::string &content) } } else { - cerr << "should not get here with correct input " << content << endl; + std::stringstream warning; + warning << "In HumdrumInput::setMxHarmContent: "; + warning << "Should not get here if correct input: " << content; + LogWarning(warning.str().c_str()); } } @@ -12693,29 +12933,29 @@ void HumdrumInput::fixLargeTuplets(std::vector &tg void HumdrumInput::printGroupInfo(const std::vector &tg) { - cerr << "TOK\t\tGRP\tBRAK\tNUM\tNBASE\tNSCAL\tBSTART\tBEND"; - cerr << "\tGBST\tGBEND\tTSTART\tTEND\tFORCE\tPRIORITY\n"; + std::cerr << "TOK\t\tGRP\tBRAK\tNUM\tNBASE\tNSCAL\tBSTART\tBEND"; + std::cerr << "\tGBST\tGBEND\tTSTART\tTEND\tFORCE\tPRIORITY\n"; for (int i = 0; i < (int)tg.size(); ++i) { - cerr << tg.at(i).token << "\t"; + std::cerr << tg.at(i).token << "\t"; if (tg.at(i).token && (tg.at(i).token->size() < 8)) { - cerr << "\t"; + std::cerr << "\t"; } - cerr << tg.at(i).group << "\t"; - cerr << tg.at(i).bracket << "\t"; - cerr << tg.at(i).num << "\t"; - cerr << tg.at(i).numbase << "\t"; - cerr << tg.at(i).numscale << "\t"; - cerr << tg.at(i).beamstart << "\t"; - cerr << tg.at(i).beamend << "\t"; - cerr << tg.at(i).gbeamstart << "\t"; - cerr << tg.at(i).gbeamend << "\t"; - cerr << "TS:" << tg.at(i).tupletstart << "\t"; - cerr << "TE:" << tg.at(i).tupletend << "\t"; - cerr << tg.at(i).force << "\t"; - cerr << tg.at(i).priority; - cerr << endl; + std::cerr << tg.at(i).group << "\t"; + std::cerr << tg.at(i).bracket << "\t"; + std::cerr << tg.at(i).num << "\t"; + std::cerr << tg.at(i).numbase << "\t"; + std::cerr << tg.at(i).numscale << "\t"; + std::cerr << tg.at(i).beamstart << "\t"; + std::cerr << tg.at(i).beamend << "\t"; + std::cerr << tg.at(i).gbeamstart << "\t"; + std::cerr << tg.at(i).gbeamend << "\t"; + std::cerr << "TS:" << tg.at(i).tupletstart << "\t"; + std::cerr << "TE:" << tg.at(i).tupletend << "\t"; + std::cerr << tg.at(i).force << "\t"; + std::cerr << tg.at(i).priority; + std::cerr << std::endl; } - cerr << "============================================" << endl; + std::cerr << "============================================" << std::endl; } ////////////////////////////// @@ -13007,7 +13247,10 @@ bool HumdrumInput::checkForTremolo( int beams = -log(duration.getFloat()) / log(2.0); if (beams <= 0) { // something went wrong calculating durations. - cerr << "PROBLEM WITH TREMOLO2 CALCULATION: " << beams << endl; + std::stringstream warning; + warning << "In HumdrumInput::checkForTremolo: "; + warning << "Problem with tremolo2 calculation, beam count: " << beams; + LogWarning(warning.str().c_str()); return false; } @@ -13048,7 +13291,7 @@ bool HumdrumInput::checkForInvisibleBeam( int beamnum = tgs.at(layerindex).beamstart; for (int i = layerindex; i < (int)tgs.size(); ++i) { if (!tgs.at(i).token) { - cerr << "WARNING in checkForInvisibleBeam: NULL token\n"; + LogWarning("In HumdrumInput::checkForInvisibleBeam: Encountered NULL token"); return false; } int len = (int)tgs.at(i).token->size(); @@ -13489,7 +13732,8 @@ void HumdrumInput::checkForVerseLabels(hum::HTp token) current = current->getNextFieldToken(); } while (current && !current->isStaff()) { - if (!(current->isDataTypeLike("**text") || current->isDataTypeLike("**vdata"))) { + if (!(current->isDataTypeLike("**text") || current->isDataTypeLike("**silbe") + || current->isDataTypeLike("**vdata"))) { current = current->getNextFieldToken(); continue; } @@ -13775,9 +14019,13 @@ bool HumdrumInput::fillContentsOfLayer(int track, int startline, int endline, in } } else { - std::cerr << "Strange error for adding rest " << trest << std::endl; - std::cerr << "LINE: " << trest->getLineNumber() << ", FIELD: " << trest->getFieldNumber() - << std::endl; + std::stringstream warning; + warning << "In HumdrumInput::fillContentsOfLayer: "; + warning << "Strange error when adding rest " << trest; + LogWarning(warning.str().c_str()); + std::stringstream warning2; + warning2 << " Line: " << trest->getLineNumber() << ", Field: " << trest->getFieldNumber(); + LogWarning(warning2.str().c_str()); } } @@ -15844,7 +16092,7 @@ void HumdrumInput::convertMensuralToken( } } else { - std::cerr << "WARNING: unmatched ligature ending" << std::endl; + LogWarning("In HumdrumInput::convertMensuralToken: Unmatched ligature ending"); } } if (roff) { @@ -17708,7 +17956,10 @@ void HumdrumInput::processLinkedDirection(int index, hum::HTp token, int staffin setLocationId(tempo, dirtok); } else { - cerr << "DIRTOK FOR " << token << " IS EMPTY " << endl; + std::stringstream warning; + warning << "In HumdrumInput::processLinkedDirection: dirtok for "; + warning << token << " is empty"; + LogWarning(warning.str().c_str()); } hum::HumNum tstamp = getMeasureTstamp(token, staffindex); if (token->isMensLike()) { @@ -17753,7 +18004,10 @@ void HumdrumInput::processLinkedDirection(int index, hum::HTp token, int staffin setLocationId(dir, dirtok); } else { - cerr << "DIRTOK FOR " << token << " IS EMPTY " << endl; + std::stringstream warning; + warning << "In HumdrumInput::processLinkedDirection: (2) dirtok for "; + warning << token << " is empty"; + LogWarning(warning.str().c_str()); } if (token->isMensLike()) { @@ -18070,11 +18324,11 @@ bool HumdrumInput::setLabelContent(Label *label, const std::string &name) } if (symbol.empty()) { - addTextElement(label, name2); + insertTextWithNewlines(label, name2); } else { if (!prestring.empty()) { - addTextElement(label, prestring); + insertTextWithNewlines(label, prestring); } Rend *rend = new Rend(); Text *text = new Text(); @@ -18083,7 +18337,7 @@ bool HumdrumInput::setLabelContent(Label *label, const std::string &name) label->AddChild(rend); rend->SetGlyphAuth("smufl"); if (!poststring.empty()) { - addTextElement(label, poststring); + insertTextWithNewlines(label, poststring); } // verovio probably eats the space surronding the // rend, so may need to force to be non-breaking space. @@ -18092,6 +18346,30 @@ bool HumdrumInput::setLabelContent(Label *label, const std::string &name) return true; } +////////////////////////////// +// +// HumdrumInput::insertTextWithNewlines -- +// + +void HumdrumInput::insertTextWithNewlines(Label *label, const std::string &text) +{ + vector pieces; + hum::HumRegex hre; + hre.split(pieces, text, "\\\\n"); + if (pieces.size() == 1) { + addTextElement(label, text); + } + else { + for (int i = 0; i < (int)pieces.size(); i++) { + addTextElement(label, pieces.at(i)); + if (i < (int)pieces.size() - 1) { + Lb *lb = new Lb(); + label->AddChild(lb); + } + } + } +} + ////////////////////////////// // // HumdrumInput::setTempoContent -- @@ -19122,7 +19400,7 @@ void HumdrumInput::processDynamics(hum::HTp token, int staffindex) ////////////////////////////// // -// HumdrumInput::addDynamicsMark -- Add dynamics marks such as p, f, sfz, rfz. +// HumdrumInput::addDynamicsMark -- Add dynamics marks such as p, f, sfz, rz, rfz. // The dynamics marking will be added at a tstamp rather than a startid. // @@ -19168,6 +19446,9 @@ void HumdrumInput::addDynamicsMark(hum::HTp dyntok, hum::HTp token, hum::HLp lin else if (hre.search(letters, "^s?f+z?p+$")) { dynamic = letters; } + else if (letters == "rz") { + dynamic = letters; + } if (!dynamic.empty()) { int staffadj = ss[si].m_dynamstaffadj; @@ -19415,9 +19696,14 @@ void HumdrumInput::addDynamicsMark(hum::HTp dyntok, hum::HTp token, hum::HLp lin ////////////////////////////// // // HumdrumInput::addSforzandoToNote -- A "z" on a note/chord indicates -// a sforzando mark ("sf", or use "zz" for "sfz"). This will be -// inserted into the floating elements as a with a @startid -// pointing to the note/chord. Other dynamics are placed using @tstamp. +// a sforzando mark. Repeated z's will choose one of the following accents: +// z = sf +// zz = sfz +// zzz = rz +// zzzz = rfz +// This accent be inserted into the floating elements as a with +// a @startid pointing to the note/chord. Other dynamics are placed +// using @tstamp. // void HumdrumInput::addSforzandoToNote(hum::HTp token, int staffindex) @@ -19549,7 +19835,13 @@ void HumdrumInput::addSforzandoToNote(hum::HTp token, int staffindex) data_FONTSIZE fs; fs.SetTerm(FONTSIZETERM_large); rend->SetFontsize(fs); - if (token->find("zz") != std::string::npos) { + if (token->find("zzzz") != std::string::npos) { + addTextElement(rend, "rfz "); + } + else if (token->find("zzzz") != std::string::npos) { + addTextElement(rend, "rz "); + } + else if (token->find("zz") != std::string::npos) { addTextElement(rend, "sfz "); } else { @@ -19566,7 +19858,13 @@ void HumdrumInput::addSforzandoToNote(hum::HTp token, int staffindex) } } else { - if (token->find("zz") != std::string::npos) { + if (token->find("zzzz") != std::string::npos) { + addTextElement(dynam, "rfz"); + } + else if (token->find("zzz") != std::string::npos) { + addTextElement(dynam, "rz"); + } + else if (token->find("zz") != std::string::npos) { addTextElement(dynam, "sfz"); } else { @@ -19649,7 +19947,7 @@ template void HumdrumInput::setAttachmentType(ELEMENT *element, template void HumdrumInput::attachToToken(ELEMENT *element, hum::HTp token) { if (token->isNull()) { - cerr << "ERROR: Cannot input null tokens into HumdrumInput::attachToToken() function." << endl; + LogWarning("In HumdrumInput::attachToToken: Cannot input null tokens into this function"); return; } if (token->isChord()) { @@ -22474,9 +22772,9 @@ void HumdrumInput::analyzeLayerBeams( if (m_debug) { for (int i = 0; i < (int)beamstate.size(); ++i) { - cerr << layerdata[i] << "(" << beamstate[i] << ") "; + std::cerr << layerdata[i] << "(" << beamstate[i] << ") "; } - cerr << endl; + std::cerr << std::endl; } // int beamstartindex = -1; @@ -22917,10 +23215,14 @@ Beam *HumdrumInput::insertGBeam( void HumdrumInput::removeBeam(std::vector &elements, std::vector &pointers) { if (elements.back() != "beam") { - cerr << "ERROR REMOVING BEAM" << endl; - cerr << "ELEMENT STACK:" << endl; + LogWarning("In HumdrumInput::removeBeam: Error removing beam"); + std::stringstream warning; + LogWarning(" Element stack: "); for (int i = (int)elements.size() - 1; i >= 0; i--) { - cerr << i << ":\t" << elements[i] << endl; + std::stringstream warning; + warning.str(""); + warning << " " << i << ":\t" << elements[i]; + LogWarning(warning.str().c_str()); } return; } @@ -22935,10 +23237,13 @@ void HumdrumInput::removeBeam(std::vector &elements, std::vector &elements, std::vector &pointers) { if (elements.back() != "gbeam") { - cerr << "ERROR REMOVING GBEAM" << endl; - cerr << "ELEMENT STACK:" << endl; + LogWarning("In HumdrumInput::removeGBeam: Error removing gbeam"); + LogWarning(" Element stack: "); for (int i = (int)elements.size() - 1; i >= 0; i--) { - cerr << i << ":\t" << elements[i] << endl; + std::stringstream warning; + warning.str(""); + warning << " " << i << ":\t" << elements[i]; + LogWarning(warning.str().c_str()); } return; } @@ -22953,11 +23258,16 @@ void HumdrumInput::removeGBeam(std::vector &elements, std::vector &elements, std::vector &pointers) { if (elements.back() != "tuplet") { - cerr << "ERROR REMOVING TUPLET" << endl; - cerr << "ELEMENT BACK IS " << elements.back() << endl; - cerr << "ELEMENT STACK:" << endl; + LogWarning("In HumdrumInput::removeTuplet: Error removing tuplet"); + std::stringstream warning; + warning << " Last element is: " << elements.back(); + LogWarning(warning.str().c_str()); + LogWarning(" Element stack: "); for (int i = (int)elements.size() - 1; i >= 0; i--) { - cerr << i << ":\t" << elements[i] << endl; + std::stringstream warning; + warning.str(""); + warning << " " << i << ":\t" << elements[i]; + LogWarning(warning.str().c_str()); } return; } @@ -23830,7 +24140,7 @@ void HumdrumInput::mergeTupletsCuttingBeam(std::vectortupletstart; scaleadj.at(i) = 2; @@ -23847,7 +24157,7 @@ void HumdrumInput::mergeTupletsCuttingBeam(std::vectortupletend = 0; @@ -23875,11 +24185,11 @@ void HumdrumInput::mergeTupletsCuttingBeam(std::vectortupletstart << "\t" - << newtg.at(i)->tupletend << "\t" << newtg.at(i)->num << "\t" << newtg.at(i)->numbase - << "\tSA=" << scaleadj.at(i) << endl; + std::cerr << "I " << i << ":\t" << inbeam.at(i) << "\t" << newtg.at(i)->tupletstart << "\t" + << newtg.at(i)->tupletend << "\t" << newtg.at(i)->num << "\t" << newtg.at(i)->numbase + << "\tSA=" << scaleadj.at(i) << std::endl; } } @@ -25286,6 +25596,7 @@ void HumdrumInput::adjustChordNoteDuration(Note *note, hum::HumNum hdur, int mei void HumdrumInput::setNoteMeiDur(Note *note, int meidur) { + std::stringstream warning; switch (meidur) { case -1: note->SetDur(DURATION_maxima); break; case 0: note->SetDur(DURATION_long); break; @@ -25301,7 +25612,9 @@ void HumdrumInput::setNoteMeiDur(Note *note, int meidur) case 10: note->SetDur(DURATION_256); break; case 11: note->SetDur(DURATION_512); break; case 12: note->SetDur(DURATION_1024); break; - default: cerr << "UNKNOWN MEI DUR: " << meidur << endl; + default: + warning << "In HumdrumInput::setNoteMeiDur: Unknown MEI @dur: " << meidur; + LogWarning(warning.str().c_str()); } } @@ -25483,7 +25796,10 @@ void HumdrumInput::appendElement(const std::vector &name, const std appendElement((Ligature *)pointers.back(), child); } else { - std::cerr << "WARNING: Cannot append to unknown element: " << name.back() << std::endl; + std::stringstream warning; + warning << "In HumdrumInput::appendElement: "; + warning << "Cannot append to unknown element: " << name.back(); + LogWarning(warning.str().c_str()); } } @@ -26280,7 +26596,12 @@ void HumdrumInput::convertNote(Note *note, hum::HTp token, int staffadj, int sta case -1: myaccid->SetAccid(ACCIDENTAL_WRITTEN_f); break; case -2: myaccid->SetAccid(ACCIDENTAL_WRITTEN_ff); break; case -3: myaccid->SetAccid(ACCIDENTAL_WRITTEN_tf); break; - default: std::cerr << "Do not know how to convert accidental: " << accidCount << endl; + default: { + std::stringstream warning; + warning << "In HumdrumInput::convertNote: "; + warning << "Do not know how to convert accidental: " << accidCount; + LogWarning(warning.str().c_str()); + } } if (accidlevel != 0) { @@ -26328,7 +26649,12 @@ void HumdrumInput::convertNote(Note *note, hum::HTp token, int staffadj, int sta case -1: accid->SetAccid(ACCIDENTAL_WRITTEN_f); break; case -2: accid->SetAccid(ACCIDENTAL_WRITTEN_ff); break; case -3: accid->SetAccid(ACCIDENTAL_WRITTEN_tf); break; - default: std::cerr << "Do not know how to convert accidental: " << accidCount << endl; + default: { + std::stringstream warning; + warning << "In HumdrumInput::convertNote: "; + warning << "Do not know how to convert accidental: " << accidCount; + LogWarning(warning.str().c_str()); + } } } else if (!loaccid.empty()) { @@ -26376,7 +26702,10 @@ void HumdrumInput::convertNote(Note *note, hum::HTp token, int staffadj, int sta showInAccidGes = true; } else { - std::cerr << "Warning: unknown accidental type " << std::endl; + std::stringstream warning; + warning << "In HumdrumInput::convertNote: "; + warning << "Unknown accidental type: " << loaccid; + LogWarning(warning.str().c_str()); } // add more accidentals here as necessary. Mostly left are quarter tones // which are not dealt with directly in **kern data: su, sd, fu, fd, nu, @@ -28380,8 +28709,11 @@ void HumdrumInput::addTurn(hum::HTp token, const string &tok, int noteIndex) } bool singleQ = false; if (turnstart == turnend) { - LogWarning( - "Humdrum: Single turn character on line %d, field, %d\n", token->getLineNumber(), token->getFieldNumber()); + std::stringstream warning; + warning << "In HumdrumInput::addTurn: Single turn character "; + warning << "on line " << token->getLineNumber() << ", "; + warning << "field, " << token->getFieldNumber() << "."; + LogWarning(warning.str().c_str()); singleQ = true; } @@ -29416,15 +29748,15 @@ void HumdrumInput::printMeasureTokens() { std::vector>> < = m_layertokens; int i, j, k; - cerr << endl; + std::cerr << std::endl; for (i = 0; i < (int)lt.size(); ++i) { - cerr << "STAFF " << i + 1 << "\t"; + std::cerr << "STAFF " << i + 1 << "\t"; for (j = 0; j < (int)lt[i].size(); ++j) { - cerr << "LAYER " << j + 1 << ":\t"; + std::cerr << "LAYER " << j + 1 << ":\t"; for (k = 0; k < (int)lt[i][j].size(); ++k) { - cout << " " << *lt[i][j][k]; + std::cout << " " << *lt[i][j][k]; } - cerr << endl; + std::cerr << std::endl; } } } @@ -29504,7 +29836,10 @@ template hum::HumNum HumdrumInput::setDuration(ELEMENT element, } // Don't know what to do, so return duration // There will be an error in the data. - cerr << "Unprintable rhythm: " << duration << endl; + std::stringstream warning; + warning << "In HumdrumInput::setDuration: "; + warning << "Unprintable duration" << duration << " quarter notes"; + LogWarning(warning.str().c_str()); return duration; } @@ -29572,7 +29907,7 @@ Tie *HumdrumInput::tieToPreviousItem(hum::HTp token, int subindex, hum::HumNum m tstamp += 1; } else { - cerr << "STRANGE CASE IN TIE INSERTION" << endl; + LogWarning("In HumdrumInput::tieToPreviousItem: Strange case for tie insertion."); } tie->SetTstamp(tstamp.getFloat()); // attach start to beginning of measure @@ -31430,7 +31765,7 @@ std::vector HumdrumInput::analyzeMultiRest(hum::HumdrumFile &infile) } // for (int i = 0; i < infile.getLineCount(); ++i) { - // cout << infile[i] << "\t" << output[i] << "\n"; + // std:: cout << infile[i] << "\t" << output[i] << "\n"; //} // Example analysis, with measure 4 staring a rest with num="6". // Measures 5-9 marked as whole-measure rests which will be merged into diff --git a/src/iomei.cpp b/src/iomei.cpp index eeb2f763486..c74c8a8939a 100644 --- a/src/iomei.cpp +++ b/src/iomei.cpp @@ -2381,6 +2381,7 @@ void MEIOutput::WriteBarLine(pugi::xml_node currentNode, BarLine *barLine) this->WriteLayerElement(currentNode, barLine); barLine->WriteBarLineLog(currentNode); + barLine->WriteBarLineVis(currentNode); barLine->WriteColor(currentNode); barLine->WriteNNumberLike(currentNode); barLine->WriteVisibility(currentNode); @@ -6428,6 +6429,7 @@ bool MEIInput::ReadBarLine(Object *parent, pugi::xml_node barLine) this->ReadLayerElement(barLine, vrvBarLine); vrvBarLine->ReadBarLineLog(barLine); + vrvBarLine->ReadBarLineVis(barLine); vrvBarLine->ReadColor(barLine); vrvBarLine->ReadNNumberLike(barLine); vrvBarLine->ReadVisibility(barLine); diff --git a/src/iomusxml.cpp b/src/iomusxml.cpp index 001017a6f9d..0432bf0caf1 100644 --- a/src/iomusxml.cpp +++ b/src/iomusxml.cpp @@ -2591,6 +2591,11 @@ void MusicXmlInput::ReadMusicXmlHarmony(pugi::xml_node node, Measure *measure, c std::string harmText = GetContentOfChild(node, "root/root-step"); pugi::xpath_node alter = node.select_node("root/root-alter"); + if (harmText.empty()) { + pugi::xml_node numeral = node.select_node("numeral/numeral-root").node(); + harmText = numeral.attribute("text") ? numeral.attribute("text").as_string() : numeral.text().as_string(); + alter = node.select_node("numeral/numeral-alter"); + } if (alter) harmText += ConvertAlterToSymbol(GetContent(alter.node())); pugi::xml_node kind = node.child("kind"); if (kind) { diff --git a/src/iovolpiano.cpp b/src/iovolpiano.cpp index bfdd67ca4f2..58313c9e66d 100644 --- a/src/iovolpiano.cpp +++ b/src/iovolpiano.cpp @@ -16,6 +16,7 @@ //---------------------------------------------------------------------------- +#include "barline.h" #include "clef.h" #include "doc.h" #include "layer.h" @@ -110,6 +111,28 @@ bool VolpianoInput::Import(const std::string &volpiano) else if (ch == 'I' || ch == 'W' || ch == 'X' || ch == 'Y' || ch == 'Z') { accidVal = ACCIDENTAL_WRITTEN_n; } + else if (ch == '3') { + BarLine *single = new BarLine(); + layer->AddChild(single); + } + else if (ch == '4') { + BarLine *dbl = new BarLine(); + dbl->SetForm(BARRENDITION_dbl); + layer->AddChild(dbl); + } + else if (ch == '5') { + BarLine *end = new BarLine(); + end->SetForm(BARRENDITION_end); + layer->AddChild(end); + } + else if (ch == '6') { + LogWarning("Volpiano '6' barline is not supported"); + } + else if (ch == '7') { + BarLine *takt = new BarLine(); + takt->SetMethod(BARMETHOD_takt); + layer->AddChild(takt); + } } // add minimal scoreDef diff --git a/src/layerelement.cpp b/src/layerelement.cpp index 93766929238..78cae4a13e8 100644 --- a/src/layerelement.cpp +++ b/src/layerelement.cpp @@ -901,13 +901,16 @@ MapOfDotLocs LayerElement::CalcOptimalDotLocations() // Special treatment for two layers if (layerCount == 2) { - // Find the first note on the other layer + // Find the first note on the other layer, but in the same staff Alignment *alignment = this->GetAlignment(); + const Staff *currentStaff = this->GetAncestorStaff(RESOLVE_CROSS_STAFF); const int currentLayerN = abs(this->GetAlignmentLayerN()); ListOfObjects notes = alignment->FindAllDescendantsByType(NOTE, false); - auto noteIt = std::find_if(notes.cbegin(), notes.cend(), [currentLayerN](Object *obj) { - const int otherLayerN = abs(vrv_cast(obj)->GetAlignmentLayerN()); - return (currentLayerN != otherLayerN); + auto noteIt = std::find_if(notes.cbegin(), notes.cend(), [currentLayerN, currentStaff](Object *obj) { + const Note *otherNote = vrv_cast(obj); + const Staff *otherStaff = otherNote->GetAncestorStaff(RESOLVE_CROSS_STAFF); + const int otherLayerN = abs(otherNote->GetAlignmentLayerN()); + return ((currentLayerN != otherLayerN) && (currentStaff == otherStaff)); }); if (noteIt != notes.cend()) { diff --git a/src/options.cpp b/src/options.cpp index 94b640dc35b..ba0265ef74a 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -914,8 +914,8 @@ Options::Options() m_baseOptions.AddOption(&m_allPages); m_inputFrom.SetInfo("Input from", - "Select input format from: \"abc\", \"darms\", \"humdrum\", \"mei\", \"pae\", \"volpiano\", \"xml\" " - "(musicxml)"); + "Select input format from: \"abc\", \"darms\", \"esac\", \"humdrum\", \"mei\", \"pae\", \"volpiano\", \"xml\" " + "(musicxml), \"musicxml-hum\" (musicxml via humdrum)"); m_inputFrom.Init("mei"); m_inputFrom.SetKey("inputFrom"); m_inputFrom.SetShortOption('f', false); diff --git a/src/page.cpp b/src/page.cpp index f336123d746..17388407eb8 100644 --- a/src/page.cpp +++ b/src/page.cpp @@ -253,6 +253,9 @@ void Page::LayOutTranscription(bool force) CalcAlignmentPitchPosFunctor calcAlignmentPitchPos(doc); this->Process(calcAlignmentPitchPos); + CalcLigatureOrNeumePosFunctor calcLigatureOrNeumePos(doc); + this->Process(calcLigatureOrNeumePos); + CalcStemFunctor calcStem(doc); this->Process(calcStem); @@ -262,13 +265,15 @@ void Page::LayOutTranscription(bool force) CalcDotsFunctor calcDots(doc); this->Process(calcDots); - // Render it for filling the bounding box - View view; - view.SetDoc(doc); - BBoxDeviceContext bBoxDC(&view, 0, 0, BBOX_HORIZONTAL_ONLY); - // Do not do the layout in this view - otherwise we will loop... - view.SetPage(this->GetIdx(), false); - view.DrawCurrentPage(&bBoxDC, false); + if (!m_layoutDone) { + // Render it for filling the bounding box + View view; + view.SetDoc(doc); + BBoxDeviceContext bBoxDC(&view, 0, 0, BBOX_HORIZONTAL_ONLY); + // Do not do the layout in this view - otherwise we will loop... + view.SetPage(this->GetIdx(), false); + view.DrawCurrentPage(&bBoxDC, false); + } AdjustXRelForTranscriptionFunctor adjustXRelForTranscription; this->Process(adjustXRelForTranscription); diff --git a/src/preparedatafunctor.cpp b/src/preparedatafunctor.cpp index c33f2677f1e..aad6ed25246 100644 --- a/src/preparedatafunctor.cpp +++ b/src/preparedatafunctor.cpp @@ -501,7 +501,8 @@ void PreparePlistFunctor::InsertInterfaceIDPair(const std::string &elementID, Pl FunctorCode PreparePlistFunctor::VisitObject(Object *object) { if (this->IsCollectingData()) { - if (object->HasInterface(INTERFACE_PLIST)) { + // Skip expansion elements because these are handled in Doc::ExpandExpansions + if (object->HasInterface(INTERFACE_PLIST) && !object->Is(EXPANSION)) { PlistInterface *interface = object->GetPlistInterface(); assert(interface); return interface->InterfacePreparePlist(*this, object); diff --git a/src/staff.cpp b/src/staff.cpp index 9360300b579..2a71ace62d8 100644 --- a/src/staff.cpp +++ b/src/staff.cpp @@ -298,18 +298,6 @@ int Staff::GetNearestInterStaffPosition(int y, const Doc *doc, data_STAFFREL pla // LedgerLine //---------------------------------------------------------------------------- -LedgerLine::LedgerLine() -{ - this->Reset(); -} - -LedgerLine::~LedgerLine() {} - -void LedgerLine::Reset() -{ - m_dashes.clear(); -} - void LedgerLine::AddDash(int left, int right, int extension) { assert(left < right); diff --git a/src/toolkit.cpp b/src/toolkit.cpp index 7b4edae8ea2..a81a8c61034 100644 --- a/src/toolkit.cpp +++ b/src/toolkit.cpp @@ -70,6 +70,8 @@ Toolkit::Toolkit(bool initFont) m_humdrumBuffer = NULL; m_cString = NULL; + m_cerrOriginalBuf = NULL; + if (initFont) { Resources &resources = m_doc.GetResourcesForModification(); resources.InitFonts(); @@ -298,11 +300,17 @@ FileFormat Toolkit::IdentifyInputFrom(const std::string &data) return UNKNOWN; } if (initial.find("\n!!") != std::string::npos) { + // Case where there are empty lines before content in Humdrum files. return HUMDRUM; } if (initial.find("\n**") != std::string::npos) { + // Case where there are empty lines before content in Humdrum files. return HUMDRUM; } + if (initial.find("\nCUT[") != std::string::npos) { + // Title record for a melody in EsAC format. + return ESAC; + } // Assume that the input is MEI if other input types were not detected. // This means that DARMS cannot be auto-detected. @@ -311,6 +319,8 @@ FileFormat Toolkit::IdentifyInputFrom(const std::string &data) bool Toolkit::LoadFile(const std::string &filename) { + this->ResetLogBuffer(); + if (this->IsUTF16(filename)) { return this->LoadUTF16File(filename); } @@ -332,7 +342,7 @@ bool Toolkit::LoadFile(const std::string &filename) std::string content(fileSize, 0); in.read(&content[0], fileSize); - return this->LoadData(content); + return this->LoadData(content, false); } bool Toolkit::IsUTF16(const std::string &filename) @@ -393,7 +403,7 @@ bool Toolkit::LoadUTF16File(const std::string &filename) std::wstring_convert, char16_t> convert; std::string utf8line = convert.to_bytes(u16data); - return this->LoadData(utf8line); + return this->LoadData(utf8line, false); } bool Toolkit::IsZip(const std::string &filename) @@ -438,6 +448,7 @@ bool Toolkit::LoadZipFile(const std::string &filename) bool Toolkit::LoadZipData(const std::vector &bytes) { + this->ResetLogBuffer(); #ifndef NO_MXL_SUPPORT ZipFileReader zipFileReader; zipFileReader.LoadBytes(bytes); @@ -456,7 +467,7 @@ bool Toolkit::LoadZipData(const std::vector &bytes) if (!filename.empty()) { LogInfo("Loading file '%s' in the archive", filename.c_str()); - return this->LoadData(zipFileReader.ReadTextFile(filename)); + return this->LoadData(zipFileReader.ReadTextFile(filename), false); } else { LogError("No file to load found in the archive"); @@ -481,10 +492,19 @@ bool Toolkit::LoadZipDataBuffer(const unsigned char *data, int length) } bool Toolkit::LoadData(const std::string &data) +{ + return this->LoadData(data, true); +} + +bool Toolkit::LoadData(const std::string &data, bool resetLogBuffer) { std::string newData; Input *input = NULL; + if (resetLogBuffer) { + this->ResetLogBuffer(); + } + m_doc.m_expansionMap.Reset(); if (m_options->m_xmlIdChecksum.GetValue()) { @@ -603,7 +623,14 @@ bool Toolkit::LoadData(const std::string &data) pugi::xml_document xmlfile; xmlfile.load_string(data.c_str()); stringstream conversion; + + LogRedirectStart(); bool status = converter.convert(conversion, xmlfile); + LogRedirectStop(); + if (!status) { + LogWarning("Problem converting MusicXML to Humdrum (see warning above this line for possible reasons"); + } + if (!status) { LogError("Error converting MusicXML data"); return false; @@ -651,7 +678,14 @@ bool Toolkit::LoadData(const std::string &data) // This is the indirect converter from MuseData to MEI using iohumdrum: hum::Tool_musedata2hum converter; stringstream conversion; + + LogRedirectStart(); bool status = converter.convertString(conversion, data); + LogRedirectStop(); + if (!status) { + LogWarning("Problem converting MuseData to Humdrum (see warning above this line for possible reasons"); + } + if (!status) { LogError("Error converting MuseData data"); return false; @@ -678,8 +712,15 @@ bool Toolkit::LoadData(const std::string &data) else if (inputFormat == ESAC) { // This is the indirect converter from EsAC to MEI using iohumdrum: hum::Tool_esac2hum converter; - stringstream conversion; + std::stringstream conversion; + + LogRedirectStart(); bool status = converter.convert(conversion, data); + LogRedirectStop(); + if (!status) { + LogWarning("Problem converting EsAC to Humdrum (see warning above this line for possible reasons"); + } + if (!status) { LogError("Error converting EsAC data"); return false; @@ -1423,6 +1464,7 @@ std::string Toolkit::GetLog() for (const std::string &logStr : logBuffer) { str += logStr; } + this->ResetLogBuffer(); return str; } @@ -1442,6 +1484,35 @@ void Toolkit::ResetLogBuffer() logBuffer.clear(); } +void Toolkit::LogRedirectStart() +{ + if (m_cerrOriginalBuf) { + vrv::LogError("In Toolkit::LogRedirectStart: Only one log redirect can be active at a time."); + return; + } + if (!m_cerrCaptured.str().empty()) { + vrv::LogWarning("In Toolkit::LogRedirectStart: Log capture buffer not empty, sending current contents to " + "LogWarning and resetting."); + vrv::LogWarning(m_cerrCaptured.str().c_str()); + m_cerrCaptured.str(""); + } + m_cerrOriginalBuf = std::cerr.rdbuf(); + std::cerr.rdbuf(m_cerrCaptured.rdbuf()); +} + +void Toolkit::LogRedirectStop() +{ + if (!m_cerrCaptured.str().empty()) { + vrv::LogWarning(m_cerrCaptured.str().c_str()); + m_cerrCaptured.str(""); + } + + if (m_cerrOriginalBuf) { + std::cerr.rdbuf(m_cerrOriginalBuf); + m_cerrOriginalBuf = NULL; + } +} + void Toolkit::RedoLayout(const std::string &jsonOptions) { bool resetCache = true; @@ -1560,7 +1631,7 @@ bool Toolkit::RenderToDeviceContext(int pageNo, DeviceContext *deviceContext) std::string Toolkit::RenderData(const std::string &data, const std::string &jsonOptions) { - if (this->SetOptions(jsonOptions) && this->LoadData(data)) return this->RenderToSVG(1); + if (this->SetOptions(jsonOptions) && this->LoadData(data, false)) return this->RenderToSVG(1); // Otherwise just return an empty string. return ""; @@ -2034,7 +2105,14 @@ const char *Toolkit::GetHumdrumBuffer() infile.load_string(meidata.c_str()); stringstream out; hum::Tool_mei2hum converter; - converter.convert(out, infile); + + LogRedirectStart(); + bool status = converter.convert(out, infile); + LogRedirectStop(); + if (!status) { + LogWarning("Problem converting MEI to Humdrum (see warning above this line for possible reasons"); + } + this->SetHumdrumBuffer(out.str().c_str()); #endif if (m_humdrumBuffer) { @@ -2095,7 +2173,11 @@ std::string Toolkit::ConvertMEIToHumdrum(const std::string &meiData) pugi::xml_document xmlfile; xmlfile.load_string(meiData.c_str()); std::stringstream conversion; + + LogRedirectStart(); bool status = converter.convert(conversion, xmlfile); + LogRedirectStop(); + if (!status) { LogError("Error converting MEI data to Humdrum: %s", conversion.str().c_str()); } diff --git a/src/view_element.cpp b/src/view_element.cpp index eb253fc26fd..250400ac4bd 100644 --- a/src/view_element.cpp +++ b/src/view_element.cpp @@ -420,11 +420,23 @@ void View::DrawBarLine(DeviceContext *dc, LayerElement *element, Layer *layer, S barLine->SetEmptyBB(); return; } + StaffDef *drawingStaffDef = staff->m_drawingStaffDef; + // Determine the method + assert(drawingStaffDef); + auto [hasMethod, method] = barLine->GetMethodFromContext(drawingStaffDef); + if (barLine->HasMethod()) { + method = barLine->GetMethod(); + } dc->StartGraphic(element, "", element->GetID()); - const int yTop = staff->GetDrawingY(); - const int yBottom = yTop - (staff->m_drawingLines - 1) * m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); + int yTop = staff->GetDrawingY(); + int yBottom = yTop - (staff->m_drawingLines - 1) * m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); + + if (method == BARMETHOD_takt) { + yTop += m_doc->GetDrawingUnit(staff->m_drawingStaffSize); + yBottom = yTop - m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize); + } const int offset = (yTop == yBottom) ? m_doc->GetDrawingDoubleUnit(staff->m_drawingStaffSize) : 0; diff --git a/src/view_page.cpp b/src/view_page.cpp index 626aa7f91e7..d8e7797b34a 100644 --- a/src/view_page.cpp +++ b/src/view_page.cpp @@ -772,7 +772,7 @@ void View::DrawBarLines(DeviceContext *dc, Measure *measure, StaffGrp *staffGrp, } // Determine the method - const auto [hasMethod, method] = barLine->GetMethod(staffDef); + const auto [hasMethod, method] = barLine->GetMethodFromContext(staffDef); const bool methodMensur = hasMethod && (method == BARMETHOD_mensur); const bool methodTakt = hasMethod && (method == BARMETHOD_takt); @@ -798,7 +798,7 @@ void View::DrawBarLines(DeviceContext *dc, Measure *measure, StaffGrp *staffGrp, // Adjust start and length if (!methodMensur && !methodTakt) { - const auto [hasPlace, place] = barLine->GetPlace(staffDef); + const auto [hasPlace, place] = barLine->GetPlaceFromContext(staffDef); if (hasPlace) { // bar.place counts upwards (note order). yBottom += place * unit; @@ -808,7 +808,7 @@ void View::DrawBarLines(DeviceContext *dc, Measure *measure, StaffGrp *staffGrp, yBottom -= 2 * unit; } - const auto [hasLength, length] = barLine->GetLength(staffDef); + const auto [hasLength, length] = barLine->GetLengthFromContext(staffDef); if (hasLength) { yLength = length * unit; } diff --git a/src/vrv.cpp b/src/vrv.cpp index f4ac75c2d33..7f2c66833a8 100644 --- a/src/vrv.cpp +++ b/src/vrv.cpp @@ -76,7 +76,7 @@ void LogElapsedTimeStart() gettimeofday(&start, NULL); } -void LogElapsedTimeEnd(const char *msg) +void LogElapsedTimeStop(const char *msg) { double elapsedTime; struct timeval end;