Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 186 additions & 7 deletions src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#include "engraving/dom/measure.h"
#include "engraving/dom/mscore.h"
#include "engraving/dom/note.h"
#include "engraving/dom/ornament.h"
#include "engraving/dom/ottava.h"
#include "engraving/dom/part.h"
#include "engraving/dom/pedal.h"
Expand Down Expand Up @@ -118,6 +119,53 @@ static std::shared_ptr<mu::iex::musicxml::IMusicXmlConfiguration> configuration(
return muse::modularity::globalIoc()->resolve<mu::iex::musicxml::IMusicXmlConfiguration>("iex_musicxml");
}

static void setOrnamentIntervalFromAccidental(Ornament* ornam, Note* mainNote, AccidentalType accType, bool above)
{
if (!mainNote) {
return;
}

// Desired alter value
AccidentalVal desiredAlter = Accidental::subtype2value(accType);

// Try different interval types
// MAJOR, MINOR, PERFECT, AUGMENTED, DIMINISHED
static const std::vector<IntervalType> types = {
IntervalType::MAJOR, IntervalType::MINOR,
IntervalType::AUGMENTED, IntervalType::DIMINISHED,
IntervalType::PERFECT
};

for (IntervalType it : types) {
OrnamentInterval oi;
oi.type = it;
oi.step = IntervalStep::SECOND;

Interval interval = Interval::fromOrnamentInterval(oi);
if (!above) {
interval.flip();
}

int tpc = mainNote->tpc();
int transposedTpc = Transpose::transposeTpc(tpc, interval, true);
if (tpcIsValid(transposedTpc)) {
if (tpc2alter(transposedTpc) == desiredAlter) {
if (above) {
ornam->setIntervalAbove(oi);
} else {
ornam->setIntervalBelow(oi);
}
ornam->setShowAccidental(OrnamentShowAccidental::ALWAYS);
return;
}
}
}

// Fallback: if no match found with SECOND step, try UNISON for unusual accidentals?
// Or just force the accidental visibility anyway.
ornam->setShowAccidental(OrnamentShowAccidental::ALWAYS);
}

static std::shared_ptr<mu::engraving::IEngravingFontsProvider> engravingFonts()
{
return muse::modularity::globalIoc()->resolve<mu::engraving::IEngravingFontsProvider>("iex_musicxml");
Expand Down Expand Up @@ -1360,6 +1408,25 @@ static void addMordentToChord(const Notation& notation, ChordRest* cr)
mordent->setVisible(notation.visible());
colorItem(mordent, Color::fromString(notation.attribute(u"color")));
cr->add(mordent);

const String accidAbove = notation.attribute(u"above");
if (!accidAbove.empty()) {
const AccidentalType type = musicXmlString2accidentalType(accidAbove, notation.attribute(u"above-smufl"));
if (type != AccidentalType::NONE) {
Chord* chord = cr->isChord() ? toChord(cr) : nullptr;
Note* mainNote = chord ? chord->upNote() : nullptr;
setOrnamentIntervalFromAccidental(mordent, mainNote, type, true);
}
}
const String accidBelow = notation.attribute(u"below");
if (!accidBelow.empty()) {
const AccidentalType type = musicXmlString2accidentalType(accidBelow, notation.attribute(u"below-smufl"));
if (type != AccidentalType::NONE) {
Chord* chord = cr->isChord() ? toChord(cr) : nullptr;
Note* mainNote = chord ? chord->upNote() : nullptr;
setOrnamentIntervalFromAccidental(mordent, mainNote, type, false);
}
}
} else {
LOGD("unknown ornament: name '%s' long '%s' approach '%s' departure '%s'",
muPrintable(name), muPrintable(attrLong), muPrintable(attrAppr), muPrintable(attrDep)); // TODO
Expand All @@ -1377,6 +1444,9 @@ static void addMordentToChord(const Notation& notation, ChordRest* cr)
static void addTurnToChord(const Notation& notation, ChordRest* cr)
{
SymId turnSym = notation.symId();
const String accidAbove = notation.attribute(u"above");
const String accidBelow = notation.attribute(u"below");
const Color color = Color::fromString(notation.attribute(u"color"));
const String place = notation.attribute(u"placement");
if (notation.attribute(u"slash") == "yes") {
// TODO: currently this is the only available SMuFL turn with a slash
Expand All @@ -1394,6 +1464,23 @@ static void addTurnToChord(const Notation& notation, ChordRest* cr)
turn->setVisible(notation.visible());
colorItem(turn, Color::fromString(notation.attribute(u"color")));
cr->add(turn);

if (!accidAbove.empty()) {
const AccidentalType type = musicXmlString2accidentalType(accidAbove, notation.attribute(u"above-smufl"));
if (type != AccidentalType::NONE) {
Chord* chord = cr->isChord() ? toChord(cr) : nullptr;
Note* mainNote = chord ? chord->upNote() : nullptr;
setOrnamentIntervalFromAccidental(turn, mainNote, type, true);
}
}
if (!accidBelow.empty()) {
const AccidentalType type = musicXmlString2accidentalType(accidBelow, notation.attribute(u"below-smufl"));
if (type != AccidentalType::NONE) {
Chord* chord = cr->isChord() ? toChord(cr) : nullptr;
Note* mainNote = chord ? chord->upNote() : nullptr;
setOrnamentIntervalFromAccidental(turn, mainNote, type, false);
}
}
}

//---------------------------------------------------------
Expand All @@ -1417,6 +1504,25 @@ static void addOtherOrnamentToChord(const Notation& notation, ChordRest* cr)
ornam->setVisible(notation.visible());
colorItem(ornam, Color::fromString(notation.attribute(u"color")));
cr->add(ornam);

const String accidAbove = notation.attribute(u"above");
if (!accidAbove.empty()) {
const AccidentalType type = musicXmlString2accidentalType(accidAbove, notation.attribute(u"above-smufl"));
if (type != AccidentalType::NONE) {
Chord* chord = cr->isChord() ? toChord(cr) : nullptr;
Note* mainNote = chord ? chord->upNote() : nullptr;
setOrnamentIntervalFromAccidental(ornam, mainNote, type, true);
}
}
const String accidBelow = notation.attribute(u"below");
if (!accidBelow.empty()) {
const AccidentalType type = musicXmlString2accidentalType(accidBelow, notation.attribute(u"below-smufl"));
if (type != AccidentalType::NONE) {
Chord* chord = cr->isChord() ? toChord(cr) : nullptr;
Note* mainNote = chord ? chord->upNote() : nullptr;
setOrnamentIntervalFromAccidental(ornam, mainNote, type, false);
}
}
} else {
LOGD("unknown ornament: name '%s': '%s'.", muPrintable(name), muPrintable(symname));
}
Expand Down Expand Up @@ -8552,6 +8658,8 @@ void MusicXmlParserNotations::articulations()
void MusicXmlParserNotations::ornaments()
{
bool trillMark = false;
String trillAccidAbove, trillAccidBelow, trillAccidAboveSmufl, trillAccidBelowSmufl;

// <trill-mark placement="above"/>
while (m_e.readNextStartElement()) {
SymId id { SymId::noSym };
Expand All @@ -8563,6 +8671,9 @@ void MusicXmlParserNotations::ornaments()
m_e.skipCurrentElement(); // skip but don't log
} else if (m_e.name() == "trill-mark") {
trillMark = true;
Notation notation = Notation::notationWithAttributes(u"trill-mark", m_e.attributes(), u"ornaments", SymId::ornamentTrill);
notation.setVisible(m_visible);
m_notations.push_back(notation);
m_e.skipCurrentElement(); // skip but don't log
} else if (m_e.name() == "wavy-line") {
bool wavyLineTypeWasStart = (m_wavyLineType == "start");
Expand All @@ -8581,6 +8692,13 @@ void MusicXmlParserNotations::ornaments()
if (wavyLineTypeWasStart && m_wavyLineType == u"stop") {
m_wavyLineType = u"startstop";
}
// If it is a start, it might be associated with a preceding trill-mark's accidentals
if (m_wavyLineType == u"start" || m_wavyLineType == u"startstop") {
m_wavyLineAccidAbove = trillAccidAbove;
m_wavyLineAccidBelow = trillAccidBelow;
m_wavyLineAccidAboveSmufl = trillAccidAboveSmufl;
m_wavyLineAccidBelowSmufl = trillAccidBelowSmufl;
}
m_e.skipCurrentElement(); // skip but don't log
} else if (m_e.name() == "tremolo") {
m_hasTremolo = true;
Expand All @@ -8597,17 +8715,59 @@ void MusicXmlParserNotations::ornaments()
notation.setVisible(m_visible);
m_notations.push_back(notation);
m_e.skipCurrentElement(); // skip but don't log
} else if (m_e.name() == "accidental-mark") {
if (!m_notations.empty()) {
Notation& lastNotation = m_notations.back();
String placement = m_e.attribute("placement");
String smufl = m_e.attribute("smufl");
if (lastNotation.parent() == u"ornaments") {
if (placement.empty()) {
if (lastNotation.name() == u"turn" || lastNotation.name() == u"inverted-turn") {
if (lastNotation.attribute(u"above").empty()) {
placement = u"above";
} else {
placement = u"below";
}
} else {
placement = (lastNotation.name() == u"mordent" ? u"below" : u"above");
}
}
String text = m_e.readText();
lastNotation.addAttribute(placement, text);
if (!smufl.empty()) {
lastNotation.addAttribute(placement + u"-smufl", smufl);
}

// Also store if it's a trill-mark, for possible wavy-line association
if (lastNotation.name() == u"trill-mark") {
if (placement == u"above") {
trillAccidAbove = text;
trillAccidAboveSmufl = smufl;
} else {
trillAccidBelow = text;
trillAccidBelowSmufl = smufl;
}
}
} else {
m_e.skipCurrentElement();
}
} else {
m_e.skipCurrentElement(); // skip but don't log
}
} else {
skipLogCurrElem();
}
}

// note that mscore wavy line already implicitly includes a trillsym
// so don't add an additional one
if (trillMark && m_wavyLineType != "start" && m_wavyLineType != "startstop") {
Notation ornament = Notation::notationWithAttributes(u"trill-mark", m_e.attributes(), u"ornaments", SymId::ornamentTrill);
ornament.setVisible(m_visible);
m_notations.push_back(ornament);
// so remove it if it was added
if (trillMark && (m_wavyLineType == "start" || m_wavyLineType == "startstop")) {
for (auto it = m_notations.begin(); it != m_notations.end(); ++it) {
if (it->name() == u"trill-mark") {
m_notations.erase(it);
break;
}
}
}
}

Expand Down Expand Up @@ -9150,7 +9310,9 @@ static void addTie(const Notation& notation, Note* note, const track_idx_t track
static void addWavyLine(ChordRest* cr, const Fraction& tick,
const int wavyLineNo, const String& wavyLineType,
MusicXmlSpannerMap& spanners, TrillStack& trills,
MusicXmlLogger* logger, const XmlStreamReader* const xmlreader)
MusicXmlLogger* logger, const XmlStreamReader* const xmlreader,
const String& accidAbove = {}, const String& accidBelow = {},
const String& accidAboveSmufl = {}, const String& accidBelowSmufl = {})
{
if (!wavyLineType.empty()) {
const Fraction ticks = cr->ticks();
Expand All @@ -9167,6 +9329,22 @@ static void addWavyLine(ChordRest* cr, const Fraction& tick,
trill->setOrnament(Factory::createOrnament(cr));
trill->ornament()->setAnchor(ArticulationAnchor::AUTO);

Chord* chord = cr->isChord() ? toChord(cr) : nullptr;
Note* mainNote = chord ? chord->upNote() : nullptr;

if (!accidAbove.empty()) {
const AccidentalType type = musicXmlString2accidentalType(accidAbove, accidAboveSmufl);
if (type != AccidentalType::NONE) {
setOrnamentIntervalFromAccidental(trill->ornament(), mainNote, type, true);
}
}
if (!accidBelow.empty()) {
const AccidentalType type = musicXmlString2accidentalType(accidBelow, accidBelowSmufl);
if (type != AccidentalType::NONE) {
setOrnamentIntervalFromAccidental(trill->ornament(), mainNote, type, false);
}
}

if (wavyLineType == u"start") {
spanners[trill] = std::pair<int, int>(tick.ticks(), -1);
// LOGD("trill=%p inserted at first tick %d", trill, tick);
Expand Down Expand Up @@ -9449,7 +9627,8 @@ void MusicXmlParserNotations::addToScore(ChordRest* const cr, Note* const note,
DelayedArpMap& delayedArps)
{
addArpeggio(cr, m_arpeggioType, m_arpeggioNo, m_arpeggioColor, arpMap, delayedArps);
addWavyLine(cr, tick, m_wavyLineNo, m_wavyLineType, spanners, trills, m_logger, &m_e);
addWavyLine(cr, tick, m_wavyLineNo, m_wavyLineType, spanners, trills, m_logger, &m_e,
m_wavyLineAccidAbove, m_wavyLineAccidBelow, m_wavyLineAccidAboveSmufl, m_wavyLineAccidBelowSmufl);

for (const Notation& notation : m_notations) {
if (notation.symId() != SymId::noSym) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,10 @@ class MusicXmlParserNotations
engraving::Color m_tremoloColor;
muse::String m_wavyLineType;
int m_wavyLineNo = 0;
muse::String m_wavyLineAccidAbove;
muse::String m_wavyLineAccidBelow;
muse::String m_wavyLineAccidAboveSmufl;
muse::String m_wavyLineAccidBelowSmufl;
muse::String m_arpeggioType;
int m_arpeggioNo = 0;
engraving::Color m_arpeggioColor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ extern muse::String accSymId2MusicXmlString(const engraving::SymId id);
extern muse::String accSymId2SmuflMusicXmlString(const engraving::SymId id);
extern muse::String accidentalType2MusicXmlString(const engraving::AccidentalType type);
extern muse::String accidentalType2SmuflMusicXmlString(const engraving::AccidentalType type);
extern engraving::AccidentalType musicXmlString2accidentalType(const muse::String mxmlName, const muse::String smufl);
extern engraving::AccidentalType musicXmlString2accidentalType(const muse::String mxmlName, const muse::String smufl = {});
extern muse::String musicXmlAccidentalTextToChar(const muse::String mxmlName);
extern engraving::SymId musicXmlString2accSymId(const muse::String mxmlName, const muse::String smufl = {});
extern engraving::AccidentalType microtonalGuess(double val);
Expand Down
Loading