Skip to content

Commit

Permalink
XML ottava fixes
Browse files Browse the repository at this point in the history
Backport of musescore#24776
  • Loading branch information
miiizen authored and Jojo-Schmitz committed Oct 4, 2024
1 parent 48f8fc7 commit 49b7523
Show file tree
Hide file tree
Showing 9 changed files with 421 additions and 53 deletions.
36 changes: 32 additions & 4 deletions importexport/musicxml/exportxml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ class ExportMusicXml {
void write(QIODevice* dev);
void credits(XmlWriter& xml);
void moveToTick(const Fraction& t, const Fraction& stretch = {1, 1});
void moveToTickIfNeed(const Fraction& t, const Fraction& stretch = {1, 1});
void words(TextBase const* const text, int staff);
void tboxTextAsWords(TextBase const* const text, int staff, QPointF position);
void rehearsal(RehearsalMark const* const rmk, int staff);
Expand Down Expand Up @@ -2066,6 +2067,14 @@ void ExportMusicXml::moveToTick(const Fraction& t, const Fraction& stretch)
_tick = t;
}

void ExportMusicXml::moveToTickIfNeed(const Fraction& t, const Fraction& stretch)
{
if (_tick != t) {
_attr.doAttr(_xml, false);
moveToTick(t, stretch);
}
}

//---------------------------------------------------------
// timesig
//---------------------------------------------------------
Expand Down Expand Up @@ -4852,6 +4861,9 @@ void ExportMusicXml::hairpin(Hairpin const* const hp, int staff, const Fraction&
if (!hp->lineVisible()) {
if ((isStart && hp->beginText().isEmpty()) || (!isStart && hp->endText().isEmpty()))
return;
// generate backup or forward to the start time of the element
const Fraction tickToWrite = isStart ? hp->tick() : hp->tick2();
moveToTickIfNeed(tickToWrite);
directionTag(_xml, _attr, hp);
writeHairpinText(_xml, hp, isStart);
directionETag(_xml, staff);
Expand Down Expand Up @@ -4886,6 +4898,10 @@ void ExportMusicXml::hairpin(Hairpin const* const hp, int staff, const Fraction&
}
}

// generate backup or forward to the start time of the element
const Fraction tickToWrite = isStart ? hp->tick() : hp->tick2();
moveToTickIfNeed(tickToWrite);

directionTag(_xml, _attr, hp);
if (isStart)
writeHairpinText(_xml, hp, isStart);
Expand Down Expand Up @@ -4959,6 +4975,7 @@ int ExportMusicXml::findOttava(const Ottava* ot) const
void ExportMusicXml::ottava(Ottava const* const ot, int staff, const Fraction& tick)
{
int n = findOttava(ot);
bool isStart = ot->tick() == tick;
if (n >= 0)
ottavas[n] = 0;
else {
Expand Down Expand Up @@ -5009,6 +5026,10 @@ void ExportMusicXml::ottava(Ottava const* const ot, int staff, const Fraction& t
}

if (!octaveShiftXml.isEmpty()) {
// generate backup or forward to the start time of the element
const Fraction tickToWrite = isStart ? ot->tick() : ot->tick2();
moveToTickIfNeed(tickToWrite);

directionTag(_xml, _attr, ot);
_xml.stag("direction-type");
octaveShiftXml += color2xml(ot);
Expand All @@ -5029,6 +5050,12 @@ void ExportMusicXml::pedal(Pedal const* const pd, int staff, const Fraction& tic
if (pd->tick() != tick && pd->endHookType() == HookType::HOOK_45)
return;

bool isStart = pd->tick() == tick;

// generate backup or forward to the start time of the element
const Fraction tickToWrite = isStart ? pd->tick() : pd->tick2();
moveToTickIfNeed(tickToWrite);

directionTag(_xml, _attr, pd);
_xml.stag("direction-type");
QString pedalType;
Expand Down Expand Up @@ -5195,6 +5222,10 @@ void ExportMusicXml::textLine(TextLineBase const* const tl, int staff, const Fra

rest += positioningAttributes(tl, tl->tick() == tick);

// generate backup or forward to the start time of the element
const Fraction tickToWrite = isStart ? tl->tick() : tl->tick2();
moveToTickIfNeed(tickToWrite);

directionTag(_xml, _attr, tl);

if (!tl->beginText().isEmpty() && tl->tick() == tick) {
Expand Down Expand Up @@ -7272,10 +7303,7 @@ void ExportMusicXml::writeMeasureTracks(const Measure* const m,
continue;

// generate backup or forward to the start time of the element
if (_tick != seg->tick()) {
_attr.doAttr(_xml, false);
moveToTick(seg->tick(), stretch(_score, st, m->tick()));
}
moveToTickIfNeed(seg->tick(), stretch(_score, st, m->tick()));

// handle annotations and spanners (directions attached to this note or rest)
if (el->isChordRest()) {
Expand Down
38 changes: 33 additions & 5 deletions importexport/musicxml/importmxmlpass1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,22 @@ void MusicXMLParserPass1::addError(const QString& error)
}
}

void MusicXMLParserPass1::setExporterSoftware(QString& exporter)
{
if (exporter.contains("sibelius")) {
if (exporter.contains("dolet 6"))
_exporterSoftware = MusicXMLExporterSoftware::DOLET6;
else if (exporter.contains("dolet 8"))
_exporterSoftware = MusicXMLExporterSoftware::DOLET8;
else
_exporterSoftware = MusicXMLExporterSoftware::SIBELIUS;
}
else if (exporter.contains("finale"))
_exporterSoftware = MusicXMLExporterSoftware::FINALE;
else if (exporter.contains("noteflight"))
_exporterSoftware = MusicXMLExporterSoftware::NOTEFLIGHT;
}

//---------------------------------------------------------
// initPartState
//---------------------------------------------------------
Expand Down Expand Up @@ -885,7 +901,7 @@ void MusicXMLParserPass1::createMeasuresAndVboxes(Score* const score,
if (pageStartMeasureNrs.count(i) || i == 0) {
++pageNr;
if (pageNr == 1) {
vbox = addCreditWords(score, crWords, pageSize, true, _exporterString.contains("sibelius"));
vbox = addCreditWords(score, crWords, pageSize, true, sibOrDolet());
//if (i == 0 && vbox)
// vbox->setExcludeFromOtherParts(false);
}
Expand All @@ -908,10 +924,20 @@ void MusicXMLParserPass1::createMeasuresAndVboxes(Score* const score,

// add a footer vbox if the next measure is on a new page or end of score has been reached
if ((pageStartMeasureNrs.count(i + 1) || i == (ml.size() - 1)) && pageNr == 1)
addCreditWords(score, crWords, pageSize, false, _exporterString.contains("sibelius"));
addCreditWords(score, crWords, pageSize, false, sibOrDolet());
}
}

bool MusicXMLParserPass1::sibOrDolet() const
{
return _exporterSoftware == MusicXMLExporterSoftware::SIBELIUS || dolet();
}

bool MusicXMLParserPass1::dolet() const
{
return _exporterSoftware == MusicXMLExporterSoftware::DOLET6 || _exporterSoftware == MusicXMLExporterSoftware::DOLET8;
}

//---------------------------------------------------------
// determineMeasureStart
//---------------------------------------------------------
Expand Down Expand Up @@ -1242,8 +1268,10 @@ void MusicXMLParserPass1::identification()
else if (_e.name() == "encoding") {
// TODO
while (_e.readNextStartElement()) {
if (_e.name() == "software")
_exporterString += _e.readElementText().toLower();
if (_e.name() == "software") {
QString exporterString = _e.readElementText().toLower();
setExporterSoftware(exporterString);
}
else if (_e.name() == "supports" && _e.attributes().value("element") == "beam" && _e.attributes().value("type") == "yes") {
_hasBeamingInfo = true;
_e.skipCurrentElement();
Expand Down Expand Up @@ -2242,7 +2270,7 @@ void MusicXMLParserPass1::scoreInstrument(const QString& partId, const QString&
*/

// Finale exports all instrument names as 'Grand Piano' - use part name
if (_exporterString.contains("finale")) {
if (exporterSoftware() == MusicXMLExporterSoftware::FINALE) {
instrName = _parts[partId].getName();
if (instrName.size() <= 1)
instrName = curPartGroupName;
Expand Down
16 changes: 14 additions & 2 deletions importexport/musicxml/importmxmlpass1.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ enum class MxmlTupletFlag : char {
STOP_CURRENT = 8
};

enum class MusicXMLExporterSoftware : char {
SIBELIUS,
DOLET6,
DOLET8,
FINALE,
NOTEFLIGHT,
OTHER
};

typedef QFlags<MxmlTupletFlag> MxmlTupletFlags;

struct MxmlTupletState {
Expand Down Expand Up @@ -191,15 +200,18 @@ class MusicXMLParserPass1 {
void addInferredTranspose(const QString& partId);
void setHasInferredHeaderText(bool b) { _hasInferredHeaderText = b; }
bool hasInferredHeaderText() const { return _hasInferredHeaderText; }
QString exporterString() const { return _exporterString; }
MusicXMLExporterSoftware exporterSoftware() const { return _exporterSoftware; }
bool sibOrDolet() const;
bool dolet() const;

private:
// functions
void addError(const QString& error); ///< Add an error to be shown in the GUI
void setExporterSoftware(QString& exporter);

// generic pass 1 data
QXmlStreamReader _e;
QString _exporterString; ///< Name of the software which exported the file
MusicXMLExporterSoftware _exporterSoftware = MusicXMLExporterSoftware::OTHER; // Software which exported the file
int _divs; ///< Current MusicXML divisions value
QMap<QString, MusicXmlPart> _parts; ///< Parts data, mapped on part id
std::set<int> _systemStartMeasureNrs; ///< Measure numbers of measures starting a page
Expand Down
46 changes: 30 additions & 16 deletions importexport/musicxml/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2299,7 +2299,7 @@ void MusicXMLParserPass2::part()
// Clean up unterminated ties
for (auto tie : _ties) {
if (tie.second) {
cleanupUnterminatedTie(tie.second, _score, _pass1.exporterString().contains("dolet 6"));
cleanupUnterminatedTie(tie.second, _score, _pass1.exporterSoftware() == MusicXMLExporterSoftware::DOLET6);
_ties[tie.first] = nullptr;
}
}
Expand Down Expand Up @@ -3189,7 +3189,7 @@ void MusicXMLParserDirection::direction(const QString& partId,
int track = _pass1.trackForPart(partId);
bool isVocalStaff = _pass1.isVocalStaff(partId);
bool isExpressionText = false;
bool delayOttava = _pass1.exporterString().contains("sibelius");
bool delayOttava = _pass1.exporterSoftware() == MusicXMLExporterSoftware::SIBELIUS;
_systemDirection = _e.attributes().value("system").toString() == "only-top";
//qDebug("direction track %d", track);
QList<MusicXmlSpannerDesc> starts;
Expand Down Expand Up @@ -3464,21 +3464,25 @@ void MusicXMLParserDirection::direction(const QString& partId,
}
else {
if (spdesc._isStarted) {
// Adjustments to ottavas by the offset value are unwanted
const Fraction spTick = spdesc._sp && spdesc._sp->isOttava() ? tick : tick + _offset;
if (spdesc._sp && spdesc._sp->isOttava() && delayOttava) {
// Sibelius writes ottava ends 1 note too early
_pass2.setDelayedOttava(spdesc._sp);
_pass2.delayedOttava()->setTrack2(track);
_pass2.delayedOttava()->setTick2(tick + _offset);
_pass2.delayedOttava()->setTick2(spTick);
// need to set tick again later
_pass2.clearSpanner(desc);
}
else {
handleSpannerStop(spdesc._sp, track, tick + _offset, spanners);
handleSpannerStop(spdesc._sp, track, spTick, spanners);
_pass2.clearSpanner(desc);
}
}
else {
spdesc._sp = desc._sp;
spdesc._tick2 = tick + _offset;
const Fraction spTick = spdesc._sp && spdesc._sp->isOttava() ? tick : tick + _offset;
spdesc._tick2 = spTick;
spdesc._track2 = track;
spdesc._isStopped = true;
}
Expand Down Expand Up @@ -3668,7 +3672,7 @@ void MusicXMLParserDirection::otherDirection()
// TODO: Multiple sets of maps for exporters other than Dolet 6/Sibelius
// TODO: Add more symbols from Sibelius
QMap<QString, QString> otherDirectionStrings;
if (_pass1.exporterString().contains("dolet")) {
if (_pass1.dolet()) {
otherDirectionStrings = {
{ QString("To Coda"), QString("To Coda") },
{ QString("Segno"), QString("<sym>segno</sym>") },
Expand Down Expand Up @@ -4480,12 +4484,20 @@ void MusicXMLParserDirection::octaveShift(const QString& type, const int number,
else {
Ottava* o = spdesc._isStopped ? toOttava(spdesc._sp) : new Ottava(_score);

// if (placement.isEmpty()) placement = "above"; // TODO ? set default

if (type == "down" && ottavasize == 8) o->setOttavaType(OttavaType::OTTAVA_8VA);
if (type == "down" && ottavasize == 15) o->setOttavaType(OttavaType::OTTAVA_15MA);
if (type == "up" && ottavasize == 8) o->setOttavaType(OttavaType::OTTAVA_8VB);
if (type == "up" && ottavasize == 15) o->setOttavaType(OttavaType::OTTAVA_15MB);
if (type == "down") {
_placement = _placement.isEmpty() ? "above" : _placement;
if (ottavasize == 8)
o->setOttavaType(OttavaType::OTTAVA_8VA);
else if (ottavasize == 15)
o->setOttavaType(OttavaType::OTTAVA_15MA);
}
else /*if (type == "up")*/ {
_placement = _placement.isEmpty() ? "below" : _placement;
if (ottavasize == 8)
o->setOttavaType(OttavaType::OTTAVA_8VB);
else if (ottavasize == 15)
o->setOttavaType(OttavaType::OTTAVA_15MB);
}

const QColor color { _e.attributes().value("color").toString() };
if (color.isValid())
Expand Down Expand Up @@ -6096,7 +6108,7 @@ Note* MusicXMLParserPass2::note(const QString& partId,
QString noteheadValue = _e.readElementText();
if (noteheadValue == "none")
hasHead = false;
else if (noteheadValue == "named" && _pass1.exporterString().contains("noteflight"))
else if (noteheadValue == "named" && _pass1.exporterSoftware() == MusicXMLExporterSoftware::NOTEFLIGHT)
headScheme = NoteHead::Scheme::HEAD_PITCHNAME;
else
headGroup = convertNotehead(noteheadValue);
Expand Down Expand Up @@ -6391,7 +6403,8 @@ Note* MusicXMLParserPass2::note(const QString& partId,
Notation notation { "tied" };
const QString ctype { "type" };
notation.addAttribute(&ctype, &tieType);
addTie(notation, _score, note, cr->track(), _ties, _logger, &_e, _pass1.exporterString().contains("dolet 6"));
addTie(notation, _score, note, cr->track(), _ties, _logger, &_e,
_pass1.exporterSoftware() == MusicXMLExporterSoftware::DOLET6);
}
}

Expand Down Expand Up @@ -7003,7 +7016,7 @@ void MusicXMLParserPass2::harmony(const QString& partId, Measure* measure, const

const HarmonyDesc newHarmonyDesc(track, ha, fd);
bool insert = true;
if (_pass1.exporterString().contains("dolet")) {
if (_pass1.sibOrDolet()) {
const int ticks = (sTime + offset).ticks();
for (auto itr = harmonyMap.begin(); itr != harmonyMap.end(); itr++) {
if (itr->first != ticks)
Expand Down Expand Up @@ -8173,7 +8186,8 @@ void MusicXMLParserNotations::addToScore(ChordRest* const cr, Note* const note,
addGlissandoSlide(notation, note, glissandi, spanners, _logger, &_e);
}
else if (note && notation.name() == "tied") {
addTie(notation, _score, note, cr->track(), ties, _logger, &_e, _pass1.exporterString().contains("dolet 6"));
addTie(notation, _score, note, cr->track(), ties, _logger, &_e,
_pass1.exporterSoftware() == MusicXMLExporterSoftware::DOLET6);
}
else if (note && notation.parent() == "technical") {
addTechnical(notation, note);
Expand Down
Binary file added mtest/musicxml/io/testDoletOttavas.xml
Binary file not shown.
Loading

0 comments on commit 49b7523

Please sign in to comment.