diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp index a65ec0ef7d793..081f8e9cbd98a 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.cpp @@ -239,6 +239,23 @@ void MusicXMLParserPass1::addError(const String& error) } } +void MusicXMLParserPass1::setExporterSoftware(String& exporter) +{ + if (exporter.contains(u"sibelius")) { + if (exporter.contains(u"dolet 6")) { + m_exporterSoftware = MusicXMLExporterSoftware::DOLET6; + } else if (exporter.contains(u"dolet 8")) { + m_exporterSoftware = MusicXMLExporterSoftware::DOLET8; + } else { + m_exporterSoftware = MusicXMLExporterSoftware::SIBELIUS; + } + } else if (exporter.contains(u"finale")) { + m_exporterSoftware = MusicXMLExporterSoftware::FINALE; + } else if (exporter.contains(u"noteflight")) { + m_exporterSoftware = MusicXMLExporterSoftware::NOTEFLIGHT; + } +} + //--------------------------------------------------------- // initPartState //--------------------------------------------------------- @@ -951,7 +968,7 @@ void MusicXMLParserPass1::createMeasuresAndVboxes(Score* score, ++pageNr; if (pageNr == 1) { - vbox = addCreditWords(score, crWords, pageSize, true, m_exporterString.contains(u"sibelius")); + vbox = addCreditWords(score, crWords, pageSize, true, sibOrDolet()); if (i == 0 && vbox) { vbox->setExcludeFromOtherParts(false); } @@ -978,11 +995,22 @@ void MusicXMLParserPass1::createMeasuresAndVboxes(Score* score, // add a footer vbox if the next measure is on a new page or end of score has been reached if ((pageStartMeasureNrs.count(int(i + 1)) || i == (ml.size() - 1)) && pageNr == 1) { - addCreditWords(score, crWords, pageSize, false, m_exporterString.contains(u"sibelius")); + addCreditWords(score, crWords, pageSize, false, sibOrDolet()); } } } +bool MusicXMLParserPass1::sibOrDolet() const +{ + return m_exporterSoftware == MusicXMLExporterSoftware::SIBELIUS || m_exporterSoftware == MusicXMLExporterSoftware::DOLET6 + || m_exporterSoftware == MusicXMLExporterSoftware::DOLET8; +} + +bool MusicXMLParserPass1::dolet() const +{ + return m_exporterSoftware == MusicXMLExporterSoftware::DOLET6 || m_exporterSoftware == MusicXMLExporterSoftware::DOLET8; +} + //--------------------------------------------------------- // determineMeasureStart //--------------------------------------------------------- @@ -1272,7 +1300,8 @@ void MusicXMLParserPass1::identification() // TODO while (m_e.readNextStartElement()) { if (m_e.name() == "software") { - m_exporterString += m_e.readText().toLower(); + String exporterString = m_e.readText().toLower(); + setExporterSoftware(exporterString); } else if (m_e.name() == "supports" && m_e.asciiAttribute("element") == "beam" && m_e.asciiAttribute("type") == "yes") { m_hasBeamingInfo = true; m_e.skipCurrentElement(); @@ -2248,7 +2277,7 @@ void MusicXMLParserPass1::scoreInstrument(const String& partId, const String& cu */ // Finale exports all instrument names as 'Grand Piano' - use part name - if (m_exporterString.contains(u"finale")) { + if (exporterSoftware() == MusicXMLExporterSoftware::FINALE) { instrName = m_parts[partId].getName(); if (instrName.size() <= 1) { instrName = curPartGroupName; diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h index d039879cdee1a..a3f6465a7d4cf 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass1.h @@ -90,6 +90,15 @@ enum class MxmlTupletFlag : char { STOP_CURRENT = 8 }; +enum class MusicXMLExporterSoftware : char { + SIBELIUS, + DOLET6, + DOLET8, + FINALE, + NOTEFLIGHT, + OTHER +}; + typedef muse::Flags MxmlTupletFlags; struct MxmlTupletState { @@ -195,21 +204,24 @@ class MusicXMLParserPass1 void insertAdjustedDuration(Fraction key, Fraction value) { m_adjustedDurations.insert({ key, value }); } std::map& adjustedDurations() { return m_adjustedDurations; } void insertSeenDenominator(int val) { m_seenDenominators.emplace(val); } - String exporterString() const { return m_exporterString; } + MusicXMLExporterSoftware exporterSoftware() const { return m_exporterSoftware; } + bool sibOrDolet() const; + bool dolet() const; private: // functions void addError(const String& error); // Add an error to be shown in the GUI + void setExporterSoftware(String& exporter); // generic pass 1 data muse::XmlStreamReader m_e; - String m_exporterString; // Name of the software which exported the file + MusicXMLExporterSoftware m_exporterSoftware = MusicXMLExporterSoftware::OTHER; // Software which exported the file int m_divs = 0; // Current MusicXML divisions value - std::map m_parts; // Parts data, mapped on part id + std::map m_parts; // Parts data, mapped on part id std::set m_systemStartMeasureNrs; // Measure numbers of measures starting a page std::set m_pageStartMeasureNrs; // Measure numbers of measures starting a page - std::vector m_measureLength; // Length of each measure - std::vector m_measureStart; // Start time of each measure + std::vector m_measureLength; // Length of each measure + std::vector m_measureStart; // Start time of each measure CreditWordsList m_credits; // All credits collected PartMap m_partMap; // TODO merge into MusicXmlPart ?? std::map m_instruments; // instruments for each part, mapped on part id diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp index fa0c364b7776f..81c054e514c9f 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp @@ -2196,10 +2196,11 @@ void MusicXMLParserPass2::part() // Clean up unterminated ties for (auto tie : m_ties) { if (tie.second) { - cleanupUnterminatedTie(tie.second, m_score, m_pass1.exporterString().contains(u"dolet 6")); + cleanupUnterminatedTie(tie.second, m_score, m_pass1.exporterSoftware() == MusicXMLExporterSoftware::DOLET6); m_ties[tie.first] = nullptr; } } + m_ties.clear(); if (m_hasDrumset) { @@ -3213,7 +3214,7 @@ void MusicXMLParserDirection::direction(const String& partId, bool isVocalStaff = m_pass1.isVocalStaff(partId); bool isPercussionStaff = m_pass1.isPercussionStaff(partId); bool isExpressionText = false; - bool delayOttava = m_pass1.exporterString().contains(u"sibelius"); + bool delayOttava = m_pass1.exporterSoftware() == MusicXMLExporterSoftware::SIBELIUS; m_systemDirection = m_e.attribute("system") == "only-top"; //LOGD("direction track %d", track); std::vector starts; @@ -3520,19 +3521,23 @@ void MusicXMLParserDirection::direction(const String& partId, delete desc.sp; } else { if (spdesc.isStarted) { + // Adjustments to ottavas by the offset value are unwanted + const Fraction spTick = spdesc.sp && spdesc.sp->isOttava() ? tick : tick + m_offset; if (spdesc.sp && spdesc.sp->isOttava() && delayOttava) { // Sibelius writes ottava ends 1 note too early m_pass2.setDelayedOttava(spdesc.sp); m_pass2.delayedOttava()->setTrack2(m_track); - m_pass2.delayedOttava()->setTick2(tick + m_offset); + m_pass2.delayedOttava()->setTick2(spTick); + // need to set tick again later m_pass2.clearSpanner(desc); } else { - handleSpannerStop(spdesc.sp, m_track, tick + m_offset, spanners); + handleSpannerStop(spdesc.sp, m_track, spTick, spanners); m_pass2.clearSpanner(desc); } } else { spdesc.sp = desc.sp; - spdesc.tick2 = tick + m_offset; + const Fraction spTick = spdesc.sp && spdesc.sp->isOttava() ? tick : tick + m_offset; + spdesc.tick2 = spTick; spdesc.track2 = m_track; spdesc.isStopped = true; } @@ -3876,7 +3881,7 @@ void MusicXMLParserDirection::otherDirection() // TODO: Multiple sets of maps for exporters other than Dolet 6/Sibelius // TODO: Add more symbols from Sibelius std::map otherDirectionStrings; - if (m_pass1.exporterString().contains(u"dolet")) { + if (m_pass1.dolet()) { otherDirectionStrings = { { String(u"To Coda"), String(u"To Coda") }, { String(u"Segno"), String(u"segno") }, @@ -4114,7 +4119,7 @@ void MusicXMLParserDirection::textToDynamic(String& text) } String simplifiedText = MScoreTextToMXML::toPlainText(text).simplified(); // Correct finale's incorrect dynamic export - if (m_pass1.exporterString().contains(u"finale")) { + if (m_pass1.exporterSoftware() == MusicXMLExporterSoftware::FINALE) { static const std::map finaleDynamicSubs = { { u"π", u"pp" }, { u"P", u"mp" }, { u"F", u"mf" }, { u"ƒ", u"ff" }, { u"Ï", u"fff" }, { u"S", u"sf" }, { u"ß", u"sfz" }, @@ -4515,7 +4520,7 @@ void MusicXMLParserDirection::handleTempo(String& wordsString) }; MetronomeTextMap textMap; - if (m_pass1.exporterString().contains(u"sibelius")) { + if (m_pass1.sibOrDolet()) { textMap = sibeliusSyms; } else if (m_fontFamily == u"MetTimes Plain") { textMap = metTimesSyms; @@ -4822,19 +4827,22 @@ void MusicXMLParserDirection::octaveShift(const String& type, const int number, } else { Ottava* o = spdesc.isStopped ? toOttava(spdesc.sp) : Factory::createOttava(m_score->dummy()); - // if (placement.empty()) placement = "above"; // TODO ? set default - - if (type == u"down" && ottavasize == 8) { - o->setOttavaType(OttavaType::OTTAVA_8VA); - } - if (type == u"down" && ottavasize == 15) { - o->setOttavaType(OttavaType::OTTAVA_15MA); - } - if (type == u"up" && ottavasize == 8) { - o->setOttavaType(OttavaType::OTTAVA_8VB); + if (type == u"down") { + m_placement = m_placement.empty() ? u"above" : m_placement; + if (ottavasize == 8) { + o->setOttavaType(OttavaType::OTTAVA_8VA); + } else if (ottavasize == 15) { + o->setOttavaType(OttavaType::OTTAVA_15MA); + } } - if (type == u"up" && ottavasize == 15) { - o->setOttavaType(OttavaType::OTTAVA_15MB); + + if (type == u"up") { + m_placement = m_placement.empty() ? u"below" : m_placement; + if (ottavasize == 8) { + o->setOttavaType(OttavaType::OTTAVA_8VB); + } else if (ottavasize == 15) { + o->setOttavaType(OttavaType::OTTAVA_15MB); + } } const Color color = Color::fromString(m_e.asciiAttribute("color").ascii()); @@ -6550,7 +6558,7 @@ Note* MusicXMLParserPass2::note(const String& partId, String noteheadValue = m_e.readText(); if (noteheadValue == "none") { hasHead = false; - } else if (noteheadValue == "named" && m_pass1.exporterString().contains(u"noteflight")) { + } else if (noteheadValue == "named" && m_pass1.exporterSoftware() == MusicXMLExporterSoftware::NOTEFLIGHT) { headScheme = NoteHeadScheme::HEAD_PITCHNAME; } else { headGroup = convertNotehead(noteheadValue); @@ -6847,7 +6855,8 @@ Note* MusicXMLParserPass2::note(const String& partId, Notation notation = Notation(u"tied"); const String type2 = u"type"; notation.addAttribute(type2, tieType); - addTie(notation, m_score, note, cr->track(), m_ties, m_logger, &m_e, m_pass1.exporterString().contains(u"dolet 6")); + addTie(notation, m_score, note, cr->track(), m_ties, m_logger, &m_e, + m_pass1.exporterSoftware() == MusicXMLExporterSoftware::DOLET6); } } @@ -7449,7 +7458,7 @@ void MusicXMLParserPass2::harmony(const String& partId, Measure* measure, const const HarmonyDesc newHarmonyDesc(track, ha, fd); bool insert = true; - if (m_pass1.exporterString().contains(u"dolet")) { + if (m_pass1.sibOrDolet()) { const int ticks = (sTime + offset).ticks(); for (auto itr = harmonyMap.begin(); itr != harmonyMap.end(); itr++) { if (itr->first != ticks) { @@ -8689,7 +8698,8 @@ void MusicXMLParserNotations::addToScore(ChordRest* const cr, Note* const note, } else if (note && (notation.name() == "glissando" || notation.name() == "slide")) { addGlissandoSlide(notation, note, glissandi, spanners, m_logger, &m_e); } else if (note && notation.name() == "tied") { - addTie(notation, m_score, note, cr->track(), ties, m_logger, &m_e, m_pass1.exporterString().contains(u"dolet 6")); + addTie(notation, m_score, note, cr->track(), ties, m_logger, &m_e, + m_pass1.exporterSoftware() == MusicXMLExporterSoftware::DOLET6); } else if (note && notation.parent() == "technical") { addTechnical(notation, note); } else { diff --git a/src/importexport/musicxml/tests/data/testDoletOttavas.xml b/src/importexport/musicxml/tests/data/testDoletOttavas.xml new file mode 100644 index 0000000000000..9cb4976ee2d21 Binary files /dev/null and b/src/importexport/musicxml/tests/data/testDoletOttavas.xml differ diff --git a/src/importexport/musicxml/tests/data/testDoletOttavas_ref.mscx b/src/importexport/musicxml/tests/data/testDoletOttavas_ref.mscx new file mode 100644 index 0000000000000..92af5f061eb6c --- /dev/null +++ b/src/importexport/musicxml/tests/data/testDoletOttavas_ref.mscx @@ -0,0 +1,285 @@ + + + + 480 + + 1 + 1 + 1 + 0 + + + + + + + + + + + + + + stdNormal + + 3 + + [Unnamed (treble staff)] + + + 21 + 108 + 21 + 108 + keyboard.piano.grand + F + 0 + + 100 + 95 + + + 100 + 33 + + + 100 + 50 + + + 100 + 67 + + + 100 + 100 + + + 120 + 67 + + + 150 + 100 + + + 150 + 50 + + + 120 + 50 + + + 120 + 100 + + + + + + + + + + 10 + + + 5 + + + + + G + G + 1 + + + 4 + 4 + + + + 8va + + + + 1/2 + + + + + eighth + up + + 65 + 13 + + + + 1 + quarter + up + + 65 + 13 + + + + + + -1/2 + + + + + + 8vb + + + + 1 + + + + + half + up + + + + + + + 1 + -1/2 + + + + 65 + 13 + + + + + + + + quarter + up + + + + + -1 + 1/2 + + + + 65 + 13 + + + + quarter + up + + 65 + 13 + + + + + + -1 + + + + + half + + + end + + + + + + diff --git a/src/importexport/musicxml/tests/data/testSibOttavas.xml b/src/importexport/musicxml/tests/data/testSibOttavas.xml index 9cb4976ee2d21..2feec15ed9192 100644 Binary files a/src/importexport/musicxml/tests/data/testSibOttavas.xml and b/src/importexport/musicxml/tests/data/testSibOttavas.xml differ diff --git a/src/importexport/musicxml/tests/data/testSibOttavas_ref.mscx b/src/importexport/musicxml/tests/data/testSibOttavas_ref.mscx index 92af5f061eb6c..19a25ec046b74 100644 --- a/src/importexport/musicxml/tests/data/testSibOttavas_ref.mscx +++ b/src/importexport/musicxml/tests/data/testSibOttavas_ref.mscx @@ -3,82 +3,138 @@ 480 1 1 @@ -101,19 +157,13 @@ 3 - [Unnamed (treble staff)] + - 21 - 108 - 21 - 108 keyboard.piano.grand - F - 0 100 - 95 + 100 100 @@ -160,6 +210,9 @@ 10 + + page + 5 @@ -186,6 +239,7 @@ + no eighth up @@ -198,7 +252,7 @@ quarter up - 65 + 77 13 @@ -225,6 +279,7 @@ + down @@ -261,7 +316,7 @@ quarter up - 65 + 53 13 diff --git a/src/importexport/musicxml/tests/musicxml_tests.cpp b/src/importexport/musicxml/tests/musicxml_tests.cpp index 37a648477c034..917816be62fd8 100644 --- a/src/importexport/musicxml/tests/musicxml_tests.cpp +++ b/src/importexport/musicxml/tests/musicxml_tests.cpp @@ -550,6 +550,9 @@ TEST_F(Musicxml_Tests, divisionsDefinedTooLate2) { TEST_F(Musicxml_Tests, divisionsDuration) { mxmlIoTest("testDivisionsDuration"); } +TEST_F(Musicxml_Tests, doletOttavas) { + mxmlImportTestRef("testDoletOttavas"); +} TEST_F(Musicxml_Tests, doubleClefError) { mxmlIoTestRef("testDoubleClefError"); }