Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix get_piano_roll pedal implementation #195

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions pretty_midi/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,29 +118,42 @@ def get_piano_roll(self, fs=100, times=None,
for note in self.notes:
# Should interpolate
piano_roll[note.pitch,
int(note.start*fs):int(note.end*fs)] += note.velocity
int(note.start*fs):int(note.end*fs)+1] += note.velocity

# Process sustain pedals
if pedal_threshold is not None:
CC_SUSTAIN_PEDAL = 64
time_pedal_on = 0
time_pedal_on_exact = 0
is_pedal_on = False
# Keep track of non-quantized offset times
end_times = np.zeros((128, int(fs * end_time)))
for note in self.notes:
end_times[note.pitch, int(note.start*fs):int(note.end*fs)+1] = note.end

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 >= pedal_threshold)
if not is_pedal_on and is_current_pedal_on:
time_pedal_on = time_now
time_pedal_on_exact = cc.time
is_pedal_on = True
elif is_pedal_on and not is_current_pedal_on:
# For each pitch, a sustain pedal "retains"
# the maximum velocity up to now due to
# logarithmic nature of human loudness perception
subpr = piano_roll[:, time_pedal_on:time_now]

# Rounding might cause notes to be held for longer
# than would be correct, so we have to take the
# exact timing into account
held_notes = end_times[:, time_pedal_on] >= time_pedal_on_exact
piano_roll[:, time_pedal_on] *= held_notes
subpr = piano_roll[:, time_pedal_on:time_now+1]

# Take the running maximum
pedaled = np.maximum.accumulate(subpr, axis=1)
piano_roll[:, time_pedal_on:time_now] = pedaled
piano_roll[:, time_pedal_on:time_now+1] = pedaled
is_pedal_on = False

# Process pitch changes
Expand Down