Skip to content

Between-note tremolo gains a spurious visible tuplet bracket on MusicXML round-trip #1907

@leahchilders

Description

@leahchilders
Image Image

music21 version

9.9.2 and 10.3.0

Operating System(s) checked
This was checked on Linux but is not OS-specific because it's pure Python logic

Problem summary

When using tremolos between notes, roundtrips add a spurious visible tuplet bracket with the number visible. Two notes in the time of one note give a 2/1 time-modification which is not a printed tuplet, but tuplet notation is not properly suppressed after a parse --> write reconstruction back into musicxml roundtrip.

Steps to reproduce

import music21
from music21 import converter

# Minimal between-note tremolo: two whole notes (E3, G3) in the time of one.
# NOTE: there is no <tuplet> element anywhere -- only <time-modification>.
src = '''<?xml version="1.0" encoding="UTF-8"?>
<score-partwise version="4.0">
  <part-list><score-part id="P1"><part-name>Piano</part-name></score-part></part-list>
  <part id="P1">
    <measure number="1">
      <attributes>
        <divisions>256</divisions>
        <key><fifths>0</fifths></key>
        <time><beats>2</beats><beat-type>2</beat-type></time>
        <clef><sign>F</sign><line>4</line></clef>
      </attributes>
      <note>
        <pitch><step>E</step><octave>3</octave></pitch>
        <duration>512</duration><voice>1</voice><type>whole</type>
        <time-modification><actual-notes>2</actual-notes><normal-notes>1</normal-notes></time-modification>
        <notations><ornaments><tremolo type="start">4</tremolo></ornaments></notations>
      </note>
      <note>
        <pitch><step>G</step><octave>3</octave></pitch>
        <duration>512</duration><voice>1</voice><type>whole</type>
        <time-modification><actual-notes>2</actual-notes><normal-notes>1</normal-notes></time-modification>
        <notations><ornaments><tremolo type="stop">4</tremolo></ornaments></notations>
      </note>
    </measure>
  </part>
</score-partwise>'''

s = converter.parse(src, format='musicxml')

# The importer left the auto-created tuplet visible, even though the source
# had no <tuplet> element:
for n in s.recurse().notes:
    t = n.duration.tuplets[0]
    print(n.nameWithOctave, 'bracket=', t.bracket, 'actualShow=', t.tupletActualShow)
# E3 bracket= True actualShow= number
# G3 bracket= True actualShow= number

out = s.write('musicxml')
xml = open(out, encoding='utf-8').read()
print('bracket="yes" in output:', 'bracket="yes"' in xml)   # -> True

Expected vs. actual behavior

Expected: Round trip should keep the tremolo and time-modification but NOT emit a visible tuplet bracket (either no <tuplet> element, or <tuplet bracket="no" show-number="none">), since the source drew no bracket.)

Actual: output contains <tuplet bracket="yes" number="1" placement="above" type="start"><tuplet type="stop"/>
More information

Issue appears to be a fallback tuplet from when there are no tuplet definitions in the tuplet tag. (MeasureParser.xmlToTuplets() in music21/musicxml/xmlToM21.py)

Possible fix: when the fallback 2:1 tuplet is created from <time-modification> alone (no matching <tuplet>), set tup.bracket = False, tup.tupletActualShow = None, and tup.tupletNormalShow = None. Clearest when note has a <tremolo type="start|stop"> ornament, where the 2:1 time-modification is directly from the tremolo and not a tuplet.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions