From 68fee2ec89cb2df09b6fe188b3ad8bf8c5814629 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Fri, 13 Sep 2024 15:42:13 +0100 Subject: [PATCH 1/8] Move type definitions to musicxmltypes.h/cpp Remove unused code from musicxml.h --- .../musicxml/internal/musicxml/exportxml.cpp | 1 + .../musicxml/importmxmlnoteduration.h | 1 + .../internal/musicxml/importmxmlpass1.cpp | 1 + .../internal/musicxml/importmxmlpass1.h | 5 +- .../internal/musicxml/importmxmlpass2.cpp | 1 + .../internal/musicxml/importmxmlpass2.h | 3 +- .../musicxml/internal/musicxml/importxml.cpp | 68 ---- .../internal/musicxml/importxmlfirstpass.h | 2 +- .../musicxml/internal/musicxml/musicxml.cmake | 2 + .../musicxml/internal/musicxml/musicxml.h | 116 ------- .../internal/musicxml/musicxmlsupport.cpp | 237 +++---------- .../internal/musicxml/musicxmlsupport.h | 212 ------------ .../internal/musicxml/musicxmltypes.cpp | 222 ++++++++++++ .../internal/musicxml/musicxmltypes.h | 317 ++++++++++++++++++ 14 files changed, 599 insertions(+), 589 deletions(-) create mode 100644 src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp create mode 100644 src/importexport/musicxml/internal/musicxml/musicxmltypes.h diff --git a/src/importexport/musicxml/internal/musicxml/exportxml.cpp b/src/importexport/musicxml/internal/musicxml/exportxml.cpp index 00fab5c16acdd..dabd48fa2011e 100644 --- a/src/importexport/musicxml/internal/musicxml/exportxml.cpp +++ b/src/importexport/musicxml/internal/musicxml/exportxml.cpp @@ -127,6 +127,7 @@ #include "musicxml.h" #include "musicxmlfonthandler.h" #include "musicxmlsupport.h" +#include "musicxmltypes.h" #include "modularity/ioc.h" #include "../../imusicxmlconfiguration.h" diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h b/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h index 50610a48917cf..f6cecbe1dcd3f 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h @@ -26,6 +26,7 @@ #include "engraving/dom/durationtype.h" #include "engraving/types/fraction.h" #include "importmxmlpass1.h" +#include "musicxmltypes.h" namespace mu::engraving { class MxmlLogger; diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp index 41febe37340ec..b30e6e6d81c9d 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp @@ -43,6 +43,7 @@ #include "importmxmllogger.h" #include "importmxmlnoteduration.h" #include "importmxmlpass1.h" +#include "musicxmltypes.h" #include "modularity/ioc.h" #include "importexport/musicxml/imusicxmlconfiguration.h" diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h index a3f6465a7d4cf..3f1526319ac12 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h @@ -29,8 +29,8 @@ #include "draw/types/geometry.h" #include "importxmlfirstpass.h" -#include "musicxml.h" // for the creditwords and MusicXmlPartGroupList definitions #include "musicxmlsupport.h" +#include "musicxmltypes.h" #include "engraving/engravingerrors.h" @@ -53,9 +53,6 @@ struct PageFormat { bool twosided = false; }; -typedef std::map PartMap; -typedef std::map MusicXmlPartGroupMap; - //--------------------------------------------------------- // MxmlOctaveShiftDesc //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp index d3594d09fc6d9..4f22d497fade3 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp @@ -91,6 +91,7 @@ #include "importmxmlpass2.h" #include "musicxmlfonthandler.h" #include "musicxmlsupport.h" +#include "musicxmltypes.h" #include "modularity/ioc.h" #include "importexport/musicxml/imusicxmlconfiguration.h" diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h index 2b3dc6608be0f..c11e7377da358 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h @@ -28,7 +28,8 @@ #include "importmxmlpass1.h" #include "importxmlfirstpass.h" #include "internal/musicxml/musicxmlsupport.h" -#include "musicxml.h" // a.o. for Slur +#include "musicxml.h" +#include "musicxmltypes.h" #include "engraving/dom/instrument.h" #include "engraving/dom/types.h" diff --git a/src/importexport/musicxml/internal/musicxml/importxml.cpp b/src/importexport/musicxml/internal/musicxml/importxml.cpp index a6a6009e26b7f..a5b1bc9153160 100644 --- a/src/importexport/musicxml/internal/musicxml/importxml.cpp +++ b/src/importexport/musicxml/internal/musicxml/importxml.cpp @@ -183,72 +183,4 @@ Err importCompressedMusicXml(MasterScore* score, const String& name, bool forceM // and import it return doValidateAndImport(score, name, data, forceMode); } - -//--------------------------------------------------------- -// VoiceDesc -//--------------------------------------------------------- - -// TODO: move somewhere else - -VoiceDesc::VoiceDesc() - : m_staff(-1), m_voice(-1), m_overlaps(false) -{ - for (int i = 0; i < MAX_STAVES; ++i) { - m_chordRests[i] = 0; - m_staffAlloc[i] = -1; - m_voices[i] = -1; - } -} - -void VoiceDesc::incrChordRests(int s) -{ - if (0 <= s && s < MAX_STAVES) { - m_chordRests[s]++; - } -} - -int VoiceDesc::numberChordRests() const -{ - int res = 0; - for (int i = 0; i < MAX_STAVES; ++i) { - res += m_chordRests[i]; - } - return res; -} - -int VoiceDesc::preferredStaff() const -{ - int max = 0; - int res = 0; - for (int i = 0; i < MAX_STAVES; ++i) { - if (m_chordRests[i] > max) { - max = m_chordRests[i]; - res = i; - } - } - return res; -} - -String VoiceDesc::toString() const -{ - String res = u"["; - for (int i = 0; i < MAX_STAVES; ++i) { - res += String(u" %1").arg(m_chordRests[i]); - } - res += String(u" ] overlaps %1").arg(m_overlaps); - if (m_overlaps) { - res += u" staffAlloc ["; - for (int i = 0; i < MAX_STAVES; ++i) { - res += String(u" %1").arg(m_staffAlloc[i]); - } - res += u" ] voices ["; - for (int i = 0; i < MAX_STAVES; ++i) { - res += String(u" %1").arg(m_voices[i]); - } - res += u" ]"; - } else { - res += String(u" staff %1 voice %2").arg(m_staff + 1).arg(m_voice + 1); - } - return res; -} } // namespace Ms diff --git a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h b/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h index 370dd694a2a48..574a10de10a25 100644 --- a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h +++ b/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h @@ -25,7 +25,7 @@ #include "engraving/types/fraction.h" #include "engraving/dom/interval.h" -#include "musicxmlsupport.h" +#include "musicxmltypes.h" namespace mu::engraving { typedef std::map VoiceList; diff --git a/src/importexport/musicxml/internal/musicxml/musicxml.cmake b/src/importexport/musicxml/internal/musicxml/musicxml.cmake index 09e0c1145a2da..8af748ff6204b 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxml.cmake +++ b/src/importexport/musicxml/internal/musicxml/musicxml.cmake @@ -22,6 +22,8 @@ set (MUSICXML_SRC ${CMAKE_CURRENT_LIST_DIR}/musicxmlfonthandler.h ${CMAKE_CURRENT_LIST_DIR}/musicxmlsupport.cpp ${CMAKE_CURRENT_LIST_DIR}/musicxmlsupport.h + ${CMAKE_CURRENT_LIST_DIR}/musicxmltypes.cpp + ${CMAKE_CURRENT_LIST_DIR}/musicxmltypes.h ${CMAKE_CURRENT_LIST_DIR}/musicxmlvalidation.cpp ${CMAKE_CURRENT_LIST_DIR}/musicxmlvalidation.h ) diff --git a/src/importexport/musicxml/internal/musicxml/musicxml.h b/src/importexport/musicxml/internal/musicxml/musicxml.h index 58eaa374fd689..00a74662a7803 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxml.h +++ b/src/importexport/musicxml/internal/musicxml/musicxml.h @@ -23,125 +23,9 @@ #ifndef __MUSICXML_H__ #define __MUSICXML_H__ -/** - \file - Definition of class MusicXML -*/ - -#include "engraving/types/fraction.h" -#include "engraving/dom/mscore.h" -#include "engraving/dom/pitchspelling.h" -#include "engraving/dom/line.h" -#include "importxmlfirstpass.h" -#include "musicxmlsupport.h" - namespace mu::engraving { -//--------------------------------------------------------- -// MusicXmlPartGroup -//--------------------------------------------------------- - -struct MusicXmlPartGroup { - int span = 0; - int start = 0; - BracketType type = BracketType::NO_BRACKET; - bool barlineSpan = false; - int column = 0; -}; - const int MAX_LYRICS = 16; const int MAX_PART_GROUPS = 8; const int MAX_NUMBER_LEVEL = 16; // maximum number of overlapping MusicXML objects - -//--------------------------------------------------------- -// CreditWords -// a single parsed MusicXML credit-words element -//--------------------------------------------------------- - -struct CreditWords { - int page = 0; - String type; - double defaultX = 0.0; - double defaultY = 0.0; - double fontSize = 0.0; - String justify; - String hAlign; - String vAlign; - String words; - CreditWords(int p, String tp, double dx, double dy, double fs, String j, String ha, String va, String w) - { - page = p; - type = tp; - defaultX = dx; - defaultY = dy; - fontSize = fs; - justify = j; - hAlign = ha; - vAlign = va; - words = w; - } -}; - -typedef std::vector CreditWordsList; -typedef CreditWordsList::iterator iCreditWords; -typedef CreditWordsList::const_iterator ciCreditWords; - -//--------------------------------------------------------- -// JumpMarkerDesc -//--------------------------------------------------------- - -/** - The description of Jumps and Markers to be added later -*/ - -class JumpMarkerDesc -{ -public: - JumpMarkerDesc(EngravingItem* el, Measure* meas) - : m_el(el), m_meas(meas) {} - EngravingItem* el() const { return m_el; } - Measure* meas() const { return m_meas; } - -private: - EngravingItem* m_el = nullptr; - Measure* m_meas = nullptr; -}; - -typedef std::vector JumpMarkerDescList; - -//--------------------------------------------------------- -// SlurDesc -//--------------------------------------------------------- - -/** - The description of Slurs being handled - */ - -class SlurDesc -{ -public: - enum class State : char { - NONE, START, STOP - }; - SlurDesc() - : m_slur(0), m_state(State::NONE) {} - Slur* slur() const { return m_slur; } - void start(Slur* slur) { m_slur = slur; m_state = State::START; } - void stop(Slur* slur) { m_slur = slur; m_state = State::STOP; } - bool isStart() const { return m_state == State::START; } - bool isStop() const { return m_state == State::STOP; } -private: - Slur* m_slur = nullptr; - State m_state; -}; - -// Ties are identified by the pitch and track of their first note -typedef std::pair TieLocation; - -//--------------------------------------------------------- -// MusicXml -//--------------------------------------------------------- - -typedef std::vector MusicXmlPartGroupList; -typedef std::map > MusicXmlSpannerMap; } // namespace Ms #endif diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp index bdbfe65ab7717..e19a6f232068a 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp +++ b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp @@ -27,198 +27,13 @@ #include "global/serialization/xmlstreamreader.h" #include "translation.h" -#include "engraving/dom/accidental.h" #include "engraving/dom/articulation.h" #include "engraving/dom/chord.h" #include "types/symnames.h" - +#include "musicxmltypes.h" #include "musicxmlsupport.h" -#include "log.h" - -using AccidentalType = mu::engraving::AccidentalType; -using SymId = mu::engraving::SymId; - -static const std::map smuflAccidentalTypes { - { u"accidentalDoubleFlatOneArrowDown", AccidentalType::DOUBLE_FLAT_ONE_ARROW_DOWN }, - { u"accidentalFlatOneArrowDown", AccidentalType::FLAT_ONE_ARROW_DOWN }, - { u"accidentalNaturalOneArrowDown", AccidentalType::NATURAL_ONE_ARROW_DOWN }, - { u"accidentalSharpOneArrowDown", AccidentalType::SHARP_ONE_ARROW_DOWN }, - { u"accidentalDoubleSharpOneArrowDown", AccidentalType::DOUBLE_SHARP_ONE_ARROW_DOWN }, - { u"accidentalDoubleFlatOneArrowUp", AccidentalType::DOUBLE_FLAT_ONE_ARROW_UP }, - { u"accidentalFlatOneArrowUp", AccidentalType::FLAT_ONE_ARROW_UP }, - { u"accidentalNaturalOneArrowUp", AccidentalType::NATURAL_ONE_ARROW_UP }, - { u"accidentalSharpOneArrowUp", AccidentalType::SHARP_ONE_ARROW_UP }, - { u"accidentalDoubleSharpOneArrowUp", AccidentalType::DOUBLE_SHARP_ONE_ARROW_UP }, - { u"accidentalDoubleFlatTwoArrowsDown", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_DOWN }, - { u"accidentalFlatTwoArrowsDown", AccidentalType::FLAT_TWO_ARROWS_DOWN }, - { u"accidentalNaturalTwoArrowsDown", AccidentalType::NATURAL_TWO_ARROWS_DOWN }, - { u"accidentalSharpTwoArrowsDown", AccidentalType::SHARP_TWO_ARROWS_DOWN }, - { u"accidentalDoubleSharpTwoArrowsDown", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_DOWN }, - { u"accidentalDoubleFlatTwoArrowsUp", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_UP }, - { u"accidentalFlatTwoArrowsUp", AccidentalType::FLAT_TWO_ARROWS_UP }, - { u"accidentalNaturalTwoArrowsUp", AccidentalType::NATURAL_TWO_ARROWS_UP }, - { u"accidentalSharpTwoArrowsUp", AccidentalType::SHARP_TWO_ARROWS_UP }, - { u"accidentalDoubleSharpTwoArrowsUp", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_UP }, - { u"accidentalDoubleFlatThreeArrowsDown", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_DOWN }, - { u"accidentalFlatThreeArrowsDown", AccidentalType::FLAT_THREE_ARROWS_DOWN }, - { u"accidentalNaturalThreeArrowsDown", AccidentalType::NATURAL_THREE_ARROWS_DOWN }, - { u"accidentalSharpThreeArrowsDown", AccidentalType::SHARP_THREE_ARROWS_DOWN }, - { u"accidentalDoubleSharpThreeArrowsDown", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_DOWN }, - { u"accidentalDoubleFlatThreeArrowsUp", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_UP }, - { u"accidentalFlatThreeArrowsUp", AccidentalType::FLAT_THREE_ARROWS_UP }, - { u"accidentalNaturalThreeArrowsUp", AccidentalType::NATURAL_THREE_ARROWS_UP }, - { u"accidentalSharpThreeArrowsUp", AccidentalType::SHARP_THREE_ARROWS_UP }, - { u"accidentalDoubleSharpThreeArrowsUp", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_UP }, - { u"accidentalLowerOneSeptimalComma", AccidentalType::LOWER_ONE_SEPTIMAL_COMMA }, - { u"accidentalRaiseOneSeptimalComma", AccidentalType::RAISE_ONE_SEPTIMAL_COMMA }, - { u"accidentalLowerTwoSeptimalCommas", AccidentalType::LOWER_TWO_SEPTIMAL_COMMAS }, - { u"accidentalRaiseTwoSeptimalCommas", AccidentalType::RAISE_TWO_SEPTIMAL_COMMAS }, - { u"accidentalLowerOneUndecimalQuartertone", AccidentalType::LOWER_ONE_UNDECIMAL_QUARTERTONE }, - { u"accidentalRaiseOneUndecimalQuartertone", AccidentalType::RAISE_ONE_UNDECIMAL_QUARTERTONE }, - { u"accidentalLowerOneTridecimalQuartertone", AccidentalType::LOWER_ONE_TRIDECIMAL_QUARTERTONE }, - { u"accidentalRaiseOneTridecimalQuartertone", AccidentalType::RAISE_ONE_TRIDECIMAL_QUARTERTONE }, - { u"accidentalDoubleFlatEqualTempered", AccidentalType::DOUBLE_FLAT_EQUAL_TEMPERED }, - { u"accidentalFlatEqualTempered", AccidentalType::FLAT_EQUAL_TEMPERED }, - { u"accidentalNaturalEqualTempered", AccidentalType::NATURAL_EQUAL_TEMPERED }, - { u"accidentalSharpEqualTempered", AccidentalType::SHARP_EQUAL_TEMPERED }, - { u"accidentalDoubleSharpEqualTempered", AccidentalType::DOUBLE_SHARP_EQUAL_TEMPERED }, - { u"accidentalQuarterFlatEqualTempered", AccidentalType::QUARTER_FLAT_EQUAL_TEMPERED }, - { u"accidentalQuarterSharpEqualTempered", AccidentalType::QUARTER_SHARP_EQUAL_TEMPERED } -}; - namespace mu::engraving { -NoteList::NoteList() -{ - _staffNoteLists.reserve(MAX_STAVES); - for (int i = 0; i < MAX_STAVES; ++i) { - _staffNoteLists.push_back(StartStopList()); - } -} - -void NoteList::addNote(const int startTick, const int endTick, const size_t staff) -{ - if (staff < _staffNoteLists.size()) { - _staffNoteLists[staff].push_back(StartStop(startTick, endTick)); - } -} - -void NoteList::dump(const int& voice) const -{ - // dump contents - for (int i = 0; i < MAX_STAVES; ++i) { - printf("voice %d staff %d:", voice, i); - for (size_t j = 0; j < _staffNoteLists.at(i).size(); ++j) { - printf(" %d-%d", _staffNoteLists.at(i).at(j).first, _staffNoteLists.at(i).at(j).second); - } - printf("\n"); - } - // show overlap - printf("overlap voice %d:", voice); - for (int i = 0; i < MAX_STAVES - 1; ++i) { - for (int j = i + 1; j < MAX_STAVES; ++j) { - stavesOverlap(i, j); - } - } - printf("\n"); -} - -/** - Determine if notes n1 and n2 overlap. - This is NOT the case if - - n1 starts when or after n2 stops - - or n2 starts when or after n1 stops - */ - -static bool notesOverlap(const StartStop& n1, const StartStop& n2) -{ - return !(n1.first >= n2.second || n1.second <= n2.first); -} - -/** - Determine if any note in staff1 and staff2 overlaps. - */ - -bool NoteList::stavesOverlap(const int staff1, const int staff2) const -{ - for (size_t i = 0; i < _staffNoteLists.at(staff1).size(); ++i) { - for (size_t j = 0; j < _staffNoteLists.at(staff2).size(); ++j) { - if (notesOverlap(_staffNoteLists.at(staff1).at(i), _staffNoteLists.at(staff2).at(j))) { - //printf(" %d-%d", staff1, staff2); - return true; - } - } - } - return false; -} - -/** - Determine if any note in any staff overlaps. - */ - -bool NoteList::anyStaffOverlaps() const -{ - for (int i = 0; i < MAX_STAVES - 1; ++i) { - for (int j = i + 1; j < MAX_STAVES; ++j) { - if (stavesOverlap(i, j)) { - return true; - } - } - } - return false; -} - -VoiceOverlapDetector::VoiceOverlapDetector() -{ - // LOGD("VoiceOverlapDetector::VoiceOverlapDetector(staves %d)", MAX_STAVES); -} - -void VoiceOverlapDetector::addNote(const int startTick, const int endTick, const int& voice, const int staff) -{ - // if necessary, create the note list for voice - if (!muse::contains(_noteLists, voice)) { - _noteLists.insert({ voice, NoteList() }); - } - _noteLists[voice].addNote(startTick, endTick, staff); -} - -void VoiceOverlapDetector::dump() const -{ - // LOGD("VoiceOverlapDetector::dump()"); - for (auto p : _noteLists) { - p.second.dump(p.first); - } -} - -void VoiceOverlapDetector::newMeasure() -{ - // LOGD("VoiceOverlapDetector::newMeasure()"); - _noteLists.clear(); -} - -bool VoiceOverlapDetector::stavesOverlap(const int& voice) const -{ - if (muse::contains(_noteLists, voice)) { - return _noteLists.at(voice).anyStaffOverlaps(); - } else { - return false; - } -} - -String MusicXMLInstrument::toString() const -{ - return String(u"chan %1 prog %2 vol %3 pan %4 unpitched %5 name '%6' sound '%7' head %8 line %9 stemDir %10") - .arg(midiChannel) - .arg(midiProgram) - .arg(midiVolume) - .arg(midiPan) - .arg(unpitched) - .arg(name, sound) - .arg(int(notehead)) - .arg(line) - .arg(int(stemDirection)); -} - //--------------------------------------------------------- // errorStringWithLocation //--------------------------------------------------------- @@ -623,6 +438,54 @@ String accidentalType2MxmlString(const AccidentalType type) return s; } +static const std::map smuflAccidentalTypes { + { u"accidentalDoubleFlatOneArrowDown", AccidentalType::DOUBLE_FLAT_ONE_ARROW_DOWN }, + { u"accidentalFlatOneArrowDown", AccidentalType::FLAT_ONE_ARROW_DOWN }, + { u"accidentalNaturalOneArrowDown", AccidentalType::NATURAL_ONE_ARROW_DOWN }, + { u"accidentalSharpOneArrowDown", AccidentalType::SHARP_ONE_ARROW_DOWN }, + { u"accidentalDoubleSharpOneArrowDown", AccidentalType::DOUBLE_SHARP_ONE_ARROW_DOWN }, + { u"accidentalDoubleFlatOneArrowUp", AccidentalType::DOUBLE_FLAT_ONE_ARROW_UP }, + { u"accidentalFlatOneArrowUp", AccidentalType::FLAT_ONE_ARROW_UP }, + { u"accidentalNaturalOneArrowUp", AccidentalType::NATURAL_ONE_ARROW_UP }, + { u"accidentalSharpOneArrowUp", AccidentalType::SHARP_ONE_ARROW_UP }, + { u"accidentalDoubleSharpOneArrowUp", AccidentalType::DOUBLE_SHARP_ONE_ARROW_UP }, + { u"accidentalDoubleFlatTwoArrowsDown", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_DOWN }, + { u"accidentalFlatTwoArrowsDown", AccidentalType::FLAT_TWO_ARROWS_DOWN }, + { u"accidentalNaturalTwoArrowsDown", AccidentalType::NATURAL_TWO_ARROWS_DOWN }, + { u"accidentalSharpTwoArrowsDown", AccidentalType::SHARP_TWO_ARROWS_DOWN }, + { u"accidentalDoubleSharpTwoArrowsDown", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_DOWN }, + { u"accidentalDoubleFlatTwoArrowsUp", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_UP }, + { u"accidentalFlatTwoArrowsUp", AccidentalType::FLAT_TWO_ARROWS_UP }, + { u"accidentalNaturalTwoArrowsUp", AccidentalType::NATURAL_TWO_ARROWS_UP }, + { u"accidentalSharpTwoArrowsUp", AccidentalType::SHARP_TWO_ARROWS_UP }, + { u"accidentalDoubleSharpTwoArrowsUp", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_UP }, + { u"accidentalDoubleFlatThreeArrowsDown", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_DOWN }, + { u"accidentalFlatThreeArrowsDown", AccidentalType::FLAT_THREE_ARROWS_DOWN }, + { u"accidentalNaturalThreeArrowsDown", AccidentalType::NATURAL_THREE_ARROWS_DOWN }, + { u"accidentalSharpThreeArrowsDown", AccidentalType::SHARP_THREE_ARROWS_DOWN }, + { u"accidentalDoubleSharpThreeArrowsDown", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_DOWN }, + { u"accidentalDoubleFlatThreeArrowsUp", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_UP }, + { u"accidentalFlatThreeArrowsUp", AccidentalType::FLAT_THREE_ARROWS_UP }, + { u"accidentalNaturalThreeArrowsUp", AccidentalType::NATURAL_THREE_ARROWS_UP }, + { u"accidentalSharpThreeArrowsUp", AccidentalType::SHARP_THREE_ARROWS_UP }, + { u"accidentalDoubleSharpThreeArrowsUp", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_UP }, + { u"accidentalLowerOneSeptimalComma", AccidentalType::LOWER_ONE_SEPTIMAL_COMMA }, + { u"accidentalRaiseOneSeptimalComma", AccidentalType::RAISE_ONE_SEPTIMAL_COMMA }, + { u"accidentalLowerTwoSeptimalCommas", AccidentalType::LOWER_TWO_SEPTIMAL_COMMAS }, + { u"accidentalRaiseTwoSeptimalCommas", AccidentalType::RAISE_TWO_SEPTIMAL_COMMAS }, + { u"accidentalLowerOneUndecimalQuartertone", AccidentalType::LOWER_ONE_UNDECIMAL_QUARTERTONE }, + { u"accidentalRaiseOneUndecimalQuartertone", AccidentalType::RAISE_ONE_UNDECIMAL_QUARTERTONE }, + { u"accidentalLowerOneTridecimalQuartertone", AccidentalType::LOWER_ONE_TRIDECIMAL_QUARTERTONE }, + { u"accidentalRaiseOneTridecimalQuartertone", AccidentalType::RAISE_ONE_TRIDECIMAL_QUARTERTONE }, + { u"accidentalDoubleFlatEqualTempered", AccidentalType::DOUBLE_FLAT_EQUAL_TEMPERED }, + { u"accidentalFlatEqualTempered", AccidentalType::FLAT_EQUAL_TEMPERED }, + { u"accidentalNaturalEqualTempered", AccidentalType::NATURAL_EQUAL_TEMPERED }, + { u"accidentalSharpEqualTempered", AccidentalType::SHARP_EQUAL_TEMPERED }, + { u"accidentalDoubleSharpEqualTempered", AccidentalType::DOUBLE_SHARP_EQUAL_TEMPERED }, + { u"accidentalQuarterFlatEqualTempered", AccidentalType::QUARTER_FLAT_EQUAL_TEMPERED }, + { u"accidentalQuarterSharpEqualTempered", AccidentalType::QUARTER_SHARP_EQUAL_TEMPERED } +}; + //--------------------------------------------------------- // accidentalType2SmuflMxmlString //--------------------------------------------------------- @@ -784,7 +647,7 @@ AccidentalType microtonalGuess(double val) } //--------------------------------------------------------- -// isLaissezVibrer +// symIsLaissezVibrer //--------------------------------------------------------- bool isLaissezVibrer(const SymId id) diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h index 0eac04013d6dd..617caed3e98c2 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h @@ -24,7 +24,6 @@ #define __MUSICXMLSUPPORT_H__ #include "engraving/types/fraction.h" -#include "engraving/dom/mscore.h" #include "engraving/dom/note.h" #include "engraving/dom/fret.h" @@ -33,217 +32,6 @@ class XmlStreamReader; } namespace mu::engraving { -class Chord; - -//--------------------------------------------------------- -// NoteList -//--------------------------------------------------------- - -/** - List of note start/stop times in a voice in a single staff. -*/ - -typedef std::pair StartStop; -typedef std::vector StartStopList; - -//--------------------------------------------------------- -// NoteList -//--------------------------------------------------------- - -/** - List of note start/stop times in a voice in all staves. -*/ - -class NoteList -{ -public: - NoteList(); - void addNote(const int startTick, const int endTick, const size_t staff); - void dump(const int& voice) const; - bool stavesOverlap(const int staff1, const int staff2) const; - bool anyStaffOverlaps() const; -private: - std::vector _staffNoteLists; // The note start/stop times in all staves -}; - -struct MusicXmlArpeggioDesc { - Arpeggio* arp; - int no; - - MusicXmlArpeggioDesc(Arpeggio* arp, int no) - : arp(arp), no(no) {} -}; -typedef std::multimap ArpeggioMap; - -/** - The description of a chord symbol with or without a fret diagram - */ - -struct HarmonyDesc -{ - track_idx_t m_track; - bool fretDiagramVisible() const { return m_fretDiagram ? m_fretDiagram->visible() : false; } - Harmony* m_harmony; - FretDiagram* m_fretDiagram; - - HarmonyDesc(track_idx_t m_track, Harmony* m_harmony, FretDiagram* m_fretDiagram) - : m_track(m_track), m_harmony(m_harmony), - m_fretDiagram(m_fretDiagram) {} - - HarmonyDesc() - : m_track(0), m_harmony(nullptr), m_fretDiagram(nullptr) {} -}; - -using HarmonyMap = std::multimap; - -//--------------------------------------------------------- -// VoiceDesc -//--------------------------------------------------------- - -/** - The description of a single voice in a MusicXML part. -*/ - -class VoiceDesc -{ -public: - VoiceDesc(); - void incrChordRests(int s); - int numberChordRests() const; - int numberChordRests(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_chordRests[s] : 0; } - int preferredStaff() const; // Determine preferred staff for this voice - void setStaff(int s) - { - if (s >= 0) { - m_staff = s; - } - } - - int staff() const { return m_staff; } - void setVoice(int v) - { - if (v >= 0) { - m_voice = v; - } - } - - int voice() const { return m_voice; } - void setVoice(int s, int v) - { - if (s >= 0 && s < MAX_STAVES) { - m_voices[s] = v; - } - } - - int voice(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_voices[s] : -1; } - void setOverlap(bool b) { m_overlaps = b; } - bool overlaps() const { return m_overlaps; } - void setStaffAlloc(int s, int i) - { - if (s >= 0 && s < MAX_STAVES) { - m_staffAlloc[s] = i; - } - } - - int staffAlloc(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_staffAlloc[s] : -1; } - String toString() const; -private: - int m_chordRests[MAX_STAVES]; // The number of chordrests on each MusicXML staff - int m_staff; // The MuseScore staff allocated - int m_voice; // The MuseScore voice allocated - bool m_overlaps; // This voice contains active notes in multiple staves at the same time - int m_staffAlloc[MAX_STAVES]; // For overlapping voices: voice is allocated on these staves (note: -2=unalloc -1=undef 1=alloc) - int m_voices[MAX_STAVES]; // For every voice allocated on the staff, the voice number -}; - -//--------------------------------------------------------- -// VoiceOverlapDetector -//--------------------------------------------------------- - -/** - Detect overlap in a voice, which is when a voice has two or more notes - active at the same time. In theory this should not happen, as voices - only move forward in time, but Sibelius 7 reuses voice numbers in multi- - staff parts, which leads to overlap. - - Current implementation does not detect voice overlap within a staff, - but only between staves. -*/ - -class VoiceOverlapDetector -{ -public: - VoiceOverlapDetector(); - void addNote(const int startTick, const int endTick, const int& voice, const int staff); - void dump() const; - void newMeasure(); - bool stavesOverlap(const int& voice) const; -private: - std::map _noteLists; // The notelists for all the voices -}; - -//--------------------------------------------------------- -// MusicXMLInstrument -//--------------------------------------------------------- - -/** - A single instrument in a MusicXML part. - Used for both a drum part and a (non-drum) multi-instrument part - */ - -struct MusicXMLInstrument { - int unpitched; // midi-unpitched read from MusicXML - String name; // instrument-name read from MusicXML - String sound; // instrument-sound read from MusicXML - String virtLib; // virtual-library read from MusicXML - String virtName; // virtual-name read from MusicXML - int midiChannel; // midi-channel read from MusicXML - int midiPort; // port read from MusicXML - int midiProgram; // midi-program read from MusicXML - int midiVolume; // volume read from MusicXML - int midiPan; // pan value read from MusicXML - NoteHeadGroup notehead; // notehead symbol set - int line = 0; // place notehead onto this line - DirectionV stemDirection; - - String toString() const; - - MusicXMLInstrument() // required by std::map - : unpitched(-1), name(), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63), - notehead(NoteHeadGroup::HEAD_INVALID), line(0), stemDirection(DirectionV::AUTO) {} - MusicXMLInstrument(String s) - : unpitched(-1), name(s), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63), - notehead(NoteHeadGroup::HEAD_NORMAL), line(0), stemDirection(DirectionV::AUTO) {} - /* - MusicXMLInstrument(int p, String s, NoteHead::Group nh, int l, Direction d) - : unpitched(p), name(s), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63), - notehead(nh), line(l), stemDirection(d) {} - */ -}; - -struct InferredPercInstr { - int pitch; - track_idx_t track; - String name; - Fraction tick; - - InferredPercInstr(int pitch, track_idx_t track, String name, Fraction tick) - : pitch(pitch), track(track), name(name), tick(tick) {} - - InferredPercInstr() - : pitch(-1), track(muse::nidx), name(u""), tick(Fraction(0, -1)) {} -}; - -typedef std::vector InferredPercList; - -/** - A MusicXML drumset or set of instruments in a multi-instrument part. - */ - -typedef std::map MusicXMLInstruments; - -typedef std::map > MetronomeTextMap; - //--------------------------------------------------------- // MxmlSupport -- MusicXML import support functions //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp new file mode 100644 index 0000000000000..a5d5ccb7a33c2 --- /dev/null +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp @@ -0,0 +1,222 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2024 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "musicxmltypes.h" + +using namespace mu::engraving; + +NoteList::NoteList() +{ + _staffNoteLists.reserve(MAX_STAVES); + for (int i = 0; i < MAX_STAVES; ++i) { + _staffNoteLists.push_back(StartStopList()); + } +} + +void NoteList::addNote(const int startTick, const int endTick, const size_t staff) +{ + if (staff < _staffNoteLists.size()) { + _staffNoteLists[staff].push_back(StartStop(startTick, endTick)); + } +} + +void NoteList::dump(const int& voice) const +{ + // dump contents + for (int i = 0; i < MAX_STAVES; ++i) { + printf("voice %d staff %d:", voice, i); + for (size_t j = 0; j < _staffNoteLists.at(i).size(); ++j) { + printf(" %d-%d", _staffNoteLists.at(i).at(j).first, _staffNoteLists.at(i).at(j).second); + } + printf("\n"); + } + // show overlap + printf("overlap voice %d:", voice); + for (int i = 0; i < MAX_STAVES - 1; ++i) { + for (int j = i + 1; j < MAX_STAVES; ++j) { + stavesOverlap(i, j); + } + } + printf("\n"); +} + +/** + Determine if notes n1 and n2 overlap. + This is NOT the case if + - n1 starts when or after n2 stops + - or n2 starts when or after n1 stops + */ + +bool NoteList::notesOverlap(const StartStop& n1, const StartStop& n2) const +{ + return !(n1.first >= n2.second || n1.second <= n2.first); +} + +/** + Determine if any note in staff1 and staff2 overlaps. + */ + +bool NoteList::stavesOverlap(const int staff1, const int staff2) const +{ + for (size_t i = 0; i < _staffNoteLists.at(staff1).size(); ++i) { + for (size_t j = 0; j < _staffNoteLists.at(staff2).size(); ++j) { + if (notesOverlap(_staffNoteLists.at(staff1).at(i), _staffNoteLists.at(staff2).at(j))) { + //printf(" %d-%d", staff1, staff2); + return true; + } + } + } + return false; +} + +/** + Determine if any note in any staff overlaps. + */ + +bool NoteList::anyStaffOverlaps() const +{ + for (int i = 0; i < MAX_STAVES - 1; ++i) { + for (int j = i + 1; j < MAX_STAVES; ++j) { + if (stavesOverlap(i, j)) { + return true; + } + } + } + return false; +} + +VoiceOverlapDetector::VoiceOverlapDetector() +{ + // LOGD("VoiceOverlapDetector::VoiceOverlapDetector(staves %d)", MAX_STAVES); +} + +void VoiceOverlapDetector::addNote(const int startTick, const int endTick, const int& voice, const int staff) +{ + // if necessary, create the note list for voice + if (!muse::contains(_noteLists, voice)) { + _noteLists.insert({ voice, NoteList() }); + } + _noteLists[voice].addNote(startTick, endTick, staff); +} + +void VoiceOverlapDetector::dump() const +{ + // LOGD("VoiceOverlapDetector::dump()"); + for (auto& p : _noteLists) { + p.second.dump(p.first); + } +} + +void VoiceOverlapDetector::newMeasure() +{ + // LOGD("VoiceOverlapDetector::newMeasure()"); + _noteLists.clear(); +} + +bool VoiceOverlapDetector::stavesOverlap(const int& voice) const +{ + if (muse::contains(_noteLists, voice)) { + return _noteLists.at(voice).anyStaffOverlaps(); + } else { + return false; + } +} + +String MusicXMLInstrument::toString() const +{ + return String(u"chan %1 prog %2 vol %3 pan %4 unpitched %5 name '%6' sound '%7' head %8 line %9 stemDir %10") + .arg(midiChannel) + .arg(midiProgram) + .arg(midiVolume) + .arg(midiPan) + .arg(unpitched) + .arg(name, sound) + .arg(int(notehead)) + .arg(line) + .arg(int(stemDirection)); +} + +//--------------------------------------------------------- +// VoiceDesc +//--------------------------------------------------------- + +VoiceDesc::VoiceDesc() + : m_staff(-1), m_voice(-1), m_overlaps(false) +{ + for (int i = 0; i < MAX_STAVES; ++i) { + m_chordRests[i] = 0; + m_staffAlloc[i] = -1; + m_voices[i] = -1; + } +} + +void VoiceDesc::incrChordRests(int s) +{ + if (0 <= s && s < MAX_STAVES) { + m_chordRests[s]++; + } +} + +int VoiceDesc::numberChordRests() const +{ + int res = 0; + for (int i = 0; i < MAX_STAVES; ++i) { + res += m_chordRests[i]; + } + return res; +} + +int VoiceDesc::preferredStaff() const +{ + int max = 0; + int res = 0; + for (int i = 0; i < MAX_STAVES; ++i) { + if (m_chordRests[i] > max) { + max = m_chordRests[i]; + res = i; + } + } + return res; +} + +String VoiceDesc::toString() const +{ + String res = u"["; + for (int i = 0; i < MAX_STAVES; ++i) { + res += String(u" %1").arg(m_chordRests[i]); + } + res += String(u" ] overlaps %1").arg(m_overlaps); + if (m_overlaps) { + res += u" staffAlloc ["; + for (int i = 0; i < MAX_STAVES; ++i) { + res += String(u" %1").arg(m_staffAlloc[i]); + } + res += u" ] voices ["; + for (int i = 0; i < MAX_STAVES; ++i) { + res += String(u" %1").arg(m_voices[i]); + } + res += u" ]"; + } else { + res += String(u" staff %1 voice %2").arg(m_staff + 1).arg(m_voice + 1); + } + return res; +} diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h new file mode 100644 index 0000000000000..7ef3f8a7ea084 --- /dev/null +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h @@ -0,0 +1,317 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2024 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MUSICXMLTYPES_H +#define MUSICXMLTYPES_H +#include "engraving/dom/line.h" +#include "engraving/dom/mscore.h" +#include "engraving/dom/fret.h" + +namespace mu::engraving { +//--------------------------------------------------------- +// MusicXmlPartGroup +//--------------------------------------------------------- + +struct MusicXmlPartGroup { + int span = 0; + int start = 0; + BracketType type = BracketType::NO_BRACKET; + bool barlineSpan = false; + int column = 0; +}; +typedef std::vector MusicXmlPartGroupList; +typedef std::map PartMap; +typedef std::map MusicXmlPartGroupMap; + +//--------------------------------------------------------- +// CreditWords +// a single parsed MusicXML credit-words element +//--------------------------------------------------------- + +struct CreditWords { + int page = 0; + String type; + double defaultX = 0.0; + double defaultY = 0.0; + double fontSize = 0.0; + String justify; + String hAlign; + String vAlign; + String words; + CreditWords(int p, String tp, double dx, double dy, double fs, String j, String ha, String va, String w) + { + page = p; + type = tp; + defaultX = dx; + defaultY = dy; + fontSize = fs; + justify = j; + hAlign = ha; + vAlign = va; + words = w; + } +}; +typedef std::vector CreditWordsList; + +//--------------------------------------------------------- +// SlurDesc +//--------------------------------------------------------- + +/** + The description of Slurs being handled + */ + +class SlurDesc +{ +public: + enum class State : char { + NONE, START, STOP + }; + SlurDesc() + : m_slur(0), m_state(State::NONE) {} + Slur* slur() const { return m_slur; } + void start(Slur* slur) { m_slur = slur; m_state = State::START; } + void stop(Slur* slur) { m_slur = slur; m_state = State::STOP; } + bool isStart() const { return m_state == State::START; } + bool isStop() const { return m_state == State::STOP; } +private: + Slur* m_slur = nullptr; + State m_state; +}; +typedef std::map > MusicXmlSpannerMap; + +//--------------------------------------------------------- +// NoteList +//--------------------------------------------------------- + +/** + List of note start/stop times in a voice in a single staff. +*/ + +//--------------------------------------------------------- +// MxmlStartStop +//--------------------------------------------------------- + +enum class MxmlStartStop : char { + NONE, START, STOP +}; + +typedef std::pair StartStop; +typedef std::vector StartStopList; + +//--------------------------------------------------------- +// NoteList +//--------------------------------------------------------- + +/** + List of note start/stop times in a voice in all staves. +*/ + +class NoteList +{ +public: + NoteList(); + void addNote(const int startTick, const int endTick, const size_t staff); + void dump(const int& voice) const; + bool stavesOverlap(const int staff1, const int staff2) const; + bool anyStaffOverlaps() const; +private: + std::vector _staffNoteLists; // The note start/stop times in all staves + bool notesOverlap(const StartStop& n1, const StartStop& n2) const; +}; + +struct MusicXmlArpeggioDesc { + Arpeggio* arp; + int no; + + MusicXmlArpeggioDesc(Arpeggio* arp, int no) + : arp(arp), no(no) {} +}; +typedef std::multimap ArpeggioMap; + +/** + The description of a chord symbol with or without a fret diagram + */ + +struct HarmonyDesc +{ + track_idx_t m_track; + bool fretDiagramVisible() const { return m_fretDiagram ? m_fretDiagram->visible() : false; } + Harmony* m_harmony; + FretDiagram* m_fretDiagram; + + HarmonyDesc(track_idx_t m_track, Harmony* m_harmony, FretDiagram* m_fretDiagram) + : m_track(m_track), m_harmony(m_harmony), + m_fretDiagram(m_fretDiagram) {} + + HarmonyDesc() + : m_track(0), m_harmony(nullptr), m_fretDiagram(nullptr) {} +}; +using HarmonyMap = std::multimap; + +//--------------------------------------------------------- +// VoiceDesc +//--------------------------------------------------------- + +/** + The description of a single voice in a MusicXML part. +*/ + +class VoiceDesc +{ +public: + VoiceDesc(); + void incrChordRests(int s); + int numberChordRests() const; + int numberChordRests(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_chordRests[s] : 0; } + int preferredStaff() const; // Determine preferred staff for this voice + void setStaff(int s) + { + if (s >= 0) { + m_staff = s; + } + } + + int staff() const { return m_staff; } + void setVoice(int v) + { + if (v >= 0) { + m_voice = v; + } + } + + int voice() const { return m_voice; } + void setVoice(int s, int v) + { + if (s >= 0 && s < MAX_STAVES) { + m_voices[s] = v; + } + } + + int voice(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_voices[s] : -1; } + void setOverlap(bool b) { m_overlaps = b; } + bool overlaps() const { return m_overlaps; } + void setStaffAlloc(int s, int i) + { + if (s >= 0 && s < MAX_STAVES) { + m_staffAlloc[s] = i; + } + } + + int staffAlloc(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_staffAlloc[s] : -1; } + String toString() const; +private: + int m_chordRests[MAX_STAVES]; // The number of chordrests on each MusicXML staff + int m_staff; // The MuseScore staff allocated + int m_voice; // The MuseScore voice allocated + bool m_overlaps; // This voice contains active notes in multiple staves at the same time + int m_staffAlloc[MAX_STAVES]; // For overlapping voices: voice is allocated on these staves (note: -2=unalloc -1=undef 1=alloc) + int m_voices[MAX_STAVES]; // For every voice allocated on the staff, the voice number +}; + +//--------------------------------------------------------- +// VoiceOverlapDetector +//--------------------------------------------------------- + +/** + Detect overlap in a voice, which is when a voice has two or more notes + active at the same time. In theory this should not happen, as voices + only move forward in time, but Sibelius 7 reuses voice numbers in multi- + staff parts, which leads to overlap. + + Current implementation does not detect voice overlap within a staff, + but only between staves. +*/ + +class VoiceOverlapDetector +{ +public: + VoiceOverlapDetector(); + void addNote(const int startTick, const int endTick, const int& voice, const int staff); + void dump() const; + void newMeasure(); + bool stavesOverlap(const int& voice) const; +private: + std::map _noteLists; // The notelists for all the voices +}; + +//--------------------------------------------------------- +// MusicXMLInstrument +//--------------------------------------------------------- + +/** + A single instrument in a MusicXML part. + Used for both a drum part and a (non-drum) multi-instrument part + */ + +struct MusicXMLInstrument { + int unpitched; // midi-unpitched read from MusicXML + String name; // instrument-name read from MusicXML + String sound; // instrument-sound read from MusicXML + String virtLib; // virtual-library read from MusicXML + String virtName; // virtual-name read from MusicXML + int midiChannel; // midi-channel read from MusicXML + int midiPort; // port read from MusicXML + int midiProgram; // midi-program read from MusicXML + int midiVolume; // volume read from MusicXML + int midiPan; // pan value read from MusicXML + NoteHeadGroup notehead; // notehead symbol set + int line = 0; // place notehead onto this line + DirectionV stemDirection; + + String toString() const; + + MusicXMLInstrument() // required by std::map + : unpitched(-1), name(), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63), + notehead(NoteHeadGroup::HEAD_INVALID), line(0), stemDirection(DirectionV::AUTO) {} + MusicXMLInstrument(String s) + : unpitched(-1), name(s), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63), + notehead(NoteHeadGroup::HEAD_NORMAL), line(0), stemDirection(DirectionV::AUTO) {} + /* + MusicXMLInstrument(int p, String s, NoteHead::Group nh, int l, Direction d) + : unpitched(p), name(s), midiChannel(-1), midiPort(-1), midiProgram(-1), midiVolume(100), midiPan(63), + notehead(nh), line(l), stemDirection(d) {} + */ +}; +typedef std::map MusicXMLInstruments; + +struct InferredPercInstr { + int pitch; + track_idx_t track; + String name; + Fraction tick; + + InferredPercInstr(int pitch, track_idx_t track, String name, Fraction tick) + : pitch(pitch), track(track), name(name), tick(tick) {} + + InferredPercInstr() + : pitch(-1), track(muse::nidx), name(u""), tick(Fraction(0, -1)) {} +}; +typedef std::vector InferredPercList; + +typedef std::map > MetronomeTextMap; + +// Ties are identified by the pitch and track of their first note +typedef std::pair TieLocation; +} + +#endif // MUSICXMLTYPES_H From 7ce8fa874e355dfba3bf03fd48902cc6d7482bc4 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Fri, 13 Sep 2024 17:18:57 +0100 Subject: [PATCH 2/8] refactor MxmlTupletState --- .../internal/musicxml/importmxmlpass1.cpp | 343 ------------------ .../internal/musicxml/importmxmlpass1.h | 37 +- .../internal/musicxml/importmxmlpass2.h | 10 +- .../musicxml/internal/musicxml/musicxml.cmake | 2 + .../internal/musicxml/musicxmlsupport.cpp | 75 ++++ .../internal/musicxml/musicxmlsupport.h | 2 + .../internal/musicxml/musicxmltupletstate.cpp | 289 +++++++++++++++ .../internal/musicxml/musicxmltupletstate.h | 60 +++ 8 files changed, 430 insertions(+), 388 deletions(-) create mode 100644 src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp create mode 100644 src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp index b30e6e6d81c9d..4cd5333260601 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp @@ -3101,252 +3101,6 @@ void MusicXMLParserPass1::notations(MxmlStartStop& tupletStartStop) } } -//--------------------------------------------------------- -// smallestTypeAndCount -//--------------------------------------------------------- - -/** - Determine the smallest note type and the number of those - present in a ChordRest. - For a note without dots the type equals the note type - and count is one. - For a single dotted note the type equals half the note type - and count is three. - A double dotted note is similar. - Note: code assumes when duration().type() is incremented, - the note length is divided by two, checked by tupletAssert(). - */ - -static void smallestTypeAndCount(const TDuration durType, int& type, int& count) -{ - type = int(durType.type()); - count = 1; - switch (durType.dots()) { - case 0: - // nothing to do - break; - case 1: - type += 1; // next-smaller type - count = 3; - break; - case 2: - type += 2; // next-next-smaller type - count = 7; - break; - default: - LOGD("smallestTypeAndCount() does not support more than 2 dots"); - } -} - -//--------------------------------------------------------- -// matchTypeAndCount -//--------------------------------------------------------- - -/** - Given two note types and counts, if the types are not equal, - make them equal by successively doubling the count of the - largest type. - */ - -static void matchTypeAndCount(int& type1, int& count1, int& type2, int& count2) -{ - while (type1 < type2) { - type1++; - count1 *= 2; - } - while (type2 < type1) { - type2++; - count2 *= 2; - } -} - -//--------------------------------------------------------- -// addDurationToTuplet -//--------------------------------------------------------- - -/** - Add duration to tuplet duration - Determine type and number of smallest notes in the tuplet - */ - -void MxmlTupletState::addDurationToTuplet(const Fraction dur, const Fraction timeMod) -{ - /* - LOGD("1 duration %s timeMod %s -> state.tupletType %d state.tupletCount %d state.actualNotes %d state.normalNotes %d", - muPrintable(duration.print()), - muPrintable(timeMod.print()), - m_tupletType, - m_tupletCount, - m_actualNotes, - m_normalNotes - ); - */ - if (duration <= Fraction(0, 1)) { - // first note: init variables - actualNotes = timeMod.denominator(); - normalNotes = timeMod.numerator(); - smallestTypeAndCount(dur / timeMod, tupletType, tupletCount); - } else { - int noteType = 0; - int noteCount = 0; - smallestTypeAndCount(dur / timeMod, noteType, noteCount); - // match the types - matchTypeAndCount(tupletType, tupletCount, noteType, noteCount); - tupletCount += noteCount; - } - duration += dur; - /* - LOGD("2 duration %s -> state.tupletType %d state.tupletCount %d state.actualNotes %d state.normalNotes %d", - muPrintable(duration.print()), - m_tupletType, - m_tupletCount, - m_actualNotes, - m_normalNotes - ); - */ -} - -//--------------------------------------------------------- -// determineTupletFractionAndFullDuration -//--------------------------------------------------------- - -/** - Split duration into two factors where fullDuration is note sized - (i.e. the denominator is a power of 2), 1/2 < fraction <= 1/1 - and fraction * fullDuration equals duration. - */ - -void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration) -{ - fraction = duration; - fullDuration = Fraction(1, 1); - // move denominator's powers of 2 from fraction to fullDuration - while (fraction.denominator() % 2 == 0) { - fraction *= 2; - fraction.reduce(); - fullDuration *= Fraction(1, 2); - } - // move numerator's powers of 2 from fraction to fullDuration - while (fraction.numerator() % 2 == 0) { - fraction *= Fraction(1, 2); - fraction.reduce(); - fullDuration *= 2; - fullDuration.reduce(); - } - // make sure 1/2 < fraction <= 1/1 - while (fraction <= Fraction(1, 2)) { - fullDuration *= Fraction(1, 2); - fraction *= 2; - } - fullDuration.reduce(); - fraction.reduce(); - - /* - Examples (note result when denominator is not a power of two): - 3:2 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/2 - 2:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4 - 4:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4 - 3:4 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/1 - - Bring back fraction in 1/2 .. 1/1 range. - */ - - if (fraction > Fraction(1, 1) && fraction.denominator() == 1) { - fullDuration *= fraction; - fullDuration.reduce(); - fraction = Fraction(1, 1); - } - - /* - LOGD("duration %s fraction %s fullDuration %s", - muPrintable(duration.toString()), - muPrintable(fraction.toString()), - muPrintable(fullDuration.toString()) - ); - */ -} - -//--------------------------------------------------------- -// isTupletFilled -//--------------------------------------------------------- - -/** - Determine if the tuplet is completely filled, - because either (1) it is at least the same duration - as the specified number of the specified normal type notes - or (2) the duration adds up to a normal note duration. - - Example (1): a 3:2 tuplet with a 1/4 and a 1/8 note - is filled if normal type is 1/8, - it is not filled if normal type is 1/4. - - Example (2): a 3:2 tuplet with a 1/4 and a 1/8 note is filled. - */ - -static bool isTupletFilled(const MxmlTupletState& state, const TDuration normalType, const Fraction timeMod) -{ - UNUSED(timeMod); - bool res = false; - const int actualNotes = state.actualNotes; - /* - const auto normalNotes = state.m_normalNotes; - LOGD("duration %s normalType %s timeMod %s normalNotes %d actualNotes %d", - muPrintable(state.m_duration.toString()), - muPrintable(normalType.fraction().toString()), - muPrintable(timeMod.toString()), - normalNotes, - actualNotes - ); - */ - - int tupletType = state.tupletType; - int tupletCount = state.tupletCount; - - if (normalType.isValid()) { - int matchedNormalType = int(normalType.type()); - int matchedNormalCount = actualNotes; - // match the types - matchTypeAndCount(tupletType, tupletCount, matchedNormalType, matchedNormalCount); - // ... result scenario (1) - res = tupletCount >= matchedNormalCount; - /* - LOGD("normalType valid tupletType %d tupletCount %d matchedNormalType %d matchedNormalCount %d res %d", - tupletType, - tupletCount, - matchedNormalType, - matchedNormalCount, - res - ); - */ - } else { - // ... result scenario (2) - res = tupletCount >= actualNotes; - /* - LOGD("normalType not valid tupletCount %d actualNotes %d res %d", - tupletCount, - actualNotes, - res - ); - */ - } - return res; -} - -//--------------------------------------------------------- -// missingTupletDuration -//--------------------------------------------------------- - -Fraction missingTupletDuration(const Fraction duration) -{ - Fraction tupletFraction; - Fraction tupletFullDuration; - - determineTupletFractionAndFullDuration(duration, tupletFraction, tupletFullDuration); - Fraction missing = (Fraction(1, 1) - tupletFraction) * tupletFullDuration; - - return missing; -} - //--------------------------------------------------------- // voiceToInt //--------------------------------------------------------- @@ -3370,103 +3124,6 @@ int MusicXMLParserPass1::voiceToInt(const String& voice) return voiceInt; } -//--------------------------------------------------------- -// determineTupletAction -//--------------------------------------------------------- - -/** - Update tuplet state using parse result tupletDesc. - Tuplets with and but without - are handled correctly. - TODO Nested tuplets are not (yet) supported. - */ - -MxmlTupletFlags MxmlTupletState::determineTupletAction(const Fraction noteDuration, - const Fraction timeMod, - const MxmlStartStop tupletStartStop, - const TDuration normalType, - Fraction& missingPreviousDuration, - Fraction& missingCurrentDuration) -{ - const int actNotes = timeMod.denominator(); - const int norNotes = timeMod.numerator(); - MxmlTupletFlags res = MxmlTupletFlag::NONE; - - // check for unexpected termination of previous tuplet - if (inTuplet && timeMod == Fraction(1, 1)) { - // recover by simply stopping the current tuplet first - if (!isTupletFilled(*this, normalType, timeMod)) { - missingPreviousDuration = missingTupletDuration(duration); - //LOGD("tuplet incomplete, missing %s", muPrintable(missingPreviousDuration.print())); - } - *this = {}; - res |= MxmlTupletFlag::STOP_PREVIOUS; - } - - // check for obvious errors - if (inTuplet && tupletStartStop == MxmlStartStop::START) { - LOGD("tuplet already started"); - // recover by simply stopping the current tuplet first - if (!isTupletFilled(*this, normalType, timeMod)) { - missingPreviousDuration = missingTupletDuration(duration); - //LOGD("tuplet incomplete, missing %s", muPrintable(missingPreviousDuration.print())); - } - *this = {}; - res |= MxmlTupletFlag::STOP_PREVIOUS; - } - if (tupletStartStop == MxmlStartStop::STOP && !inTuplet) { - LOGD("tuplet stop but no tuplet started"); // TODO - // recovery handled later (automatically, no special case needed) - } - - // Tuplet are either started by the tuplet start - // or when the time modification is first found. - if (!inTuplet) { - if (tupletStartStop == MxmlStartStop::START - || (!inTuplet && (actNotes != 1 || norNotes != 1))) { - if (tupletStartStop != MxmlStartStop::START) { - implicit = true; - } else { - implicit = false; - } - // create a new tuplet - inTuplet = true; - res |= MxmlTupletFlag::START_NEW; - } - } - - // Add chord to the current tuplet. - // Must also check for actual/normal notes to prevent - // adding one chord too much if tuplet stop is missing. - if (inTuplet && !(actNotes == 1 && norNotes == 1)) { - addDurationToTuplet(noteDuration, timeMod); - res |= MxmlTupletFlag::ADD_CHORD; - } - - // Tuplets are stopped by the tuplet stop - // or when the tuplet is filled completely - // (either with knowledge of the normal type - // or as a last resort calculated based on - // actual and normal notes plus total duration) - // or when the time-modification is not found. - - if (inTuplet) { - if (tupletStartStop == MxmlStartStop::STOP - || (implicit && isTupletFilled(*this, normalType, timeMod)) - || (actNotes == 1 && norNotes == 1)) { // incorrect ??? check scenario incomplete tuplet w/o start - if (actNotes > norNotes && !isTupletFilled(*this, normalType, timeMod)) { - missingCurrentDuration = missingTupletDuration(duration); - LOGD("current tuplet incomplete, missing %s", muPrintable(missingCurrentDuration.toString())); - } - - *this = {}; - res |= MxmlTupletFlag::STOP_CURRENT; - } - } - - return res; -} - //--------------------------------------------------------- // note //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h index 3f1526319ac12..9b0c3bfb89edb 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h @@ -25,12 +25,12 @@ #include "global/serialization/xmlstreamreader.h" #include "global/containers.h" -#include "global/types/flags.h" #include "draw/types/geometry.h" #include "importxmlfirstpass.h" #include "musicxmlsupport.h" #include "musicxmltypes.h" +#include "musicxmltupletstate.h" #include "engraving/engravingerrors.h" @@ -71,22 +71,6 @@ struct MxmlOctaveShiftDesc { : tp(_tp), size(_size), time(_tm), num(-1) {} }; -//--------------------------------------------------------- -// MxmlStartStop (also used in pass 2) -//--------------------------------------------------------- - -enum class MxmlStartStop : char { - NONE, START, STOP -}; - -enum class MxmlTupletFlag : char { - NONE = 0, - STOP_PREVIOUS = 1, - START_NEW = 2, - ADD_CHORD = 4, - STOP_CURRENT = 8 -}; - enum class MusicXMLExporterSoftware : char { SIBELIUS, DOLET6, @@ -96,29 +80,10 @@ enum class MusicXMLExporterSoftware : char { OTHER }; -typedef muse::Flags MxmlTupletFlags; - -struct MxmlTupletState { - void addDurationToTuplet(const Fraction duration, const Fraction timeMod); - MxmlTupletFlags determineTupletAction(const Fraction noteDuration, const Fraction timeMod, const MxmlStartStop tupletStartStop, - const TDuration normalType, Fraction& missingPreviousDuration, Fraction& missingCurrentDuration); - bool inTuplet = false; - bool implicit = false; - int actualNotes = 1; - int normalNotes = 1; - Fraction duration { 0, 1 }; - int tupletType = 0; // smallest note type in the tuplet // TODO_NOW rename ? - int tupletCount = 0; // number of smallest notes in the tuplet // TODO_NOW rename ? -}; - -using MxmlTupletStates = std::map; - //--------------------------------------------------------- // declarations //--------------------------------------------------------- -void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration); -Fraction missingTupletDuration(const Fraction duration); bool isLikelyCreditText(const String& text, const bool caseInsensitive); bool isLikelySubtitleText(const String& text, const bool caseInsensitive); diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h index c11e7377da358..317f881f8e58f 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h @@ -30,6 +30,7 @@ #include "internal/musicxml/musicxmlsupport.h" #include "musicxml.h" #include "musicxmltypes.h" +#include "musicxmltupletstate.h" #include "engraving/dom/instrument.h" #include "engraving/dom/types.h" @@ -47,15 +48,6 @@ using FiguredBassList = std::vector; using Tuplets = std::map; using Beams = std::map; -//--------------------------------------------------------- -// MxmlStartStop -//--------------------------------------------------------- -/* -enum class MxmlStartStop : char { - START, STOP, NONE - }; - */ - //--------------------------------------------------------- // MusicXmlSlash //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/musicxml.cmake b/src/importexport/musicxml/internal/musicxml/musicxml.cmake index 8af748ff6204b..38c3ebced2251 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxml.cmake +++ b/src/importexport/musicxml/internal/musicxml/musicxml.cmake @@ -22,6 +22,8 @@ set (MUSICXML_SRC ${CMAKE_CURRENT_LIST_DIR}/musicxmlfonthandler.h ${CMAKE_CURRENT_LIST_DIR}/musicxmlsupport.cpp ${CMAKE_CURRENT_LIST_DIR}/musicxmlsupport.h + ${CMAKE_CURRENT_LIST_DIR}/musicxmltupletstate.cpp + ${CMAKE_CURRENT_LIST_DIR}/musicxmltupletstate.h ${CMAKE_CURRENT_LIST_DIR}/musicxmltypes.cpp ${CMAKE_CURRENT_LIST_DIR}/musicxmltypes.h ${CMAKE_CURRENT_LIST_DIR}/musicxmlvalidation.cpp diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp index e19a6f232068a..1ca6aeec33759 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp +++ b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp @@ -670,4 +670,79 @@ const Articulation* findLaissezVibrer(const Chord* chord) } return nullptr; } + +//--------------------------------------------------------- +// determineTupletFractionAndFullDuration +//--------------------------------------------------------- + +/** + Split duration into two factors where fullDuration is note sized + (i.e. the denominator is a power of 2), 1/2 < fraction <= 1/1 + and fraction * fullDuration equals duration. + */ + +void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration) +{ + fraction = duration; + fullDuration = Fraction(1, 1); + // move denominator's powers of 2 from fraction to fullDuration + while (fraction.denominator() % 2 == 0) { + fraction *= 2; + fraction.reduce(); + fullDuration *= Fraction(1, 2); + } + // move numerator's powers of 2 from fraction to fullDuration + while (fraction.numerator() % 2 == 0) { + fraction *= Fraction(1, 2); + fraction.reduce(); + fullDuration *= 2; + fullDuration.reduce(); + } + // make sure 1/2 < fraction <= 1/1 + while (fraction <= Fraction(1, 2)) { + fullDuration *= Fraction(1, 2); + fraction *= 2; + } + fullDuration.reduce(); + fraction.reduce(); + + /* + Examples (note result when denominator is not a power of two): + 3:2 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/2 + 2:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4 + 4:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4 + 3:4 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/1 + + Bring back fraction in 1/2 .. 1/1 range. + */ + + if (fraction > Fraction(1, 1) && fraction.denominator() == 1) { + fullDuration *= fraction; + fullDuration.reduce(); + fraction = Fraction(1, 1); + } + + /* + LOGD("duration %s fraction %s fullDuration %s", + muPrintable(duration.toString()), + muPrintable(fraction.toString()), + muPrintable(fullDuration.toString()) + ); + */ +} + +//--------------------------------------------------------- +// missingTupletDuration +//--------------------------------------------------------- + +Fraction missingTupletDuration(const Fraction duration) +{ + Fraction tupletFraction; + Fraction tupletFullDuration; + + determineTupletFractionAndFullDuration(duration, tupletFraction, tupletFullDuration); + Fraction missing = (Fraction(1, 1) - tupletFraction) * tupletFullDuration; + + return missing; +} } diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h index 617caed3e98c2..4dec306fc0e39 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h @@ -56,5 +56,7 @@ extern bool isLaissezVibrer(const SymId id); extern const Articulation* findLaissezVibrer(const Chord* chord); extern String errorStringWithLocation(int line, int col, const String& error); extern String checkAtEndElement(const muse::XmlStreamReader& e, const String& expName); +extern void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration); +extern Fraction missingTupletDuration(const Fraction duration); } // namespace Ms #endif diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp new file mode 100644 index 0000000000000..11e290f84bd3c --- /dev/null +++ b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp @@ -0,0 +1,289 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2024 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "musicxmltupletstate.h" +#include "internal/musicxml/musicxmlsupport.h" + +using namespace mu::engraving; + +//--------------------------------------------------------- +// isTupletFilled +//--------------------------------------------------------- + +/** + Determine if the tuplet is completely filled, + because either (1) it is at least the same duration + as the specified number of the specified normal type notes + or (2) the duration adds up to a normal note duration. + + Example (1): a 3:2 tuplet with a 1/4 and a 1/8 note + is filled if normal type is 1/8, + it is not filled if normal type is 1/4. + + Example (2): a 3:2 tuplet with a 1/4 and a 1/8 note is filled. + */ + +bool MxmlTupletState::isTupletFilled(const TDuration normalType, const Fraction timeMod) +{ + UNUSED(timeMod); + bool res = false; + /* + const auto normalNotes = state.m_normalNotes; + LOGD("duration %s normalType %s timeMod %s normalNotes %d actualNotes %d", + muPrintable(state.m_duration.toString()), + muPrintable(normalType.fraction().toString()), + muPrintable(timeMod.toString()), + normalNotes, + actualNotes + ); + */ + + if (normalType.isValid()) { + int matchedNormalType = int(normalType.type()); + int matchedNormalCount = actualNotes; + // match the types + matchTypeAndCount(matchedNormalType, matchedNormalCount); + // ... result scenario (1) + res = smallestNoteCount >= matchedNormalCount; + /* + LOGD("normalType valid tupletType %d tupletCount %d matchedNormalType %d matchedNormalCount %d res %d", + tupletType, + tupletCount, + matchedNormalType, + matchedNormalCount, + res + ); + */ + } else { + // ... result scenario (2) + res = smallestNoteCount >= actualNotes; + /* + LOGD("normalType not valid tupletCount %d actualNotes %d res %d", + tupletCount, + actualNotes, + res + ); + */ + } + return res; +} + +//--------------------------------------------------------- +// smallestTypeAndCount +//--------------------------------------------------------- + +/** + Determine the smallest note type and the number of those + present in a ChordRest. + For a note without dots the type equals the note type + and count is one. + For a single dotted note the type equals half the note type + and count is three. + A double dotted note is similar. + Note: code assumes when duration().type() is incremented, + the note length is divided by two, checked by tupletAssert(). + */ + +void MxmlTupletState::smallestTypeAndCount(const TDuration durType, int& type, int& count) +{ + type = int(durType.type()); + count = 1; + switch (durType.dots()) { + case 0: + // nothing to do + break; + case 1: + type += 1; // next-smaller type + count = 3; + break; + case 2: + type += 2; // next-next-smaller type + count = 7; + break; + default: + LOGD("smallestTypeAndCount() does not support more than 2 dots"); + } +} + +//--------------------------------------------------------- +// matchTypeAndCount +//--------------------------------------------------------- + +/** + Given two note types and counts, if the types are not equal, + make them equal by successively doubling the count of the + largest type. + */ + +void MxmlTupletState::matchTypeAndCount(int& noteType, int& noteCount) +{ + while (smallestNoteType < noteType) { + smallestNoteType++; + smallestNoteCount *= 2; + } + while (noteType < smallestNoteType) { + noteType++; + noteCount *= 2; + } +} + +//--------------------------------------------------------- +// addDurationToTuplet +//--------------------------------------------------------- + +/** + Add duration to tuplet duration + Determine type and number of smallest notes in the tuplet + */ + +void MxmlTupletState::addDurationToTuplet(const Fraction dur, const Fraction timeMod) +{ + /* + LOGD("1 duration %s timeMod %s -> state.tupletType %d state.tupletCount %d state.actualNotes %d state.normalNotes %d", + muPrintable(duration.print()), + muPrintable(timeMod.print()), + m_tupletType, + m_tupletCount, + m_actualNotes, + m_normalNotes + ); + */ + if (duration <= Fraction(0, 1)) { + // first note: init variables + actualNotes = timeMod.denominator(); + normalNotes = timeMod.numerator(); + smallestTypeAndCount(dur / timeMod, smallestNoteType, smallestNoteCount); + } else { + int noteType = 0; + int noteCount = 0; + smallestTypeAndCount(dur / timeMod, noteType, noteCount); + // match the types + matchTypeAndCount(noteType, noteCount); + smallestNoteCount += noteCount; + } + duration += dur; + /* + LOGD("2 duration %s -> state.tupletType %d state.tupletCount %d state.actualNotes %d state.normalNotes %d", + muPrintable(duration.print()), + m_tupletType, + m_tupletCount, + m_actualNotes, + m_normalNotes + ); + */ +} + +//--------------------------------------------------------- +// determineTupletAction +//--------------------------------------------------------- + +/** + Update tuplet state using parse result tupletDesc. + Tuplets with and but without + are handled correctly. + TODO Nested tuplets are not (yet) supported. + */ + +MxmlTupletFlags MxmlTupletState::determineTupletAction(const Fraction noteDuration, + const Fraction timeMod, + const MxmlStartStop tupletStartStop, + const TDuration normalType, + Fraction& missingPreviousDuration, + Fraction& missingCurrentDuration) +{ + const int actNotes = timeMod.denominator(); + const int norNotes = timeMod.numerator(); + MxmlTupletFlags res = MxmlTupletFlag::NONE; + + // check for unexpected termination of previous tuplet + if (inTuplet && timeMod == Fraction(1, 1)) { + // recover by simply stopping the current tuplet first + if (!isTupletFilled(normalType, timeMod)) { + missingPreviousDuration = missingTupletDuration(duration); + //LOGD("tuplet incomplete, missing %s", muPrintable(missingPreviousDuration.print())); + } + *this = {}; + res |= MxmlTupletFlag::STOP_PREVIOUS; + } + + // check for obvious errors + if (inTuplet && tupletStartStop == MxmlStartStop::START) { + LOGD("tuplet already started"); + // recover by simply stopping the current tuplet first + if (!isTupletFilled(normalType, timeMod)) { + missingPreviousDuration = missingTupletDuration(duration); + //LOGD("tuplet incomplete, missing %s", muPrintable(missingPreviousDuration.print())); + } + *this = {}; + res |= MxmlTupletFlag::STOP_PREVIOUS; + } + if (tupletStartStop == MxmlStartStop::STOP && !inTuplet) { + LOGD("tuplet stop but no tuplet started"); // TODO + // recovery handled later (automatically, no special case needed) + } + + // Tuplet are either started by the tuplet start + // or when the time modification is first found. + if (!inTuplet) { + if (tupletStartStop == MxmlStartStop::START + || (!inTuplet && (actNotes != 1 || norNotes != 1))) { + if (tupletStartStop != MxmlStartStop::START) { + implicit = true; + } else { + implicit = false; + } + // create a new tuplet + inTuplet = true; + res |= MxmlTupletFlag::START_NEW; + } + } + + // Add chord to the current tuplet. + // Must also check for actual/normal notes to prevent + // adding one chord too much if tuplet stop is missing. + if (inTuplet && !(actNotes == 1 && norNotes == 1)) { + addDurationToTuplet(noteDuration, timeMod); + res |= MxmlTupletFlag::ADD_CHORD; + } + + // Tuplets are stopped by the tuplet stop + // or when the tuplet is filled completely + // (either with knowledge of the normal type + // or as a last resort calculated based on + // actual and normal notes plus total duration) + // or when the time-modification is not found. + + if (inTuplet) { + if (tupletStartStop == MxmlStartStop::STOP + || (implicit && isTupletFilled(normalType, timeMod)) + || (actNotes == 1 && norNotes == 1)) { // incorrect ??? check scenario incomplete tuplet w/o start + if (actNotes > norNotes && !isTupletFilled(normalType, timeMod)) { + missingCurrentDuration = missingTupletDuration(duration); + LOGD("current tuplet incomplete, missing %s", muPrintable(missingCurrentDuration.toString())); + } + + *this = {}; + res |= MxmlTupletFlag::STOP_CURRENT; + } + } + + return res; +} diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h new file mode 100644 index 0000000000000..2d70a529406c7 --- /dev/null +++ b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2024 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef MUSICXMLTUPLETSTATE_H +#define MUSICXMLTUPLETSTATE_H + +#include "musicxmltypes.h" +#include "dom/durationtype.h" + +namespace mu::engraving { +enum class MxmlTupletFlag : char { + NONE = 0, + STOP_PREVIOUS = 1, + START_NEW = 2, + ADD_CHORD = 4, + STOP_CURRENT = 8 +}; +typedef muse::Flags MxmlTupletFlags; + +class MxmlTupletState +{ +public: + MxmlTupletFlags determineTupletAction(const Fraction noteDuration, const Fraction timeMod, const MxmlStartStop tupletStartStop, + const TDuration normalType, Fraction& missingPreviousDuration, Fraction& missingCurrentDuration); +private: + void addDurationToTuplet(const Fraction duration, const Fraction timeMod); + void smallestTypeAndCount(const TDuration durType, int& type, int& count); + void matchTypeAndCount(int& noteType, int& noteCount); + bool isTupletFilled(const TDuration normalType, const Fraction timeMod); + + bool inTuplet = false; + bool implicit = false; + int actualNotes = 1; + int normalNotes = 1; + Fraction duration { 0, 1 }; + int smallestNoteType = 0; // smallest note type in the tuplet + int smallestNoteCount = 0; // number of smallest notes in the tuplet +}; +using MxmlTupletStates = std::map; +} + +#endif // MUSICXMLTUPLETSTATE_H From ecf7a204a8069da9671ed65f7a7b5a6483813aa3 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Tue, 17 Sep 2024 15:09:45 +0100 Subject: [PATCH 3/8] Break up musicxmltypes --- .../internal/musicxml/importmxmlpass1.cpp | 117 ++++++++++++ .../internal/musicxml/importmxmlpass1.h | 95 ++++++++++ .../internal/musicxml/importmxmlpass2.h | 67 +++++++ .../internal/musicxml/musicxmltypes.cpp | 117 ------------ .../internal/musicxml/musicxmltypes.h | 174 +----------------- 5 files changed, 281 insertions(+), 289 deletions(-) diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp index 4cd5333260601..4d9222cb68779 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp @@ -79,6 +79,123 @@ static bool musicxmlImportLayout() } namespace mu::engraving { +NoteList::NoteList() +{ + _staffNoteLists.reserve(MAX_STAVES); + for (int i = 0; i < MAX_STAVES; ++i) { + _staffNoteLists.push_back(StartStopList()); + } +} + +void NoteList::addNote(const int startTick, const int endTick, const size_t staff) +{ + if (staff < _staffNoteLists.size()) { + _staffNoteLists[staff].push_back(StartStop(startTick, endTick)); + } +} + +void NoteList::dump(const int& voice) const +{ + // dump contents + for (int i = 0; i < MAX_STAVES; ++i) { + printf("voice %d staff %d:", voice, i); + for (size_t j = 0; j < _staffNoteLists.at(i).size(); ++j) { + printf(" %d-%d", _staffNoteLists.at(i).at(j).first, _staffNoteLists.at(i).at(j).second); + } + printf("\n"); + } + // show overlap + printf("overlap voice %d:", voice); + for (int i = 0; i < MAX_STAVES - 1; ++i) { + for (int j = i + 1; j < MAX_STAVES; ++j) { + stavesOverlap(i, j); + } + } + printf("\n"); +} + +/** + Determine if notes n1 and n2 overlap. + This is NOT the case if + - n1 starts when or after n2 stops + - or n2 starts when or after n1 stops + */ + +bool NoteList::notesOverlap(const StartStop& n1, const StartStop& n2) const +{ + return !(n1.first >= n2.second || n1.second <= n2.first); +} + +/** + Determine if any note in staff1 and staff2 overlaps. + */ + +bool NoteList::stavesOverlap(const int staff1, const int staff2) const +{ + for (size_t i = 0; i < _staffNoteLists.at(staff1).size(); ++i) { + for (size_t j = 0; j < _staffNoteLists.at(staff2).size(); ++j) { + if (notesOverlap(_staffNoteLists.at(staff1).at(i), _staffNoteLists.at(staff2).at(j))) { + //printf(" %d-%d", staff1, staff2); + return true; + } + } + } + return false; +} + +/** + Determine if any note in any staff overlaps. + */ + +bool NoteList::anyStaffOverlaps() const +{ + for (int i = 0; i < MAX_STAVES - 1; ++i) { + for (int j = i + 1; j < MAX_STAVES; ++j) { + if (stavesOverlap(i, j)) { + return true; + } + } + } + return false; +} + +VoiceOverlapDetector::VoiceOverlapDetector() +{ + // LOGD("VoiceOverlapDetector::VoiceOverlapDetector(staves %d)", MAX_STAVES); +} + +void VoiceOverlapDetector::addNote(const int startTick, const int endTick, const int& voice, const int staff) +{ + // if necessary, create the note list for voice + if (!muse::contains(_noteLists, voice)) { + _noteLists.insert({ voice, NoteList() }); + } + _noteLists[voice].addNote(startTick, endTick, staff); +} + +void VoiceOverlapDetector::dump() const +{ + // LOGD("VoiceOverlapDetector::dump()"); + for (auto& p : _noteLists) { + p.second.dump(p.first); + } +} + +void VoiceOverlapDetector::newMeasure() +{ + // LOGD("VoiceOverlapDetector::newMeasure()"); + _noteLists.clear(); +} + +bool VoiceOverlapDetector::stavesOverlap(const int& voice) const +{ + if (muse::contains(_noteLists, voice)) { + return _noteLists.at(voice).anyStaffOverlaps(); + } else { + return false; + } +} + //--------------------------------------------------------- // allocateStaves //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h index 9b0c3bfb89edb..96710e066fbf5 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h @@ -53,6 +53,56 @@ struct PageFormat { bool twosided = false; }; +typedef std::pair StartStop; +typedef std::vector StartStopList; + +//--------------------------------------------------------- +// NoteList +//--------------------------------------------------------- + +/** + List of note start/stop times in a voice in all staves. +*/ + +class NoteList +{ +public: + NoteList(); + void addNote(const int startTick, const int endTick, const size_t staff); + void dump(const int& voice) const; + bool stavesOverlap(const int staff1, const int staff2) const; + bool anyStaffOverlaps() const; +private: + std::vector _staffNoteLists; // The note start/stop times in all staves + bool notesOverlap(const StartStop& n1, const StartStop& n2) const; +}; + +//--------------------------------------------------------- +// VoiceOverlapDetector +//--------------------------------------------------------- + +/** + Detect overlap in a voice, which is when a voice has two or more notes + active at the same time. In theory this should not happen, as voices + only move forward in time, but Sibelius 7 reuses voice numbers in multi- + staff parts, which leads to overlap. + + Current implementation does not detect voice overlap within a staff, + but only between staves. +*/ + +class VoiceOverlapDetector +{ +public: + VoiceOverlapDetector(); + void addNote(const int startTick, const int endTick, const int& voice, const int staff); + void dump() const; + void newMeasure(); + bool stavesOverlap(const int& voice) const; +private: + std::map _noteLists; // The notelists for all the voices +}; + //--------------------------------------------------------- // MxmlOctaveShiftDesc //--------------------------------------------------------- @@ -80,6 +130,51 @@ enum class MusicXMLExporterSoftware : char { OTHER }; +//--------------------------------------------------------- +// MusicXmlPartGroup +//--------------------------------------------------------- + +struct MusicXmlPartGroup { + int span = 0; + int start = 0; + BracketType type = BracketType::NO_BRACKET; + bool barlineSpan = false; + int column = 0; +}; +typedef std::vector MusicXmlPartGroupList; +typedef std::map PartMap; +typedef std::map MusicXmlPartGroupMap; + +//--------------------------------------------------------- +// CreditWords +// a single parsed MusicXML credit-words element +//--------------------------------------------------------- + +struct CreditWords { + int page = 0; + String type; + double defaultX = 0.0; + double defaultY = 0.0; + double fontSize = 0.0; + String justify; + String hAlign; + String vAlign; + String words; + CreditWords(int p, String tp, double dx, double dy, double fs, String j, String ha, String va, String w) + { + page = p; + type = tp; + defaultX = dx; + defaultY = dy; + fontSize = fs; + justify = j; + hAlign = ha; + vAlign = va; + words = w; + } +}; +typedef std::vector CreditWordsList; + //--------------------------------------------------------- // declarations //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h index 317f881f8e58f..d37f4d2f986c2 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h @@ -73,6 +73,33 @@ struct MusicXmlTupletDesc { TupletNumberType shownumber; }; +//--------------------------------------------------------- +// SlurDesc +//--------------------------------------------------------- + +/** + The description of Slurs being handled + */ + +class SlurDesc +{ +public: + enum class State : char { + NONE, START, STOP + }; + SlurDesc() + : m_slur(0), m_state(State::NONE) {} + Slur* slur() const { return m_slur; } + void start(Slur* slur) { m_slur = slur; m_state = State::START; } + void stop(Slur* slur) { m_slur = slur; m_state = State::STOP; } + bool isStart() const { return m_state == State::START; } + bool isStop() const { return m_state == State::STOP; } +private: + Slur* m_slur = nullptr; + State m_state; +}; +typedef std::map > MusicXmlSpannerMap; + //--------------------------------------------------------- // MusicXmlSpannerDesc //--------------------------------------------------------- @@ -102,6 +129,30 @@ struct MusicXmlExtendedSpannerDesc { String toString() const; }; +//--------------------------------------------------------- +// HarmonyDesc +//--------------------------------------------------------- + +/** + The description of a chord symbol with or without a fret diagram + */ + +struct HarmonyDesc +{ + track_idx_t m_track; + bool fretDiagramVisible() const { return m_fretDiagram ? m_fretDiagram->visible() : false; } + Harmony* m_harmony; + FretDiagram* m_fretDiagram; + + HarmonyDesc(track_idx_t m_track, Harmony* m_harmony, FretDiagram* m_fretDiagram) + : m_track(m_track), m_harmony(m_harmony), + m_fretDiagram(m_fretDiagram) {} + + HarmonyDesc() + : m_track(0), m_harmony(nullptr), m_fretDiagram(nullptr) {} +}; +using HarmonyMap = std::multimap; + //--------------------------------------------------------- // MusicXmlLyricsExtend //--------------------------------------------------------- @@ -127,6 +178,22 @@ struct GraceNoteLyrics { : lyric(lyric), extend(extend), no(no) {} }; +struct InferredPercInstr { + int pitch; + track_idx_t track; + String name; + Fraction tick; + + InferredPercInstr(int pitch, track_idx_t track, String name, Fraction tick) + : pitch(pitch), track(track), name(name), tick(tick) {} + + InferredPercInstr() + : pitch(-1), track(muse::nidx), name(u""), tick(Fraction(0, -1)) {} +}; +typedef std::vector InferredPercList; + +typedef std::map > MetronomeTextMap; + //--------------------------------------------------------- // MusicXMLParserLyric //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp index a5d5ccb7a33c2..e1a2ee751a8e9 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp @@ -24,123 +24,6 @@ using namespace mu::engraving; -NoteList::NoteList() -{ - _staffNoteLists.reserve(MAX_STAVES); - for (int i = 0; i < MAX_STAVES; ++i) { - _staffNoteLists.push_back(StartStopList()); - } -} - -void NoteList::addNote(const int startTick, const int endTick, const size_t staff) -{ - if (staff < _staffNoteLists.size()) { - _staffNoteLists[staff].push_back(StartStop(startTick, endTick)); - } -} - -void NoteList::dump(const int& voice) const -{ - // dump contents - for (int i = 0; i < MAX_STAVES; ++i) { - printf("voice %d staff %d:", voice, i); - for (size_t j = 0; j < _staffNoteLists.at(i).size(); ++j) { - printf(" %d-%d", _staffNoteLists.at(i).at(j).first, _staffNoteLists.at(i).at(j).second); - } - printf("\n"); - } - // show overlap - printf("overlap voice %d:", voice); - for (int i = 0; i < MAX_STAVES - 1; ++i) { - for (int j = i + 1; j < MAX_STAVES; ++j) { - stavesOverlap(i, j); - } - } - printf("\n"); -} - -/** - Determine if notes n1 and n2 overlap. - This is NOT the case if - - n1 starts when or after n2 stops - - or n2 starts when or after n1 stops - */ - -bool NoteList::notesOverlap(const StartStop& n1, const StartStop& n2) const -{ - return !(n1.first >= n2.second || n1.second <= n2.first); -} - -/** - Determine if any note in staff1 and staff2 overlaps. - */ - -bool NoteList::stavesOverlap(const int staff1, const int staff2) const -{ - for (size_t i = 0; i < _staffNoteLists.at(staff1).size(); ++i) { - for (size_t j = 0; j < _staffNoteLists.at(staff2).size(); ++j) { - if (notesOverlap(_staffNoteLists.at(staff1).at(i), _staffNoteLists.at(staff2).at(j))) { - //printf(" %d-%d", staff1, staff2); - return true; - } - } - } - return false; -} - -/** - Determine if any note in any staff overlaps. - */ - -bool NoteList::anyStaffOverlaps() const -{ - for (int i = 0; i < MAX_STAVES - 1; ++i) { - for (int j = i + 1; j < MAX_STAVES; ++j) { - if (stavesOverlap(i, j)) { - return true; - } - } - } - return false; -} - -VoiceOverlapDetector::VoiceOverlapDetector() -{ - // LOGD("VoiceOverlapDetector::VoiceOverlapDetector(staves %d)", MAX_STAVES); -} - -void VoiceOverlapDetector::addNote(const int startTick, const int endTick, const int& voice, const int staff) -{ - // if necessary, create the note list for voice - if (!muse::contains(_noteLists, voice)) { - _noteLists.insert({ voice, NoteList() }); - } - _noteLists[voice].addNote(startTick, endTick, staff); -} - -void VoiceOverlapDetector::dump() const -{ - // LOGD("VoiceOverlapDetector::dump()"); - for (auto& p : _noteLists) { - p.second.dump(p.first); - } -} - -void VoiceOverlapDetector::newMeasure() -{ - // LOGD("VoiceOverlapDetector::newMeasure()"); - _noteLists.clear(); -} - -bool VoiceOverlapDetector::stavesOverlap(const int& voice) const -{ - if (muse::contains(_noteLists, voice)) { - return _noteLists.at(voice).anyStaffOverlaps(); - } else { - return false; - } -} - String MusicXMLInstrument::toString() const { return String(u"chan %1 prog %2 vol %3 pan %4 unpitched %5 name '%6' sound '%7' head %8 line %9 stemDir %10") diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h index 7ef3f8a7ea084..2d218f755481c 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h @@ -22,91 +22,10 @@ #ifndef MUSICXMLTYPES_H #define MUSICXMLTYPES_H -#include "engraving/dom/line.h" -#include "engraving/dom/mscore.h" -#include "engraving/dom/fret.h" +#include "dom/mscore.h" +#include "dom/arpeggio.h" namespace mu::engraving { -//--------------------------------------------------------- -// MusicXmlPartGroup -//--------------------------------------------------------- - -struct MusicXmlPartGroup { - int span = 0; - int start = 0; - BracketType type = BracketType::NO_BRACKET; - bool barlineSpan = false; - int column = 0; -}; -typedef std::vector MusicXmlPartGroupList; -typedef std::map PartMap; -typedef std::map MusicXmlPartGroupMap; - -//--------------------------------------------------------- -// CreditWords -// a single parsed MusicXML credit-words element -//--------------------------------------------------------- - -struct CreditWords { - int page = 0; - String type; - double defaultX = 0.0; - double defaultY = 0.0; - double fontSize = 0.0; - String justify; - String hAlign; - String vAlign; - String words; - CreditWords(int p, String tp, double dx, double dy, double fs, String j, String ha, String va, String w) - { - page = p; - type = tp; - defaultX = dx; - defaultY = dy; - fontSize = fs; - justify = j; - hAlign = ha; - vAlign = va; - words = w; - } -}; -typedef std::vector CreditWordsList; - -//--------------------------------------------------------- -// SlurDesc -//--------------------------------------------------------- - -/** - The description of Slurs being handled - */ - -class SlurDesc -{ -public: - enum class State : char { - NONE, START, STOP - }; - SlurDesc() - : m_slur(0), m_state(State::NONE) {} - Slur* slur() const { return m_slur; } - void start(Slur* slur) { m_slur = slur; m_state = State::START; } - void stop(Slur* slur) { m_slur = slur; m_state = State::STOP; } - bool isStart() const { return m_state == State::START; } - bool isStop() const { return m_state == State::STOP; } -private: - Slur* m_slur = nullptr; - State m_state; -}; -typedef std::map > MusicXmlSpannerMap; - -//--------------------------------------------------------- -// NoteList -//--------------------------------------------------------- - -/** - List of note start/stop times in a voice in a single staff. -*/ - //--------------------------------------------------------- // MxmlStartStop //--------------------------------------------------------- @@ -115,30 +34,6 @@ enum class MxmlStartStop : char { NONE, START, STOP }; -typedef std::pair StartStop; -typedef std::vector StartStopList; - -//--------------------------------------------------------- -// NoteList -//--------------------------------------------------------- - -/** - List of note start/stop times in a voice in all staves. -*/ - -class NoteList -{ -public: - NoteList(); - void addNote(const int startTick, const int endTick, const size_t staff); - void dump(const int& voice) const; - bool stavesOverlap(const int staff1, const int staff2) const; - bool anyStaffOverlaps() const; -private: - std::vector _staffNoteLists; // The note start/stop times in all staves - bool notesOverlap(const StartStop& n1, const StartStop& n2) const; -}; - struct MusicXmlArpeggioDesc { Arpeggio* arp; int no; @@ -148,26 +43,6 @@ struct MusicXmlArpeggioDesc { }; typedef std::multimap ArpeggioMap; -/** - The description of a chord symbol with or without a fret diagram - */ - -struct HarmonyDesc -{ - track_idx_t m_track; - bool fretDiagramVisible() const { return m_fretDiagram ? m_fretDiagram->visible() : false; } - Harmony* m_harmony; - FretDiagram* m_fretDiagram; - - HarmonyDesc(track_idx_t m_track, Harmony* m_harmony, FretDiagram* m_fretDiagram) - : m_track(m_track), m_harmony(m_harmony), - m_fretDiagram(m_fretDiagram) {} - - HarmonyDesc() - : m_track(0), m_harmony(nullptr), m_fretDiagram(nullptr) {} -}; -using HarmonyMap = std::multimap; - //--------------------------------------------------------- // VoiceDesc //--------------------------------------------------------- @@ -228,32 +103,6 @@ class VoiceDesc int m_voices[MAX_STAVES]; // For every voice allocated on the staff, the voice number }; -//--------------------------------------------------------- -// VoiceOverlapDetector -//--------------------------------------------------------- - -/** - Detect overlap in a voice, which is when a voice has two or more notes - active at the same time. In theory this should not happen, as voices - only move forward in time, but Sibelius 7 reuses voice numbers in multi- - staff parts, which leads to overlap. - - Current implementation does not detect voice overlap within a staff, - but only between staves. -*/ - -class VoiceOverlapDetector -{ -public: - VoiceOverlapDetector(); - void addNote(const int startTick, const int endTick, const int& voice, const int staff); - void dump() const; - void newMeasure(); - bool stavesOverlap(const int& voice) const; -private: - std::map _noteLists; // The notelists for all the voices -}; - //--------------------------------------------------------- // MusicXMLInstrument //--------------------------------------------------------- @@ -293,25 +142,6 @@ struct MusicXMLInstrument { */ }; typedef std::map MusicXMLInstruments; - -struct InferredPercInstr { - int pitch; - track_idx_t track; - String name; - Fraction tick; - - InferredPercInstr(int pitch, track_idx_t track, String name, Fraction tick) - : pitch(pitch), track(track), name(name), tick(tick) {} - - InferredPercInstr() - : pitch(-1), track(muse::nidx), name(u""), tick(Fraction(0, -1)) {} -}; -typedef std::vector InferredPercList; - -typedef std::map > MetronomeTextMap; - -// Ties are identified by the pitch and track of their first note -typedef std::pair TieLocation; } #endif // MUSICXMLTYPES_H From 04b8d6f136c2ad9bb12ef2315f42eceeaaa38c10 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Tue, 17 Sep 2024 15:50:08 +0100 Subject: [PATCH 4/8] Break up importxmlfirstpass This file didn't have a very relevant name --- .../internal/musicxml/importmxmlpass1.cpp | 1 + .../internal/musicxml/importmxmlpass1.h | 2 +- .../internal/musicxml/importmxmlpass2.h | 1 - .../musicxml/internal/musicxml/musicxml.cmake | 4 +- ...mportxmlfirstpass.cpp => musicxmlpart.cpp} | 229 +++++------------- .../{importxmlfirstpass.h => musicxmlpart.h} | 47 +--- .../internal/musicxml/musicxmltypes.cpp | 116 +++++++++ .../internal/musicxml/musicxmltypes.h | 29 +++ 8 files changed, 212 insertions(+), 217 deletions(-) rename src/importexport/musicxml/internal/musicxml/{importxmlfirstpass.cpp => musicxmlpart.cpp} (63%) rename src/importexport/musicxml/internal/musicxml/{importxmlfirstpass.h => musicxmlpart.h} (77%) diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp index 4d9222cb68779..0ae545bf00993 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp @@ -43,6 +43,7 @@ #include "importmxmllogger.h" #include "importmxmlnoteduration.h" #include "importmxmlpass1.h" +#include "musicxml.h" #include "musicxmltypes.h" #include "modularity/ioc.h" diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h index 96710e066fbf5..a1769cf46d59c 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h @@ -27,10 +27,10 @@ #include "global/containers.h" #include "draw/types/geometry.h" -#include "importxmlfirstpass.h" #include "musicxmlsupport.h" #include "musicxmltypes.h" #include "musicxmltupletstate.h" +#include "musicxmlpart.h" #include "engraving/engravingerrors.h" diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h index d37f4d2f986c2..2a40f927f213c 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h @@ -26,7 +26,6 @@ #include #include "importmxmlpass1.h" -#include "importxmlfirstpass.h" #include "internal/musicxml/musicxmlsupport.h" #include "musicxml.h" #include "musicxmltypes.h" diff --git a/src/importexport/musicxml/internal/musicxml/musicxml.cmake b/src/importexport/musicxml/internal/musicxml/musicxml.cmake index 38c3ebced2251..2843feaf95c16 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxml.cmake +++ b/src/importexport/musicxml/internal/musicxml/musicxml.cmake @@ -15,11 +15,11 @@ set (MUSICXML_SRC ${CMAKE_CURRENT_LIST_DIR}/importmxmlpass2.cpp ${CMAKE_CURRENT_LIST_DIR}/importmxmlpass2.h ${CMAKE_CURRENT_LIST_DIR}/importxml.cpp - ${CMAKE_CURRENT_LIST_DIR}/importxmlfirstpass.cpp - ${CMAKE_CURRENT_LIST_DIR}/importxmlfirstpass.h ${CMAKE_CURRENT_LIST_DIR}/musicxml.h ${CMAKE_CURRENT_LIST_DIR}/musicxmlfonthandler.cpp ${CMAKE_CURRENT_LIST_DIR}/musicxmlfonthandler.h + ${CMAKE_CURRENT_LIST_DIR}/musicxmlpart.cpp + ${CMAKE_CURRENT_LIST_DIR}/musicxmlpart.h ${CMAKE_CURRENT_LIST_DIR}/musicxmlsupport.cpp ${CMAKE_CURRENT_LIST_DIR}/musicxmlsupport.h ${CMAKE_CURRENT_LIST_DIR}/musicxmltupletstate.cpp diff --git a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.cpp b/src/importexport/musicxml/internal/musicxml/musicxmlpart.cpp similarity index 63% rename from src/importexport/musicxml/internal/musicxml/importxmlfirstpass.cpp rename to src/importexport/musicxml/internal/musicxml/musicxmlpart.cpp index 5938522275e2d..ba99ab14bdb05 100644 --- a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.cpp +++ b/src/importexport/musicxml/internal/musicxml/musicxmlpart.cpp @@ -5,7 +5,7 @@ * MuseScore Studio * Music Composition & Notation * - * Copyright (C) 2021 MuseScore Limited + * Copyright (C) 2024 MuseScore Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as @@ -20,12 +20,10 @@ * along with this program. If not, see . */ -#include "importxmlfirstpass.h" +#include "musicxmlpart.h" +#include "dom/mscore.h" -#include "log.h" - -namespace mu::engraving { -// TODO: move somewhere else +using namespace mu::engraving; static const std::vector vocalInstrumentNames = { u"Voice", @@ -59,6 +57,57 @@ static const std::vector percussionInstrumentNames = { u"Stamp" }; +int MusicXmlOctaveShiftList::octaveShift(const Fraction f) const +{ + if (empty()) { + return 0; + } + auto i = upper_bound(f); + if (i == begin()) { + return 0; + } + --i; + return i->second; +} + +void MusicXmlOctaveShiftList::addOctaveShift(const int shift, const Fraction f) +{ + IF_ASSERT_FAILED(Fraction(0, 1) <= f) { + return; + } + + //LOGD("addOctaveShift(shift %d f %s)", shift, muPrintable(f.print())); + auto i = find(f); + if (i == end()) { + //LOGD("addOctaveShift: not found, inserting"); + insert({ f, shift }); + } else { + //LOGD("addOctaveShift: found %d, adding", (*this)[f]); + (*this)[f] += shift; + //LOGD("addOctaveShift: res %d", (*this)[f]); + } +} + +void MusicXmlOctaveShiftList::calcOctaveShiftShifts() +{ + /* + for (auto i = cbegin(); i != cend(); ++i) + LOGD(" [%s : %d]", muPrintable((*i).first.print()), (*i).second); + */ + + // to each MusicXmlOctaveShiftList entry, add the sum of all previous ones + int currentShift = 0; + for (auto i = begin(); i != end(); ++i) { + currentShift += i->second; + i->second = currentShift; + } + + /* + for (auto i = cbegin(); i != cend(); ++i) + LOGD(" [%s : %d]", muPrintable((*i).first.print()), (*i).second); + */ +} + MusicXmlPart::MusicXmlPart(String id, String name) : m_id(id), m_name(name) { @@ -181,171 +230,3 @@ bool MusicXmlPart::isPercussionStaff() const } return false; } - -//--------------------------------------------------------- -// interval -//--------------------------------------------------------- - -Interval MusicXmlIntervalList::interval(const Fraction f) const -{ - if (empty()) { - return {}; - } - auto i = upper_bound(f); - if (i == begin()) { - return {}; - } - --i; - return i->second; -} - -//--------------------------------------------------------- -// instrument -//--------------------------------------------------------- - -const String MusicXmlInstrList::instrument(const Fraction f) const -{ - if (empty()) { - return String(); - } - auto i = upper_bound(f); - if (i == begin()) { - return String(); - } - --i; - return i->second; -} - -//--------------------------------------------------------- -// setInstrument -//--------------------------------------------------------- - -void MusicXmlInstrList::setInstrument(const String instr, const Fraction f) -{ - // TODO determine how to handle multiple instrument changes at the same time - // current implementation keeps the first one - if (!insert({ f, instr }).second) { - LOGD() << "element already exists, instr: " << instr - << ", tick: " << f.toString() << "(" << f.ticks() << ")"; - } - //(*this)[f] = instr; -} - -int MusicXmlOctaveShiftList::octaveShift(const Fraction f) const -{ - if (empty()) { - return 0; - } - auto i = upper_bound(f); - if (i == begin()) { - return 0; - } - --i; - return i->second; -} - -void MusicXmlOctaveShiftList::addOctaveShift(const int shift, const Fraction f) -{ - IF_ASSERT_FAILED(Fraction(0, 1) <= f) { - return; - } - - //LOGD("addOctaveShift(shift %d f %s)", shift, muPrintable(f.print())); - auto i = find(f); - if (i == end()) { - //LOGD("addOctaveShift: not found, inserting"); - insert({ f, shift }); - } else { - //LOGD("addOctaveShift: found %d, adding", (*this)[f]); - (*this)[f] += shift; - //LOGD("addOctaveShift: res %d", (*this)[f]); - } -} - -void MusicXmlOctaveShiftList::calcOctaveShiftShifts() -{ - /* - for (auto i = cbegin(); i != cend(); ++i) - LOGD(" [%s : %d]", muPrintable((*i).first.print()), (*i).second); - */ - - // to each MusicXmlOctaveShiftList entry, add the sum of all previous ones - int currentShift = 0; - for (auto i = begin(); i != end(); ++i) { - currentShift += i->second; - i->second = currentShift; - } - - /* - for (auto i = cbegin(); i != cend(); ++i) - LOGD(" [%s : %d]", muPrintable((*i).first.print()), (*i).second); - */ -} - -//--------------------------------------------------------- -// LyricNumberHandler -// collect lyric numbering information and determine order -// -// MusicXML lyrics may contain name and number attributes, -// plus position information (typically default-y). -// Name and number are simply tokens with no specified usage. -// Default-y cannot easily be used to determine the lyrics -// line, as it tends to differ per system depending on the -// actual notes present. -// -// Simply collecting all possible lyric number attributes -// within a MusicXML part and assigning lyrics position -// based on alphabetically sorting works well for all -// common MusicXML files. -//--------------------------------------------------------- - -//--------------------------------------------------------- -// addNumber -//--------------------------------------------------------- - -void LyricNumberHandler::addNumber(const String& number) -{ - if (m_numberToNo.find(number) == m_numberToNo.end()) { - m_numberToNo[number] = -1; // unassigned - } -} - -//--------------------------------------------------------- -// toString -//--------------------------------------------------------- - -String LyricNumberHandler::toString() const -{ - String res; - for (const auto& p : m_numberToNo) { - if (!res.isEmpty()) { - res += u" "; - } - res += String(u"%1:%2").arg(p.first).arg(p.second); - } - return res; -} - -//--------------------------------------------------------- -// getLyricNo -//--------------------------------------------------------- - -int LyricNumberHandler::getLyricNo(const String& number) const -{ - const auto it = m_numberToNo.find(number); - return it == m_numberToNo.end() ? 0 : it->second; -} - -//--------------------------------------------------------- -// determineLyricNos -//--------------------------------------------------------- - -void LyricNumberHandler::determineLyricNos() -{ - int i = 0; - for (auto& p : m_numberToNo) { - p.second = i; - ++i; - } -} -} diff --git a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h b/src/importexport/musicxml/internal/musicxml/musicxmlpart.h similarity index 77% rename from src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h rename to src/importexport/musicxml/internal/musicxml/musicxmlpart.h index 574a10de10a25..2bba71f164440 100644 --- a/src/importexport/musicxml/internal/musicxml/importxmlfirstpass.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlpart.h @@ -5,7 +5,7 @@ * MuseScore Studio * Music Composition & Notation * - * Copyright (C) 2021 MuseScore Limited + * Copyright (C) 2024 MuseScore Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as @@ -19,33 +19,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef MUSICXMLPART_H +#define MUSICXMLPART_H -#ifndef __IMPORTXMLFIRSTPASS_H__ -#define __IMPORTXMLFIRSTPASS_H__ - -#include "engraving/types/fraction.h" -#include "engraving/dom/interval.h" +#include "types/fraction.h" +#include "dom/interval.h" #include "musicxmltypes.h" namespace mu::engraving { -typedef std::map VoiceList; -//using Intervals = std::map; - -class MusicXmlIntervalList : public std::map -{ -public: - MusicXmlIntervalList() {} - Interval interval(const Fraction f) const; -}; - -class MusicXmlInstrList : public std::map -{ -public: - MusicXmlInstrList() {} - const String instrument(const Fraction f) const; - void setInstrument(const String instr, const Fraction f); -}; - class MusicXmlOctaveShiftList : public std::map { public: @@ -55,18 +36,6 @@ class MusicXmlOctaveShiftList : public std::map void calcOctaveShiftShifts(); }; -class LyricNumberHandler -{ -public: - LyricNumberHandler() {} - void addNumber(const String& number); - String toString() const; - int getLyricNo(const String& number) const; - void determineLyricNos(); -private: - std::map m_numberToNo; -}; - class MusicXmlPart { public: @@ -114,7 +83,7 @@ class MusicXmlPart int m_maxStaff = -1; // maximum staff value found (0 based), -1 = none bool m_hasLyrics = false; std::map m_staffNumberToIndex; // Mapping from staff number to index in staff list. - // Only for when staves are discarded in MusicXMLParserPass1::attributes. + // Only for when staves are discarded in MusicXMLParserPass1::attributes. }; -} // namespace Ms -#endif +} +#endif // MUSICXMLPART_H diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp index e1a2ee751a8e9..9c895ecb9efe2 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp @@ -103,3 +103,119 @@ String VoiceDesc::toString() const } return res; } + +//--------------------------------------------------------- +// interval +//--------------------------------------------------------- + +Interval MusicXmlIntervalList::interval(const Fraction f) const +{ + if (empty()) { + return {}; + } + auto i = upper_bound(f); + if (i == begin()) { + return {}; + } + --i; + return i->second; +} + +//--------------------------------------------------------- +// instrument +//--------------------------------------------------------- + +const String MusicXmlInstrList::instrument(const Fraction f) const +{ + if (empty()) { + return String(); + } + auto i = upper_bound(f); + if (i == begin()) { + return String(); + } + --i; + return i->second; +} + +//--------------------------------------------------------- +// setInstrument +//--------------------------------------------------------- + +void MusicXmlInstrList::setInstrument(const String instr, const Fraction f) +{ + // TODO determine how to handle multiple instrument changes at the same time + // current implementation keeps the first one + if (!insert({ f, instr }).second) { + LOGD() << "element already exists, instr: " << instr + << ", tick: " << f.toString() << "(" << f.ticks() << ")"; + } + //(*this)[f] = instr; +} + +//--------------------------------------------------------- +// LyricNumberHandler +// collect lyric numbering information and determine order +// +// MusicXML lyrics may contain name and number attributes, +// plus position information (typically default-y). +// Name and number are simply tokens with no specified usage. +// Default-y cannot easily be used to determine the lyrics +// line, as it tends to differ per system depending on the +// actual notes present. +// +// Simply collecting all possible lyric number attributes +// within a MusicXML part and assigning lyrics position +// based on alphabetically sorting works well for all +// common MusicXML files. +//--------------------------------------------------------- + +//--------------------------------------------------------- +// addNumber +//--------------------------------------------------------- + +void LyricNumberHandler::addNumber(const String& number) +{ + if (m_numberToNo.find(number) == m_numberToNo.end()) { + m_numberToNo[number] = -1; // unassigned + } +} + +//--------------------------------------------------------- +// toString +//--------------------------------------------------------- + +String LyricNumberHandler::toString() const +{ + String res; + for (const auto& p : m_numberToNo) { + if (!res.isEmpty()) { + res += u" "; + } + res += String(u"%1:%2").arg(p.first).arg(p.second); + } + return res; +} + +//--------------------------------------------------------- +// getLyricNo +//--------------------------------------------------------- + +int LyricNumberHandler::getLyricNo(const String& number) const +{ + const auto it = m_numberToNo.find(number); + return it == m_numberToNo.end() ? 0 : it->second; +} + +//--------------------------------------------------------- +// determineLyricNos +//--------------------------------------------------------- + +void LyricNumberHandler::determineLyricNos() +{ + int i = 0; + for (auto& p : m_numberToNo) { + p.second = i; + ++i; + } +} diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h index 2d218f755481c..6776d8ab0f685 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h @@ -24,6 +24,7 @@ #define MUSICXMLTYPES_H #include "dom/mscore.h" #include "dom/arpeggio.h" +#include "dom/interval.h" namespace mu::engraving { //--------------------------------------------------------- @@ -102,6 +103,7 @@ class VoiceDesc int m_staffAlloc[MAX_STAVES]; // For overlapping voices: voice is allocated on these staves (note: -2=unalloc -1=undef 1=alloc) int m_voices[MAX_STAVES]; // For every voice allocated on the staff, the voice number }; +typedef std::map VoiceList; //--------------------------------------------------------- // MusicXMLInstrument @@ -142,6 +144,33 @@ struct MusicXMLInstrument { */ }; typedef std::map MusicXMLInstruments; + +class MusicXmlIntervalList : public std::map +{ +public: + MusicXmlIntervalList() {} + Interval interval(const Fraction f) const; +}; + +class MusicXmlInstrList : public std::map +{ +public: + MusicXmlInstrList() {} + const String instrument(const Fraction f) const; + void setInstrument(const String instr, const Fraction f); +}; + +class LyricNumberHandler +{ +public: + LyricNumberHandler() {} + void addNumber(const String& number); + String toString() const; + int getLyricNo(const String& number) const; + void determineLyricNos(); +private: + std::map m_numberToNo; +}; } #endif // MUSICXMLTYPES_H From 19c5d90aab9871c715597bd030cd4456e5be9ac0 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Wed, 18 Sep 2024 14:42:45 +0100 Subject: [PATCH 5/8] Move voiceDesc to its own file --- .../musicxml/internal/musicxml/musicxml.cmake | 2 + .../musicxml/internal/musicxml/musicxmlpart.h | 1 + .../internal/musicxml/musicxmltypes.cpp | 66 -------------- .../internal/musicxml/musicxmltypes.h | 62 ------------- .../internal/musicxml/musicxmlvoicedesc.cpp | 90 +++++++++++++++++++ .../internal/musicxml/musicxmlvoicedesc.h | 68 ++++++++++++++ 6 files changed, 161 insertions(+), 128 deletions(-) create mode 100644 src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.cpp create mode 100644 src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h diff --git a/src/importexport/musicxml/internal/musicxml/musicxml.cmake b/src/importexport/musicxml/internal/musicxml/musicxml.cmake index 2843feaf95c16..6216e13347cc6 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxml.cmake +++ b/src/importexport/musicxml/internal/musicxml/musicxml.cmake @@ -28,4 +28,6 @@ set (MUSICXML_SRC ${CMAKE_CURRENT_LIST_DIR}/musicxmltypes.h ${CMAKE_CURRENT_LIST_DIR}/musicxmlvalidation.cpp ${CMAKE_CURRENT_LIST_DIR}/musicxmlvalidation.h + ${CMAKE_CURRENT_LIST_DIR}/musicxmlvoicedesc.cpp + ${CMAKE_CURRENT_LIST_DIR}/musicxmlvoicedesc.h ) diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlpart.h b/src/importexport/musicxml/internal/musicxml/musicxmlpart.h index 2bba71f164440..b3a7bdbeaacc3 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlpart.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlpart.h @@ -25,6 +25,7 @@ #include "types/fraction.h" #include "dom/interval.h" #include "musicxmltypes.h" +#include "musicxmlvoicedesc.h" namespace mu::engraving { class MusicXmlOctaveShiftList : public std::map diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp index 9c895ecb9efe2..94300d8bc6c20 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.cpp @@ -38,72 +38,6 @@ String MusicXMLInstrument::toString() const .arg(int(stemDirection)); } -//--------------------------------------------------------- -// VoiceDesc -//--------------------------------------------------------- - -VoiceDesc::VoiceDesc() - : m_staff(-1), m_voice(-1), m_overlaps(false) -{ - for (int i = 0; i < MAX_STAVES; ++i) { - m_chordRests[i] = 0; - m_staffAlloc[i] = -1; - m_voices[i] = -1; - } -} - -void VoiceDesc::incrChordRests(int s) -{ - if (0 <= s && s < MAX_STAVES) { - m_chordRests[s]++; - } -} - -int VoiceDesc::numberChordRests() const -{ - int res = 0; - for (int i = 0; i < MAX_STAVES; ++i) { - res += m_chordRests[i]; - } - return res; -} - -int VoiceDesc::preferredStaff() const -{ - int max = 0; - int res = 0; - for (int i = 0; i < MAX_STAVES; ++i) { - if (m_chordRests[i] > max) { - max = m_chordRests[i]; - res = i; - } - } - return res; -} - -String VoiceDesc::toString() const -{ - String res = u"["; - for (int i = 0; i < MAX_STAVES; ++i) { - res += String(u" %1").arg(m_chordRests[i]); - } - res += String(u" ] overlaps %1").arg(m_overlaps); - if (m_overlaps) { - res += u" staffAlloc ["; - for (int i = 0; i < MAX_STAVES; ++i) { - res += String(u" %1").arg(m_staffAlloc[i]); - } - res += u" ] voices ["; - for (int i = 0; i < MAX_STAVES; ++i) { - res += String(u" %1").arg(m_voices[i]); - } - res += u" ]"; - } else { - res += String(u" staff %1 voice %2").arg(m_staff + 1).arg(m_voice + 1); - } - return res; -} - //--------------------------------------------------------- // interval //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h index 6776d8ab0f685..4d3708a6145e6 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h @@ -22,7 +22,6 @@ #ifndef MUSICXMLTYPES_H #define MUSICXMLTYPES_H -#include "dom/mscore.h" #include "dom/arpeggio.h" #include "dom/interval.h" @@ -44,67 +43,6 @@ struct MusicXmlArpeggioDesc { }; typedef std::multimap ArpeggioMap; -//--------------------------------------------------------- -// VoiceDesc -//--------------------------------------------------------- - -/** - The description of a single voice in a MusicXML part. -*/ - -class VoiceDesc -{ -public: - VoiceDesc(); - void incrChordRests(int s); - int numberChordRests() const; - int numberChordRests(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_chordRests[s] : 0; } - int preferredStaff() const; // Determine preferred staff for this voice - void setStaff(int s) - { - if (s >= 0) { - m_staff = s; - } - } - - int staff() const { return m_staff; } - void setVoice(int v) - { - if (v >= 0) { - m_voice = v; - } - } - - int voice() const { return m_voice; } - void setVoice(int s, int v) - { - if (s >= 0 && s < MAX_STAVES) { - m_voices[s] = v; - } - } - - int voice(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_voices[s] : -1; } - void setOverlap(bool b) { m_overlaps = b; } - bool overlaps() const { return m_overlaps; } - void setStaffAlloc(int s, int i) - { - if (s >= 0 && s < MAX_STAVES) { - m_staffAlloc[s] = i; - } - } - - int staffAlloc(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_staffAlloc[s] : -1; } - String toString() const; -private: - int m_chordRests[MAX_STAVES]; // The number of chordrests on each MusicXML staff - int m_staff; // The MuseScore staff allocated - int m_voice; // The MuseScore voice allocated - bool m_overlaps; // This voice contains active notes in multiple staves at the same time - int m_staffAlloc[MAX_STAVES]; // For overlapping voices: voice is allocated on these staves (note: -2=unalloc -1=undef 1=alloc) - int m_voices[MAX_STAVES]; // For every voice allocated on the staff, the voice number -}; -typedef std::map VoiceList; - //--------------------------------------------------------- // MusicXMLInstrument //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.cpp b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.cpp new file mode 100644 index 0000000000000..ec3e1f0980029 --- /dev/null +++ b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.cpp @@ -0,0 +1,90 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2024 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "musicxmlvoicedesc.h" + +using namespace mu::engraving; +//--------------------------------------------------------- +// VoiceDesc +//--------------------------------------------------------- + +VoiceDesc::VoiceDesc() + : m_staff(-1), m_voice(-1), m_overlaps(false) +{ + for (int i = 0; i < MAX_STAVES; ++i) { + m_chordRests[i] = 0; + m_staffAlloc[i] = -1; + m_voices[i] = -1; + } +} + +void VoiceDesc::incrChordRests(int s) +{ + if (0 <= s && s < MAX_STAVES) { + m_chordRests[s]++; + } +} + +int VoiceDesc::numberChordRests() const +{ + int res = 0; + for (int i = 0; i < MAX_STAVES; ++i) { + res += m_chordRests[i]; + } + return res; +} + +int VoiceDesc::preferredStaff() const +{ + int max = 0; + int res = 0; + for (int i = 0; i < MAX_STAVES; ++i) { + if (m_chordRests[i] > max) { + max = m_chordRests[i]; + res = i; + } + } + return res; +} + +String VoiceDesc::toString() const +{ + String res = u"["; + for (int i = 0; i < MAX_STAVES; ++i) { + res += String(u" %1").arg(m_chordRests[i]); + } + res += String(u" ] overlaps %1").arg(m_overlaps); + if (m_overlaps) { + res += u" staffAlloc ["; + for (int i = 0; i < MAX_STAVES; ++i) { + res += String(u" %1").arg(m_staffAlloc[i]); + } + res += u" ] voices ["; + for (int i = 0; i < MAX_STAVES; ++i) { + res += String(u" %1").arg(m_voices[i]); + } + res += u" ]"; + } else { + res += String(u" staff %1 voice %2").arg(m_staff + 1).arg(m_voice + 1); + } + return res; +} diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h new file mode 100644 index 0000000000000..f540708599a58 --- /dev/null +++ b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h @@ -0,0 +1,68 @@ +#ifndef MUSICXMLVOICEDESC_H +#define MUSICXMLVOICEDESC_H + +#include "dom/mscore.h" + +namespace mu::engraving { +//--------------------------------------------------------- +// VoiceDesc +//--------------------------------------------------------- + +/** + The description of a single voice in a MusicXML part. +*/ + +class VoiceDesc +{ +public: + VoiceDesc(); + void incrChordRests(int s); + int numberChordRests() const; + int numberChordRests(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_chordRests[s] : 0; } + int preferredStaff() const; // Determine preferred staff for this voice + void setStaff(int s) + { + if (s >= 0) { + m_staff = s; + } + } + + int staff() const { return m_staff; } + void setVoice(int v) + { + if (v >= 0) { + m_voice = v; + } + } + + int voice() const { return m_voice; } + void setVoice(int s, int v) + { + if (s >= 0 && s < MAX_STAVES) { + m_voices[s] = v; + } + } + + int voice(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_voices[s] : -1; } + void setOverlap(bool b) { m_overlaps = b; } + bool overlaps() const { return m_overlaps; } + void setStaffAlloc(int s, int i) + { + if (s >= 0 && s < MAX_STAVES) { + m_staffAlloc[s] = i; + } + } + + int staffAlloc(int s) const { return (s >= 0 && s < MAX_STAVES) ? m_staffAlloc[s] : -1; } + String toString() const; +private: + int m_chordRests[MAX_STAVES]; // The number of chordrests on each MusicXML staff + int m_staff; // The MuseScore staff allocated + int m_voice; // The MuseScore voice allocated + bool m_overlaps; // This voice contains active notes in multiple staves at the same time + int m_staffAlloc[MAX_STAVES]; // For overlapping voices: voice is allocated on these staves (note: -2=unalloc -1=undef 1=alloc) + int m_voices[MAX_STAVES]; // For every voice allocated on the staff, the voice number +}; +typedef std::map VoiceList; +} +#endif // MUSICXMLVOICEDESC_H From af00615f1e5e263142aeaf2acc72f7b018ad890d Mon Sep 17 00:00:00 2001 From: James Mizen Date: Fri, 27 Sep 2024 08:40:42 +0100 Subject: [PATCH 6/8] Review changes --- .../musicxml/internal/musicxml/exportxml.cpp | 3 +- .../musicxml/internal/musicxml/exportxml.h | 1 - .../musicxml/importmxmlnoteduration.h | 1 - .../internal/musicxml/importmxmlpass1.cpp | 48 +++++++++++- .../internal/musicxml/importmxmlpass1.h | 48 +----------- .../internal/musicxml/importmxmlpass2.cpp | 2 + .../internal/musicxml/importmxmlpass2.h | 4 +- .../musicxml/internal/musicxml/musicxml.cmake | 1 - .../musicxml/internal/musicxml/musicxml.h | 31 -------- .../internal/musicxml/musicxmlsupport.cpp | 77 +------------------ .../internal/musicxml/musicxmlsupport.h | 2 - .../internal/musicxml/musicxmltupletstate.cpp | 75 ++++++++++++++++++ .../internal/musicxml/musicxmltupletstate.h | 3 + .../internal/musicxml/musicxmltypes.h | 2 + 14 files changed, 136 insertions(+), 162 deletions(-) delete mode 100644 src/importexport/musicxml/internal/musicxml/musicxml.h diff --git a/src/importexport/musicxml/internal/musicxml/exportxml.cpp b/src/importexport/musicxml/internal/musicxml/exportxml.cpp index dabd48fa2011e..eb5ea6a487bed 100644 --- a/src/importexport/musicxml/internal/musicxml/exportxml.cpp +++ b/src/importexport/musicxml/internal/musicxml/exportxml.cpp @@ -124,7 +124,6 @@ #include "engraving/dom/volta.h" #include "engraving/dom/whammybar.h" -#include "musicxml.h" #include "musicxmlfonthandler.h" #include "musicxmlsupport.h" #include "musicxmltypes.h" @@ -158,6 +157,8 @@ namespace mu::engraving { #define clefDebug(...) {} #endif +constexpr int MAX_PART_GROUPS = 8; + //--------------------------------------------------------- // typedefs //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/exportxml.h b/src/importexport/musicxml/internal/musicxml/exportxml.h index d6d488ae71fa6..105bbd8389726 100644 --- a/src/importexport/musicxml/internal/musicxml/exportxml.h +++ b/src/importexport/musicxml/internal/musicxml/exportxml.h @@ -28,7 +28,6 @@ namespace mu::engraving { class Score; - bool saveMxl(Score*, muse::io::IODevice*); bool saveXml(Score*, muse::io::IODevice*); bool saveXml(Score*, const muse::String&); diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h b/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h index f6cecbe1dcd3f..50610a48917cf 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h @@ -26,7 +26,6 @@ #include "engraving/dom/durationtype.h" #include "engraving/types/fraction.h" #include "importmxmlpass1.h" -#include "musicxmltypes.h" namespace mu::engraving { class MxmlLogger; diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp index 0ae545bf00993..422386fa0fd4f 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp @@ -43,7 +43,6 @@ #include "importmxmllogger.h" #include "importmxmlnoteduration.h" #include "importmxmlpass1.h" -#include "musicxml.h" #include "musicxmltypes.h" #include "modularity/ioc.h" @@ -80,6 +79,53 @@ static bool musicxmlImportLayout() } namespace mu::engraving { +//--------------------------------------------------------- +// NoteList +//--------------------------------------------------------- + +/** + List of note start/stop times in a voice in all staves. +*/ + +class NoteList +{ +public: + NoteList(); + void addNote(const int startTick, const int endTick, const size_t staff); + void dump(const int& voice) const; + bool stavesOverlap(const int staff1, const int staff2) const; + bool anyStaffOverlaps() const; +private: + std::vector _staffNoteLists; // The note start/stop times in all staves + bool notesOverlap(const StartStop& n1, const StartStop& n2) const; +}; + +//--------------------------------------------------------- +// VoiceOverlapDetector +//--------------------------------------------------------- + +/** + Detect overlap in a voice, which is when a voice has two or more notes + active at the same time. In theory this should not happen, as voices + only move forward in time, but Sibelius 7 reuses voice numbers in multi- + staff parts, which leads to overlap. + + Current implementation does not detect voice overlap within a staff, + but only between staves. +*/ + +class VoiceOverlapDetector +{ +public: + VoiceOverlapDetector(); + void addNote(const int startTick, const int endTick, const int& voice, const int staff); + void dump() const; + void newMeasure(); + bool stavesOverlap(const int& voice) const; +private: + std::map _noteLists; // The notelists for all the voices +}; + NoteList::NoteList() { _staffNoteLists.reserve(MAX_STAVES); diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h index a1769cf46d59c..bee69b67ae180 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h @@ -36,6 +36,7 @@ namespace mu::engraving { class Score; +class VoiceOverlapDetector; //--------------------------------------------------------- // PageFormat @@ -56,53 +57,6 @@ struct PageFormat { typedef std::pair StartStop; typedef std::vector StartStopList; -//--------------------------------------------------------- -// NoteList -//--------------------------------------------------------- - -/** - List of note start/stop times in a voice in all staves. -*/ - -class NoteList -{ -public: - NoteList(); - void addNote(const int startTick, const int endTick, const size_t staff); - void dump(const int& voice) const; - bool stavesOverlap(const int staff1, const int staff2) const; - bool anyStaffOverlaps() const; -private: - std::vector _staffNoteLists; // The note start/stop times in all staves - bool notesOverlap(const StartStop& n1, const StartStop& n2) const; -}; - -//--------------------------------------------------------- -// VoiceOverlapDetector -//--------------------------------------------------------- - -/** - Detect overlap in a voice, which is when a voice has two or more notes - active at the same time. In theory this should not happen, as voices - only move forward in time, but Sibelius 7 reuses voice numbers in multi- - staff parts, which leads to overlap. - - Current implementation does not detect voice overlap within a staff, - but only between staves. -*/ - -class VoiceOverlapDetector -{ -public: - VoiceOverlapDetector(); - void addNote(const int startTick, const int endTick, const int& voice, const int staff); - void dump() const; - void newMeasure(); - bool stavesOverlap(const int& voice) const; -private: - std::map _noteLists; // The notelists for all the voices -}; - //--------------------------------------------------------- // MxmlOctaveShiftDesc //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp index 4f22d497fade3..9f4bf2c0ba1e1 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp @@ -122,6 +122,8 @@ static std::shared_ptr engravingFonts() //#define DEBUG_VOICE_MAPPER true +constexpr int MAX_LYRICS = 16; + //--------------------------------------------------------- // function declarations //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h index 2a40f927f213c..72c7f0f74711f 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h @@ -27,7 +27,6 @@ #include "importmxmlpass1.h" #include "internal/musicxml/musicxmlsupport.h" -#include "musicxml.h" #include "musicxmltypes.h" #include "musicxmltupletstate.h" @@ -301,6 +300,9 @@ using SpannerSet = std::set; using DelayedArpMap = std::map; using SegnoStack = std::map; using SystemElements = std::multimap; + +// Ties are identified by the pitch and track of their first note +typedef std::pair TieLocation; using MusicXMLTieMap = std::map; //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/musicxml.cmake b/src/importexport/musicxml/internal/musicxml/musicxml.cmake index 6216e13347cc6..ec89cc56654b4 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxml.cmake +++ b/src/importexport/musicxml/internal/musicxml/musicxml.cmake @@ -15,7 +15,6 @@ set (MUSICXML_SRC ${CMAKE_CURRENT_LIST_DIR}/importmxmlpass2.cpp ${CMAKE_CURRENT_LIST_DIR}/importmxmlpass2.h ${CMAKE_CURRENT_LIST_DIR}/importxml.cpp - ${CMAKE_CURRENT_LIST_DIR}/musicxml.h ${CMAKE_CURRENT_LIST_DIR}/musicxmlfonthandler.cpp ${CMAKE_CURRENT_LIST_DIR}/musicxmlfonthandler.h ${CMAKE_CURRENT_LIST_DIR}/musicxmlpart.cpp diff --git a/src/importexport/musicxml/internal/musicxml/musicxml.h b/src/importexport/musicxml/internal/musicxml/musicxml.h deleted file mode 100644 index 00a74662a7803..0000000000000 --- a/src/importexport/musicxml/internal/musicxml/musicxml.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-3.0-only - * MuseScore-Studio-CLA-applies - * - * MuseScore Studio - * Music Composition & Notation - * - * Copyright (C) 2021 MuseScore Limited - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __MUSICXML_H__ -#define __MUSICXML_H__ - -namespace mu::engraving { -const int MAX_LYRICS = 16; -const int MAX_PART_GROUPS = 8; -const int MAX_NUMBER_LEVEL = 16; // maximum number of overlapping MusicXML objects -} // namespace Ms -#endif diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp index 1ca6aeec33759..dd83f835a409e 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp +++ b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.cpp @@ -647,7 +647,7 @@ AccidentalType microtonalGuess(double val) } //--------------------------------------------------------- -// symIsLaissezVibrer +// isLaissezVibrer //--------------------------------------------------------- bool isLaissezVibrer(const SymId id) @@ -670,79 +670,4 @@ const Articulation* findLaissezVibrer(const Chord* chord) } return nullptr; } - -//--------------------------------------------------------- -// determineTupletFractionAndFullDuration -//--------------------------------------------------------- - -/** - Split duration into two factors where fullDuration is note sized - (i.e. the denominator is a power of 2), 1/2 < fraction <= 1/1 - and fraction * fullDuration equals duration. - */ - -void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration) -{ - fraction = duration; - fullDuration = Fraction(1, 1); - // move denominator's powers of 2 from fraction to fullDuration - while (fraction.denominator() % 2 == 0) { - fraction *= 2; - fraction.reduce(); - fullDuration *= Fraction(1, 2); - } - // move numerator's powers of 2 from fraction to fullDuration - while (fraction.numerator() % 2 == 0) { - fraction *= Fraction(1, 2); - fraction.reduce(); - fullDuration *= 2; - fullDuration.reduce(); - } - // make sure 1/2 < fraction <= 1/1 - while (fraction <= Fraction(1, 2)) { - fullDuration *= Fraction(1, 2); - fraction *= 2; - } - fullDuration.reduce(); - fraction.reduce(); - - /* - Examples (note result when denominator is not a power of two): - 3:2 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/2 - 2:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4 - 4:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4 - 3:4 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/1 - - Bring back fraction in 1/2 .. 1/1 range. - */ - - if (fraction > Fraction(1, 1) && fraction.denominator() == 1) { - fullDuration *= fraction; - fullDuration.reduce(); - fraction = Fraction(1, 1); - } - - /* - LOGD("duration %s fraction %s fullDuration %s", - muPrintable(duration.toString()), - muPrintable(fraction.toString()), - muPrintable(fullDuration.toString()) - ); - */ -} - -//--------------------------------------------------------- -// missingTupletDuration -//--------------------------------------------------------- - -Fraction missingTupletDuration(const Fraction duration) -{ - Fraction tupletFraction; - Fraction tupletFullDuration; - - determineTupletFractionAndFullDuration(duration, tupletFraction, tupletFullDuration); - Fraction missing = (Fraction(1, 1) - tupletFraction) * tupletFullDuration; - - return missing; -} } diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h index 4dec306fc0e39..617caed3e98c2 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h @@ -56,7 +56,5 @@ extern bool isLaissezVibrer(const SymId id); extern const Articulation* findLaissezVibrer(const Chord* chord); extern String errorStringWithLocation(int line, int col, const String& error); extern String checkAtEndElement(const muse::XmlStreamReader& e, const String& expName); -extern void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration); -extern Fraction missingTupletDuration(const Fraction duration); } // namespace Ms #endif diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp index 11e290f84bd3c..54557255949eb 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp +++ b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.cpp @@ -287,3 +287,78 @@ MxmlTupletFlags MxmlTupletState::determineTupletAction(const Fraction noteDurati return res; } + +//--------------------------------------------------------- +// determineTupletFractionAndFullDuration +//--------------------------------------------------------- + +/** + Split duration into two factors where fullDuration is note sized + (i.e. the denominator is a power of 2), 1/2 < fraction <= 1/1 + and fraction * fullDuration equals duration. + */ + +void mu::engraving::determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration) +{ + fraction = duration; + fullDuration = Fraction(1, 1); + // move denominator's powers of 2 from fraction to fullDuration + while (fraction.denominator() % 2 == 0) { + fraction *= 2; + fraction.reduce(); + fullDuration *= Fraction(1, 2); + } + // move numerator's powers of 2 from fraction to fullDuration + while (fraction.numerator() % 2 == 0) { + fraction *= Fraction(1, 2); + fraction.reduce(); + fullDuration *= 2; + fullDuration.reduce(); + } + // make sure 1/2 < fraction <= 1/1 + while (fraction <= Fraction(1, 2)) { + fullDuration *= Fraction(1, 2); + fraction *= 2; + } + fullDuration.reduce(); + fraction.reduce(); + + /* + Examples (note result when denominator is not a power of two): + 3:2 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/2 + 2:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4 + 4:3 tuplet of 1/4 results in fraction 3/1 and fullDuration 1/4 + 3:4 tuplet of 1/4 results in fraction 1/1 and fullDuration 1/1 + + Bring back fraction in 1/2 .. 1/1 range. + */ + + if (fraction > Fraction(1, 1) && fraction.denominator() == 1) { + fullDuration *= fraction; + fullDuration.reduce(); + fraction = Fraction(1, 1); + } + + /* + LOGD("duration %s fraction %s fullDuration %s", + muPrintable(duration.toString()), + muPrintable(fraction.toString()), + muPrintable(fullDuration.toString()) + ); + */ +} + +//--------------------------------------------------------- +// missingTupletDuration +//--------------------------------------------------------- + +Fraction mu::engraving::missingTupletDuration(const Fraction duration) +{ + Fraction tupletFraction; + Fraction tupletFullDuration; + + determineTupletFractionAndFullDuration(duration, tupletFraction, tupletFullDuration); + Fraction missing = (Fraction(1, 1) - tupletFraction) * tupletFullDuration; + + return missing; +} diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h index 2d70a529406c7..49bf76fd14e3e 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h @@ -55,6 +55,9 @@ class MxmlTupletState int smallestNoteCount = 0; // number of smallest notes in the tuplet }; using MxmlTupletStates = std::map; + +void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration); +Fraction missingTupletDuration(const Fraction duration); } #endif // MUSICXMLTUPLETSTATE_H diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h index 4d3708a6145e6..316fb7dc306ea 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h @@ -26,6 +26,8 @@ #include "dom/interval.h" namespace mu::engraving { +const int MAX_NUMBER_LEVEL = 16; // maximum number of overlapping MusicXML objects + //--------------------------------------------------------- // MxmlStartStop //--------------------------------------------------------- From 188b7a5f96f413003959762dc234db4dd9cae898 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Fri, 27 Sep 2024 08:46:11 +0100 Subject: [PATCH 7/8] Replace xml include guards with pragma once --- .../musicxml/imusicxmlconfiguration.h | 5 +--- .../musicxml/internal/musicxml/exportxml.h | 5 +--- .../musicxml/internal/musicxml/importmxml.h | 5 +--- .../internal/musicxml/importmxmllogger.h | 5 +--- .../musicxml/importmxmlnoteduration.h | 5 +--- .../internal/musicxml/importmxmlnotepitch.h | 5 +--- .../internal/musicxml/importmxmlpass1.h | 4 +-- .../internal/musicxml/importmxmlpass2.h | 4 +-- .../internal/musicxml/musicxmlfonthandler.h | 5 +--- .../musicxml/internal/musicxml/musicxmlpart.h | 4 +-- .../internal/musicxml/musicxmlsupport.h | 4 +-- .../internal/musicxml/musicxmltupletstate.h | 5 +--- .../internal/musicxml/musicxmltypes.h | 5 +--- .../internal/musicxml/musicxmlvalidation.h | 5 +--- .../internal/musicxml/musicxmlvoicedesc.h | 25 ++++++++++++++++--- .../musicxml/internal/musicxmlconfiguration.h | 5 +--- .../musicxml/internal/musicxmlreader.h | 4 +-- .../musicxml/internal/musicxmlwriter.h | 6 +---- .../musicxml/internal/mxlwriter.h | 6 +---- src/importexport/musicxml/musicxmlmodule.h | 5 +--- 20 files changed, 41 insertions(+), 76 deletions(-) diff --git a/src/importexport/musicxml/imusicxmlconfiguration.h b/src/importexport/musicxml/imusicxmlconfiguration.h index 106aabe314039..cd4df3cecb515 100644 --- a/src/importexport/musicxml/imusicxmlconfiguration.h +++ b/src/importexport/musicxml/imusicxmlconfiguration.h @@ -19,8 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef MU_IMPORTEXPORT_IMUSICXMLCONFIGURATION_H -#define MU_IMPORTEXPORT_IMUSICXMLCONFIGURATION_H +#pragma once #include "modularity/imoduleinterface.h" #include "io/path.h" @@ -67,5 +66,3 @@ class IMusicXmlConfiguration : MODULE_EXPORT_INTERFACE virtual void setInferTextTypeOverride(std::optional value) = 0; }; } - -#endif // MU_IMPORTEXPORT_IMUSICXMLCONFIGURATION_H diff --git a/src/importexport/musicxml/internal/musicxml/exportxml.h b/src/importexport/musicxml/internal/musicxml/exportxml.h index 105bbd8389726..1c29d85a83994 100644 --- a/src/importexport/musicxml/internal/musicxml/exportxml.h +++ b/src/importexport/musicxml/internal/musicxml/exportxml.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef MU_IMPORTEXPORT_EXPORTXML_H -#define MU_IMPORTEXPORT_EXPORTXML_H +#pragma once #include "io/iodevice.h" #include "global/types/string.h" @@ -32,5 +31,3 @@ bool saveMxl(Score*, muse::io::IODevice*); bool saveXml(Score*, muse::io::IODevice*); bool saveXml(Score*, const muse::String&); } - -#endif // MU_IMPORTEXPORT_EXPORTXML_H diff --git a/src/importexport/musicxml/internal/musicxml/importmxml.h b/src/importexport/musicxml/internal/musicxml/importmxml.h index f7813d4c1be56..7a476d085ec48 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxml.h +++ b/src/importexport/musicxml/internal/musicxml/importmxml.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef __IMPORTMXML_H__ -#define __IMPORTMXML_H__ +#pragma once #include "global/types/string.h" #include "engravingerrors.h" @@ -31,5 +30,3 @@ class Score; Err importMusicXMLfromBuffer(Score* score, const muse::String&, const muse::ByteArray& data); } - -#endif diff --git a/src/importexport/musicxml/internal/musicxml/importmxmllogger.h b/src/importexport/musicxml/internal/musicxml/importmxmllogger.h index 71ba0885b57f2..fbca301eae13d 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmllogger.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmllogger.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef __IMPORTMXMLLOGGER_H__ -#define __IMPORTMXMLLOGGER_H__ +#pragma once #include "global/types/string.h" @@ -45,5 +44,3 @@ class MxmlLogger Level m_level = Level::MXML_INFO; }; } // namespace Ms - -#endif diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h b/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h index 50610a48917cf..f343c7b84e110 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlnoteduration.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef __IMPORTMXMLNOTEDURATION_H__ -#define __IMPORTMXMLNOTEDURATION_H__ +#pragma once #include "engraving/dom/durationtype.h" #include "engraving/types/fraction.h" @@ -66,5 +65,3 @@ class MxmlNoteDuration MxmlLogger* m_logger = nullptr; ///< Error logger }; } // namespace Ms - -#endif diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlnotepitch.h b/src/importexport/musicxml/internal/musicxml/importmxmlnotepitch.h index 3bdb09e32d97a..dacec58ab7ccf 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlnotepitch.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlnotepitch.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef __IMPORTMXMLNOTEPITCH_H__ -#define __IMPORTMXMLNOTEPITCH_H__ +#pragma once #include "global/serialization/xmlstreamreader.h" #include "engraving/dom/accidental.h" @@ -69,5 +68,3 @@ class MxmlNotePitch MxmlLogger* m_logger = nullptr; // Error logger }; } // namespace Ms - -#endif diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h index bee69b67ae180..ada5b46f0a2ab 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef __IMPORTMXMLPASS1_H__ -#define __IMPORTMXMLPASS1_H__ +#pragma once #include "global/serialization/xmlstreamreader.h" #include "global/containers.h" @@ -252,4 +251,3 @@ class MusicXMLParserPass1 std::set m_seenDenominators; // Denominators seen. Used for rounding errors. }; } // namespace Ms -#endif diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h index 72c7f0f74711f..11af49954a51a 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef __IMPORTMXMLPASS2_H__ -#define __IMPORTMXMLPASS2_H__ +#pragma once #include @@ -674,4 +673,3 @@ class MusicXMLInferredFingering void addToNotes(std::vector& notes) const; }; } // namespace Ms -#endif diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlfonthandler.h b/src/importexport/musicxml/internal/musicxml/musicxmlfonthandler.h index b015d0f9675e0..370ab3aa03c33 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlfonthandler.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlfonthandler.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef __MUSICXMLFONTHANDLER_H__ -#define __MUSICXMLFONTHANDLER_H__ +#pragma once #include "engraving/dom/text.h" @@ -50,5 +49,3 @@ class MScoreTextToMXML String musicalTextFont; }; } // namespace Ms - -#endif diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlpart.h b/src/importexport/musicxml/internal/musicxml/musicxmlpart.h index b3a7bdbeaacc3..9404973697fe3 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlpart.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlpart.h @@ -19,8 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef MUSICXMLPART_H -#define MUSICXMLPART_H +#pragma once #include "types/fraction.h" #include "dom/interval.h" @@ -87,4 +86,3 @@ class MusicXmlPart // Only for when staves are discarded in MusicXMLParserPass1::attributes. }; } -#endif // MUSICXMLPART_H diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h index 617caed3e98c2..b2a7916f6fce0 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlsupport.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef __MUSICXMLSUPPORT_H__ -#define __MUSICXMLSUPPORT_H__ +#pragma once #include "engraving/types/fraction.h" #include "engraving/dom/note.h" @@ -57,4 +56,3 @@ extern const Articulation* findLaissezVibrer(const Chord* chord); extern String errorStringWithLocation(int line, int col, const String& error); extern String checkAtEndElement(const muse::XmlStreamReader& e, const String& expName); } // namespace Ms -#endif diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h index 49bf76fd14e3e..9df19463ced95 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmltupletstate.h @@ -19,8 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef MUSICXMLTUPLETSTATE_H -#define MUSICXMLTUPLETSTATE_H +#pragma once #include "musicxmltypes.h" #include "dom/durationtype.h" @@ -59,5 +58,3 @@ using MxmlTupletStates = std::map; void determineTupletFractionAndFullDuration(const Fraction duration, Fraction& fraction, Fraction& fullDuration); Fraction missingTupletDuration(const Fraction duration); } - -#endif // MUSICXMLTUPLETSTATE_H diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h index 316fb7dc306ea..ca7fe7496ce5b 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef MUSICXMLTYPES_H -#define MUSICXMLTYPES_H +#pragma once #include "dom/arpeggio.h" #include "dom/interval.h" @@ -112,5 +111,3 @@ class LyricNumberHandler std::map m_numberToNo; }; } - -#endif // MUSICXMLTYPES_H diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlvalidation.h b/src/importexport/musicxml/internal/musicxml/musicxmlvalidation.h index d59f95a86476e..e6e180c6a1795 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlvalidation.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlvalidation.h @@ -20,8 +20,7 @@ * along with this program. If not, see . */ -#ifndef MU_MUSICXML_MUSICXMLVALIDATION_H -#define MU_MUSICXML_MUSICXMLVALIDATION_H +#pragma once #include "global/types/bytearray.h" #include "global/types/string.h" @@ -35,5 +34,3 @@ class MusicxmlValidation static engraving::Err validate(const muse::String& name, const muse::ByteArray& data); }; } - -#endif // MU_MUSICXML_MUSICXMLVALIDATION_H diff --git a/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h index f540708599a58..19ef75f3fb7b6 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmlvoicedesc.h @@ -1,5 +1,25 @@ -#ifndef MUSICXMLVOICEDESC_H -#define MUSICXMLVOICEDESC_H +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2024 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once #include "dom/mscore.h" @@ -65,4 +85,3 @@ class VoiceDesc }; typedef std::map VoiceList; } -#endif // MUSICXMLVOICEDESC_H diff --git a/src/importexport/musicxml/internal/musicxmlconfiguration.h b/src/importexport/musicxml/internal/musicxmlconfiguration.h index fbf9c82482a51..c2075c9950056 100644 --- a/src/importexport/musicxml/internal/musicxmlconfiguration.h +++ b/src/importexport/musicxml/internal/musicxmlconfiguration.h @@ -19,8 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef MU_IMPORTEXPORT_MUSICXMLCONFIGURATION_H -#define MU_IMPORTEXPORT_MUSICXMLCONFIGURATION_H +#pragma once #include "../imusicxmlconfiguration.h" @@ -64,5 +63,3 @@ class MusicXmlConfiguration : public IMusicXmlConfiguration std::optional m_inferTextTypeOverride; }; } - -#endif // MU_IMPORTEXPORT_MUSICXMLCONFIGURATION_H diff --git a/src/importexport/musicxml/internal/musicxmlreader.h b/src/importexport/musicxml/internal/musicxmlreader.h index 25bcc04d53c4e..6c4f687532be1 100644 --- a/src/importexport/musicxml/internal/musicxmlreader.h +++ b/src/importexport/musicxml/internal/musicxmlreader.h @@ -19,8 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef MU_IMPORTEXPORT_MUSICXMLREADER_H -#define MU_IMPORTEXPORT_MUSICXMLREADER_H +#pragma once #include "project/inotationreader.h" @@ -31,4 +30,3 @@ class MusicXmlReader : public project::INotationReader muse::Ret read(mu::engraving::MasterScore* score, const muse::io::path_t& path, const Options& options = Options()) override; }; } -#endif // MU_IMPORTEXPORT_MUSICXMLREADER_H diff --git a/src/importexport/musicxml/internal/musicxmlwriter.h b/src/importexport/musicxml/internal/musicxmlwriter.h index 23d22b5a7b05b..5a8bfba88722e 100644 --- a/src/importexport/musicxml/internal/musicxmlwriter.h +++ b/src/importexport/musicxml/internal/musicxmlwriter.h @@ -19,9 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -#ifndef MU_IMPORTEXPORT_MUSICXMLWRITER_H -#define MU_IMPORTEXPORT_MUSICXMLWRITER_H +#pragma once #include "project/inotationwriter.h" @@ -38,5 +36,3 @@ class MusicXmlWriter : public project::INotationWriter const Options& options = Options()) override; }; } - -#endif // MU_IMPORTEXPORT_MUSICXMLWRITER_H diff --git a/src/importexport/musicxml/internal/mxlwriter.h b/src/importexport/musicxml/internal/mxlwriter.h index f456fbe251ced..fa772f38e4140 100644 --- a/src/importexport/musicxml/internal/mxlwriter.h +++ b/src/importexport/musicxml/internal/mxlwriter.h @@ -19,9 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -#ifndef MU_IMPORTEXPORT_MXLWRITER_H -#define MU_IMPORTEXPORT_MXLWRITER_H +#pragma once #include "musicxmlwriter.h" @@ -32,5 +30,3 @@ class MxlWriter : public MusicXmlWriter muse::Ret write(notation::INotationPtr notation, muse::io::IODevice& dstDevice, const Options& options = Options()) override; }; } - -#endif // MU_IMPORTEXPORT_MXLWRITER_H diff --git a/src/importexport/musicxml/musicxmlmodule.h b/src/importexport/musicxml/musicxmlmodule.h index 40a6d19736d4e..7481c1fd6cbd6 100644 --- a/src/importexport/musicxml/musicxmlmodule.h +++ b/src/importexport/musicxml/musicxmlmodule.h @@ -19,8 +19,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef MU_IMPORTEXPORT_MUSICXMLMODULE_H -#define MU_IMPORTEXPORT_MUSICXMLMODULE_H +#pragma once #include @@ -42,5 +41,3 @@ class MusicXmlModule : public muse::modularity::IModuleSetup std::shared_ptr m_configuration; }; } - -#endif // MU_IMPORTEXPORT_MUSICXMLMODULE_H From ed4436d2ebde615ab5d48af2a72903cd62160f9a Mon Sep 17 00:00:00 2001 From: James Mizen Date: Thu, 3 Oct 2024 16:10:45 +0100 Subject: [PATCH 8/8] Move exporter software to types --- .../musicxml/internal/musicxml/importmxmlpass1.h | 9 --------- .../musicxml/internal/musicxml/musicxmltypes.h | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h index ada5b46f0a2ab..acbc98cc720a9 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h @@ -74,15 +74,6 @@ struct MxmlOctaveShiftDesc { : tp(_tp), size(_size), time(_tm), num(-1) {} }; -enum class MusicXMLExporterSoftware : char { - SIBELIUS, - DOLET6, - DOLET8, - FINALE, - NOTEFLIGHT, - OTHER -}; - //--------------------------------------------------------- // MusicXmlPartGroup //--------------------------------------------------------- diff --git a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h index ca7fe7496ce5b..a6d480e6f94ce 100644 --- a/src/importexport/musicxml/internal/musicxml/musicxmltypes.h +++ b/src/importexport/musicxml/internal/musicxml/musicxmltypes.h @@ -27,6 +27,15 @@ namespace mu::engraving { const int MAX_NUMBER_LEVEL = 16; // maximum number of overlapping MusicXML objects +enum class MusicXMLExporterSoftware : char { + SIBELIUS, + DOLET6, + DOLET8, + FINALE, + NOTEFLIGHT, + OTHER +}; + //--------------------------------------------------------- // MxmlStartStop //---------------------------------------------------------