MIDI钢琴乐曲过滤及Control Change 64事件处理技术问询
Great question—this is a super common problem when you’re trying to streamline MIDI data without losing the musical "feel" of the original track. Let’s walk through how to translate Control Change 64 (sustain pedal) events into note duration tweaks, so you can ditch the CC events entirely and stick to that clean note-only format you want.
First, let’s align on basics: Control Change 64 is indeed the sustain pedal. A value ≥64 means the pedal is pressed (sustain on), and <64 means it’s lifted (sustain off). When the pedal is down, any notes you release (via a note-off event) keep sounding until the pedal is lifted—this is the key behavior we need to replicate with note durations.
Step 1: Prep Your Event Data
First, ensure all your events (notes and remaining CC64 events) are sorted by their tick timestamp. Since you’ve already normalized the tempo to 120 BPM and adjusted ticks, you’re working with a consistent timeline, which makes this way easier.
Step 2: Track Pedal State Over Time
You’ll need to iterate through your events and track when the pedal is pressed or lifted. Think of this as splitting your track into "pedal on" and "pedal off" segments:
- Start with the pedal in the "off" state.
- As you walk through events, update the pedal state every time you hit a CC64 event. Note the exact tick where each state change happens.
Step 3: Adjust Note Durations Based on Pedal State
For each note, calculate its actual effective duration (the time it would sound with the pedal) instead of just using the original note-on to note-off interval. Here’s how to handle different scenarios:
Scenario 1: Pedal is off for the entire note
- Keep the original duration (from note-on to note-off tick). No changes needed here.
Scenario 2: Pedal is pressed when the note ends
- If you release the note (note-off) while the pedal is still down, the note’s sound continues until the pedal is lifted. Extend the note’s duration to the tick of the next pedal-lift event.
- If the pedal stays down until the end of the track, extend the note’s duration to the final tick of the entire track.
Scenario 3: Overlapping notes of the same pitch
- Even with the pedal down, pressing the same pitch again will cut off the previous instance of that note (since you’re re-striking the key on a real piano). So if a new note of the same pitch starts before the extended duration would end, truncate the first note’s duration to the start tick of the new note.
Step 4: Example Pseudocode
Here’s a simplified code snippet to illustrate the logic (using Python-like syntax):
# Assume we have sorted lists: # notes = [{"start_tick": s, "end_tick": e, "pitch": p, "velocity": v}, ...] # pedal_events = sorted([(tick, is_pressed), ...], key=lambda x: x[0]) modified_notes = [] current_pedal_pressed = False pedal_event_idx = 0 max_track_tick = max(max(n["end_tick"] for n in notes), max(t for t, _ in pedal_events)) for note in notes: # Update pedal state to match the time when the note starts while pedal_event_idx < len(pedal_events) and pedal_events[pedal_event_idx][0] <= note["start_tick"]: current_pedal_pressed = pedal_events[pedal_event_idx][1] pedal_event_idx += 1 # Calculate base end tick actual_end_tick = note["end_tick"] # Extend if pedal is pressed when note ends if current_pedal_pressed and note["end_tick"] < max_track_tick: # Find the next pedal lift after the note ends temp_idx = pedal_event_idx while temp_idx < len(pedal_events): tick, pressed = pedal_events[temp_idx] if not pressed and tick > note["end_tick"]: actual_end_tick = tick break temp_idx += 1 # If no lift found, extend to track end else: actual_end_tick = max_track_tick # Truncate if same pitch note starts before extended end for next_note in notes[notes.index(note)+1:]: if next_note["pitch"] == note["pitch"] and next_note["start_tick"] < actual_end_tick: actual_end_tick = next_note["start_tick"] break # Add modified note with duration instead of end tick modified_notes.append({ "start_tick": note["start_tick"], "duration_ticks": actual_end_tick - note["start_tick"], "pitch": note["pitch"], "velocity": note["velocity"] })
Key Notes for Accuracy
- Velocity stays the same: The sustain pedal doesn’t change a note’s volume or attack, so keep the original velocity value for each note.
- Handle multiple pedal toggles: If the pedal is pressed and lifted multiple times in quick succession, make sure you’re updating the pedal state for each segment—don’t just assume a single on/off interval.
- Edge cases: Watch for notes that start and end between pedal presses, or notes that start before a pedal press and end during it (these should also be extended to the next pedal lift).
Limitations to Keep in Mind
This method replicates the core sustain effect, but it’s not a perfect 1:1 match to real piano pedal behavior. Real sustain creates overlapping harmonics and resonance between notes, while extending note durations just makes individual notes longer. However, for most ML or data analysis use cases, this simplification preserves enough musical intent to be useful.
内容的提问来源于stack exchange,提问作者Daniel Casasampera

