Skip to content

Commit

Permalink
MusicXML: Add support for part-name-display
Browse files Browse the repository at this point in the history
Backport of musescore#22322
  • Loading branch information
rettinghaus authored and Jojo-Schmitz committed May 29, 2024
1 parent f86dbd4 commit 3e777d5
Show file tree
Hide file tree
Showing 10 changed files with 703 additions and 51 deletions.
58 changes: 46 additions & 12 deletions importexport/musicxml/exportxml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2614,6 +2614,31 @@ static void writeAccidental(XmlWriter& xml, const QString& tagName, const Accide
}
}

//---------------------------------------------------------
// writeDisplayName
//---------------------------------------------------------

static void writeDisplayName(XmlWriter& xml, const QString& partName)
{
QString displayText;
for (int i = 0; i < partName.size(); ++i) {
QChar ch = partName.at(i);
if (ch != u'' && ch != u'')
displayText += ch;
else {
if (!displayText.isEmpty())
xml.tag("display-text", displayText);
if (ch == u'')
xml.tag("accidental-text", "flat");
else if (ch == u'')
xml.tag("accidental-text", "sharp");
displayText.clear();
}
}
if (!displayText.isEmpty())
xml.tag("display-text", displayText);
}

//---------------------------------------------------------
// wavyLineStart
//---------------------------------------------------------
Expand Down Expand Up @@ -6701,21 +6726,30 @@ static void partList(XmlWriter& xml, Score* score, MxmlInstrumentMap& instrMap)

xml.stag(QString("score-part id=\"P%1\"").arg(idx+1));
initInstrMap(instrMap, part->instruments(), score);
static const QRegExp acc("[♭♯]");
QString attributes;
// by default export the parts long name as part-name
if (!part->longName().isEmpty())
xml.tag("part-name", MScoreTextToMXML::toPlainText(part->longName()));
else {
if (!part->partName().isEmpty()) {
// use the track name if no part long name
// to prevent an empty track name on import
xml.tag("part-name print-object=\"no\"", MScoreTextToMXML::toPlainText(part->partName()));
QString partName = part->longName();
// use the track name if no part long name
if (partName.isEmpty()) {
partName = part->partName();
if (!partName.isEmpty())
attributes = " print-object=\"no\"";
}
xml.tag("part-name" + attributes, MScoreTextToMXML::toPlainText(partName).replace(u'', 'b').replace(u'', '#'));
if (partName.contains(acc)) {
xml.stag("part-name-display");
writeDisplayName(xml, partName);
xml.etag();
}
if (!part->shortName().isEmpty()) {
xml.tag("part-abbreviation", MScoreTextToMXML::toPlainText(part->shortName()).replace(u'', 'b').replace(u'', '#'));
if (part->shortName().contains(acc)) {
xml.stag("part-abbreviation-display");
writeDisplayName(xml, part->shortName());
xml.etag();
}
else
// part-name is required
xml.tag("part-name", "");
}
if (!part->shortName().isEmpty())
xml.tag("part-abbreviation", MScoreTextToMXML::toPlainText(part->shortName()));

if (part->instrument()->useDrumset()) {
const Drumset* drumset = part->instrument()->drumset();
Expand Down
29 changes: 0 additions & 29 deletions importexport/musicxml/importmxml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,34 +90,6 @@ static int musicXMLImportErrorDialog(QString text, QString detailedText)
return errorDialog.exec();
}

#if 0
static void updateNamesForAccidentals(Instrument* inst)
{
auto replace = [](QString name) {
name = name.replace(QRegExp(
R"(((?:^|\s)([A-Ga-g]|[Uu][Tt]|[Dd][Oo]|[Rr][EeÉé]|[MmSsTt][Ii]|[FfLl][Aa]|[Ss][Oo][Ll]))b(?=\s|$))"),
QString::fromStdString(R"($1♭)"));

name = name.replace(QRegExp(
R"(((?:^|\s)([A-Ga-g]|[Uu][Tt]|[Dd][Oo]|[Rr][EeÉé]|[MmSsTt][Ii]|[FfLl][Aa]|[Ss][Oo][Ll]))#(?=\s|$))"),
QString::fromStdString(R"($1♯)"));

return name;
};
// change staff names from simple text (eg 'Eb') to text using accidental symbols (eg 'E♭')

// Instrument::longNames() is const af so we need to make a deep copy, update it, and then set it again
QList<StaffName> longNamesCopy = inst->longNames();
for (StaffName& sn : longNamesCopy)
sn.setName(replace(sn.name()));
QList<StaffName> shortNamesCopy = inst->shortNames();
for (StaffName& sn : shortNamesCopy)
sn.setName(replace(sn.name()));
inst->setLongNames(longNamesCopy);
inst->setShortNames(shortNamesCopy);
}
#endif

//---------------------------------------------------------
// importMusicXMLfromBuffer
//---------------------------------------------------------
Expand Down Expand Up @@ -154,7 +126,6 @@ Score::FileError importMusicXMLfromBuffer(Score* score, const QString& /*name*/,
for (const Part* part : score->parts()) {
for (const auto& pair : *part->instruments()) {
pair.second->updateInstrumentId();
updateNamesForAccidentals(pair.second);
}
}
#endif
Expand Down
32 changes: 28 additions & 4 deletions importexport/musicxml/importmxmlpass1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2142,8 +2142,17 @@ void MusicXMLParserPass1::scorePart()
_parts[id].setName(name);
}
else if (_e.name() == "part-name-display") {
// TODO
_e.skipCurrentElement(); // skip but don't log
QString name;
while (_e.readNextStartElement()) {
if (_e.name() == "display-text")
name += _e.readElementText();
else if (_e.name() == "accidental-text")
name += mxmlAccidentalTextToChar(_e.readElementText());
else
skipLogCurrElem();
}
if (!name.isEmpty())
_parts[id].setName(name);
}
else if (_e.name() == "part-abbreviation") {
// Element part-name contains the displayed (abbreviated) part name
Expand All @@ -2154,10 +2163,25 @@ void MusicXMLParserPass1::scorePart()
QString name = _e.readElementText();
_parts[id].setAbbr(name);
}
else if (_e.name() == "part-abbreviation-display")
_e.skipCurrentElement(); // skip but don't log
else if (_e.name() == "part-abbreviation-display") {
QString name;
while (_e.readNextStartElement()) {
if (_e.name() == "display-text")
name += _e.readElementText();
else if (_e.name() == "accidental-text")
name += mxmlAccidentalTextToChar(_e.readElementText());
else
skipLogCurrElem();
}
if (!name.isEmpty())
_parts[id].setAbbr(name);
}
else if (_e.name() == "group") // TODO
_e.skipCurrentElement(); // skip but don't log
else if (_e.name() == "score-instrument")
scoreInstrument(id);
else if (_e.name() == "player") // unsupported
_e.skipCurrentElement(); // skip but don't log
else if (_e.name() == "midi-device") {
if (!_e.attributes().hasAttribute("port")) {
_e.readElementText(); // empty string
Expand Down
24 changes: 24 additions & 0 deletions importexport/musicxml/musicxmlsupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,30 @@ AccidentalType mxmlString2accidentalType(const QString mxmlName, const QString s
return AccidentalType::NONE;
}

//---------------------------------------------------------
// mxmlAccidentalTextToChar
//---------------------------------------------------------

/**
Convert a MusicXML accidental text to a accidental character.
*/

QString mxmlAccidentalTextToChar(const QString mxmlName)
{
static QMap<QString, QString> map; // map MusicXML accidental name to MuseScore enum AccidentalType
if (map.empty()) {
map["sharp"] = "";
map["natural"] = "";
map["flat"] = "";
}

if (map.contains(mxmlName))
return map.value(mxmlName);
else
qDebug("mxmlAccidentalTextToChar: unsupported accidental '%s'", qPrintable(mxmlName));
return "";
}

//---------------------------------------------------------
// isAppr
//---------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions importexport/musicxml/musicxmlsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ extern QString accSymId2SmuflMxmlString(const SymId id);
extern QString accidentalType2MxmlString(const AccidentalType type);
extern QString accidentalType2SmuflMxmlString(const AccidentalType type);
extern AccidentalType mxmlString2accidentalType(const QString mxmlName, const QString smufl);
extern QString mxmlAccidentalTextToChar(const QString mxmlName);
extern SymId mxmlString2accSymId(const QString mxmlName, const QString smufl = QString());
extern AccidentalType microtonalGuess(double val);
extern bool isLaissezVibrer(const SymId id);
Expand Down
12 changes: 10 additions & 2 deletions mtest/musicxml/io/testBarlineSpan.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,16 @@
</midi-instrument>
</score-part>
<score-part id="P4">
<part-name>Clarinet in B♭</part-name>
<part-abbreviation>Cl. in B♭</part-abbreviation>
<part-name>Clarinet in Bb</part-name>
<part-name-display>
<display-text>Clarinet in B</display-text>
<accidental-text>flat</accidental-text>
</part-name-display>
<part-abbreviation>Cl. in Bb</part-abbreviation>
<part-abbreviation-display>
<display-text>Cl. in B</display-text>
<accidental-text>flat</accidental-text>
</part-abbreviation-display>
<score-instrument id="P4-I1">
<instrument-name>Clarinet</instrument-name>
</score-instrument>
Expand Down
14 changes: 12 additions & 2 deletions mtest/musicxml/io/testKeysig2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,18 @@
</identification>
<part-list>
<score-part id="P1">
<part-name>B♭ Clarinet</part-name>
<part-abbreviation>B♭ Cl.</part-abbreviation>
<part-name>Bb Clarinet</part-name>
<part-name-display>
<display-text>B</display-text>
<accidental-text>flat</accidental-text>
<display-text> Clarinet</display-text>
</part-name-display>
<part-abbreviation>Bb Cl.</part-abbreviation>
<part-abbreviation-display>
<display-text>B</display-text>
<accidental-text>flat</accidental-text>
<display-text> Cl.</display-text>
</part-abbreviation-display>
<score-instrument id="P1-I1">
<instrument-name>B♭ Clarinet</instrument-name>
</score-instrument>
Expand Down
Loading

0 comments on commit 3e777d5

Please sign in to comment.