From 2e01e4555722e0b088f6aa33541fd8616136f345 Mon Sep 17 00:00:00 2001 From: Craig Stuart Sapp Date: Mon, 16 Sep 2024 06:35:16 -0700 Subject: [PATCH 1/8] Humlib updates. --- include/hum/humlib.h | 36 +- src/hum/humlib.cpp | 934 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 966 insertions(+), 4 deletions(-) diff --git a/include/hum/humlib.h b/include/hum/humlib.h index d9b39732d30..ae61be04adf 100644 --- a/include/hum/humlib.h +++ b/include/hum/humlib.h @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Sun Sep 8 23:07:16 PDT 2024 +// Last Modified: Thu Sep 12 14:59:38 PDT 2024 // Filename: min/humlib.h // URL: https://github.com/craigsapp/humlib/blob/master/min/humlib.h // Syntax: C++11 @@ -5647,6 +5647,40 @@ class HumdrumFileSet { +class Tool_1520ify : public HumTool { + public: + Tool_1520ify (void); + ~Tool_1520ify () {}; + + 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 (HumdrumFile& infile); + void processFile (HumdrumFile& infile); + void updateKeySignatures(HumdrumFile& infile, int lineindex); + void checkDataLine (HumdrumFile& infile, int lineindex); + void clearStates (void); + void addBibliographicRecords(HumdrumFile& infile); + void deleteBreaks (HumdrumFile& infile); + void fixEditorialAccidentals(HumdrumFile& infile); + void fixInstrumentAbbreviations(HumdrumFile& infile); + void addTerminalLongs (HumdrumFile& infile); + void deleteDummyTranspositions(HumdrumFile& infile); + std::string getDate (void); + int getYear (void); + void adjustSystemDecoration(HumdrumFile& infile); + + private: + std::vector> m_pstates; + std::vector> m_kstates; + std::vector> m_estates; + +}; + + class Tool_addic : public HumTool { public: Tool_addic (void); diff --git a/src/hum/humlib.cpp b/src/hum/humlib.cpp index ed7780c0419..fc61393a15e 100644 --- a/src/hum/humlib.cpp +++ b/src/hum/humlib.cpp @@ -1,7 +1,7 @@ // // Programmer: Craig Stuart Sapp // Creation Date: Sat Aug 8 12:24:49 PDT 2015 -// Last Modified: Sun Sep 8 23:07:16 PDT 2024 +// Last Modified: Thu Sep 12 14:59:38 PDT 2024 // Filename: min/humlib.cpp // URL: https://github.com/craigsapp/humlib/blob/master/min/humlib.cpp // Syntax: C++11 @@ -53905,6 +53905,928 @@ ostream& operator<<(ostream& out, PixelColor apixel) { +///////////////////////////////// +// +// Tool_1520ify::Tool_1520ify -- Set the recognized options for the tool. +// + +Tool_1520ify::Tool_1520ify(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"); + + define("E|do-not-fix-editorial-accidentals=b", "do not fix instrument abbreviations"); + define("e|only-fix-editorial-accidentals=b", "only fix editorial accidentals"); + + 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("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_1520ify::run -- Primary interfaces to the tool. +// + +bool Tool_1520ify::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; 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; + } + } +} + + + +////////////////////////////// +// +// Tool_1520ify::deleteDummyTranspositions -- Somehow empty +// transpositions that go to the same pitch can appear in the +// MusicXML data, so remove them here. Example: +// *Trd0c0 +// + +void Tool_1520ify::deleteDummyTranspositions(HumdrumFile& infile) { + std::vector ldel; + for (int i=0; iisKern()) { + empty = false; + continue; + } + if (*token == "*Trd0c0") { + token->setText("*"); + } else { + empty = false; + } + } + 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_1520ify::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. +// + +void Tool_1520ify::fixEditorialAccidentals(HumdrumFile& infile) { + m_pstates.resize(infile.getMaxTrack() + 1); + m_estates.resize(infile.getMaxTrack() + 1); + m_kstates.resize(infile.getMaxTrack() + 1); + + 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); + } + + 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") != std::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_1520ify::fixInstrumentAbbreviations -- +// + +void Tool_1520ify::fixInstrumentAbbreviations(HumdrumFile& infile) { + int iline = -1; + int aline = -1; + + std::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; + } + if (aline < 0) { + // not creating a new abbreviation for now + // (could add later). + return; + } + if (infile[iline].getFieldCount() != infile[aline].getFieldCount()) { + // no spine splitting between the two lines. + return; + } + // 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); + } +} + + + +////////////////////////////// +// +// Tool_1520ify::deleteBreaks -- +// + +void Tool_1520ify::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_1520ify::addBibliographicRecords -- +// +// !!!!SEGMENT: +// !!!renid: +// !!!AGN: +// !!!voices: +// !!!COM: +// !!!CDT: +// !!!OTL: +// !!!OPR: +// +// At end: +// !!!RDF**kern: l = terminal long (if needed) +// !!!RDF**kern: i = editorial accidental (if needed) +// !!!ENC: Benjamin Ory +// !!!END: +// !!!EED: Benjamin Ory +// !!!EEV: +// !!!YEC: Copyright Benjamin Ory, All Rights Reserved +// !!!ONB: Translated from MusicXML and edited on $DATE +// + +void Tool_1520ify::addBibliographicRecords(HumdrumFile& infile) { + std::vector refinfo = infile.getReferenceRecords(); + std::map refs; + for (int i=0; i<(int)refinfo.size(); i++) { + string key = refinfo[i]->getReferenceKey(); + refs[key] = refinfo[i]; + } + + // header records (inserted in reverse order) + // automatic placement before/after existing records + // could be improved later. + if (refs.find("OPR") == refs.end()) { + if (infile.token(0, 0)->find("!!!OPR") != std::string::npos) { + infile.insertLine(1, "!!!OPR:"); + } else { + infile.insertLine(0, "!!!OPR:"); + } + } + + if (refs.find("OTL") == refs.end()) { + if (infile.token(0, 0)->find("!!!OTL") != std::string::npos) { + infile.insertLine(1, "!!!OTL:"); + } else { + infile.insertLine(0, "!!!OTL:"); + } + } + + if (refs.find("CDT") == refs.end()) { + if (infile.token(0, 0)->find("!!!CDT") != std::string::npos) { + infile.insertLine(1, "!!!CDT:"); + } else { + infile.insertLine(0, "!!!CDT:"); + } + } + + if (refs.find("COM") == refs.end()) { + if (infile.token(0, 0)->find("!!!COM") != std::string::npos) { + infile.insertLine(1, "!!!COM:"); + } else { + infile.insertLine(0, "!!!COM:"); + } + } + + if (refs.find("voices") == refs.end()) { + if (infile.token(0, 0)->find("!!!voices") != std::string::npos) { + infile.insertLine(1, "!!!voices:"); + } else { + infile.insertLine(0, "!!!voices:"); + } + } + + if (refs.find("AGN") == refs.end()) { + if (infile.token(0, 0)->find("!!!AGN") != std::string::npos) { + infile.insertLine(1, "!!!AGN:"); + } else { + infile.insertLine(0, "!!!AGN:"); + } + } + + if (refs.find("renid") == refs.end()) { + if (infile.token(0, 0)->find("!!!renid") != std::string::npos) { + infile.insertLine(1, "!!!renid:"); + } else { + infile.insertLine(0, "!!!renid:"); + } + } + + infile.insertLine(0, "!!!!SEGMENT:"); + + int year = getYear(); + string date = getDate(); + + + + // trailer records + bool foundi = false; + bool foundl = false; + for (int i=0; ifind("!!!RDF**kern:") == std::string::npos) { + continue; + } + if (infile.token(i, 0)->find("terminal long") != std::string::npos) { + foundl = true; + } else if (infile.token(i, 0)->find("editorial accidental") != std::string::npos) { + foundi = true; + } + } + if (!foundi) { + infile.appendLine("!!!RDF**kern: i = editorial accidental"); + } + if (!foundl) { + infile.appendLine("!!!RDF**kern: l = terminal long"); + } + + if (refs.find("ENC") == refs.end()) { + infile.appendLine("!!!ENC: Benjamin Ory"); + } + if (refs.find("END") == refs.end()) { + infile.appendLine("!!!END:"); + } + if (refs.find("EED") == refs.end()) { + infile.appendLine("!!!EED: Benjamin Ory"); + } + if (refs.find("EEV") == refs.end()) { + string line = "!!!EEV: " + date; + infile.appendLine(line); + } + if (refs.find("YEC") == refs.end()) { + string line = "!!!YEC: Copyright "; + line += to_string(year); + line += " Benjamin Ory, All Rights Reserved"; + infile.appendLine(line); + } + if (refs.find("ONB") == refs.end()) { + string line = "!!!ONB: Translated from MusicXML on " + date; + infile.appendLine(line); + } + +} + + + +//////////////////////////////// +// +// Tool_1520ify::checkDataLine -- +// + +void Tool_1520ify::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->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; + } + + if (removeQ) { + string temp = *token; + hre.replaceDestructive(temp, "", "X"); + token->setText(temp); + } + + 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 (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 (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("#") != std::string::npos) { + hasaccid = true; + } else if (token->find("-") != std::string::npos) { + hasaccid = true; + } + int hashide = false; + if (token->find("-y") != std::string::npos) { + hashide = true; + } + else if (token->find("#y") != std::string::npos) { + hashide = true; + } + if (hasaccid && !hashide) { + string text = *token; + hre.replaceDestructive(text, "#y", "#"); + hre.replaceDestructive(text, "-y", "-"); + token->setText(text); + } + + 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). + + m_estates[track][base7] = true; + m_pstates[track][base7] = accid; + + string text = token->getText(); + 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]; + } + } + + if (foundQ) { + token->setText(output); + continue; + } + + // 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); + } +} + + + +//////////////////////////////// +// +// Tool_1520ify::updateKeySignatures -- Fill in the accidental +// states for each diatonic pitch. +// + +void Tool_1520ify::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; + + case 'd': case 'D': + switch (text[j+1]) { + case '#': m_kstates[track][1] = +1; + break; + case '-': m_kstates[track][1] = -1; + break; + } + break; + + case 'e': case 'E': + switch (text[j+1]) { + case '#': m_kstates[track][2] = +1; + break; + case '-': m_kstates[track][2] = -1; + break; + } + break; + + case 'f': case 'F': + switch (text[j+1]) { + case '#': m_kstates[track][3] = +1; + break; + case '-': m_kstates[track][3] = -1; + break; + } + break; + + 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]; + } + } + } + } + + // 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]; + } + } + +} + + + +//////////////////////////////// +// +// Tool_1520ify::clearStates -- +// + +void Tool_1520ify::clearStates(void) { + for (int i=0; i<(int)m_pstates.size(); i++) { + fill(m_pstates[i].begin(), m_pstates[i].end(), 0); + } + for (int i=0; i<(int)m_estates.size(); i++) { + fill(m_estates[i].begin(), m_estates[i].end(), false); + } +} + + +////////////////////////////// +// +// Tool_1520ify::getDate -- +// + +string Tool_1520ify::getDate(void) { + auto now = std::chrono::system_clock::now(); + std::time_t now_time = std::chrono::system_clock::to_time_t(now); + std::tm* local_time = std::localtime(&now_time); + int year = local_time->tm_year + 1900; + int month = local_time->tm_mon + 1; + int day = local_time->tm_mday; + stringstream ss; + ss << year << "/"; + ss << std::setw(2) << std::setfill('0') << month << "/"; + ss << std::setw(2) << std::setfill('0') << day; + return ss.str(); +} + + + +////////////////////////////// +// +// Tool_1520ify::getYear -- +// + +int Tool_1520ify::getYear(void) { + auto now = std::chrono::system_clock::now(); + std::time_t now_time = std::chrono::system_clock::to_time_t(now); + std::tm* local_time = std::localtime(&now_time); + int year = local_time->tm_year + 1900; + return year; +} + + + + + ///////////////////////////////// // // Tool_addic::Tool_addic -- Set the recognized options for the tool. @@ -80355,7 +81277,11 @@ void Tool_esac2hum::embedAnalyses(ostream& output) { // void Tool_esac2hum::printPdfLinks(ostream& output) { - output << "!!!URL: http://webesac.pcss.pl WebEsAC" << endl; + if (m_dwokQ) { + output << "!!!URL: https://kolberg.ispan.pl/webesac Kolberg WebEsAC" << endl; + } else { + output << "!!!URL: http://webesac.pcss.pl WebEsAC" << endl; + } if (!m_dwokQ) { return; @@ -80966,7 +81892,7 @@ string Tool_esac2hum::getKolbergUrl(int volume) { if (pageinfo.find("-") != string::npos) { url += "p"; } - url += "." + pageinfo; + url += ". " + pageinfo; url += "}"; } @@ -86154,6 +87080,8 @@ bool Tool_filter::run(HumdrumFileSet& infiles) { RUNTOOL(trillspell, infile, commands[i].second, status); } else if (commands[i].first == "vcross") { RUNTOOL(vcross, infile, commands[i].second, status); + } else if (commands[i].first == "1520ify") { + RUNTOOL(1520ify, infile, commands[i].second, status); // filters with aliases: From 739eb04c45f01f5ae143a59cd6569129024172d6 Mon Sep 17 00:00:00 2001 From: Klaus Rettinghaus Date: Mon, 16 Sep 2024 15:31:02 +0200 Subject: [PATCH 2/8] set initial tempo only if not default --- src/doc.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/doc.cpp b/src/doc.cpp index 4300e41b8d8..9c7a8eeb866 100644 --- a/src/doc.cpp +++ b/src/doc.cpp @@ -415,7 +415,10 @@ void Doc::ExportMIDI(smf::MidiFile *midiFile) else if (scoreDef->HasMm()) { tempo = Tempo::CalcTempo(scoreDef); } - midiFile->addTempo(0, 0, tempo); + + if (tempo != MIDI_TEMPO) { + midiFile->addTempo(0, 0, tempo); + } // Capture information for MIDI generation, i.e. from control elements InitMIDIFunctor initMIDI; From a7aab369a7ce90818f5371969517214cc3360780 Mon Sep 17 00:00:00 2001 From: Klaus Rettinghaus Date: Tue, 17 Sep 2024 09:52:26 +0200 Subject: [PATCH 3/8] fix: only write explicit tempo to midi file --- src/doc.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/doc.cpp b/src/doc.cpp index 9c7a8eeb866..3bc43359905 100644 --- a/src/doc.cpp +++ b/src/doc.cpp @@ -411,12 +411,10 @@ void Doc::ExportMIDI(smf::MidiFile *midiFile) ScoreDef *scoreDef = this->GetFirstVisibleScore()->GetScoreDef(); if (scoreDef->HasMidiBpm()) { tempo = scoreDef->GetMidiBpm(); + midiFile->addTempo(0, 0, tempo); } else if (scoreDef->HasMm()) { tempo = Tempo::CalcTempo(scoreDef); - } - - if (tempo != MIDI_TEMPO) { midiFile->addTempo(0, 0, tempo); } From d841c52e7d2263e9a9cee9f7a4866d0ceaa1929e Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Wed, 18 Sep 2024 11:10:27 +0200 Subject: [PATCH 4/8] Update GH Action clang-format to 19 --- .github/workflows/clang-format-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index f79013e8515..35b9b1caa94 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -20,6 +20,6 @@ jobs: - name: Run clang-format style check for C/C++ programs. uses: jidicula/clang-format-action@v4.11.0 with: - clang-format-version: "18" + clang-format-version: "19" check-path: ${{ matrix.path['check'] }} exclude-regex: ${{ matrix.path['exclude'] }} From 73a0cb80daa647a4bc7f511a9a39a8673f951c5d Mon Sep 17 00:00:00 2001 From: Craig Stuart Sapp Date: Fri, 27 Sep 2024 11:25:13 -0700 Subject: [PATCH 5/8] Code cleanup related to subissue https://github.com/rism-digital/verovio/pull/3789#issuecomment-2379820688 --- src/doc.cpp | 3 +++ src/toolkit.cpp | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/doc.cpp b/src/doc.cpp index b123741b1cf..0502699dafe 100644 --- a/src/doc.cpp +++ b/src/doc.cpp @@ -397,6 +397,8 @@ void Doc::CalculateTimemap() void Doc::ExportMIDI(smf::MidiFile *midiFile) { + midiFile->absoluteTicks(); + if (!this->HasTimemap()) { // generate MIDI timemap before progressing CalculateTimemap(); @@ -537,6 +539,7 @@ void Doc::ExportMIDI(smf::MidiFile *midiFile) tempoEventTicks = generateMIDI.GetTempoEventTicks(); } } + midiFile->sortTracks(); } bool Doc::ExportTimemap(std::string &output, bool includeRests, bool includeMeasures) diff --git a/src/toolkit.cpp b/src/toolkit.cpp index d97d50e3a62..9e602dfb6c4 100644 --- a/src/toolkit.cpp +++ b/src/toolkit.cpp @@ -1736,9 +1736,7 @@ std::string Toolkit::RenderToMIDI() this->ResetLogBuffer(); smf::MidiFile outputfile; - outputfile.absoluteTicks(); m_doc.ExportMIDI(&outputfile); - outputfile.sortTracks(); std::stringstream stream; outputfile.write(stream); @@ -1883,9 +1881,7 @@ bool Toolkit::RenderToMIDIFile(const std::string &filename) this->ResetLogBuffer(); smf::MidiFile outputfile; - outputfile.absoluteTicks(); m_doc.ExportMIDI(&outputfile); - outputfile.sortTracks(); outputfile.write(filename); return true; From b3784cd31b643c95974a36c53199981cf61e0fd3 Mon Sep 17 00:00:00 2001 From: Craig Stuart Sapp Date: Fri, 27 Sep 2024 11:52:52 -0700 Subject: [PATCH 6/8] Implment cut-c over 3 in Humdrum-to-MEI conversion. --- src/iohumdrum.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/iohumdrum.cpp b/src/iohumdrum.cpp index 5feedb4b297..0be215b592e 100644 --- a/src/iohumdrum.cpp +++ b/src/iohumdrum.cpp @@ -8737,7 +8737,7 @@ void HumdrumInput::setMensurationSymbol( tempus = 2; prolatio = 2; } - else if (metdata == "C|3") { // fix for regular and sesquialtera + else if (metdata == "C|3") { if (m_mens) { vrvmensur->SetTempus(TEMPUS_2); vrvmensur->SetProlatio(PROLATIO_2); @@ -8749,6 +8749,18 @@ void HumdrumInput::setMensurationSymbol( tempus = 2; prolatio = 2; } + else if (metdata == "C|/3") { + if (m_mens) { + vrvmensur->SetTempus(TEMPUS_2); + vrvmensur->SetProlatio(PROLATIO_2); + } + vrvmensur->SetSlash(1); + vrvmensur->SetNumbase(3); + maximodus = 2; + modus = 2; + tempus = 2; + prolatio = 2; + } else if (metdata == "O") { if (m_mens) { vrvmensur->SetModusmaior(MODUSMAIOR_2); From f475a11aa9fd70c37cab78abdedfb18073a25370 Mon Sep 17 00:00:00 2001 From: Craig Stuart Sapp Date: Wed, 13 Nov 2024 13:52:04 -0800 Subject: [PATCH 7/8] Humlib update. --- include/hum/humlib.h | 27 +++- src/hum/humlib.cpp | 354 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 360 insertions(+), 21 deletions(-) diff --git a/include/hum/humlib.h b/include/hum/humlib.h index ae61be04adf..db788fbafa1 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: Thu Sep 12 14:59:38 PDT 2024 +// Last Modified: Wed Nov 13 13:08:51 PST 2024 // Filename: min/humlib.h // URL: https://github.com/craigsapp/humlib/blob/master/min/humlib.h // Syntax: C++11 @@ -5957,6 +5957,31 @@ class Tool_autostem : public HumTool { }; +class Tool_bardash : public HumTool { + + public: + Tool_bardash (void); + ~Tool_bardash() {}; + + 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 removeBarStylings(HumdrumFile& infile); + void removeBarStylings(HTp spine); + void applyBarStylings(HumdrumFile& infile); + void applyBarStylings(HTp spine); + + private: + bool m_removeQ = false; // used with -r option + +}; + + class Tool_binroll : public HumTool { public: Tool_binroll (void); diff --git a/src/hum/humlib.cpp b/src/hum/humlib.cpp index fc61393a15e..b5135662855 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: Thu Sep 12 14:59:38 PDT 2024 +// Last Modified: Wed Nov 13 13:08:51 PST 2024 // Filename: min/humlib.cpp // URL: https://github.com/craigsapp/humlib/blob/master/min/humlib.cpp // Syntax: C++11 @@ -45496,8 +45496,8 @@ char& MuseRecordBasic::getColumn(int columnNumber) { int length = (int)m_recordString.size(); // originally the limit for data columns was 80: // if (realindex < 0 || realindex >= 80) { - // the new limit is somewhere above 900, but limit to 180 - if (realindex < 0 || realindex >= 180) { + // the new limit is somewhere above 900, but limit to 1024 + if (realindex < 0 || realindex >= 1024) { cerr << "Error trying to access column: " << columnNumber << endl; cerr << "CURRENT DATA: ===============================" << endl; cerr << (*this); @@ -58772,6 +58772,305 @@ void Tool_autostem::countBeamStuff(const string& token, int& start, int& stop, +///////////////////////////////// +// +// Tool_bardash::Tool_bardash -- Set the recognized options for the tool. +// + +Tool_bardash::Tool_bardash(void) { + define("r|remove=b", "remove any dot/dash/invisible barline stylings"); +} + + + +/////////////////////////////// +// +// Tool_bardash::run -- Primary interfaces to the tool. +// + +bool Tool_bardash::run(HumdrumFileSet& infiles) { + bool status = true; + for (int i=0; i kstarts = infile.getKernSpineStartList(); + for (int i=0; i<(int)kstarts.size(); i++) { + removeBarStylings(kstarts.at(i)); + } +} + + + +////////////////////////////// +// +// Tool_bardash::removeBarStylings -- +// + +void Tool_bardash::removeBarStylings(HTp spine) { + + HTp current = spine->getNextToken(); + bool activeQ = false; + bool dashQ = false; + bool dotQ = false; + bool invisQ = false; + HumRegex hre; + while (current) { + if (current->isInterpretation()) { + if (hre.search(current, "^\\*bar:")) { + if (hre.search(current, "stop")) { + activeQ = false; + dashQ = false; + dotQ = false; + invisQ = false; + } else { + activeQ = true; + if (hre.search(current, "^\\*bar:.*dash=(\\d+)")) { + dashQ = true; + dotQ = false; + invisQ = false; + } else if (hre.search(current, "^\\*bar:.*dot=(\\d+)")) { + dashQ = false; + dotQ = true; + invisQ = false; + } else if (hre.search(current, "^\\*bar:.*invis=(\\d+)")) { + dashQ = false; + dotQ = false; + invisQ = true; + } + } + } + } + if (!current->isBarline()) { + current = current->getNextToken(); + return; + } + if (!activeQ) { + current = current->getNextToken(); + return; + } + int track = current->getTrack(); + HTp rcurrent = current; + while (rcurrent) { + if (track != rcurrent->getTrack()) { + break; + } + string text = *rcurrent; + int length = (int)text.size(); + if (dashQ) { + hre.replaceDestructive(text, "", ":", "g"); + } else if (dotQ) { + hre.replaceDestructive(text, "", ".", "g"); + } else if (invisQ) { + hre.replaceDestructive(text, "", "-", "g"); + } + if (length > (int)text.size()) { + rcurrent->setText(text); + } + rcurrent = rcurrent->getNextFieldToken(); + } + current = current->getNextToken(); + } + +} + + + +////////////////////////////// +// +// Tool_bardash::applyBarStylings -- +// + +void Tool_bardash::applyBarStylings(HumdrumFile& infile) { + vector kstarts = infile.getKernSpineStartList(); + for (int i=0; i<(int)kstarts.size(); i++) { + applyBarStylings(kstarts.at(i)); + } +} + + + +////////////////////////////// +// +// Tool_bardash::applyBarStylings -- +// + +void Tool_bardash::applyBarStylings(HTp spine) { + + HTp current = spine->getNextToken(); + bool activeQ = false; + bool dashQ = false; + bool dotQ = false; + bool invisQ = false; + int dash = 0; + int dot = 0; + int invis = 0; + int counter = 0; + HumRegex hre; + while (current) { + if (current->isInterpretation()) { + if (hre.search(current, "^\\*bar:")) { + if (hre.search(current, "stop")) { + activeQ = false; + dashQ = false; + dotQ = false; + invisQ = false; + dash = 0; + dot = 0; + invis = 0; + } else { + activeQ = true; + if (hre.search(current, "^\\*bar:.*dash=(\\d+)")) { + dashQ = true; + dotQ = false; + invisQ = false; + dash = hre.getMatchInt(1); + counter = 0; + } else if (hre.search(current, "^\\*bar:.*dot=(\\d+)")) { + dashQ = false; + dotQ = true; + invisQ = false; + dot = hre.getMatchInt(1); + counter = 0; + } else if (hre.search(current, "^\\*bar:.*invis=(\\d+)")) { + dashQ = false; + dotQ = false; + invisQ = true; + invis = hre.getMatchInt(1); + counter = 0; + } + } + } + current = current->getNextToken(); + continue; + } + if (!current->isBarline()) { + current = current->getNextToken(); + continue; + } + if (!activeQ) { + current = current->getNextToken(); + continue; + } + + int track = current->getTrack(); + HTp rcurrent = current; + + if (dashQ && (dash > 0)) { + + if (counter % dash != 0) { + while (rcurrent) { + if (track != rcurrent->getTrack()) { + break; + } + string text = *rcurrent; + if (text.find(":") == string::npos) { + text += ":"; + rcurrent->setText(text); + } + rcurrent = rcurrent->getNextFieldToken(); + } + } + + } else if (dotQ && (dot > 0)) { + + if (counter % dot != 0) { + while (rcurrent) { + if (track != rcurrent->getTrack()) { + break; + } + string text = *rcurrent; + if (text.find(".") == string::npos) { + text += "."; + rcurrent->setText(text); + } + rcurrent = rcurrent->getNextFieldToken(); + } + } + + } else if (invisQ && (invis > 0)) { + + if (counter % invis != 0) { + while (rcurrent) { + if (track != rcurrent->getTrack()) { + break; + } + string text = *rcurrent; + if (text.find("-") == string::npos) { + text += "-"; + rcurrent->setText(text); + } + rcurrent = rcurrent->getNextFieldToken(); + } + } + + } + counter++; + current = current->getNextToken(); + } +} + + + + ///////////////////////////////// // // Tool_binroll::Tool_binroll -- Set the recognized options for the tool. @@ -79174,17 +79473,16 @@ Tool_esac2hum::Tool_esac2hum(void) { ////////////////////////////// // -// Tool_esac2hum::convert -- Convert a MusicXML file into -// Humdrum content. +// Tool_esac2hum::convert -- Convert an EsAC data into Humdrum data. // bool Tool_esac2hum::convertFile(ostream& out, const string& filename) { initialize(); - ifstream file(filename); - if (file) { - return convert(out, file); - } - return false; + ifstream file(filename); + if (file) { + return convert(out, file); + } + return false; } @@ -79321,6 +79619,8 @@ bool Tool_esac2hum::getSong(vector& song, istream& infile) { bool expectingCloseQ = false; + // Now collect lines for the song until another CUT[] is found + // (do not store previous line above CUT[] which is the source edition label.) while (!infile.eof()) { getline(infile, buffer); @@ -79367,13 +79667,12 @@ bool Tool_esac2hum::getSong(vector& song, istream& infile) { if (hre.search(buffer, "^[A-Za-z][^\\[\\]]*$")) { // collection line m_prevline = buffer; - continue; + return true; } if (hre.search(buffer, "^[A-Za-z]+\\s*\\[[^\\]]*\\s*$")) { // parameter with opening [ expectingCloseQ = true; - } else { } song.push_back(buffer); @@ -79711,7 +80010,7 @@ bool Tool_esac2hum::Score::parseMel(const string& mel) { analyzeTies(); analyzePhrases(); - generateHumdrumNotes(); + generateHumdrumNotes(); calculateClef(); calculateKeyInformation(); calculateTimeSignatures(); @@ -80007,11 +80306,11 @@ void Tool_esac2hum::Score::prepareMultipleTimeSignatures(const string& ts) { 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) { + if (phrase.size() < 2) { // deal with phrases with a single measure later continue; } - if (nextphrase.size() < 2) { + if (nextphrase.size() < 2) { // deal with phrases with a single measure later continue; } @@ -80805,7 +81104,7 @@ bool Tool_esac2hum::Note::parseNote(const string& note, HumNum factor) { } m_alter = s - b; - m_octave = plus - minus; + m_octave = plus - minus; m_factor = factor; @@ -81146,9 +81445,9 @@ void Tool_esac2hum::getParameters(vector& infile) { void Tool_esac2hum::printParameters(void) { cerr << endl; cerr << "========================================" << endl; - for (const auto& [key, value] : m_score.m_params) { - cerr << "Key: " << key << ", Value: " << value << endl; - } + for (const auto& [key, value] : m_score.m_params) { + cerr << "Key: " << key << ", Value: " << value << endl; + } cerr << "========================================" << endl; cerr << endl; } @@ -81165,6 +81464,8 @@ void Tool_esac2hum::printBemComment(ostream& output) { if (bem.empty()) { return; } + HumRegex hre; + hre.replaceDestructive(bem, " ", "\n", "g"); output << "!!!ONB: " << bem << endl; } @@ -86952,6 +87253,8 @@ bool Tool_filter::run(HumdrumFileSet& infiles) { 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 == "bardash") { + RUNTOOL(bardash, infile, commands[i].second, status); } else if (commands[i].first == "binroll") { RUNTOOL(binroll, infile, commands[i].second, status); } else if (commands[i].first == "chantize") { @@ -101237,6 +101540,9 @@ void Tool_melisma::extractWordlist(vector& wordinfo, map& mincount = 2; } string word; + vector lastTimes; + lastTimes.resize(infile.getMaxTrack() + 1); + std::fill(lastTimes.begin(), lastTimes.end(), -1); WordInfo winfo; for (int i=0; i<(int)notecount.size(); i++) { for (int j=0; j<(int)notecount[i].size(); j++) { @@ -101245,8 +101551,13 @@ void Tool_melisma::extractWordlist(vector& wordinfo, map& } HTp token = infile.token(i, j); word = extractWord(winfo, token, notecount); - wordlist[word]++; int track = token->getTrack(); + if (winfo.starttime == lastTimes.at(track)) { + // Exclude duplicate entries of words (multiple melismas in same word). + continue; + } + lastTimes.at(track) = winfo.starttime; + wordlist[word]++; winfo.name = m_names[track]; winfo.abbreviation = m_abbreviations[track]; winfo.partnum = m_partnums[track]; @@ -126392,6 +126703,9 @@ void Tool_shed::processFile(HumdrumFile& infile) { if (m_modified) { infile.createLinesFromTokens(); } + + // needed only for command-line version of tool?: + m_humdrum_text << infile; } From 881f6828907c1a5d0ddd759e07d1288abcf40f9c Mon Sep 17 00:00:00 2001 From: Laurent Pugin Date: Mon, 25 Nov 2024 08:32:17 +0100 Subject: [PATCH 8/8] Update .github/workflows/clang-format-check.yml * 19 not available --- .github/workflows/clang-format-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 35b9b1caa94..f79013e8515 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -20,6 +20,6 @@ jobs: - name: Run clang-format style check for C/C++ programs. uses: jidicula/clang-format-action@v4.11.0 with: - clang-format-version: "19" + clang-format-version: "18" check-path: ${{ matrix.path['check'] }} exclude-regex: ${{ matrix.path['exclude'] }}