From be3415074271688a8818e7b465c0f71a94dbeff2 Mon Sep 17 00:00:00 2001 From: rettinghaus Date: Tue, 9 Jun 2026 16:40:27 +0200 Subject: [PATCH 1/5] import coda and sgeno on barlines (cherry picked from commit 3a9133ea7be089b187926bf71fd510eb201c3195) --- .../internal/import/importmusicxmlpass2.cpp | 67 ++++++++++++++++--- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp b/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp index 406d1364f9755..c4dc1ea348ce9 100644 --- a/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp +++ b/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp @@ -5743,18 +5743,21 @@ Regular barlines should not be added at the start or end of a measure, as that c void MusicXmlParserPass2::barline(const String& partId, Measure* measure, const Fraction& tick) { - String loc = m_e.attribute("location"); + AsciiStringView loc = m_e.asciiAttribute("location"); if (loc.empty()) { - loc = u"right"; + loc = "right"; } // Place barline in correct place Fraction locTick = tick; - if (loc == u"left") { + if (loc == "left") { locTick = measure->tick(); - } else if (loc == u"right") { + } else if (loc == "right") { locTick = measure->endTick(); } + const String codaLabel = m_e.attribute("coda"); + const String segnoLabel = m_e.attribute("segno"); + String barStyle; Color barlineColor; String endingNumber; @@ -5769,12 +5772,50 @@ void MusicXmlParserPass2::barline(const String& partId, Measure* measure, const if (m_e.name() == "bar-style") { barlineColor = Color::fromString(m_e.asciiAttribute("color").ascii()); barStyle = m_e.readText(); - } else if (m_e.name() == "ending") { - endingNumber = m_e.attribute("number"); - endingType = m_e.attribute("type"); - endingColor = Color::fromString(m_e.asciiAttribute("color").ascii()); - printEnding = m_e.asciiAttribute("print-object") != "no"; - endingText = m_e.readText(); + } else if (m_e.name() == "segno") { + const Color segnoColor = Color::fromString(m_e.asciiAttribute("color").ascii()); + const AsciiStringView segnoSymbol = m_e.asciiAttribute("smufl"); + Measure* markedMeasure = (loc == "left") ? measure : measure->nextMeasure(); + if (markedMeasure == nullptr) { + m_logger->logError(u"coda or segno marker cannot be placed at the end of the score", &m_e); + m_e.skipCurrentElement(); + continue; + } + Marker* m = Factory::createMarker(markedMeasure); + m->setMarkerType(MarkerType::SEGNO); + if (!segnoSymbol.empty()) { + m->setXmlText(u"" + String::fromAscii(segnoSymbol.ascii()) + u""); + } + if (!segnoLabel.empty()) { + m->setLabel(segnoLabel); + } + if (segnoColor.isValid()) { + colorItem(m, segnoColor); + } + addElemOffset(m, 0, u"above", markedMeasure, markedMeasure->tick()); + m_e.skipCurrentElement(); + } else if (m_e.name() == "coda") { + const Color codaColor = Color::fromString(m_e.asciiAttribute("color").ascii()); + const AsciiStringView codaSymbol = m_e.asciiAttribute("smufl"); + Measure* markedMeasure = (loc == "left") ? measure : measure->nextMeasure(); + if (markedMeasure == nullptr) { + m_logger->logError(u"coda or segno marker cannot be placed at the end of the score", &m_e); + m_e.skipCurrentElement(); + continue; + } + Marker* m = Factory::createMarker(markedMeasure); + m->setMarkerType(MarkerType::CODA); + if (!codaSymbol.empty()) { + m->setXmlText(u"" + String::fromAscii(codaSymbol.ascii()) + u""); + } + if (!codaLabel.empty()) { + m->setLabel(codaLabel); + } + if (codaColor.isValid()) { + colorItem(m, codaColor); + } + addElemOffset(m, 0, u"above", markedMeasure, markedMeasure->tick()); + m_e.skipCurrentElement(); } else if (m_e.name() == "fermata") { const Color fermataColor = Color::fromString(m_e.asciiAttribute("color").ascii()); const String fermataType = m_e.attribute("type"); @@ -5795,6 +5836,12 @@ void MusicXmlParserPass2::barline(const String& partId, Measure* measure, const // Terminate tempo lines const InferredTempoLineStack& lines = getInferredTempoLine(); terminateInferredLine(std::vector(lines.begin(), lines.end()), locTick, track); + } else if (m_e.name() == "ending") { + endingNumber = m_e.attribute("number"); + endingType = m_e.attribute("type"); + endingColor = Color::fromString(m_e.asciiAttribute("color").ascii()); + printEnding = m_e.asciiAttribute("print-object") != "no"; + endingText = m_e.readText(); } else if (m_e.name() == "repeat") { repeat = m_e.attribute("direction"); count = m_e.attribute("times"); From dcd1946d0da6f83a5eafea7fa650be4befc69b1f Mon Sep 17 00:00:00 2001 From: rettinghaus Date: Tue, 9 Jun 2026 16:57:17 +0200 Subject: [PATCH 2/5] import/export winged repeats (cherry picked from commit 32d32b612ff370c66319c3f6f6eb3bf5651e8865) --- .../musicxml/internal/export/exportmusicxml.cpp | 15 +++++++++++---- .../internal/import/importmusicxmlpass2.cpp | 10 ++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/importexport/musicxml/internal/export/exportmusicxml.cpp b/src/importexport/musicxml/internal/export/exportmusicxml.cpp index 883509b694a94..93ccd8e064107 100644 --- a/src/importexport/musicxml/internal/export/exportmusicxml.cpp +++ b/src/importexport/musicxml/internal/export/exportmusicxml.cpp @@ -1840,7 +1840,11 @@ void ExportMusicXml::barlineLeft(const Measure* const m, const track_idx_t track ending(m_xml, volta, true); } if (rs) { - m_xml.tag("repeat", { { "direction", "forward" } }); + XmlWriter::Attributes attrs = { { "direction", "forward" } }; + if (m_score->style().styleB(Sid::repeatBarTips)) { + attrs.push_back({ "winged", "curved" }); + } + m_xml.tag("repeat", attrs); } m_xml.endElement(); } @@ -2109,11 +2113,14 @@ void ExportMusicXml::barlineRight(const Measure* const m, const track_idx_t stra } if (bst == BarLineType::END_REPEAT || bst == BarLineType::END_START_REPEAT) { + XmlWriter::Attributes attrs = { { "direction", "backward" } }; if (m->repeatCount() > 2) { - m_xml.tag("repeat", { { "direction", "backward" }, { "times", m->repeatCount() } }); - } else { - m_xml.tag("repeat", { { "direction", "backward" } }); + attrs.push_back({ "times", m->repeatCount() }); + } + if (m_score->style().styleB(Sid::repeatBarTips)) { + attrs.push_back({ "winged", "curved" }); } + m_xml.tag("repeat", attrs); } m_xml.endElement(); diff --git a/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp b/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp index c4dc1ea348ce9..b576a83198346 100644 --- a/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp +++ b/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp @@ -5844,11 +5844,13 @@ void MusicXmlParserPass2::barline(const String& partId, Measure* measure, const endingText = m_e.readText(); } else if (m_e.name() == "repeat") { repeat = m_e.attribute("direction"); - count = m_e.attribute("times"); - if (count.empty()) { - count = u"2"; + int count = m_e.attribute("times").toInt(); + if (!count) { + count = 2; } - measure->setRepeatCount(count.toInt()); + measure->setRepeatCount(count); + bool repeatBarTips = !m_e.attribute("winged").empty() && m_e.attribute("winged") != "none"; + m_score->undoChangeStyleVal(Sid::repeatBarTips, repeatBarTips); m_e.skipCurrentElement(); } else { skipLogCurrElem(); From c820cbc1829eb3f18305b750718eb34d6aaadd4f Mon Sep 17 00:00:00 2001 From: rettinghaus Date: Tue, 9 Jun 2026 20:44:38 +0200 Subject: [PATCH 3/5] react to code review (cherry picked from commit a05d337b21005ef3e29f5e8e9ff111a7c3aa5a1e) --- .../musicxml/internal/export/exportmusicxml.cpp | 4 ++-- .../musicxml/internal/import/importmusicxmlpass2.cpp | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/importexport/musicxml/internal/export/exportmusicxml.cpp b/src/importexport/musicxml/internal/export/exportmusicxml.cpp index 93ccd8e064107..156e6c1521575 100644 --- a/src/importexport/musicxml/internal/export/exportmusicxml.cpp +++ b/src/importexport/musicxml/internal/export/exportmusicxml.cpp @@ -1842,7 +1842,7 @@ void ExportMusicXml::barlineLeft(const Measure* const m, const track_idx_t track if (rs) { XmlWriter::Attributes attrs = { { "direction", "forward" } }; if (m_score->style().styleB(Sid::repeatBarTips)) { - attrs.push_back({ "winged", "curved" }); + attrs.push_back({ "winged", "curved" }); } m_xml.tag("repeat", attrs); } @@ -2118,7 +2118,7 @@ void ExportMusicXml::barlineRight(const Measure* const m, const track_idx_t stra attrs.push_back({ "times", m->repeatCount() }); } if (m_score->style().styleB(Sid::repeatBarTips)) { - attrs.push_back({ "winged", "curved" }); + attrs.push_back({ "winged", "curved" }); } m_xml.tag("repeat", attrs); } diff --git a/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp b/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp index b576a83198346..70e12c1781c0a 100644 --- a/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp +++ b/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp @@ -5775,7 +5775,7 @@ void MusicXmlParserPass2::barline(const String& partId, Measure* measure, const } else if (m_e.name() == "segno") { const Color segnoColor = Color::fromString(m_e.asciiAttribute("color").ascii()); const AsciiStringView segnoSymbol = m_e.asciiAttribute("smufl"); - Measure* markedMeasure = (loc == "left") ? measure : measure->nextMeasure(); + Measure* markedMeasure = (loc == "right") ? measure->nextMeasure() : measure; if (markedMeasure == nullptr) { m_logger->logError(u"coda or segno marker cannot be placed at the end of the score", &m_e); m_e.skipCurrentElement(); @@ -5792,12 +5792,13 @@ void MusicXmlParserPass2::barline(const String& partId, Measure* measure, const if (segnoColor.isValid()) { colorItem(m, segnoColor); } - addElemOffset(m, 0, u"above", markedMeasure, markedMeasure->tick()); + const track_idx_t track = m_pass1.trackForPart(partId); + addElemOffset(m, track, u"above", markedMeasure, markedMeasure->tick()); m_e.skipCurrentElement(); } else if (m_e.name() == "coda") { const Color codaColor = Color::fromString(m_e.asciiAttribute("color").ascii()); const AsciiStringView codaSymbol = m_e.asciiAttribute("smufl"); - Measure* markedMeasure = (loc == "left") ? measure : measure->nextMeasure(); + Measure* markedMeasure = (loc == "right") ? measure->nextMeasure() : measure; if (markedMeasure == nullptr) { m_logger->logError(u"coda or segno marker cannot be placed at the end of the score", &m_e); m_e.skipCurrentElement(); @@ -5814,7 +5815,8 @@ void MusicXmlParserPass2::barline(const String& partId, Measure* measure, const if (codaColor.isValid()) { colorItem(m, codaColor); } - addElemOffset(m, 0, u"above", markedMeasure, markedMeasure->tick()); + const track_idx_t track = m_pass1.trackForPart(partId); + addElemOffset(m, track, u"above", markedMeasure, markedMeasure->tick()); m_e.skipCurrentElement(); } else if (m_e.name() == "fermata") { const Color fermataColor = Color::fromString(m_e.asciiAttribute("color").ascii()); From 0d45db7504bb0090c7dd94c4d4afd2c43c6530bb Mon Sep 17 00:00:00 2001 From: rettinghaus Date: Wed, 10 Jun 2026 14:21:55 +0200 Subject: [PATCH 4/5] allow fermatas on all barlines (cherry picked from commit fde275a3109b384813ce56061828af78a041eb9c) --- .../musicxml/internal/import/importmusicxmlpass2.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp b/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp index 70e12c1781c0a..0e68f6cd61da5 100644 --- a/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp +++ b/src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp @@ -5765,7 +5765,6 @@ void MusicXmlParserPass2::barline(const String& partId, Measure* measure, const Color endingColor; String endingText; String repeat; - String count; bool printEnding = true; while (m_e.readNextStartElement()) { @@ -5821,7 +5820,13 @@ void MusicXmlParserPass2::barline(const String& partId, Measure* measure, const } else if (m_e.name() == "fermata") { const Color fermataColor = Color::fromString(m_e.asciiAttribute("color").ascii()); const String fermataType = m_e.attribute("type"); - Segment* const segment = measure->getSegment(SegmentType::EndBarLine, locTick); + SegmentType st = SegmentType::BarLine; + if (locTick == measure->endTick()) { + st = SegmentType::EndBarLine; + } else if (locTick == measure->tick()) { + st = SegmentType::BeginBarLine; + } + Segment* const segment = measure->getSegment(st, locTick); const track_idx_t track = m_pass1.trackForPart(partId); Fermata* fermata = Factory::createFermata(segment); fermata->setSymId(convertFermataToSymId(m_e.readText())); From 60d89895c12d2c4705d3fcb5b9e1b01d7e2b6edf Mon Sep 17 00:00:00 2001 From: rettinghaus Date: Wed, 10 Jun 2026 14:24:02 +0200 Subject: [PATCH 5/5] add test for winged repeats (cherry picked from commit 532f1239cac718d43420d4d559c56e3c3d3882a0) --- .../musicxml/tests/data/testBarlineTips.xml | 197 ++++++++++++++++++ .../musicxml/tests/musicxml_tests.cpp | 7 +- 2 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 src/importexport/musicxml/tests/data/testBarlineTips.xml diff --git a/src/importexport/musicxml/tests/data/testBarlineTips.xml b/src/importexport/musicxml/tests/data/testBarlineTips.xml new file mode 100644 index 0000000000000..89cc1011612f3 --- /dev/null +++ b/src/importexport/musicxml/tests/data/testBarlineTips.xml @@ -0,0 +1,197 @@ + + + + + Repeats on wings + + + + MuseScore 0.7.0 + 2007-09-10 + + + + + + + + + + Piano + + Piano + + + + 1 + 1 + 78.7402 + 0 + + + + + + + 1 + + 0 + + + 2 + + G + 2 + + + F + 4 + + + + + 4 + 1 + 1 + + + 4 + + + + C + 3 + + 2 + 5 + half + up + 2 + + + + A + 3 + + 2 + 5 + half + down + 2 + + + + + heavy-light + + + + + 4 + 1 + 1 + + + 4 + + + + G + 3 + + 2 + 5 + half + down + 2 + + + + G + 2 + + 2 + 5 + half + up + 2 + + + + + + 4 + 1 + 1 + + + 4 + + + + C + 3 + + 2 + 5 + half + up + 2 + + + + E + 3 + + 2 + 5 + half + down + 2 + + + light-heavy + + + + + + + 4 + 1 + 1 + + + 4 + + + + F + 3 + + 2 + 5 + half + down + 2 + + + + C + 3 + + 2 + 5 + half + up + 2 + + + light-heavy + + + + diff --git a/src/importexport/musicxml/tests/musicxml_tests.cpp b/src/importexport/musicxml/tests/musicxml_tests.cpp index 6514490d2457a..6d8b6d1a45d53 100644 --- a/src/importexport/musicxml/tests/musicxml_tests.cpp +++ b/src/importexport/musicxml/tests/musicxml_tests.cpp @@ -396,6 +396,9 @@ TEST_F(MusicXml_Tests, articulationCombination) { TEST_F(MusicXml_Tests, backupRoundingError) { musicXmlImportTestRef("testBackupRoundingError"); } +TEST_F(MusicXml_Tests, barlineFermatas) { + musicXmlIoTest("testBarlineFermatas"); +} TEST_F(MusicXml_Tests, barlineLoc) { musicXmlImportTestRef("testBarlineLoc"); } @@ -405,8 +408,8 @@ TEST_F(MusicXml_Tests, noteflightStartRepeatBarline) { TEST_F(MusicXml_Tests, barlineSpan) { musicXmlIoTest("testBarlineSpan"); } -TEST_F(MusicXml_Tests, barlineFermatas) { - musicXmlIoTest("testBarlineFermatas"); +TEST_F(MusicXml_Tests, barlineTips) { + musicXmlIoTest("testBarlineTips"); } TEST_F(MusicXml_Tests, barStyles) { musicXmlIoTest("testBarStyles");