Skip to content

Instantly share code, notes, and snippets.

@ksamuel
Last active May 13, 2024 12:42
Show Gist options
  • Save ksamuel/49365b74bf6c87588bb5c93db7b4e5d3 to your computer and use it in GitHub Desktop.
Save ksamuel/49365b74bf6c87588bb5c93db7b4e5d3 to your computer and use it in GitHub Desktop.
import os
import queue
import re
import time
import mido
import requests
# I don't even remember why it's there. Permission issue I worked
# around? Maybe I replaced that with an udev rules?
os.geteuid = lambda: print("nope") or 0
CONTROL_MAPPING = {
9: "K2",
20: "F1",
21: "F2",
28: "K1",
}
NOTE_MAPPING = {
22: "Pad_12",
23: "Pad_13",
24: "Pad_14",
25: "Pad_15",
26: "Pad_16",
11: "Pad_1",
12: "Pad_2",
13: "Pad_3",
14: "Pad_4",
15: "Pad_5",
16: "Pad_6",
17: "Pad_7",
18: "Pad_8",
19: "Pad_9",
20: "Pad_10",
21: "Pad_11",
}
def messagereceived(key, value):
print(key, value)
try:
requests.post(f"http://localhost:8000/relay/?key={key}&value={value}")
except Exception as e:
print("error", e)
pass
# There is zero reason for a class to be used here. I probably
# had the grand delusion of making it pluggable.
class Controller:
def __init__(self):
self.midi_backend = mido.Backend("alsa_midi.mido_backend")
def listen(
self,
device_name: str,
# Yeah, definitely tried to make that generic.
device_name_is_regex: bool,
first_discovery_timeout: int = 30,
):
# Seems I loop trying to find the device, which is likely
# to allow for plugging and unplugging it
error = ""
for i in range(first_discovery_timeout * 2):
try:
device = self.find_device(device_name, device_name_is_regex)
break
except SystemError as e:
if not i:
print("Waiting for device...")
error = str(e)
time.sleep(0.5)
else:
# We can't find a device, no point in holding to life
raise SystemExit(str(error))
port = self.midi_backend.open_input(device)
# That's an event loop folks. Don't let people tell you otherwise.
while True:
try:
if self.is_device_connected(device_name, device_name_is_regex):
with self.midi_backend.open_input(device) as port:
print("Device connected:", device_name)
while self.is_device_connected(
device_name, device_name_is_regex
):
try:
for message in port.iter_pending():
if message.type == "note_on":
print(message.note)
name = NOTE_MAPPING.get(message.note, "Unknown")
messagereceived(name, message.velocity)
elif message.type == "control_change":
name = CONTROL_MAPPING.get(
message.control, "Unknown"
)
messagereceived(name, message.value)
except queue.Empty:
time.sleep(0.1)
# unplug tolerance again, and beautiful black whole for any other source
# of error
except OSError:
pass
print("Device disconnected", name)
print("Waiting for device...")
time.sleep(0.5)
def list_devices(self):
return self.midi_backend.get_input_names()
def find_device(self, name, regex=False):
# I don't even think I own another midi device, but hey
inputs = self.list_devices()
if not regex:
matches = [n for n in inputs if name.lower() in n.lower()]
else:
matches = [n for n in inputs if re.search(name, n)]
if not matches:
raise SystemError(f"No device matching '{name}' found")
if len(matches) > 1:
raise SystemError(f"{len(matches)} devices matching '{name}' found")
return matches[0]
def is_device_connected(self, name, regex=False):
try:
return self.find_device(name, regex)
# When you catch SystemError, you know you reached the bottom
except SystemError:
return False
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment