Skip to content

Commit 4352e7e

Browse files
authored
Merge pull request #22978 from rism-digital/develop-mei-mscore-ids
Add support for MEI export and import of MuseScore IDs
2 parents 262af63 + 5dd039b commit 4352e7e

File tree

9 files changed

+155
-29
lines changed

9 files changed

+155
-29
lines changed

src/importexport/mei/imeiconfiguration.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ class IMeiConfiguration : MODULE_EXPORT_INTERFACE
3737

3838
virtual bool meiExportLayout() const = 0;
3939
virtual void setMeiExportLayout(bool value) = 0;
40+
41+
virtual bool meiUseMuseScoreIds() const = 0;
42+
virtual void setMeiUseMuseScoreIds(bool value) = 0;
4043
};
4144
}
4245

src/importexport/mei/internal/meiconfiguration.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ static const std::string module_name("iex_mei");
3131

3232
static const Settings::Key MEI_IMPORT_LAYOUT_KEY(module_name, "import/mei/importMeiLayout");
3333
static const Settings::Key MEI_EXPORT_LAYOUT_KEY(module_name, "export/mei/exportMeiLayout");
34+
static const Settings::Key MEI_USE_MUSESCORE_IDS_KEY(module_name, "export/mei/useMuseScoreIds");
3435

3536
void MeiConfiguration::init()
3637
{
3738
settings()->setDefaultValue(MEI_IMPORT_LAYOUT_KEY, Val(true));
3839
settings()->setDefaultValue(MEI_EXPORT_LAYOUT_KEY, Val(true));
40+
settings()->setDefaultValue(MEI_USE_MUSESCORE_IDS_KEY, Val(false));
3941
}
4042

4143
bool MeiConfiguration::meiImportLayout() const
@@ -57,3 +59,13 @@ void MeiConfiguration::setMeiExportLayout(bool value)
5759
{
5860
settings()->setSharedValue(MEI_EXPORT_LAYOUT_KEY, Val(value));
5961
}
62+
63+
bool MeiConfiguration::meiUseMuseScoreIds() const
64+
{
65+
return settings()->value(MEI_USE_MUSESCORE_IDS_KEY).toBool();
66+
}
67+
68+
void MeiConfiguration::setMeiUseMuseScoreIds(bool value)
69+
{
70+
settings()->setSharedValue(MEI_USE_MUSESCORE_IDS_KEY, Val(value));
71+
}

src/importexport/mei/internal/meiconfiguration.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class MeiConfiguration : public IMeiConfiguration
3535

3636
bool meiExportLayout() const override;
3737
void setMeiExportLayout(bool value) override;
38+
39+
bool meiUseMuseScoreIds() const override;
40+
void setMeiUseMuseScoreIds(bool value) override;
3841
};
3942
}
4043

src/importexport/mei/internal/meiexporter.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "engraving/dom/laissezvib.h"
4747
#include "engraving/dom/lyrics.h"
4848
#include "engraving/dom/marker.h"
49+
#include "engraving/dom/masterscore.h"
4950
#include "engraving/dom/measure.h"
5051
#include "engraving/dom/measurerepeat.h"
5152
#include "engraving/dom/note.h"
@@ -93,6 +94,8 @@ using namespace mu::engraving;
9394

9495
bool MeiExporter::write(std::string& meiData)
9596
{
97+
const bool useMuseScoreIds = configuration()->meiUseMuseScoreIds();
98+
9699
m_uids = UIDRegister::instance();
97100
m_xmlIDCounter = 0;
98101

@@ -129,10 +132,24 @@ bool MeiExporter::write(std::string& meiData)
129132
m_mei = meiDoc.append_child("mei");
130133
m_mei.append_attribute("xmlns") = "http://www.music-encoding.org/ns/mei";
131134

132-
// Save xml:id metaTag's as mei@xml:id
133-
String xmlId = m_score->metaTag(u"xml:id");
134-
if (!xmlId.isEmpty()) {
135-
m_mei.append_attribute("xml:id") = xmlId.toStdString().c_str();
135+
// Option to use MuseScore Ids has priority
136+
if (useMuseScoreIds) {
137+
std::stringstream xmlId;
138+
EID eid = m_score->masterScore()->eid();
139+
if (!eid.isValid()) {
140+
eid = m_score->masterScore()->assignNewEID();
141+
}
142+
String eidStr = String::fromStdString(eid.toStdString().c_str());
143+
xmlId << "mscore-" << eidStr.replace('/', '.').replace('+', '-').toStdString();
144+
m_mei.append_attribute("xml:id") = xmlId.str().c_str();
145+
}
146+
// Otherwise check if we have a metaTag
147+
else {
148+
// Save xml:id metaTag's as mei@xml:id
149+
String xmlId = m_score->metaTag(u"xml:id");
150+
if (!xmlId.isEmpty()) {
151+
m_mei.append_attribute("xml:id") = xmlId.toStdString().c_str();
152+
}
136153
}
137154

138155
libmei::AttConverter converter;
@@ -2539,7 +2556,16 @@ std::string MeiExporter::generateHashID()
25392556

25402557
std::string MeiExporter::getXmlIdFor(const EngravingItem* item, const char c)
25412558
{
2542-
if (m_uids->hasUid(item)) {
2559+
const bool useMuseScoreIds = configuration()->meiUseMuseScoreIds();
2560+
2561+
if (useMuseScoreIds) {
2562+
EID eid = item->eid();
2563+
if (!eid.isValid()) {
2564+
eid = item->assignNewEID();
2565+
}
2566+
String eidStr = String::fromStdString(eid.toStdString().c_str());
2567+
return "mscore-" + eidStr.replace('/', '.').replace('+', '-').toStdString();
2568+
} else if (m_uids->hasUid(item)) {
25432569
return m_uids->uid(item);
25442570
}
25452571

src/importexport/mei/internal/meiimporter.cpp

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ bool MeiImporter::read(const muse::io::path_t& path)
9898
{
9999
m_uids = UIDRegister::instance();
100100
m_uids->clear();
101+
m_hasMuseScoreIds = false;
101102

102103
m_lastMeasure = nullptr;
103104
m_tremoloId.clear();
@@ -134,12 +135,30 @@ bool MeiImporter::read(const muse::io::path_t& path)
134135

135136
success = success && this->readMeiHead(root);
136137

137-
success = success && this->readScore(root);
138-
139138
pugi::xml_attribute xmlId = root.attribute("xml:id");
139+
bool hasRootXmlId = false;
140140
if (xmlId && !String(xmlId.value()).empty()) {
141-
m_score->setMetaTag(u"xml:id", String(xmlId.value()));
142-
// Do not keep a xml:id map when having a xml:id seed.
141+
hasRootXmlId = true;
142+
String xmlIdStr = String(xmlId.value());
143+
if (xmlIdStr.startsWith(u"mscore-")) {
144+
// Keep a global flag since we are going to read them only if mei@xml:id is given with mscore EID
145+
m_hasMuseScoreIds = true;
146+
String valStr = xmlIdStr.remove(u"mscore-").replace('.', '/').replace('-', '+');
147+
// The mei@xml:id store the score EID
148+
EID eid = EID::fromStdString(valStr.toStdString());
149+
if (eid.isValid()) {
150+
m_score->setEID(eid);
151+
}
152+
} else {
153+
// Keep it as a seed
154+
m_score->setMetaTag(u"xml:id", xmlIdStr);
155+
}
156+
}
157+
158+
success = success && this->readScore(root);
159+
160+
if (hasRootXmlId) {
161+
// Do not keep a xml:id map when having a xml:id seed or MscoreIds
143162
m_uids->clear();
144163
}
145164

@@ -260,7 +279,11 @@ ChordRest* MeiImporter::addChordRest(pugi::xml_node node, Measure* measure, int
260279
} else {
261280
chordRest = Factory::createChord(segment);
262281
}
263-
m_uids->reg(chordRest, meiElement.m_xmlId);
282+
283+
// Do not use single note xml:id / EID for the ChordRest
284+
if (!dynamic_cast<const libmei::Note*>(&meiElement)) {
285+
this->readXmlId(chordRest, meiElement.m_xmlId);
286+
}
264287

265288
if (m_startIdChordRests.count(meiElement.m_xmlId)) {
266289
m_startIdChordRests[meiElement.m_xmlId] = chordRest;
@@ -458,7 +481,7 @@ EngravingItem* MeiImporter::addAnnotation(const libmei::Element& meiElement, Mea
458481
} else {
459482
return nullptr;
460483
}
461-
m_uids->reg(item, meiElement.m_xmlId);
484+
this->readXmlId(item, meiElement.m_xmlId);
462485

463486
item->setTrack(chordRest->track());
464487
segment->add(item);
@@ -504,7 +527,7 @@ Spanner* MeiImporter::addSpanner(const libmei::Element& meiElement, Measure* mea
504527
} else {
505528
return nullptr;
506529
}
507-
m_uids->reg(item, meiElement.m_xmlId);
530+
this->readXmlId(item, meiElement.m_xmlId);
508531

509532
item->setTick(chordRest->tick());
510533
item->setStartElement(chordRest);
@@ -549,7 +572,7 @@ EngravingItem* MeiImporter::addToChordRest(const libmei::Element& meiElement, Me
549572
} else {
550573
return nullptr;
551574
}
552-
m_uids->reg(item, meiElement.m_xmlId);
575+
this->readXmlId(item, meiElement.m_xmlId);
553576

554577
item->setTrack(chordRest->track());
555578
chordRest->add(item);
@@ -856,6 +879,23 @@ void MeiImporter::setOrnamentAccid(engraving::Ornament* ornament, const Convert:
856879
}
857880
}
858881

882+
void MeiImporter::readXmlId(engraving::EngravingItem* item, const std::string& meiUID)
883+
{
884+
String xmlIdStr = String::fromStdString(meiUID);
885+
// We have a file that has MuseScore EIDs and one on this element
886+
if (m_hasMuseScoreIds && xmlIdStr.startsWith(u"mscore-")) {
887+
String valStr = xmlIdStr.remove(u"mscore-").replace('.', '/').replace('-', '+');
888+
EID eid = EID::fromStdString(valStr.toStdString());
889+
if (!eid.isValid()) {
890+
Convert::logs.push_back(String("A valid MuseScore ID could not be extracted from '%1'").arg(xmlIdStr));
891+
} else {
892+
item->setEID(eid);
893+
}
894+
} else {
895+
m_uids->reg(item, meiUID);
896+
}
897+
}
898+
859899
//---------------------------------------------------------
860900
// parsing methods
861901
//---------------------------------------------------------
@@ -1323,7 +1363,7 @@ bool MeiImporter::readEnding(pugi::xml_node endingNode)
13231363
} else {
13241364
Volta* volta = Factory::createVolta(m_score->dummy());
13251365
Convert::endingFromMEI(volta, meiEnding, warning);
1326-
m_uids->reg(volta, meiEnding.m_xmlId);
1366+
this->readXmlId(volta, meiEnding.m_xmlId);
13271367
volta->setTrack(0);
13281368
volta->setTrack2(0);
13291369
volta->setTick(m_endingStart->tick());
@@ -1356,7 +1396,7 @@ bool MeiImporter::readMeasure(pugi::xml_node measureNode)
13561396
Convert::MeasureStruct measureSt = Convert::measureFromMEI(meiMeasure, warning);
13571397

13581398
Measure* measure = Factory::createMeasure(m_score->dummy()->system());
1359-
m_uids->reg(measure, meiMeasure.m_xmlId);
1399+
this->readXmlId(measure, meiMeasure.m_xmlId);
13601400
measure->setTick(m_ticks);
13611401
measure->setTimesig(m_currentTimeSig);
13621402

@@ -1730,7 +1770,7 @@ bool MeiImporter::readChord(pugi::xml_node chordNode, Measure* measure, int trac
17301770
NOT_SUPPORTED;
17311771
} else {
17321772
TremoloSingleChord* tremolo = Factory::createTremoloSingleChord(chord);
1733-
m_uids->reg(tremolo, m_tremoloId);
1773+
this->readXmlId(tremolo, m_tremoloId);
17341774
tremolo->setTremoloType(ttype);
17351775
chord->add(tremolo);
17361776
}
@@ -1761,7 +1801,7 @@ bool MeiImporter::readClef(pugi::xml_node clefNode, Measure* measure, int track,
17611801
Segment* segment = measure->getSegment(SegmentType::Clef, ticks + measure->tick());
17621802
Clef* clef = Factory::createClef(segment);
17631803
Convert::colorFromMEI(clef, meiClef);
1764-
m_uids->reg(clef, meiClef.m_xmlId);
1804+
this->readXmlId(clef, meiClef.m_xmlId);
17651805
clef->setClefType(ClefTypeList(Convert::clefFromMEI(meiClef, warning)));
17661806
if (warning) {
17671807
this->addLog("clef", clefNode);
@@ -1828,7 +1868,7 @@ bool MeiImporter::readMRest(pugi::xml_node mRestNode, Measure* measure, int trac
18281868
Segment* segment = measure->getSegment(SegmentType::ChordRest, ticks + measure->tick());
18291869
Rest* rest = Factory::createRest(segment, TDuration(DurationType::V_MEASURE));
18301870
Convert::colorFromMEI(rest, meiMRest);
1831-
m_uids->reg(rest, meiMRest.m_xmlId);
1871+
this->readXmlId(rest, meiMRest.m_xmlId);
18321872
rest->setTicks(m_currentTimeSig);
18331873
rest->setDurationType(DurationType::V_MEASURE);
18341874
rest->setTrack(track);
@@ -1868,7 +1908,7 @@ bool MeiImporter::readMRpt(pugi::xml_node mRptNode, Measure* measure, int track,
18681908
Segment* segment = measure->getSegment(SegmentType::ChordRest, ticks + measure->tick());
18691909
MeasureRepeat* measureRepeat = Factory::createMeasureRepeat(segment);
18701910
Convert::colorFromMEI(measureRepeat, meiMRpt);
1871-
m_uids->reg(measureRepeat, meiMRpt.m_xmlId);
1911+
this->readXmlId(measureRepeat, meiMRpt.m_xmlId);
18721912
measureRepeat->setTrack(track);
18731913
measureRepeat->setTicks(measure->ticks());
18741914
measureRepeat->setNumMeasures(1);
@@ -1904,6 +1944,8 @@ bool MeiImporter::readNote(pugi::xml_node noteNode, Measure* measure, int track,
19041944
} else {
19051945
// Support for non MEI-Basic accid and accid.ges encoded in <note> - this is not academic...
19061946
meiAccid.Read(noteNode);
1947+
// Remove the xml:id read from the note in that case
1948+
meiAccid.m_xmlId = "";
19071949
}
19081950

19091951
Staff* staff = m_score->staff(track2staff(track));
@@ -1926,7 +1968,7 @@ bool MeiImporter::readNote(pugi::xml_node noteNode, Measure* measure, int track,
19261968
NOT_SUPPORTED;
19271969
} else {
19281970
TremoloSingleChord* tremolo = Factory::createTremoloSingleChord(chord);
1929-
m_uids->reg(tremolo, m_tremoloId);
1971+
this->readXmlId(tremolo, m_tremoloId);
19301972
tremolo->setTremoloType(ttype);
19311973
chord->add(tremolo);
19321974
}
@@ -1935,7 +1977,7 @@ bool MeiImporter::readNote(pugi::xml_node noteNode, Measure* measure, int track,
19351977

19361978
Note* note = Factory::createNote(chord);
19371979
Convert::colorFromMEI(note, meiNote);
1938-
m_uids->reg(note, meiNote.m_xmlId);
1980+
this->readXmlId(note, meiNote.m_xmlId);
19391981

19401982
// If there is a reference to the note in the MEI, add it the maps (e.g., for ties)
19411983
if (m_startIdChordRests.count(meiNote.m_xmlId)) {
@@ -1950,7 +1992,7 @@ bool MeiImporter::readNote(pugi::xml_node noteNode, Measure* measure, int track,
19501992

19511993
Accidental* accid = Factory::createAccidental(note);
19521994
Convert::colorFromMEI(accid, meiAccid);
1953-
m_uids->reg(accid, meiAccid.m_xmlId);
1995+
this->readXmlId(accid, meiAccid.m_xmlId);
19541996
accid->setAccidentalType(pitchSt.accidType);
19551997
//accid->setBracket(AccidentalBracket::BRACKET); // Not supported in MEI-Basic
19561998
accid->setRole(pitchSt.accidRole);
@@ -2046,7 +2088,7 @@ bool MeiImporter::readTuplet(pugi::xml_node tupletNode, Measure* measure, int tr
20462088
meiTuplet.Read(tupletNode);
20472089

20482090
m_tuplet = Factory::createTuplet(measure);
2049-
m_uids->reg(m_tuplet, meiTuplet.m_xmlId);
2091+
this->readXmlId(m_tuplet, meiTuplet.m_xmlId);
20502092
Convert::tupletFromMEI(m_tuplet, meiTuplet, warning);
20512093
if (warning) {
20522094
this->addLog("tuplet", tupletNode);
@@ -2123,7 +2165,7 @@ bool MeiImporter::readVerse(pugi::xml_node verseNode, Chord* chord)
21232165
}
21242166

21252167
Lyrics* lyrics = Factory::createLyrics(chord);
2126-
m_uids->reg(lyrics, meiVerse.m_xmlId);
2168+
this->readXmlId(lyrics, meiVerse.m_xmlId);
21272169
Convert::colorFromMEI(lyrics, meiVerse);
21282170

21292171
bool success = true;
@@ -2402,7 +2444,7 @@ bool MeiImporter::readF(pugi::xml_node fNode, engraving::FiguredBass* figuredBas
24022444

24032445
const int line = static_cast<int>(figuredBass->itemsCount());
24042446
FiguredBassItem* figuredBassItem = figuredBass->createItem(line);
2405-
m_uids->reg(figuredBassItem, meiF.m_xmlId);
2447+
this->readXmlId(figuredBassItem, meiF.m_xmlId);
24062448
figuredBassItem->setTrack(figuredBass->track());
24072449
figuredBassItem->setParent(figuredBass);
24082450

@@ -2444,7 +2486,7 @@ bool MeiImporter::readFb(pugi::xml_node harmNode, Measure* measure)
24442486
return true;
24452487
}
24462488
// Needs to be registered by hand because we pass meiHarm to MeiImporter::addAnnotation
2447-
m_uids->reg(figuredBass, meiFb.m_xmlId);
2489+
this->readXmlId(figuredBass, meiFb.m_xmlId);
24482490

24492491
Convert::fbFromMEI(figuredBass, meiHarm, meiFb, warning);
24502492

@@ -2478,7 +2520,7 @@ bool MeiImporter::readFermata(pugi::xml_node fermataNode, Measure* measure)
24782520
if (fermataPos == measure->ticks()) {
24792521
Segment* segment = measure->getSegment(SegmentType::EndBarLine, measure->tick() + measure->ticks());
24802522
fermata = Factory::createFermata(segment);
2481-
m_uids->reg(fermata, meiFermata.m_xmlId);
2523+
this->readXmlId(fermata, meiFermata.m_xmlId);
24822524
const int staffIdx
24832525
= (meiFermata.HasStaff() && meiFermata.GetStaff().size() > 0) ? this->getStaffIndex(meiFermata.GetStaff().at(0)) : 0;
24842526
fermata->setTrack(staffIdx * VOICES);
@@ -2791,7 +2833,7 @@ bool MeiImporter::readRepeatMark(pugi::xml_node repeatMarkNode, Measure* measure
27912833
item = Factory::createMarker(measure);
27922834
Convert::markerFromMEI(dynamic_cast<Marker*>(item), meiRepeatMark, warning);
27932835
}
2794-
m_uids->reg(item, meiRepeatMark.m_xmlId);
2836+
this->readXmlId(item, meiRepeatMark.m_xmlId);
27952837
item->setTrack(0);
27962838
measure->add(item);
27972839

@@ -2874,7 +2916,7 @@ bool MeiImporter::readTie(pugi::xml_node tieNode, Measure* measure)
28742916
}
28752917

28762918
Tie* tie = new Tie(m_score->dummy());
2877-
m_uids->reg(tie, meiTie.m_xmlId);
2919+
this->readXmlId(tie, meiTie.m_xmlId);
28782920
startNote->setTieFor(tie);
28792921
tie->setStartNote(startNote);
28802922
tie->setTrack(startNote->track());

src/importexport/mei/internal/meiimporter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,18 @@ class MeiImporter
198198
void extendLyrics();
199199
void setOrnamentAccid(engraving::Ornament* ornament, const Convert::OrnamStruct& ornamSt);
200200

201+
/** Read the xmlId and process it appropriately */
202+
void readXmlId(engraving::EngravingItem* item, const std::string& meiUID);
203+
201204
/** The Score pointer */
202205
engraving::Score* m_score = nullptr;
203206

204207
/** The uid register */
205208
UIDRegister* m_uids;
206209

210+
/** A flag indicating the file has MuseScore EIDs as xml:ids */
211+
bool m_hasMuseScoreIds;
212+
207213
engraving::Fraction m_ticks;
208214
int m_lastMeasureN;
209215
engraving::Measure* m_lastMeasure;

0 commit comments

Comments
 (0)