Skip to content

Commit

Permalink
Added support for elongating note when encountering CC64
Browse files Browse the repository at this point in the history
  • Loading branch information
akira-maezawa committed Apr 10, 2018
1 parent 7d7ea0a commit f6de572
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 3 deletions.
25 changes: 24 additions & 1 deletion pretty_midi/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def get_onsets(self):
# Return them sorted (because why not?)
return np.sort(onsets)

def get_piano_roll(self, fs=100, times=None):
def get_piano_roll(self, fs=100, times=None, sustain_pedal_elongates_note=False, sustain_pedal_elongate_thres=64):
"""Compute a piano roll matrix of this instrument.
Parameters
Expand All @@ -84,6 +84,13 @@ def get_piano_roll(self, fs=100, times=None):
times : np.ndarray
Times of the start of each column in the piano roll.
Default ``None`` which is ``np.arange(0, get_end_time(), 1./fs)``.
sustain_pedal_elongates_note : Boolean
Control Change 64 (Sustain pedal) is reflected as elongation of notes.
Default is False, which does nothing. If True, then CC64 value greater
than sustain_pedal_elongate_thres will be treated as pedal on and rest as pedal off.
sustain_pedal_elongate_thres : Int
The threshold value for treating CC64 message as elongation of note.
Default is ``64``
Returns
-------
Expand Down Expand Up @@ -113,6 +120,22 @@ def get_piano_roll(self, fs=100, times=None):
piano_roll[note.pitch,
int(note.start*fs):int(note.end*fs)] += note.velocity

# Process sustain pedals
if sustain_pedal_elongates_note:
CC_SUSTAIN_PEDAL = 64
time_pedal_on = 0
is_pedal_on = False
for cc in [_e for _e in self.control_changes if _e.number == CC_SUSTAIN_PEDAL]:
time_now = int(cc.time*fs)
is_current_pedal_on = (cc.value > sustain_pedal_elongate_thres)
if not is_pedal_on and is_current_pedal_on:
time_pedal_on = time_now
is_pedal_on = True
elif is_pedal_on and not is_current_pedal_on:
subpr = piano_roll[:, time_pedal_on:time_now]
piano_roll[:, time_pedal_on:time_now] = np.minimum( subpr.cumsum(1), subpr.max(1)[:,np.newaxis])
is_pedal_on = False

# Process pitch changes
# Need to sort the pitch bend list for the following to work
ordered_bends = sorted(self.pitch_bends, key=lambda bend: bend.time)
Expand Down
13 changes: 11 additions & 2 deletions pretty_midi/pretty_midi.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ def get_onsets(self):
# Return them sorted (because why not?)
return np.sort(onsets)

def get_piano_roll(self, fs=100, times=None):
def get_piano_roll(self, fs=100, times=None, sustain_pedal_elongates_note=False, sustain_pedal_elongate_thres=64):
"""Compute a piano roll matrix of the MIDI data.
Parameters
Expand All @@ -750,6 +750,13 @@ def get_piano_roll(self, fs=100, times=None):
times : np.ndarray
Times of the start of each column in the piano roll.
Default ``None`` which is ``np.arange(0, get_end_time(), 1./fs)``.
sustain_pedal_elongates_note : Boolean
Control Change 64 (Sustain pedal) is reflected as elongation of notes.
Default is False, which does nothing. If True, then CC64 value greater
than sustain_pedal_elongate_thres will be treated as pedal on and rest as pedal off.
sustain_pedal_elongate_thres : Int
The threshold value for treating CC64 message as elongation of note.
Default is ``64``
Returns
-------
Expand All @@ -763,7 +770,9 @@ def get_piano_roll(self, fs=100, times=None):
return np.zeros((128, 0))

# Get piano rolls for each instrument
piano_rolls = [i.get_piano_roll(fs=fs, times=times)
piano_rolls = [i.get_piano_roll(fs=fs, times=times,
sustain_pedal_elongates_note=sustain_pedal_elongates_note,
sustain_pedal_elongate_thres=sustain_pedal_elongate_thres)
for i in self.instruments]
# Allocate piano roll,
# number of columns is max of # of columns in all piano rolls
Expand Down

0 comments on commit f6de572

Please sign in to comment.