fix #281592 - Trills not exported properly to musicxml
This commit is contained in:
parent
cc654b11fc
commit
5d2f1d524f
6 changed files with 124 additions and 152 deletions
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue