From 0e28d030ff55d76af8d7b9026d406600fa833735 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Mon, 16 Sep 2024 16:28:56 +0100 Subject: [PATCH 1/4] Add exporter software enum --- .../internal/musicxml/importmxmlpass1.cpp | 37 +++++++++++++++++-- .../internal/musicxml/importmxmlpass1.h | 22 ++++++++--- .../internal/musicxml/importmxmlpass2.cpp | 21 ++++++----- 3 files changed, 62 insertions(+), 18 deletions(-) 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..4cadf07fd78aa 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.dolet() || m_pass1.exporterSoftware() == MusicXMLExporterSoftware::FINALE; m_systemDirection = m_e.attribute("system") == "only-top"; //LOGD("direction track %d", track); std::vector starts; @@ -3876,7 +3877,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 +4115,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 +4516,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; @@ -6550,7 +6551,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 +6848,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 +7451,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 +8691,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 { From 2cc0b9727321847b2a7e5d51b0fa5ef90ed154d7 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Mon, 16 Sep 2024 16:29:15 +0100 Subject: [PATCH 2/4] Correct placement of ottavas below the stave --- .../internal/musicxml/importmxmlpass2.cpp | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp index 4cadf07fd78aa..15b6f3b9c4358 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp @@ -4823,19 +4823,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()); From 884525ba4910a1aa7b874ebb5ea7d18356f7edf8 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Tue, 17 Sep 2024 11:08:24 +0100 Subject: [PATCH 3/4] Correct dolet and finale ottavas These ottavas need their offsets removed --- .../musicxml/internal/musicxml/importmxmlpass2.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp index 15b6f3b9c4358..81c054e514c9f 100644 --- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp +++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp @@ -3214,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.dolet() || m_pass1.exporterSoftware() == MusicXMLExporterSoftware::FINALE; + bool delayOttava = m_pass1.exporterSoftware() == MusicXMLExporterSoftware::SIBELIUS; m_systemDirection = m_e.attribute("system") == "only-top"; //LOGD("direction track %d", track); std::vector starts; @@ -3521,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; } From 43036c3a99727e4e5e54876d52142e33f039d2c4 Mon Sep 17 00:00:00 2001 From: James Mizen Date: Tue, 17 Sep 2024 11:44:56 +0100 Subject: [PATCH 4/4] Separate dolet and sibelius ottava tests --- .../musicxml/tests/data/testDoletOttavas.xml | Bin 0 -> 10908 bytes .../tests/data/testDoletOttavas_ref.mscx | 285 ++++++++++++++++++ .../musicxml/tests/data/testSibOttavas.xml | Bin 10908 -> 7160 bytes .../tests/data/testSibOttavas_ref.mscx | 205 ++++++++----- .../musicxml/tests/musicxml_tests.cpp | 3 + 5 files changed, 418 insertions(+), 75 deletions(-) create mode 100644 src/importexport/musicxml/tests/data/testDoletOttavas.xml create mode 100644 src/importexport/musicxml/tests/data/testDoletOttavas_ref.mscx diff --git a/src/importexport/musicxml/tests/data/testDoletOttavas.xml b/src/importexport/musicxml/tests/data/testDoletOttavas.xml new file mode 100644 index 0000000000000000000000000000000000000000..9cb4976ee2d21fe1a433d0786c7cfbaa3f633079 GIT binary patch literal 10908 zcmd^_Yj0ac5Qg__k@yehQ$gY+P12+-a$Er^2vpjt7YT&;ki@w`;so1i8h$+RK0Ds* z?m2sG=T-_RYVDkzot^7Dvvcy-pI!IZy>mzI&~@Bfx8sIxb}#SjyP3rQ9m!WhD1Yu9(#GZ zA+8&u`oW!gO}5>S`unu_;klhjiZksyy5MHETJY!Y ziKZ{=zD`MNc%YG{Fop{;CUVALDBd?pS$%IYWdRx{B z_9w#0i?KV`PK)z%eU9{WG4X}jW;Xw^;+WhSc{zytI(N&W*}Sm|-0de9A&K=)JefNywyUAac$1b$$3!<)JXZbmjDtsWO4J6MYzZqKl; zkAhv<;Z&9&Vgd*AHd#p~fs=8CwIe+}^lQWx+Jd(|_rS+%+iDuC?7=_#e&2mXJW}UR z1HENcS(>imY*X@qxQF_7uIMLPa(N@oz9njp7Ay=_r8&4IJ>VWuGawZ41nO1tLfnt-mxWHN`dpgG(ncAjK_@cI z>@k~TJ8jN$Ea^|?kg!ab^D;|7c{B&dY1EM~5fwd^+A+_8l&TzERH2yh(_%2EYdh1b zP@Cs0OWIbWe$)MGT;}nmWnQXZyWDk4&|!s6YFjZ&wo&1lJe!nJlddA&Vkx&@bI+1^ zKU4-hP!4d$y!*mgO}W}U{#cz^=n0w~$19z@sSMq9-wTPbKofyq$R0!0VZ%VfuXS}6 zs~6p+#lfbcl&VagQ&r85L1MgY_ze0*M5-eR`>95~iDgQAa=a{2uDUj^wpdY1Hv`w68jXme>shUs4o@7!Ec~ zy?a?t#nXty6MxPqVI+T0O2f~M2ai!L(UZ7anM zcR%h};TzAuEjrC}-=jZQ^xCZsjP1M66At-pr7JYF8})pgU+WY)S-P-i`aIL;OL3su z4P?c5D?l&qPNlBd`a|yg^Z{1=Qj3)C%^lJ8y>>4Y)%Yt#cX|}#H}JL~M&tdVl7w6C zS4p$2GwpW7gretu)}&#eAMCdy{vG$JD3~p5d8}bl5vtT@?iXJ#n`h;+q|#f*3dd7_ zf_X00q|%h5t`GI9w6nU9wC5$6MH4O5&XL)`srdF~jo+mAximla*6a&AP9z7OJ=S;2 z7AWy;`RtTiJl#45o86F&rMLbp4OK-1*ZeBNoE5%vG%S z3cp1f^q4ML)S|9)h?+pbdU#gP0w!jae%9an52r>6gM7KY)7%4_=x;`{Gg;G-{jfN* zrJ>KXdTe85I%+Z@oD^h>yxnbmTLA!=C@vMo1)q@W=&B7=hA8}sbORux!9cPEYk*?(-?x} z&r&|@I1)ZlA$$6^sp>=>Muzx2-N8!fZ0>zllbbS;h1digZR(OfNx36WSgW+dW}Nk1 z=}NjA*~(IMRdMYW-jt4>Y|rEEw_fk~d&(QhLU&K!&sp*Jl#-RwX*{158BF*NJc7|7 z)aUCla=^Gq(Pj^1KK69n{H*`n7gypV?-l!^JChBVfQBBG{^X&5Qo_=XkD1t2Rg<2N z$sk;iklaBFJNuo-;Yv5bv_CVnz&1_I%X*b*Q@?hliFMrRQ>;NBF_uM`0;WD=4XoS| zMNjXRI3xaEr%Y`c?&$fmbO*fJ&c)wUEVGlnO<9rEElZkfIbV7R-o@CghI7&({#y}Eyz`Bp!nNm0&7!=*F>xapRWhNxmbcp0Q=!xE-S*TWJ+NTwjX`Jxl#EgtiD z>(Y5&8Q1ctZ(1j{-#>-qStN8ge|v)PNZ*VMp630#`R~KH$6||Iy5=pFj7noJSeOc*d$g^i z;1u)Up + + + 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 9cb4976ee2d21fe1a433d0786c7cfbaa3f633079..2feec15ed9192f53f56cb1d84989f0e11115baac 100644 GIT binary patch literal 7160 zcmdT}OLN;c5WeSEAnd`HP&8%BuYs7E#7V}TxN)0graeL=C?VmiAZS_r_1y(P5>(`b zvfa!yhlp4#fPG>2TbRDQ%QJGzWi85L?uC;>kMJTc6HzSZ2cJK^_0JAStyqz;tStEa zpePT>%jg+>e)-|`r@wCAkUB0U_bVpVTGX7}e17%*>NWBFAozpBRKg?=g3C{r3aq5d`b?dXgg>2sJ6?GDuWX2kqVy79`wj)1bibvDK>9K!!?uVx|7|!&} zTt>gLoY&-9{KE^&2Sm+Lm#JEVA)=3B!86g+WO_6S!%0YkZfeMQDI|{-;dfOj)eBOT zilnm4$z_>wwY{pBiPy0dl~$@dDi~Pa)xhC4r#BTIR}-G`94s{VDk%!(5w)rK+}rTl zBeyJT+HaBdha!v!6qy<)B?o}Oy z)1xCA*a2=7I6F}^iJ!4e*(kHKCakzvu2dAx&QB=Z6=1Oxy|E^&MWR-AbBE9~kARYeeF8y7af^VIGN7C58b2*g?`*R5d%IN!wI6MsTUni&W}#V@Mq|7vS1R= z^gy23$ta0doZW8vb)$~rOD^{~f)`l0_E@my38a~^Hibl+m31Tc(ONYXq-?T~EdI*X z=%YIRF=k3wy@KRC-WB`~atsvH(LOI@#cnyNS0dFqjC6U$0XOU+SDiJI-94<=@`GxB(^bMs*YBs&+%mw}j9D&J)NK-IPgh!;C;R zH1gB305Aam`6+`svYC4yDnJw;0UQ}JiU3gVd~$X?o!JtKpeWg~^!G-@Uu%GO!?hvg zf|G7-wvi&+aY?%jl@ zEE%o>@K&VD0xZ!3oi_CjIP@V4SCC(|)d{sDUqSHWvcb45Fm|WGQga$xEF`}b1~Im$ z4v78fE_txkcq#7P!opvu)=D;r(yV3tD{CA=`#ED4kanAdvJ!eLq-t2^_p`=U{Y)kx z5y?&Tju%|Aj9gz`UeTak*UswQ`bF9ouXM#EH-Z&qzoK&!$kgt#bvj}mY$=B1mWtj5 zArTDpfucR+MhJcG`}4s+-EmxyoA3p><_vTA1qsQ3W$ybr=yEepiY8xh2{*|jx(d&| z+3`smfC!8UqF%&P1{>Qrv(>&Y>}cs7SOLi{nwr7=cgM!4gbt9$E+pchi2=$X6mixz z_z2Gc@9df|A<=>rSA1$M;^Hbew54`n2Q~PHXt!#@i&zOwp+*6g24U=%u_VpQghx61Qc4gLzpZGnmHkuz z)9>3A8VCUQ$22e>c26Am*ekR%W?*;5{o1I&`UwCZ;@Er{mKZ$l2HHI;)nZvh?_e$d z+M^MDMTlMT*B*%5^#`V@pK!%QR=Y=Nqx~Cb{!l<1^0c8)LkfFHD3obh101i*rT{=ulmJe_ zBbsP6+iA(C2$m2Kl|938I@E^8M$6F|q@^c{mcJXQs~j?<6D|q7KD8GNd(fX6I(|4E z#EyUV_&m})D0Kd_z&JWO-ZwDdm1sm7nT1dvwgN+UG5S3ZVp_uEUhCP1=9d-A(m}Y4 z3=x9aRi`^!@&T_1p4e1xg)5~1h^x8g9$Z;?4d?{MZpm41x+lC~9SYHdQBZI?4hljj zS}1sYJ~cl3VU2)RXwO?e&bbB_4Qqt!T+EM;_3f@~@I>xK@gC$|2ZmU?KB^WyMLzjI z79)UTKP@aDnpz$aetkC3d4_lJ15n$vFT|49eQoP9<*vN>P#t!p$L|6y`>twz10!GK pmzI&~@Bfx8sIxb}#SjyP3rQ9m!WhD1Yu9(#GZ zA+8&u`oW!gO}5>S`unu_;klhjiZksyy5MHETJY!Y ziKZ{=zD`MNc%YG{Fop{;CUVALDBd?pS$%IYWdRx{B z_9w#0i?KV`PK)z%eU9{WG4X}jW;Xw^;+WhSc{zytI(N&W*}Sm|-0de9A&K=)JefNywyUAac$1b$$3!<)JXZbmjDtsWO4J6MYzZqKl; zkAhv<;Z&9&Vgd*AHd#p~fs=8CwIe+}^lQWx+Jd(|_rS+%+iDuC?7=_#e&2mXJW}UR z1HENcS(>imY*X@qxQF_7uIMLPa(N@oz9njp7Ay=_r8&4IJ>VWuGawZ41nO1tLfnt-mxWHN`dpgG(ncAjK_@cI z>@k~TJ8jN$Ea^|?kg!ab^D;|7c{B&dY1EM~5fwd^+A+_8l&TzERH2yh(_%2EYdh1b zP@Cs0OWIbWe$)MGT;}nmWnQXZyWDk4&|!s6YFjZ&wo&1lJe!nJlddA&Vkx&@bI+1^ zKU4-hP!4d$y!*mgO}W}U{#cz^=n0w~$19z@sSMq9-wTPbKofyq$R0!0VZ%VfuXS}6 zs~6p+#lfbcl&VagQ&r85L1MgY_ze0*M5-eR`>95~iDgQAa=a{2uDUj^wpdY1Hv`w68jXme>shUs4o@7!Ec~ zy?a?t#nXty6MxPqVI+T0O2f~M2ai!L(UZ7anM zcR%h};TzAuEjrC}-=jZQ^xCZsjP1M66At-pr7JYF8})pgU+WY)S-P-i`aIL;OL3su z4P?c5D?l&qPNlBd`a|yg^Z{1=Qj3)C%^lJ8y>>4Y)%Yt#cX|}#H}JL~M&tdVl7w6C zS4p$2GwpW7gretu)}&#eAMCdy{vG$JD3~p5d8}bl5vtT@?iXJ#n`h;+q|#f*3dd7_ zf_X00q|%h5t`GI9w6nU9wC5$6MH4O5&XL)`srdF~jo+mAximla*6a&AP9z7OJ=S;2 z7AWy;`RtTiJl#45o86F&rMLbp4OK-1*ZeBNoE5%vG%S z3cp1f^q4ML)S|9)h?+pbdU#gP0w!jae%9an52r>6gM7KY)7%4_=x;`{Gg;G-{jfN* zrJ>KXdTe85I%+Z@oD^h>yxnbmTLA!=C@vMo1)q@W=&B7=hA8}sbORux!9cPEYk*?(-?x} z&r&|@I1)ZlA$$6^sp>=>Muzx2-N8!fZ0>zllbbS;h1digZR(OfNx36WSgW+dW}Nk1 z=}NjA*~(IMRdMYW-jt4>Y|rEEw_fk~d&(QhLU&K!&sp*Jl#-RwX*{158BF*NJc7|7 z)aUCla=^Gq(Pj^1KK69n{H*`n7gypV?-l!^JChBVfQBBG{^X&5Qo_=XkD1t2Rg<2N z$sk;iklaBFJNuo-;Yv5bv_CVnz&1_I%X*b*Q@?hliFMrRQ>;NBF_uM`0;WD=4XoS| zMNjXRI3xaEr%Y`c?&$fmbO*fJ&c)wUEVGlnO<9rEElZkfIbV7R-o@CghI7&({#y}Eyz`Bp!nNm0&7!=*F>xapRWhNxmbcp0Q=!xE-S*TWJ+NTwjX`Jxl#EgtiD z>(Y5&8Q1ctZ(1j{-#>-qStN8ge|v)PNZ*VMp630#`R~KH$6||Iy5=pFj7noJSeOc*d$g^i z;1u)Up 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"); }