Skip to content

[MU3] [Musicxml Export] - Wrong tuplet calculation in two-note tremolo #6866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 16, 2020
Merged
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
7 changes: 5 additions & 2 deletions importexport/musicxml/exportxml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2253,10 +2253,13 @@ static void tupletStop(const Tuplet* const t, const int number, Notations& notat
// tupletStartStop
//---------------------------------------------------------

static void tupletStartStop(const ChordRest* const cr, Notations& notations, XmlWriter& xml)
static void tupletStartStop(ChordRest* cr, Notations& notations, XmlWriter& xml)
{
const auto nesting = tupletNesting(cr);
const bool doActualAndNormal = (nesting > 1);
bool doActualAndNormal = (nesting > 1);
if (cr->isChord() && isTwoNoteTremolo(toChord(cr))) {
doActualAndNormal = true;
}
for (int level = nesting - 1; level >= 0; --level) {
const auto startTuplet = startTupletAtLevel(cr, level + 1);
if (startTuplet)
Expand Down
29 changes: 20 additions & 9 deletions importexport/musicxml/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -864,9 +864,9 @@ static Fraction calculateTupletDuration(const Tuplet* const t)
foreach (DurationElement* de, t->elements()) {
if (de->type() == ElementType::CHORD || de->type() == ElementType::REST) {
const auto cr = static_cast<ChordRest*>(de);
const auto durationType = cr->actualDurationType();
if (durationType.isValid() && !durationType.isMeasure()) {
res += durationType.fraction();
const auto fraction = cr->ticks(); // TODO : take care of nested tuplets
if (fraction.isValid()) {
res += fraction;
}
}
}
Expand Down Expand Up @@ -914,7 +914,7 @@ static void handleTupletStart(const ChordRest* const cr, Tuplet*& tuplet,
tuplet = new Tuplet(cr->score());
tuplet->setTrack(cr->track());
tuplet->setRatio(Fraction(actualNotes, normalNotes));
// tuplet->setTick(cr->tick());
tuplet->setTick(cr->tick());
tuplet->setBracketType(tupletDesc.bracket);
tuplet->setNumberType(tupletDesc.shownumber);
// TODO type, placement, bracket
Expand Down Expand Up @@ -4065,7 +4065,8 @@ static void addFiguredBassElemens(FiguredBassList& fbl, const Fraction noteStart
static void addTremolo(ChordRest* cr,
const int tremoloNr, const QString& tremoloType,
Chord*& tremStart,
MxmlLogger* logger, const QXmlStreamReader* const xmlreader)
MxmlLogger* logger, const QXmlStreamReader* const xmlreader,
Fraction& timeMod)
{
if (!cr->isChord())
return;
Expand All @@ -4085,6 +4086,10 @@ static void addTremolo(ChordRest* cr,
else if (tremoloType == "start") {
if (tremStart) logger->logError("MusicXML::import: double tremolo start", xmlreader);
tremStart = static_cast<Chord*>(cr);
// timeMod takes into account also the factor 2 of a two-note tremolo
if (timeMod.isValid() && ((timeMod.denominator() % 2) == 0)) {
timeMod.setDenominator(timeMod.denominator() / 2);
}
}
else if (tremoloType == "stop") {
if (tremStart) {
Expand All @@ -4104,6 +4109,10 @@ static void addTremolo(ChordRest* cr,
tremolo->chord2()->setTicks(tremDur);
// add tremolo to first chord (only)
tremStart->add(tremolo);
// timeMod takes into account also the factor 2 of a two-note tremolo
if (timeMod.isValid() && ((timeMod.denominator() % 2) == 0)) {
timeMod.setDenominator(timeMod.denominator() / 2);
}
}
else logger->logError("MusicXML::import: double tremolo stop w/o start", xmlreader);
tremStart = nullptr;
Expand Down Expand Up @@ -4350,7 +4359,7 @@ Note* MusicXMLParserPass2::note(const QString& partId,
// - sTime for non-chord / first chord note
// - prevTime for others
auto noteStartTime = chord ? prevSTime : sTime;
const auto timeMod = mnd.timeMod();
auto timeMod = mnd.timeMod();

// determine tuplet state, used twice (before and after note allocation)
MxmlTupletFlags tupletAction;
Expand Down Expand Up @@ -4524,6 +4533,11 @@ Note* MusicXMLParserPass2::note(const QString& partId,
gac = gcl.size();
}

// handle tremolo before handling tuplet (two note tremolos modify timeMod)
if (cr) {
addTremolo(cr, notations.tremoloNr(), notations.tremoloType(), _tremStart, _logger, &_e, timeMod);
}

// handle tuplet state for the current chord or rest
if (cr) {
if (!chord && !grace) {
Expand Down Expand Up @@ -4574,9 +4588,6 @@ Note* MusicXMLParserPass2::note(const QString& partId,

// add figured bass element
addFiguredBassElemens(fbl, noteStartTime, msTrack, dura, measure);
if (cr) {
addTremolo(cr, notations.tremoloNr(), notations.tremoloType(), _tremStart, _logger, &_e);
}

// don't count chord or grace note duration
// note that this does not check the MusicXML requirement that notes in a chord
Expand Down
Binary file added mtest/musicxml/io/testTwoNoteTremoloTuplet.pdf
Binary file not shown.
217 changes: 217 additions & 0 deletions mtest/musicxml/io/testTwoNoteTremoloTuplet.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="3.1">
<work>
<work-number>MuseScore testfile</work-number>
<work-title>Test</work-title>
</work>
<identification>
<encoding>
<software>MuseScore 0.7.0</software>
<encoding-date>2007-09-10</encoding-date>
<supports element="accidental" type="yes"/>
<supports element="beam" type="yes"/>
<supports element="print" attribute="new-page" type="no"/>
<supports element="print" attribute="new-system" type="no"/>
<supports element="stem" type="yes"/>
</encoding>
</identification>
<part-list>
<score-part id="P1">
<part-name>Piano</part-name>
<part-abbreviation>Pno.</part-abbreviation>
<score-instrument id="P1-I1">
<instrument-name>Piano</instrument-name>
</score-instrument>
<midi-device id="P1-I1" port="1"></midi-device>
<midi-instrument id="P1-I1">
<midi-channel>1</midi-channel>
<midi-program>1</midi-program>
<volume>78.7402</volume>
<pan>0</pan>
</midi-instrument>
</score-part>
</part-list>
<part id="P1">
<measure number="1">
<attributes>
<divisions>12</divisions>
<key>
<fifths>0</fifths>
</key>
<time>
<beats>4</beats>
<beat-type>4</beat-type>
</time>
<clef>
<sign>G</sign>
<line>2</line>
</clef>
</attributes>
<note>
<pitch>
<step>A</step>
<octave>4</octave>
</pitch>
<duration>12</duration>
<voice>1</voice>
<type>half</type>
<time-modification>
<actual-notes>2</actual-notes>
<normal-notes>1</normal-notes>
</time-modification>
<stem>up</stem>
<notations>
<ornaments>
<tremolo type="start">1</tremolo>
</ornaments>
</notations>
</note>
<note>
<pitch>
<step>C</step>
<octave>5</octave>
</pitch>
<duration>12</duration>
<voice>1</voice>
<type>half</type>
<time-modification>
<actual-notes>2</actual-notes>
<normal-notes>1</normal-notes>
</time-modification>
<stem>down</stem>
<notations>
<ornaments>
<tremolo type="stop">1</tremolo>
</ornaments>
</notations>
</note>
<note>
<pitch>
<step>E</step>
<octave>5</octave>
</pitch>
<duration>6</duration>
<voice>1</voice>
<type>quarter</type>
<time-modification>
<actual-notes>2</actual-notes>
<normal-notes>1</normal-notes>
</time-modification>
<stem>down</stem>
<notations>
<ornaments>
<tremolo type="start">1</tremolo>
</ornaments>
</notations>
</note>
<note>
<pitch>
<step>G</step>
<octave>5</octave>
</pitch>
<duration>6</duration>
<voice>1</voice>
<type>quarter</type>
<time-modification>
<actual-notes>2</actual-notes>
<normal-notes>1</normal-notes>
</time-modification>
<stem>down</stem>
<notations>
<ornaments>
<tremolo type="stop">1</tremolo>
</ornaments>
</notations>
</note>
<note>
<rest/>
<duration>12</duration>
<voice>1</voice>
<type>quarter</type>
</note>
</measure>
<measure number="2">
<note>
<pitch>
<step>C</step>
<octave>5</octave>
</pitch>
<duration>8</duration>
<voice>1</voice>
<type>half</type>
<time-modification>
<actual-notes>6</actual-notes>
<normal-notes>2</normal-notes>
</time-modification>
<stem>down</stem>
<notations>
<tuplet type="start" bracket="yes">
<tuplet-actual>
<tuplet-number>3</tuplet-number>
<tuplet-type>quarter</tuplet-type>
</tuplet-actual>
<tuplet-normal>
<tuplet-number>2</tuplet-number>
<tuplet-type>quarter</tuplet-type>
</tuplet-normal>
</tuplet>
<ornaments>
<tremolo type="start">1</tremolo>
</ornaments>
</notations>
</note>
<note>
<pitch>
<step>E</step>
<octave>5</octave>
</pitch>
<duration>8</duration>
<voice>1</voice>
<type>half</type>
<time-modification>
<actual-notes>6</actual-notes>
<normal-notes>2</normal-notes>
</time-modification>
<stem>down</stem>
<notations>
<ornaments>
<tremolo type="stop">1</tremolo>
</ornaments>
</notations>
</note>
<note>
<pitch>
<step>G</step>
<octave>5</octave>
</pitch>
<duration>8</duration>
<voice>1</voice>
<type>quarter</type>
<time-modification>
<actual-notes>3</actual-notes>
<normal-notes>2</normal-notes>
</time-modification>
<stem>down</stem>
<notations>
<tuplet type="stop"/>
</notations>
</note>
<note>
<rest/>
<duration>12</duration>
<voice>1</voice>
<type>quarter</type>
</note>
<note>
<rest/>
<duration>12</duration>
<voice>1</voice>
<type>quarter</type>
</note>
<barline location="right">
<bar-style>light-heavy</bar-style>
</barline>
</measure>
</part>
</score-partwise>
1 change: 1 addition & 0 deletions mtest/musicxml/io/tst_mxml_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ private slots:
void tuplets6() { mxmlIoTestRef("testTuplets6"); }
void tuplets7() { mxmlIoTest("testTuplets7"); }
void tuplets8() { mxmlMscxExportTestRef("testTuplets8"); }
void twoNoteTremoloTuplet() { mxmlIoTest("testTwoNoteTremoloTuplet"); }
void uninitializedDivisions() { mxmlIoTestRef("testUninitializedDivisions"); }
void unusualDurations() { mxmlIoTestRef("testUnusualDurations"); }
void virtualInstruments() { mxmlIoTestRef("testVirtualInstruments"); }
Expand Down