Skip to content

MIDI import: makeTies()/makeMeasures() permanently desyncs later note offsets by whole bars when simultaneous notes of different length collide #2020

Description

@JackyChanMusic

music21 version: 10.5.0

MIDI import permanently desyncs note offsets by one bar once a short tied note ends while a longer tied note is still sounding — every later note shifts one bar late from there on. Seen on real orchestral MIDI, inflating a track's measure count.

Minimal reproduction

import mido, music21

t = 480
mid = mido.MidiFile(ticks_per_beat=t, type=1)
trk = mido.MidiTrack(); mid.tracks.append(trk)
M = mido.Message
trk.extend([
    M('note_on',  note=58, velocity=100, time=0),
    M('note_on',  note=62, velocity=100, time=0),
    M('note_on',  note=55, velocity=100, time=0),
    M('note_off', note=58, velocity=0,   time=t*8),
    M('note_off', note=62, velocity=0,   time=0),
    M('note_off', note=55, velocity=0,   time=t*4),
    M('note_on',  note=60, velocity=100, time=0),
    M('note_off', note=60, velocity=0,   time=t),
])
mid.save('repro.mid')

raw = music21.converter.parse('repro.mid', quantizePost=False)
for n in sorted(raw.flatten().notes, key=lambda x: x.offset):
    print(n.offset, n.quarterLength, n.tie)

Last note should land at offset 12.0; it lands at 16.0:

0.0  4.0  <Tie start>
0.0  4.0  <Tie start>
8.0  4.0  <Tie stop>
8.0  4.0  <Tie continue>
12.0 4.0  <Tie stop>
16.0 1.0  None

Workaround: cross-check against a raw mido decode and rebuild when it disagrees; real fix likely belongs in midiTrackToStream's makeMeasures()/makeTies().

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