diff --git a/midi2beep.py b/midi2beep.py index 66ba284..ff872cb 100644 --- a/midi2beep.py +++ b/midi2beep.py @@ -2,12 +2,12 @@ ### # -# midi2beep.py +# midi4beep.py # Author: Adrien MALINGREY # # Play MIDI files with PC speaker using the beep command. # -# Usage: python3 midi2beep.py +# Usage: python3 midi4beep.py [channel] # # Dependencies: # - Python 3 @@ -21,75 +21,64 @@ import subprocess import math import mido + +class PCSpeaker(mido.ports.BaseOutput): + def __init__(self, channel=-1): + super().__init__(self) + self.channel = channel + self.note_on = 0 + self.process = None + + def _send(self, message): + if (message.type == "note_on" or message.type == "note_off") \ + and (message.channel == self.channel or (self.channel == -1 and message.channel != 9)): + if message.type == "note_on" and message.velocity and message.note > self.note_on: + self.process = subprocess.Popen( + [ + "beep", + "-f", + str(int(440 * math.pow(2, (message.note - 69) / 12))), + "-l100000", + ] + ) + self.note_on = message.note + + elif self.process and (message.type == "note_off" or (message.type == "note_on" and message.note == self.note_on)): + self.process.terminate() + self.process = None + self.note_on = 0 + + def reset(self): + if self.process: + self.process.terminate() + self.process = None + self.note_on = 0 + + def panic(self): + if self.process: + self.process.kill() + self.process = None + self.note_on = 0 + + def _close(self): + self.reset() + + if len(sys.argv) < 2: - print("Usage: python3 midi2beep.py [channel]") + print("Usage: python3 midi4beep.py [channel]") sys.exit(1) -arpege = 0.015#ms - -FREQUENCIES = [ -# C, C#, D, D#, E, F, F#, G, G#, A, A#, B - 16.35, 17.32, 18.35, 19.45, 20.6, 21.83, 23.12, 24.5, 25.96, 27.5, 29.14, 30.87, - 32.7, 34.65, 36.71, 38.89, 41.2, 43.65, 46.25, 49, 51.91, 55, 58.27, 61.74, - 65.41, 69.3, 73.42, 77.78, 82.41, 87.31, 92.5, 98, 103.83, 110, 116.54, 123.47, - 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185, 196, 207.65, 220, 233.08, 246.94, - 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392, 415.3, 440, 466.16, 493.88, - 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880, 932.33, 987.77, - 1046.5, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760, 1864.66, 1975.53, - 2093, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520, 3729.31, 3951.07, - 4186.01, 4434.92, 4698.64, 4978.03, 5274.04, 5587.65, 5919.91, 6271.93, 6644.88, 7040, 7458.62, 7902.13, -] - -midi_file_path = sys.argv[1] +path = sys.argv[1] try: - mid=mido.MidiFile(midi_file_path) + mid = mido.MidiFile(path) except TypeError: - mido.read_syx_file(midi_file_path) - + mido.read_syx_file(path) + if len(sys.argv) > 2: channel = int(sys.argv[2]) else: channel = -1 -time = 0 -note_on = 0 -start = 0 -tempo = 500000 - -try: - for msg in mid: - time += msg.time - - if msg.type == "track_name": - print(msg.name) - - if msg.type == "set_tempo": - tempo = msg.tempo - - if (msg.type == "note_on" or msg.type == "note_off") and (msg.channel == channel or (channel == -1 and msg.channel != 9)): - if msg.time: - lines = math.ceil(4e6 * (time - start) / tempo) - if note_on: - for _ in range(lines): - print(note_on * " " + "█") - subprocess.run(["beep", f"-f{FREQUENCIES[note_on]:.0f}", f"-l{1000*(time - start):.0f}"]) - elif start: - for _ in range(lines): - print() - subprocess.run(["sleep", f"{0.8*(time - start)}"]) - - if msg.type == "note_on" and msg.velocity: - if msg.time or msg.note > note_on: - note_on = msg.note - else: - note_on = 0 - - start = time - - lines = math.ceil(4e6 * (time - start) / tempo) - for _ in range(lines): - print(note_on * " " + "█") - subprocess.run(["beep", f"-f{FREQUENCIES[note_on]:.0f}", f"-l{1000*(time - start):.0f}"]) - -except KeyboardInterrupt: - subprocess.run(["beep", "-f1", "-l1"]) +with PCSpeaker(channel) as speaker: + for msg in mid.play(): + speaker.send(msg)