From d938cdc52bef40f1680b9830d0164d7ee0ced26a Mon Sep 17 00:00:00 2001 From: rettinghaus Date: Sat, 28 Dec 2024 16:38:32 +0100 Subject: [PATCH] [MusicXML] Add full support for holes element Backport of #25932 --- importexport/musicxml/exportxml.cpp | 25 +++++ importexport/musicxml/importmxmlpass2.cpp | 68 +++++++++++- importexport/musicxml/importmxmlpass2.h | 1 + mtest/musicxml/io/testHoles.xml | 120 ++++++++++++++++++++++ mtest/musicxml/io/tst_mxml_io.cpp | 1 + 5 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 mtest/musicxml/io/testHoles.xml diff --git a/importexport/musicxml/exportxml.cpp b/importexport/musicxml/exportxml.cpp index e709d477e5327..3fd31f13703f0 100644 --- a/importexport/musicxml/exportxml.cpp +++ b/importexport/musicxml/exportxml.cpp @@ -3097,6 +3097,12 @@ static QString symIdToTechn(const SymId sid) case SymId::brassHarmonMuteStemOpen: return "harmon-mute"; break; + case SymId::windClosedHole: + case SymId::windHalfClosedHole1: + case SymId::windHalfClosedHole2: + case SymId::windHalfClosedHole3: + case SymId::windOpenHole: + return "hole"; case SymId::guitarGolpe: return "golpe"; break; @@ -3352,6 +3358,25 @@ void ExportMusicXml::chordAttributes(Chord* chord, Notations& notations, Technic _xml.tag("harmon-closed" + location, harmonClosedValue); _xml.etag(); } + else if (mxmlTechn.startsWith(u"hole")) { + _xml.stag(mxmlTechn); + QString location = {}; + QString holeClosedValue; + switch (sid) { + case SymId::windClosedHole: + holeClosedValue = "yes"; + break; + case SymId::windOpenHole: + holeClosedValue = "no"; + break; + default: + holeClosedValue = "half"; + location = QString(" location=\"%1\"").arg(sid == SymId::windHalfClosedHole1 ? "right" : "bottom"); + break; + } + _xml.tag("hole-closed" + location, holeClosedValue); + _xml.etag(); + } else _xml.tagE(mxmlTechn); } diff --git a/importexport/musicxml/importmxmlpass2.cpp b/importexport/musicxml/importmxmlpass2.cpp index 287611c09bc8c..e737375769372 100644 --- a/importexport/musicxml/importmxmlpass2.cpp +++ b/importexport/musicxml/importmxmlpass2.cpp @@ -1277,7 +1277,7 @@ static bool convertArticulationToSymId(const QString& mxmlName, SymId& id) map["stopped"] = SymId::brassMuteClosed; map["snap-pizzicato"] = SymId::pluckedSnapPizzicatoAbove; map["heal"] = SymId::keyboardPedalHeel1 ; - map["toe"] = SymId::keyboardPedalToe1 ; + map["toe"] = SymId::keyboardPedalToe2 ; map["fingernails"] = SymId::pluckedWithFingernails ; map["brass-bend"] = SymId::brassBend ; map["flip"] = SymId::brassFlip; @@ -7600,6 +7600,17 @@ void MusicXMLParserNotations::articulations() _notations.push_back(artic); _e.skipCurrentElement(); // skip but don't log } + else if (_e.name() == "other-articulation") { + const QString smufl = _e.attributes().value("smufl").toString(); + + if (!smufl.isEmpty()) { + SymId id = SymId::noSym; + Notation artic = Notation::notationWithAttributes(_e.name().toString(), + _e.attributes(), "articulations", id); + _notations.push_back(artic); + } + _e.skipCurrentElement(); // skip but don't log + } else { skipLogCurrElem(); } @@ -7619,7 +7630,7 @@ void MusicXMLParserNotations::ornaments() bool trillMark = false; // while (_e.readNextStartElement()) { - SymId id { SymId::noSym }; + SymId id = SymId::noSym; if (convertArticulationToSymId(_e.name().toString(), id)) { Notation notation = Notation::notationWithAttributes(_e.name().toString(), _e.attributes(), "articulations", id); @@ -7687,7 +7698,7 @@ void MusicXMLParserNotations::ornaments() void MusicXMLParserNotations::technical() { while (_e.readNextStartElement()) { - SymId id { SymId::noSym }; + SymId id = SymId::noSym; if (convertArticulationToSymId(_e.name().toString(), id)) { Notation notation = Notation::notationWithAttributes(_e.name().toString(), _e.attributes(), "technical", id); @@ -7704,6 +7715,8 @@ void MusicXMLParserNotations::technical() harmonic(); else if (_e.name() == "harmon-mute") harmonMute(); + else if (_e.name() == "hole") + hole(); else if (_e.name() == "other-technical") otherTechnical(); else @@ -7714,7 +7727,18 @@ void MusicXMLParserNotations::technical() void MusicXMLParserNotations::otherTechnical() { - QString text = _e.readElementText(); + const QString smufl = _e.attributes().value("smufl").toString(); + + if (!smufl.isEmpty()) { + SymId id = SymId::noSym; + Notation notation = Notation::notationWithAttributes(_e.name().toString(), + _e.attributes(), "technical", id); + _notations.push_back(notation); + _e.skipCurrentElement(); + return; + } + + const QString text = _e.readElementText(); if (text == "z") { // Buzz roll @@ -7789,6 +7813,42 @@ void MusicXMLParserNotations::harmonMute() _notations.push_back(Notation::notationWithAttributes("harmon-closed", _e.attributes(), "technical", mute)); } +//--------------------------------------------------------- +// hole +//--------------------------------------------------------- + +/** + Parse the /score-partwise/part/measure/note/notations/technical/hole node. + */ + +void MusicXMLParserNotations::hole() + { + SymId hole = SymId::noSym; + while (_e.readNextStartElement()) { + if (_e.name() == "hole-closed") { + const QString location = _e.attributes().value("location").toString(); + const QString value = _e.readElementText(); + if (value == "yes") + hole = SymId::windClosedHole; + else if (value == "no") + hole = SymId::windOpenHole; + else if (value == "half") { + if (location == "bottom") + hole = SymId::windHalfClosedHole2; + else if (location == "right") + hole = SymId::windHalfClosedHole1; + else { + _logger->logError(QString("unsupported hole-closed location '%1'").arg(location), &_e); + hole = SymId::windHalfClosedHole3; + } + } + } + else + _e.skipCurrentElement(); + } + _notations.push_back(Notation::notationWithAttributes("hole-closed", _e.attributes(), "technical", hole)); + } + //--------------------------------------------------------- // addTechnical //--------------------------------------------------------- diff --git a/importexport/musicxml/importmxmlpass2.h b/importexport/musicxml/importmxmlpass2.h index b323e5ebe7b16..fbf4b1c073ff5 100644 --- a/importexport/musicxml/importmxmlpass2.h +++ b/importexport/musicxml/importmxmlpass2.h @@ -243,6 +243,7 @@ class MusicXMLParserNotations { void addTechnical(const Notation& notation, Note* note); void harmonic(); void harmonMute(); + void hole(); void articulations(); void dynamics(); void fermata(); diff --git a/mtest/musicxml/io/testHoles.xml b/mtest/musicxml/io/testHoles.xml new file mode 100644 index 0000000000000..92459968952c9 --- /dev/null +++ b/mtest/musicxml/io/testHoles.xml @@ -0,0 +1,120 @@ + + + + + Holes test + + + Klaus Rettinghaus + + MuseScore 0.7.0 + 2007-09-10 + + + + + + + + + + Flute + Fl. + + Flute + + + + 1 + 58 + 78.7402 + 0 + + + + + + + 1 + + 0 + + + G + 2 + + + + + C + 5 + + 1 + 1 + quarter + down + + + + no + + + + + + + C + 5 + + 1 + 1 + quarter + down + + + + yes + + + + + + + C + 5 + + 1 + 1 + quarter + down + + + + half + + + + + + + C + 5 + + 1 + 1 + quarter + down + + + + half + + + + + + light-heavy + + + + diff --git a/mtest/musicxml/io/tst_mxml_io.cpp b/mtest/musicxml/io/tst_mxml_io.cpp index a5de697cdc573..686f3ac98a550 100644 --- a/mtest/musicxml/io/tst_mxml_io.cpp +++ b/mtest/musicxml/io/tst_mxml_io.cpp @@ -164,6 +164,7 @@ private slots: void hello() { mxmlIoTest("testHello"); } void helloReadCompr() { mxmlReadTestCompr("testHello"); } void helloReadWriteCompr() { mxmlReadWriteTestCompr("testHello"); } + void holes() { mxmlIoTest("testHoles"); } void implicitMeasure1() { mxmlIoTest("testImplicitMeasure1"); } void incompleteTuplet() { mxmlIoTestRef("testIncompleteTuplet"); } void incorrectStaffNumber1() { mxmlIoTestRef("testIncorrectStaffNumber1"); }