Skip to content

consolidateCompletedTuplets combines rests across different triplets that causes malformed MusicXML output #1780

@dszeto

Description

@dszeto

music21 version

9.5.0

Operating System(s) checked
macOS, Linux

Problem summary

When consecutive triplets contain elements that can combine into a full quarter length element, such element will cross triplet boundary.

>>> s.show('text')
{0.0} <music21.note.Note C>
{0.3333} <music21.note.Note C>
{0.6667} <music21.note.Rest 1/3ql>
{1.0} <music21.note.Rest 1/3ql>
{1.3333} <music21.note.Rest 1/3ql>
{1.6667} <music21.note.Note C>
>>> s2 = s.makeNotation()
>>> s2.show('text')
{0.0} <music21.stream.Measure 1 offset=0.0>
    {0.0} <music21.clef.TrebleClef>
    {0.0} <music21.meter.TimeSignature 4/4>
    {0.0} <music21.note.Note C>
    {0.3333} <music21.note.Note C>
    {0.6667} <music21.note.Rest quarter>
    {1.6667} <music21.note.Note C>
    {2.0} <music21.bar.Barline type=final>

When we write s2 out as a MusicXML output, it grouped the triplets and the quarter rest in a way such that notation software fails to interpret it. Below is MuseScore4 as an example.

Image

Looking at the MusicXML output, the tuplet stop tag is placed after the first two 1/3ql note, followed by a ql rest outside of any tuplet tag, and then another tuplet tag that only contain the last 1/3ql note.

    <measure implicit="no" number="1">
      <attributes>
        <divisions>10080</divisions>
        <time>
          <beats>4</beats>
          <beat-type>4</beat-type>
        </time>
        <clef>
          <sign>G</sign>
          <line>2</line>
        </clef>
      </attributes>
      <note>
        <pitch>
          <step>C</step>
          <octave>4</octave>
        </pitch>
        <duration>3360</duration>
        <type>eighth</type>
        <time-modification>
          <actual-notes>3</actual-notes>
          <normal-notes>2</normal-notes>
          <normal-type>eighth</normal-type>
        </time-modification>
        <stem>up</stem>
        <beam number="1">begin</beam>
        <notations>
          <tuplet bracket="yes" number="1" placement="above" type="start">
            <tuplet-actual>
              <tuplet-number>3</tuplet-number>
              <tuplet-type>eighth</tuplet-type>
            </tuplet-actual>
            <tuplet-normal>
              <tuplet-number>2</tuplet-number>
              <tuplet-type>eighth</tuplet-type>
            </tuplet-normal>
          </tuplet>
        </notations>
      </note>
      <note>
        <pitch>
          <step>C</step>
          <octave>4</octave>
        </pitch>
        <duration>3360</duration>
        <type>eighth</type>
        <time-modification>
          <actual-notes>3</actual-notes>
          <normal-notes>2</normal-notes>
          <normal-type>eighth</normal-type>
        </time-modification>
        <stem>up</stem>
        <beam number="1">end</beam>
        <notations>
          <tuplet number="1" type="stop" />
        </notations>
      </note>
      <note>
        <rest />
        <duration>3360</duration>
        <type>eighth</type>
        <time-modification>
          <actual-notes>3</actual-notes>
          <normal-notes>2</normal-notes>
          <normal-type>eighth</normal-type>
        </time-modification>
      </note>
      <note>
        <rest />
        <duration>6720</duration>
        <type>quarter</type>
        <time-modification>
          <actual-notes>3</actual-notes>
          <normal-notes>2</normal-notes>
          <normal-type>quarter</normal-type>
        </time-modification>
      </note>
      <note>
        <pitch>
          <step>C</step>
          <octave>4</octave>
        </pitch>
        <duration>3360</duration>
        <type>eighth</type>
        <time-modification>
          <actual-notes>3</actual-notes>
          <normal-notes>2</normal-notes>
          <normal-type>eighth</normal-type>
        </time-modification>
        <notations>
          <tuplet bracket="no" number="1" placement="above" type="start">
            <tuplet-actual>
              <tuplet-number>3</tuplet-number>
              <tuplet-type>eighth</tuplet-type>
            </tuplet-actual>
            <tuplet-normal>
              <tuplet-number>2</tuplet-number>
              <tuplet-type>eighth</tuplet-type>
            </tuplet-normal>
          </tuplet>
          <tuplet number="1" type="stop" />
        </notations>
      </note>
      <barline location="right">
        <bar-style>light-heavy</bar-style>
      </barline>
    </measure>

Interestingly, reading such MusicXML file back into music21 will re-create the correct structure:

{0.0} <music21.metadata.Metadata object at 0x105d04e10>
{0.0} <music21.stream.Part 0x105d05550>
    {0.0} <music21.instrument.Instrument 'P6042ee7e265acfe49a53036df8d61a0b: '>
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.clef.TrebleClef>
        {0.0} <music21.meter.TimeSignature 4/4>
        {0.0} <music21.note.Note C>
        {0.3333} <music21.note.Note C>
        {0.6667} <music21.note.Rest 1/3ql>
        {1.0} <music21.note.Rest 2/3ql>
        {1.6667} <music21.note.Note C>
        {2.0} <music21.bar.Barline type=final>
{0.0} <music21.layout.ScoreLayout>

Steps to reproduce

s = music21.stream.Stream()
n = music21.note.Note(quarterLength=1/3)
r = music21.note.Rest(quarterLength=1/3)
s.repeatAppend(n,2)
s.repeatAppend(r,3)
s.repeatAppend(n,1)
s.show("text")
s.makeNotation().write("musicxml", "test.musicxml")
# open test.musicxml with any notation software
s2 = music21.converter.parse("test.musicxml")
s2.show("text")

Expected vs. actual behavior

tuplet tags should be placed correctly around tuplet groups so that it can be displayed properly in notation software.

More information

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions