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

Assignees

No one assigned

    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