#!/usr/bin/python3 ### # # midi4beep.py # Author: Adrien MALINGREY # # Play MIDI files with PC speaker using the beep command. # # Usage: python3 midi4beep.py [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 [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)