Skip to content

Instantly share code, notes, and snippets.

@off-by-some
Created June 1, 2023 20:32
Show Gist options
  • Select an option

  • Save off-by-some/339299589b5df5cc6e1215f4b5981218 to your computer and use it in GitHub Desktop.

Select an option

Save off-by-some/339299589b5df5cc6e1215f4b5981218 to your computer and use it in GitHub Desktop.
MIDI to text
import mido
def translate_midi_to_custom_format(midi_file):
# Load the MIDI file
mid = mido.MidiFile(midi_file)
# Get the format and division from the MIDI file
format = mid.type
division = mid.ticks_per_beat
# Find all the set tempo messages to determine the BPM range
tempos = []
for msg in mid:
if msg.type == 'set_tempo':
tempo = mido.tempo2bpm(msg.tempo)
tempos.append(tempo)
# Calculate the BPM range if multiple tempos are found
bpm_range = ""
if len(tempos) > 1:
bpm_range = f" {min(tempos)}-{max(tempos)}"
elif len(tempos) == 1:
bpm_range = f" {tempos[0]}"
# Create the header for the custom format
custom_format = [f"MFile(fmt={format} div={division} bpm={bpm_range})"]
# Iterate over each track in the MIDI file
for i, track in enumerate(mid.tracks):
custom_format.append("\n")
# Create a new track entry in the custom format
custom_format.append(f"Track({i+1}) ")
# Check if the track has a name and add it to the custom format
if track.name:
custom_format.append(f'.TN("{track.name}")')
# Iterate over each message in the track
for msg in track:
# Handle known message types
if msg.type in known_message_types:
event = known_message_types[msg.type]
parameters = event["parameters"](msg)
custom_format.append(f"{event['name']} {msg.time}{parameters} ")
# Handle unknown message types
else:
custom_format.append(
f".Unknown({msg.time}, \"{msg.type}\", \"{msg.hex()}\")"
)
# Add track end marker to the custom format
# Join the custom format lines and return as a string
return "".join(custom_format)
# Dictionary of known MIDI message types and their corresponding custom format events
known_message_types = {
"note_on": {"name": "NOn", "parameters": lambda msg: f"{msg.note} {msg.velocity}"},
"note_off": {"name": "NOf", "parameters": lambda msg: f"{msg.note} {msg.velocity}"},
"control_change": {"name": "CC", "parameters": lambda msg: f"{msg.control} {msg.value}"},
"program_change": {"name": "PC", "parameters": lambda msg: f"{msg.program}"},
"aftertouch": {"name": "AT", "parameters": lambda msg: f"{msg.note} {msg.value}"},
"pitchwheel": {"name": "PB", "parameters": lambda msg: f"{msg.pitch}"},
"channel_pressure": {"name": "PA", "parameters": lambda msg: f"{msg.pressure}"},
"control_mode_change": {"name": "CMC", "parameters": lambda msg: f"{msg.control} {msg.value}"},
"songpos": {"name": "SPP", "parameters": lambda msg: f"{msg.pos}"},
"songselect": {"name": "SS", "parameters": lambda msg: f"{msg.song}"},
"tune_request": {"name": "TR", "parameters": lambda msg: ""},
"end_of_track": {"name": "EOT", "parameters": lambda msg: ""},
"set_tempo": {"name": "ST", "parameters": lambda msg: f" {mido.tempo2bpm(msg.tempo)}"},
"time_signature": { "name": "TS", "parameters": lambda msg: f"{msg.numerator} {msg.denominator} {msg.clocks_per_click} {msg.notated_32nd_notes_per_beat}"},
"key_signature": {"name": "KS", "parameters": lambda msg: f', "{msg.key}", {msg.scale}'},
"sysex": {"name": "SysEx", "parameters": lambda msg: f', "{msg.hex()}"'},
"universal_sys_ex": {"name": "USysEx", "parameters": lambda msg: f"{msg.device_id} \"{msg.hex()}\""},
"midi_time_code": {"name": "MTC", "parameters": lambda msg: f"{msg.frame_type} {msg.values}"},
"sequencer_specific": {"name": "SSMeta", "parameters": lambda msg: f', "{msg.hex()}"'},
"meta": {"name": "MetaText", "parameters": lambda msg: f', "{msg.text}"'}
}
# Example usage
midi_file = "1.mid" # Replace with the path to your MIDI file
custom_format = translate_midi_to_custom_format(midi_file)
print(custom_format)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment