Skip to content

Commit

Permalink
MusicXML: add support for numerals
Browse files Browse the repository at this point in the history
Backport of musescore#24174
  • Loading branch information
rettinghaus authored and Jojo-Schmitz committed Aug 26, 2024
1 parent e945509 commit c519902
Show file tree
Hide file tree
Showing 5 changed files with 383 additions and 27 deletions.
57 changes: 47 additions & 10 deletions importexport/musicxml/exportxml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7816,27 +7816,64 @@ void ExportMusicXml::harmony(Harmony const* const h, FretDiagram const* const fd
// export an unrecognized Chord
// which may contain arbitrary text
//
const QString textNameEscaped = h->hTextName().toHtmlEscaped();
const QString textName = h->hTextName();
switch (h->harmonyType()) {
case HarmonyType::NASHVILLE: {
_xml.tag("function", h->hFunction());
QString k = "kind text=\"" + textNameEscaped + "\"";
_xml.tag(k, "none");
QString alter;
QString functionText = h->hFunction();
if (functionText.isEmpty()) {
// we just dump the text as deprecated function
_xml.tag("function", textName);
_xml.tag("kind", "none");
break;
}
else if (!functionText.at(0).isDigit()) {
alter = functionText.at(0);
functionText = functionText.at(1);
}
_xml.stag("numeral");
_xml.tag("numeral-root", functionText);
if (alter == "b")
_xml.tag("numeral-alter", "-1");
else if (alter == "#")
_xml.tag("numeral-alter", "1");
_xml.etag();
if (!h->xmlKind().isEmpty()) {
QString s = "kind";
QString kindText = h->musicXmlText();
if (!h->musicXmlText().isEmpty())
s += " text=\"" + kindText + "\"";
if (h->xmlSymbols() == "yes")
s += " use-symbols=\"yes\"";
if (h->xmlParens() == "yes")
s += " parentheses-degrees=\"yes\"";
_xml.tag(s, h->xmlKind());
}
else {
// default is major
_xml.tag("kind", "major");
}
}
break;
case HarmonyType::ROMAN: {
// TODO: parse?
_xml.tag("function", h->hTextName()); // note: HTML escape done by tag()
QString k = "kind text=\"\"";
_xml.tag(k, "none");
static const QRegularExpression romanRegex("[iv]+", QRegularExpression::CaseInsensitiveOption);
if (textName.contains(romanRegex)) {
_xml.stag("numeral");
QString k = "numeral-root text=\"" + textName + "\"";
_xml.tag(k, "1");
_xml.etag();
// only check for major or minor
_xml.tag("kind", textName.at(0).isUpper() ? "major" : "minor");
break;
}
}
break;
// fallthrough
case HarmonyType::STANDARD:
default: {
_xml.stag("root");
_xml.tag("root-step text=\"\"", "C");
_xml.etag(); // root
QString k = "kind text=\"" + textNameEscaped + "\"";
QString k = "kind text=\"" + textName.toHtmlEscaped() + "\"";
_xml.tag(k, "none");
}
break;
Expand Down
46 changes: 41 additions & 5 deletions importexport/musicxml/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6893,7 +6893,7 @@ void MusicXMLParserPass2::harmony(const QString& partId, Measure* measure, const
//QString printStyle = _e.attributes().value("print-style").toString();
const QColor color { _e.attributes().value("color").toString() };

QString kind, kindText, functionText, symbols, parens;
QString kind, kindText, functionText, inversionText, symbols, parens;
QList<HDegree> degreeList;

FretDiagram* fd = nullptr;
Expand Down Expand Up @@ -6943,7 +6943,34 @@ void MusicXMLParserPass2::harmony(const QString& partId, Measure* measure, const
ha->setHarmonyType(HarmonyType::ROMAN);
}
//else if (_e.name() == "function") { // MusicXML 4.0 replacement for "function"
// TODO: parse to decide between ROMAN and NASHVILLE
else if (_e.name() == "numeral") {
ha->setRootTpc(Tpc::TPC_INVALID);
ha->setBaseTpc(Tpc::TPC_INVALID);
while (_e.readNextStartElement()) {
if (_e.name() == "numeral-root") {
QString numeralRoot = _e.readElementText();
//QString numeralRootText = _e.attributes().value("text").toString();
// TODO analyze text and import as roman numerals
ha->setHarmonyType(HarmonyType::NASHVILLE);
ha->setFunction(numeralRoot);
}
else if (_e.name() == "numeral-alter") {
const int alter = _e.readElementText().toInt();
switch (alter) {
case -1:
ha->setFunction("b" + ha->hFunction());
break;
case 1:
ha->setFunction("#" + ha->hFunction());
break;
default:
break;
}
}
else
skipLogCurrElem();
}
}
else if (_e.name() == "kind") {
// attributes: use-symbols yes-no
// text, stack-degrees, parentheses-degree, bracket-degrees,
Expand All @@ -6957,7 +6984,16 @@ void MusicXMLParserPass2::harmony(const QString& partId, Measure* measure, const
}
}
else if (_e.name() == "inversion") {
skipLogCurrElem();
const int inversion = _e.readElementText().toInt();
switch (inversion) {
case 1: inversionText = "6";
break;
case 2: inversionText = "64";
break;
default:
inversionText = "";
break;
}
}
else if (_e.name() == "bass") {
QString step;
Expand Down Expand Up @@ -7030,15 +7066,15 @@ void MusicXMLParserPass2::harmony(const QString& partId, Measure* measure, const
}

const ChordDescription* d = nullptr;
if (ha->rootTpc() != Tpc::TPC_INVALID)
if (ha->rootTpc() != Tpc::TPC_INVALID || !ha->hFunction().isEmpty())
d = ha->fromXml(kind, kindText, symbols, parens, degreeList);
if (d) {
ha->setId(d->id);
ha->setTextName(d->names.front());
}
else {
ha->setId(-1);
QString textName = functionText + kindText;
QString textName = functionText + kindText + inversionText;
ha->setTextName(textName);
}
ha->render();
Expand Down
32 changes: 20 additions & 12 deletions mtest/musicxml/io/testHarmony6_ref.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ containing recognized text, unrecognized plaintext and unrecognized text requiri
</measure>
<measure number="2">
<harmony print-frame="no">
<function>i</function>
<kind text="">none</kind>
<numeral>
<numeral-root text="i">1</numeral-root>
</numeral>
<kind>minor</kind>
</harmony>
<note>
<pitch>
Expand All @@ -93,8 +95,10 @@ containing recognized text, unrecognized plaintext and unrecognized text requiri
</measure>
<measure number="3">
<harmony print-frame="no">
<function>1</function>
<kind text="m">none</kind>
<numeral>
<numeral-root>1</numeral-root>
</numeral>
<kind text="m">minor</kind>
</harmony>
<note>
<pitch>
Expand Down Expand Up @@ -134,8 +138,10 @@ containing recognized text, unrecognized plaintext and unrecognized text requiri
</measure>
<measure number="5">
<harmony print-frame="no">
<function>xyz</function>
<kind text="">none</kind>
<root>
<root-step text="">C</root-step>
</root>
<kind text="xyz">none</kind>
</harmony>
<note>
<pitch>
Expand All @@ -153,8 +159,8 @@ containing recognized text, unrecognized plaintext and unrecognized text requiri
</measure>
<measure number="6">
<harmony print-frame="no">
<function></function>
<kind text="xyz">none</kind>
<function>xyz</function>
<kind>none</kind>
</harmony>
<note>
<pitch>
Expand Down Expand Up @@ -194,8 +200,10 @@ containing recognized text, unrecognized plaintext and unrecognized text requiri
</measure>
<measure number="8">
<harmony print-frame="no">
<function>&lt;&gt;&amp;&quot;</function>
<kind text="">none</kind>
<root>
<root-step text="">C</root-step>
</root>
<kind text="&lt;&gt;&amp;&quot;">none</kind>
</harmony>
<note>
<pitch>
Expand All @@ -213,8 +221,8 @@ containing recognized text, unrecognized plaintext and unrecognized text requiri
</measure>
<measure number="9">
<harmony print-frame="no">
<function></function>
<kind text="&lt;&gt;&amp;&quot;">none</kind>
<function>&lt;&gt;&amp;&quot;</function>
<kind>none</kind>
</harmony>
<note>
<pitch>
Expand Down
Loading

0 comments on commit c519902

Please sign in to comment.