From 23f47f8424c8cbf2d02fbbc26f2fe8000f915e6a Mon Sep 17 00:00:00 2001 From: George Ogden Date: Wed, 15 Jun 2022 11:59:54 +0100 Subject: [PATCH 01/23] updated .gitignore to ignore .mid files --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index d2cad58..e524a23 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# Working files +*.mid +*.midi +# add any permanent files with git add -f + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] From 798b4dae9373f77dbb6a66e7796cc17b2b77c8a5 Mon Sep 17 00:00:00 2001 From: George Ogden Date: Wed, 15 Jun 2022 17:14:27 +0100 Subject: [PATCH 02/23] fixed error with loading mixed tempi --- pypianoroll/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypianoroll/inputs.py b/pypianoroll/inputs.py index 9f79205..b84953b 100644 --- a/pypianoroll/inputs.py +++ b/pypianoroll/inputs.py @@ -234,7 +234,7 @@ def from_pretty_midi( one_more_beat = 2 * beat_times[-1] - beat_times[-2] beat_times_one_more = np.append(beat_times, one_more_beat) bpm = 60.0 / np.diff(beat_times_one_more) - tempo = np.tile(bpm, (1, 24)).reshape(-1, 1) + tempo = np.repeat(bpm, resolution).reshape(-1, 1) # Parse the tracks tracks = [] From 8151d2211f34a1ae6b4a77d2eb813a4667101ed6 Mon Sep 17 00:00:00 2001 From: George Ogden Date: Wed, 15 Jun 2022 17:29:25 +0100 Subject: [PATCH 03/23] added speed changes --- pypianoroll/outputs.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pypianoroll/outputs.py b/pypianoroll/outputs.py index b1ee31d..998bc1c 100644 --- a/pypianoroll/outputs.py +++ b/pypianoroll/outputs.py @@ -137,12 +137,13 @@ def to_pretty_midi( if default_tempo is not None: tempo = default_tempo elif multitrack.tempo is not None: - tempo = float(scipy.stats.hmean(multitrack.tempo)) + # tempo = float(scipy.stats.hmean(multitrack.tempo)) + tempo = multitrack.tempo[:,0] else: tempo = DEFAULT_TEMPO # Create a PrettyMIDI instance - midi = PrettyMIDI(initial_tempo=tempo) + midi = PrettyMIDI(initial_tempo=tempo[0]) # Compute length of a time step time_step_length = 60.0 / tempo / multitrack.resolution @@ -165,12 +166,14 @@ def to_pretty_midi( padded = np.pad(binarized, ((1, 1), (0, 0)), "constant") diff = np.diff(padded.astype(np.int8), axis=0) + prefix = np.concatenate(((0,), np.add.accumulate(time_step_length))) + positives = np.nonzero((diff > 0).T) pitches = positives[0] note_ons = positives[1] - note_on_times = time_step_length * note_ons + note_on_times = prefix[note_ons] note_offs = np.nonzero((diff < 0).T)[1] - note_off_times = time_step_length * note_offs + note_off_times = prefix[note_offs] for idx, pitch in enumerate(pitches): velocity = np.mean(clipped[note_ons[idx] : note_offs[idx], pitch]) From 8eca0fe25c4445585b484cb22593cfedbd9e921c Mon Sep 17 00:00:00 2001 From: George Ogden Date: Wed, 15 Jun 2022 18:00:59 +0100 Subject: [PATCH 04/23] added algorithm to save tempo changes (#13) --- pypianoroll/outputs.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/pypianoroll/outputs.py b/pypianoroll/outputs.py index 998bc1c..1b50d68 100644 --- a/pypianoroll/outputs.py +++ b/pypianoroll/outputs.py @@ -147,6 +147,30 @@ def to_pretty_midi( # Compute length of a time step time_step_length = 60.0 / tempo / multitrack.resolution + # Use prefix sum for fast computation of elapsed time + prefix = np.concatenate(((0,), np.add.accumulate(time_step_length))) + + # Find tempi changes + tempi_changes = np.diff(tempo).nonzero()[0] + 1 + + # Copied from pretty-midi source code (https://github.com/craffel/pretty-midi/blob/241279b91196125881724e53ea436e1b9181f74b/pretty_midi/pretty_midi.py) + # Changes tempo by updating ticks + last_tick, last_tick_scale = midi._tick_scales[0] + previous_time = 0. + for time, tempo in zip(prefix[tempi_changes], tempo[tempi_changes]): + # Compute new tick location as the last tick plus the time between + # the last and next tempo change, scaled by the tick scaling + tick = last_tick + (time - previous_time)/last_tick_scale + # Update the tick scale + tick_scale = 60.0/(tempo*midi.resolution) + # Don't add tick scales if they are repeats + if tick_scale != last_tick_scale: + # Add in the new tick scale + midi._tick_scales.append((int(round(tick)), tick_scale)) + # Update the time and values of the previous tick scale + previous_time = time + last_tick, last_tick_scale = tick, tick_scale + midi._update_tick_to_time(midi._tick_scales[-1][0] + 1) for track in multitrack.tracks: instrument = Instrument( @@ -166,8 +190,6 @@ def to_pretty_midi( padded = np.pad(binarized, ((1, 1), (0, 0)), "constant") diff = np.diff(padded.astype(np.int8), axis=0) - prefix = np.concatenate(((0,), np.add.accumulate(time_step_length))) - positives = np.nonzero((diff > 0).T) pitches = positives[0] note_ons = positives[1] From c1e2f83a0ee0bf4d142be4d0deb8e302a52b1082 Mon Sep 17 00:00:00 2001 From: George Ogden Date: Wed, 15 Jun 2022 20:36:20 +0100 Subject: [PATCH 05/23] changed default tempi --- pypianoroll/outputs.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pypianoroll/outputs.py b/pypianoroll/outputs.py index 1b50d68..242e92f 100644 --- a/pypianoroll/outputs.py +++ b/pypianoroll/outputs.py @@ -23,7 +23,6 @@ import numpy as np import pretty_midi -import scipy.stats from pretty_midi import Instrument, PrettyMIDI from .track import BinaryTrack, StandardTrack @@ -135,12 +134,11 @@ def to_pretty_midi( """ if default_tempo is not None: - tempo = default_tempo + tempo = np.full(multitrack.get_max_length(), default_tempo) elif multitrack.tempo is not None: - # tempo = float(scipy.stats.hmean(multitrack.tempo)) tempo = multitrack.tempo[:,0] else: - tempo = DEFAULT_TEMPO + tempo = np.full(multitrack.get_max_length(), DEFAULT_TEMPO) # Create a PrettyMIDI instance midi = PrettyMIDI(initial_tempo=tempo[0]) From dd71adb997ad67d0505631e69a7a7b74c2c29e1b Mon Sep 17 00:00:00 2001 From: George Ogden Date: Wed, 15 Jun 2022 20:44:53 +0100 Subject: [PATCH 06/23] updated documentation --- docs/_sources/read_write.rst.txt | 2 +- docs/doc.html | 17 ++++++++--------- docs/read_write.html | 19 +++++++++---------- pypianoroll/outputs.py | 3 +-- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/docs/_sources/read_write.rst.txt b/docs/_sources/read_write.rst.txt index e31d3c0..91b6d38 100644 --- a/docs/_sources/read_write.rst.txt +++ b/docs/_sources/read_write.rst.txt @@ -20,4 +20,4 @@ Functions :noindex: .. note:: - Writing the tempo array and downbeat array to tempo change and time signature change events are not supported yet. + Writing the downbeat array to time signature change events is not supported yet. diff --git a/docs/doc.html b/docs/doc.html index 30b7afe..9b7b178 100644 --- a/docs/doc.html +++ b/docs/doc.html @@ -10,7 +10,7 @@ - + @@ -19,10 +19,10 @@ - + - +