midi2beep/midi2beep.py

85 lines
2.1 KiB
Python

#!/usr/bin/python3
###
#
# midi4beep.py
# Author: Adrien MALINGREY <adrien@malingrey.fr>
#
# Play MIDI files with PC speaker using the beep command.
#
# Usage: python3 midi4beep.py <midi file> [channel]
#
# Dependencies:
# - Python 3
# - mido library (https://mido.readthedocs.io/)
# - beep command
#
###
import sys
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 midi4beep.py <midi file> [channel]")
sys.exit(1)
path = sys.argv[1]
try:
mid = mido.MidiFile(path)
except TypeError:
mido.read_syx_file(path)
if len(sys.argv) > 2:
channel = int(sys.argv[2])
else:
channel = -1
with PCSpeaker(channel) as speaker:
for msg in mid.play():
speaker.send(msg)