MIDI Tools - Tesla Coil MIDI Processing Suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

79 lines
2.8 KiB

import mido
from .midi_utils import has_musical_messages
CC_NAMES = {
0: "Bank Select", 1: "Modulation", 2: "Breath Controller", 4: "Foot Controller",
5: "Portamento Time", 7: "Volume", 10: "Pan", 11: "Expression",
64: "Sustain Pedal", 65: "Portamento", 66: "Sostenuto", 67: "Soft Pedal",
71: "Resonance", 72: "Release Time", 73: "Attack Time", 74: "Cutoff Frequency",
91: "Reverb", 93: "Chorus", 94: "Detune",
}
def get_track_detail(midi: mido.MidiFile, track_index: int) -> dict | None:
"""Extract detailed time-series data for a specific musical track."""
musical_idx = 0
target_track = None
for track in midi.tracks:
if not has_musical_messages(track):
continue
if musical_idx == track_index:
target_track = track
break
musical_idx += 1
if target_track is None:
return None
ticks_per_beat = midi.ticks_per_beat
track_name = f"Track {track_index + 1}"
control_changes = {} # cc_number -> list of [tick, value]
pitch_bend = [] # list of [tick, value]
velocities = [] # list of [tick, velocity]
ongoing_notes = {} # (note, channel) -> (start_tick, velocity)
notes = [] # list of [note, start, end, velocity]
absolute_tick = 0
for msg in target_track:
absolute_tick += msg.time
if msg.type == 'track_name':
track_name = msg.name
elif msg.type == 'note_on' and msg.velocity > 0:
velocities.append([absolute_tick, msg.velocity])
key = (msg.note, msg.channel)
ongoing_notes[key] = (absolute_tick, msg.velocity)
elif msg.type == 'note_off' or (msg.type == 'note_on' and msg.velocity == 0):
key = (msg.note, msg.channel)
if key in ongoing_notes:
start_tick, vel = ongoing_notes.pop(key)
notes.append([msg.note, start_tick, absolute_tick, vel])
elif msg.type == 'control_change':
cc = msg.control
if cc not in control_changes:
control_changes[cc] = {
"name": CC_NAMES.get(cc, f"CC {cc}"),
"data": []
}
control_changes[cc]["data"].append([absolute_tick, msg.value])
elif msg.type == 'pitchwheel':
pitch_bend.append([absolute_tick, msg.pitch])
# Convert cc keys to strings for JSON
cc_out = {}
for cc_num, cc_data in sorted(control_changes.items()):
cc_out[str(cc_num)] = cc_data
total_ticks = absolute_tick
return {
"track_name": track_name,
"ticks_per_beat": ticks_per_beat,
"total_ticks": total_ticks,
"notes": notes,
"control_changes": cc_out,
"pitch_bend": pitch_bend,
"velocities": velocities
}