From 65b91f49e12c86eca10aa7e19b57b1b32d10d61c Mon Sep 17 00:00:00 2001 From: Bohumir Zamecnik Date: Tue, 18 Jul 2017 13:49:14 +0700 Subject: [PATCH] Make failing test for reading tracks with overlapping notes. Make two tracks using mido - one good with correct note-on/note-off order, second (bad) with another note-on before note-off at the same tick. Current code fails on the latter. Then read it via PrettyMidi, write it and read it back. It should be the same according to expected values, but due to a bug it differs. --- tests/test_pretty_midi.py | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/test_pretty_midi.py b/tests/test_pretty_midi.py index d0068c6..98a0d39 100644 --- a/tests/test_pretty_midi.py +++ b/tests/test_pretty_midi.py @@ -1,5 +1,6 @@ import pretty_midi import numpy as np +import mido def test_get_beats(): @@ -272,3 +273,56 @@ def simple(): for ks, t, k in zip(pm.key_signature_changes, ks_times, ks_keys): assert ks.time == t assert ks.key_number == k + +def test_properly_order_overlapping_notes(): + def make_mido_track(notes_str, path): + track = mido.MidiTrack() + for line in notes_str.split('\n'): + line = line.strip() + if line: + track.append(mido.Message.from_str(line)) + mido_file = mido.MidiFile() + mido_file.tracks.append(track) + mido_file.save(path) + + # two notes with pitch 72 open at once + bad_track = """ + note_on channel=0 note=72 velocity=88 time=0 + note_on channel=0 note=72 velocity=88 time=48 + note_on channel=0 note=72 velocity=0 time=0 + note_on channel=0 note=74 velocity=88 time=48 + note_on channel=0 note=72 velocity=0 time=0 + note_on channel=0 note=72 velocity=88 time=48 + note_on channel=0 note=74 velocity=0 time=0 + note_on channel=0 note=72 velocity=0 time=48 + """ + + # the 72 note is first closed, then another one opened + good_track = """ + note_on channel=0 note=72 velocity=88 time=0 + note_on channel=0 note=72 velocity=0 time=48 + note_on channel=0 note=72 velocity=88 time=0 + note_on channel=0 note=74 velocity=88 time=48 + note_on channel=0 note=72 velocity=0 time=0 + note_on channel=0 note=72 velocity=88 time=48 + note_on channel=0 note=74 velocity=0 time=0 + note_on channel=0 note=72 velocity=0 time=48 + """ + + for kind, track in (('good', good_track), ('bad', bad_track)): + midi_path = 'overlapping_notes_%s_track.mid' % kind + make_mido_track(track, midi_path) + + pm_song = pretty_midi.PrettyMIDI(midi_path) + + def extract_notes(pm_track): + return np.array([(note.pitch, note.end-note.start) for note in pm_track.notes]) + + expected = np.array([[72, 0.05], [72, 0.05], [74, 0.05], [72, 0.05]]) + assert np.allclose(expected, extract_notes(pm_song.instruments[0])) + + midi_written_path = 'overlapping_notes_%s_track_written.mid' % kind + pm_song.write(midi_written_path) + pm_song_written = pretty_midi.PrettyMIDI(midi_written_path) + + assert np.allclose(expected, extract_notes(pm_song_written.instruments[0]))