Skip to content

Instantly share code, notes, and snippets.

@amb
Created March 30, 2021 12:16
Show Gist options
  • Save amb/145d88bf3792325e1d03cd64e097d2fb to your computer and use it in GitHub Desktop.
Save amb/145d88bf3792325e1d03cd64e097d2fb to your computer and use it in GitHub Desktop.
from mido import MidiFile
import mido
from collections import defaultdict
import time
import sys
filename = "Space Harrier.mid"
# filename = "Shovel.mid"
if len(sys.argv) == 2:
print("Loading from argument:", sys.argv[1])
filename = sys.argv[1]
mid = MidiFile(filename, clip=True)
print("Loading:", mid)
print("Ticks per beat:", mid.ticks_per_beat)
song_length = 0
track_notes = []
for track_i, track in enumerate(mid.tracks):
print("Loading track:", track_i)
notes = defaultdict(list)
timestamp = 0
for msg in track:
timestamp += msg.time
# TODO: real datatypes for saving MIDI data chunks
if msg.type == "note_on" or msg.type == "note_off":
notes[timestamp].append((msg.type, msg.note, msg.velocity))
elif msg.type == "program_change":
notes[timestamp].append((msg.type, msg.program))
elif msg.type == "set_tempo":
# print(dir(msg))
notes[timestamp].append((msg.type, msg.tempo))
elif msg.type == "control_change":
pass
elif msg.type == "end_of_track":
pass
elif msg.type == "pitchwheel":
pass
elif msg.type == "track_name":
pass
else:
print(timestamp, msg)
track_notes.append(notes)
if timestamp > song_length:
song_length = timestamp
print("Song length:", song_length)
def play_song(player, sleng, tnotes):
# play the song
start = time.time()
tempo = 500000
for i in range(sleng):
# convert MIDI tick to seconds
frac_time = i * tempo * 1e-6 / mid.ticks_per_beat
notes = []
for ti, t in enumerate(tnotes):
if i in t:
for n in t[i]:
# n: (type, note, velocity)
if n[0] == "note_on":
player.send(mido.Message('note_on', note=n[1], velocity=n[2], channel=ti))
elif n[0] == "note_off":
player.send(mido.Message('note_off', note=n[1], velocity=n[2], channel=ti))
elif n[0] == "program_change":
player.send(mido.Message('program_change', program=n[1], channel=ti))
elif n[0] == "set_tempo":
tempo = n[1]
else:
print(n[0])
# if n[0] in ["note_on", "note_off"]:
if n[0] in ["note_on"]:
notes.append(
f"{ti}:{'+' if n[0]=='note_on' else '-'}{n[1]:02x}.{n[2]:02x} "
)
if notes:
print("".join(notes))
# lets say 3200 time = 1 sec
if time.time() - start < frac_time:
while (time.time() - start) < frac_time:
time.sleep(0.001)
print("Tracks:", len(track_notes))
out_names = mido.get_output_names()
print("Out ports:", out_names)
selected_midi_out = 1
print(out_names[selected_midi_out])
midi_out = mido.open_output(out_names[selected_midi_out])
# http://www.ccarh.org/courses/253/handout/gminstruments/
if True:
play_song(midi_out, song_length, track_notes)
midi_out.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment