fix #281592 - Trills not exported properly to musicxml

This commit is contained in:
Leon Vinken 2019-07-14 12:55:43 +02:00
parent cc654b11fc
commit 5d2f1d524f
6 changed files with 124 additions and 152 deletions

View file

@ -793,7 +793,6 @@ class Score : public QObject, public ScoreElement {
Segment* tick2segmentMM(const Fraction& tick, bool first, SegmentType st) const;
Segment* tick2segmentMM(const Fraction& tick) const;
Segment* tick2segmentMM(const Fraction& tick, bool first) const;
Segment* tick2segmentEnd(int track, const Fraction& tick) const;
Segment* tick2leftSegment(const Fraction& tick) const;
Segment* tick2rightSegment(const Fraction& tick) const;
void fixTicks();

View file

@ -172,40 +172,6 @@ Segment* Score::tick2segment(const Fraction& tick, bool first) const
return tick2segment(tick, first, SegmentType::All, false);
}
//---------------------------------------------------------
// tick2segmentEnd
// Find a segment containing a note or rest in track ending at tick
// Return the segment or null
//---------------------------------------------------------
Segment* Score::tick2segmentEnd(int track, const Fraction& tick) const
{
Measure* m = tick2measure(tick);
if (m == 0) {
qDebug("tick2segment(): not found tick %d", tick.ticks());
return 0;
}
// loop over all segments
for (Segment* segment = m->first(SegmentType::ChordRest); segment; segment = segment->next(SegmentType::ChordRest)) {
ChordRest* cr = toChordRest(segment->element(track));
if (!cr)
continue;
// TODO LVI: check if following is correct, see exceptions in
// ExportMusicXmlchord() and ExportMusicXmlrest()
Fraction endTick = cr->tick() + cr->actualTicks();
if (endTick < tick)
continue; // not found yet
else if (endTick == tick) {
return segment; // found it
}
else {
// endTick > tick (beyond the tick we are looking for)
return 0;
}
}
return 0;
}
//---------------------------------------------------------
// tick2leftSegment
/// return the segment at this tick position if any or

View file

@ -881,87 +881,30 @@ public:
// trill handling
//---------------------------------------------------------
// Find chords to attach trills to. This is necessary because in MuseScore
// trills are spanners (thus attached to segments), while in MusicXML trills
// are attached to notes.
// TBD: must trill end be in the same staff as trill started ?
// if so, no need to pass in strack and etrack (trill has a track)
static void findTrillAnchors(const Trill* trill, Chord*& startChord, Chord*& stopChord)
{
const Segment* seg = trill->startSegment();
const Fraction endTick = trill->tick2();
const int strack = trill->track();
// try to find chords in the same track:
// find a track with suitable chords both for start and stop
for (int i = 0; i < VOICES; ++i) {
Element* el = seg->element(strack + i);
if (!el)
continue;
if (el->type() != ElementType::CHORD)
continue;
startChord = static_cast<Chord*>(el);
Segment* s = trill->score()->tick2segmentEnd(strack + i, endTick);
if (!s)
continue;
el = s->element(strack + i);
if (!el)
continue;
if (el->type() != ElementType::CHORD)
continue;
stopChord = static_cast<Chord*>(el);
return;
}
// try to find start/stop chords in different tracks
for (int i = 0; i < VOICES; ++i) {
Element* el = seg->element(strack + i);
if (!el)
continue;
if (el->type() != ElementType::CHORD)
continue;
startChord = static_cast<Chord*>(el);
break; // first chord found is OK
}
for (int i = 0; i < VOICES; ++i) {
Segment* s = trill->score()->tick2segmentEnd(strack + i, endTick);
if (!s)
continue;
Element* el = s->element(strack + i);
if (!el)
continue;
if (el->type() != ElementType::CHORD)
continue;
stopChord = static_cast<Chord*>(el);
break; // first chord found is OK
}
}
// find all trills in this measure and this part
static void findTrills(Measure* measure, int strack, int etrack, TrillHash& trillStart, TrillHash& trillStop)
{
// loop over all spanners in this measure
Fraction stick = measure->tick();
Fraction etick = measure->tick() + measure->ticks();
auto stick = measure->tick();
auto etick = measure->tick() + measure->ticks();
for (auto it = measure->score()->spanner().lower_bound(stick.ticks()); it != measure->score()->spanner().upper_bound(etick.ticks()); ++it) {
Spanner* e = it->second;
//qDebug("findTrills 1 trill %p type %d track %d tick %d", e, e->type(), e->track(), e->tick());
if (e->type() == ElementType::TRILL && strack <= e->track() && e->track() < etrack
auto e = it->second;
//qDebug("1 trill %p type %d track %d tick %s", e, e->type(), e->track(), qPrintable(e->tick().print()));
if (e->isTrill() && strack <= e->track() && e->track() < etrack
&& e->tick() >= measure->tick() && e->tick() < (measure->tick() + measure->ticks()))
{
//qDebug("findTrills 2 trill %p", e);
//qDebug("2 trill %p", e);
// a trill is found starting in this segment, trill end time is known
// determine notes to write trill start and stop
const Trill* tr = static_cast<const Trill*>(e);
Chord* startChord = 0; // chord where trill starts
Chord* stopChord = 0; // chord where trill stops
findTrillAnchors(tr, startChord, stopChord);
//qDebug("findTrills 3 tr %p startChord %p stopChord %p", tr, startChord, stopChord);
const auto tr = toTrill(e);
auto elem1 = tr->startElement();
auto elem2 = tr->endElement();
if (startChord && stopChord) {
trillStart.insert(startChord, tr);
trillStop.insert(stopChord, tr);
if (elem1 && elem1->isChord() && elem2 && elem2->isChord()) {
trillStart.insert(toChord(elem1), tr);
trillStop.insert(toChord(elem2), tr);
}
}
}
@ -1815,6 +1758,36 @@ int ExportMusicXml::findTrill(const Trill* tr) const
return -1;
}
//---------------------------------------------------------
// wavyLineStart
//---------------------------------------------------------
static void wavyLineStart(const Trill* tr, const int number, Notations& notations, Ornaments& ornaments, XmlWriter& xml)
{
// mscore only supports wavy-line with trill-mark
notations.tag(xml);
ornaments.tag(xml);
xml.tagE("trill-mark");
QString tagName = "wavy-line type=\"start\"";
tagName += QString(" number=\"%1\"").arg(number + 1);
tagName += color2xml(tr);
tagName += addPositioningAttributes(tr, true);
xml.tagE(tagName);
}
//---------------------------------------------------------
// wavyLineStop
//---------------------------------------------------------
static void wavyLineStop(const Trill* tr, const int number, Notations& notations, Ornaments& ornaments, XmlWriter& xml)
{
notations.tag(xml);
ornaments.tag(xml);
QString trillXml = QString("wavy-line type=\"stop\" number=\"%1\"").arg(number + 1);
trillXml += addPositioningAttributes(tr, false);
xml.tagE(trillXml);
}
//---------------------------------------------------------
// wavyLineStartStop
//---------------------------------------------------------
@ -1822,53 +1795,54 @@ int ExportMusicXml::findTrill(const Trill* tr) const
void ExportMusicXml::wavyLineStartStop(Chord* chord, Notations& notations, Ornaments& ornaments,
TrillHash& trillStart, TrillHash& trillStop)
{
if (trillStop.contains(chord)) {
const Trill* tr = trillStop.value(chord);
int n = findTrill(tr);
if (n >= 0)
// trill stop after trill start
trills[n] = 0;
else {
// trill stop before trill start
n = findTrill(0);
if (n >= 0)
trills[n] = tr;
else
qDebug("too many overlapping trills (chord %p staff %d tick %d)",
chord, chord->staffIdx(), chord->tick().ticks());
}
if (trillStart.contains(chord) && trillStop.contains(chord)) {
const auto tr = trillStart.value(chord);
auto n = findTrill(0);
if (n >= 0) {
notations.tag(_xml);
ornaments.tag(_xml);
QString trillXml = QString("wavy-line type=\"stop\" number=\"%1\"").arg(n + 1);
trillXml += addPositioningAttributes(tr, false);
_xml.tagE(trillXml);
wavyLineStart(tr, n, notations, ornaments, _xml);
wavyLineStop(tr, n, notations, ornaments, _xml);
}
trillStop.remove(chord);
else
qDebug("too many overlapping trills (chord %p staff %d tick %d)",
chord, chord->staffIdx(), chord->tick().ticks());
}
if (trillStart.contains(chord)) {
const Trill* tr = trillStart.value(chord);
int n = findTrill(tr);
if (n >= 0)
qDebug("wavyLineStartStop error");
else {
n = findTrill(0);
if (n >= 0) {
trills[n] = tr;
// mscore only supports wavy-line with trill-mark
notations.tag(_xml);
ornaments.tag(_xml);
_xml.tagE("trill-mark");
QString tagName = "wavy-line type=\"start\"";
tagName += QString(" number=\"%1\"").arg(n + 1);
tagName += color2xml(tr);
tagName += addPositioningAttributes(tr, true);
_xml.tagE(tagName);
else {
if (trillStop.contains(chord)) {
const auto tr = trillStop.value(chord);
auto n = findTrill(tr);
if (n >= 0)
// trill stop after trill start
trills[n] = 0;
else {
// trill stop before trill start
n = findTrill(0);
if (n >= 0)
trills[n] = tr;
else
qDebug("too many overlapping trills (chord %p staff %d tick %d)",
chord, chord->staffIdx(), chord->tick().ticks());
}
if (n >= 0) {
wavyLineStop(tr, n, notations, ornaments, _xml);
}
trillStop.remove(chord);
}
if (trillStart.contains(chord)) {
const auto tr = trillStart.value(chord);
auto n = findTrill(tr);
if (n >= 0)
qDebug("wavyLineStartStop error");
else {
n = findTrill(0);
if (n >= 0) {
trills[n] = tr;
wavyLineStart(tr, n, notations, ornaments, _xml);
}
else
qDebug("too many overlapping trills (chord %p staff %d tick %d)",
chord, chord->staffIdx(), chord->tick().ticks());
trillStart.remove(chord);
}
else
qDebug("too many overlapping trills (chord %p staff %d tick %d)",
chord, chord->staffIdx(), chord->tick().ticks());
trillStart.remove(chord);
}
}
}

View file

@ -5599,6 +5599,7 @@ void MusicXMLParserNotations::ornaments()
_e.readNext();
}
else if (_e.name() == "wavy-line") {
auto wavyLineTypeWasStart = (_wavyLineType == "start");
_wavyLineType = _e.attributes().value("type").toString();
_wavyLineNo = _e.attributes().value("number").toString().toInt();
if (_wavyLineNo > 0) _wavyLineNo--;
@ -5608,6 +5609,10 @@ void MusicXMLParserNotations::ornaments()
if (_wavyLineType == "stop") {
_wavyLineStop = true;
}
// check for start and stop on same note
if (wavyLineTypeWasStart && _wavyLineType == "stop") {
_wavyLineType = "startstop";
}
_e.readNext();
}
else if (_e.name() == "tremolo") {
@ -5631,7 +5636,7 @@ void MusicXMLParserNotations::ornaments()
// note that mscore wavy line already implicitly includes a trillsym
// so don't add an additional one
if (trillMark && _wavyLineType != "start")
if (trillMark && _wavyLineType != "start" && _wavyLineType != "startstop")
_articulationSymbols.push_back(SymId::ornamentTrill);
}
@ -5905,15 +5910,22 @@ static void addWavyLine(ChordRest* cr, const Fraction& tick,
const auto track = cr->track();
const auto trk = (track / VOICES) * VOICES; // first track of staff
Trill*& trill = trills[wavyLineNo];
if (wavyLineType == "start") {
if (wavyLineType == "start" || wavyLineType == "startstop") {
if (trill) {
logger->logError(QString("overlapping wavy-line number %1").arg(wavyLineNo+1), xmlreader);
}
else {
trill = new Trill(cr->score());
trill->setTrack(trk);
spanners[trill] = QPair<int, int>(tick.ticks(), -1);
// qDebug("wedge trill=%p inserted at first tick %d", trill, tick);
if (wavyLineType == "start") {
spanners[trill] = QPair<int, int>(tick.ticks(), -1);
// qDebug("trill=%p inserted at first tick %d", trill, tick);
}
if (wavyLineType == "startstop") {
spanners[trill] = QPair<int, int>(tick.ticks(), tick.ticks() + ticks.ticks());
trill = nullptr;
// qDebug("trill=%p inserted at first tick %d second tick %d", trill, tick, tick);
}
}
}
else if (wavyLineType == "stop") {
@ -5922,7 +5934,7 @@ static void addWavyLine(ChordRest* cr, const Fraction& tick,
}
else {
spanners[trill].second = tick.ticks() + ticks.ticks();
// qDebug("wedge trill=%p second tick %d", trill, tick);
// qDebug("trill=%p second tick %d", trill, tick);
trill = nullptr;
}
}

Binary file not shown.

View file

@ -361,5 +361,26 @@
<stem>down</stem>
</note>
</measure>
<measure number="7">
<note>
<pitch>
<step>G</step>
<octave>4</octave>
</pitch>
<duration>4</duration>
<voice>1</voice>
<type>whole</type>
<notations>
<ornaments>
<trill-mark/>
<wavy-line type="start" number="1"/>
<wavy-line type="stop" number="1"/>
</ornaments>
</notations>
</note>
<barline location="right">
<bar-style>light-heavy</bar-style>
</barline>
</measure>
</part>
</score-partwise>