46 lines
1.7 KiB
Python
Executable file
46 lines
1.7 KiB
Python
Executable file
#!/usr/bin/env python
|
|
import argparse
|
|
import csv
|
|
import mido
|
|
import sys
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("input", help="input file (midi)")
|
|
parser.add_argument(
|
|
"-o", "--output", default="/dev/stdout", help="output CSV (pitch, duration, pause)"
|
|
)
|
|
parser.add_argument("-t", "--track", default=0, type=int, help="track number")
|
|
parser.add_argument("-c", "--channel", default=0, type=int, help="channel number")
|
|
args = parser.parse_args()
|
|
|
|
octave = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"]
|
|
pitches = [f"{octave[i % 12]}{i // 12 + 3}" for i in range(36)]
|
|
|
|
midifile = mido.MidiFile(args.input)
|
|
time = 0
|
|
note = 0
|
|
time_on = 0
|
|
time_off = 0
|
|
with open(args.output, "w") as notes:
|
|
writer = csv.writer(notes)
|
|
for event in midifile.tracks[args.track]:
|
|
writer.writerow(["", "", event])
|
|
# convert time to 1/16th beats
|
|
time += event.time * 16 // midifile.ticks_per_beat
|
|
if event.type == "note_on" and event.channel == args.channel:
|
|
# if mulitple notes start at the same time, play the highest
|
|
if event.velocity != 0 and (time_on != time or note < event.note):
|
|
if time_off <= time_on:
|
|
time_off = time
|
|
if note != 0 and time_off - time_on > 0:
|
|
writer.writerow(
|
|
[pitches[note - 60], time_off - time_on, time - time_off]
|
|
)
|
|
note = event.note
|
|
if note - 60 < 0 or note - 60 >= len(pitches):
|
|
sys.exit("note out of range c3 to b5")
|
|
time_on = time
|
|
elif note == event.note:
|
|
time_off = time
|
|
writer.writerow([pitches[note - 60], time - time_on, 0])
|