瀏覽代碼

Initial proof of concept

Kuba Winnicki 2 周之前
當前提交
552820a9e4
共有 2 個文件被更改,包括 131 次插入0 次删除
  1. 121 0
      midiloop.py
  2. 10 0
      pyproject.toml

+ 121 - 0
midiloop.py

@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+
+import mido
+from sys import stdout
+from pythonosc import udp_client
+
+# will spit exception when server unavaliable
+atem = udp_client.SimpleUDPClient("spass.local", 3333)
+amp = udp_client.SimpleUDPClient("pig.local", 9137)
+ovly = udp_client.SimpleUDPClient("xuj.local", 9137)
+
+msg_matrix = {
+    # OVERLAY
+    0o00:  (atem, "/atem/mini/preview", 1),
+    0o30: (ovly, "/party/screen/bar/t/reset", 0),
+
+    # RETROTINK
+    0o01:  (atem, "/atem/mini/preview", 2),
+    0o11:  (amp, "/party/amp/input/dvd", 0),
+    0o21:  (amp, "/party/amp/input/video", 0),
+    0o31:  (amp, "/party/amp/input/tape", 0),
+    0o41:  (amp, "/party/amp/input/cd", 0),
+    0o51:  (amp, "/party/amp/vol/up", 0),
+    0o61:  (amp, "/party/amp/vol/down", 0),
+    0o71:  (amp, "/party/amp/power/on", 0),
+    # 0o1:  (amp, "/party/amp/power/off", 0),
+
+    # CAMERA ZOOM
+    0o02:  (atem, "/atem/mini/preview", 3),
+
+    # CAMERA WIDE
+    0o03:  (atem, "/atem/mini/preview", 4),
+
+    # STILLS
+    0o04:  (atem, "/atem/mini/preview", 3010),
+
+    # BLACK
+    0o05:  (atem, "/atem/mini/preview", 0),
+
+    # PREVIEW / VIEW
+    0o06:  (atem, "/atem/mini/transition/cut", 0),
+    0o16:  (ovly, "/party/emu/xl/stop", 0),
+    0o26:  (ovly, "/party/media/stop", 0),
+
+    # 
+    0o07:  (atem, "/atem/mini/transition/auto", 0),
+
+    0o17: (atem, "/atem/mini/usk/1/on-air", 0),
+    0o27: (atem, "/atem/mini/usk/1/on-air", 1),
+    # 0o17: (atem, "/atem/transition/type/dve", 0),
+    # 0o27: (atem, "/atem/transition/type/dve", 1),
+    0o37: (atem, "/atem/mini/usk/1/type/dve", 0),
+    0o47: (atem, "/atem/mini/usk/1/type/dve", 1),
+    0o57: (atem, "/atem/mini/transition/type/dve", 1),
+    0o67: (atem, "/atem/mini/me/1/transition/dve/style", "pushbottom"),
+}
+
+audio_controls = {
+    48: "audio/input/1",
+    49: "audio/input/2",
+    50: "audio/input/3",
+    51: "audio/input/4",
+    52: "audio/input/1301",
+    53: "audio/input/1302",
+    54: "audio/output",
+}
+for i in range(7):
+    msg_matrix[0o14+(i<<3)] = (atem, "/atem/mini/mplayer/1/still", i+1)
+
+def get_controller(device_name):
+    """Select device and open for input control"""
+    for device in mido.get_input_names():
+        if device.rsplit(' ', 1)[0] == device_name:
+            return (mido.open_input(device), mido.open_output(device))
+    return None
+
+def get_float(v):
+    return v * 258.01574 / 32768
+
+def event_loop(apc_in, apc_out): 
+    last = 0
+    direction = 1
+    while True:
+        msg = apc_in.receive()
+        if msg.type == "note_on" and msg_matrix.get(msg.note):
+            osc, addr, value = msg_matrix[msg.note]
+            osc.send_message(addr, value)
+            print(msg, 'sent', value, 'to:', addr)
+
+            # turn on led of pressed button and clear previous one for a feedback
+            apc_out.send(mido.Message('note_on', channel=6, note=last, velocity=0))
+            apc_out.send(mido.Message('note_on', channel=6, note=msg.note, velocity=5))
+            last = msg.note
+        if msg.type == "control_change" and msg.control == 56:
+            # full range
+            v = get_float(msg.value) * direction
+            atem.send_message("/atem/mini/transition/position", abs(v))
+            # atem.send_message("/atem/mini/transition/bar", 0.9999-v)
+            if msg.value == 0:
+                #direction *= -1
+                atem.send_message("/atem/mini/transition/position", 0.9999999999)
+                atem.send_message("/atem/mini/transition/cut", abs(v))
+            print("fade", msg.value)
+        if msg.type == "control_change" and msg.control == 55:
+            # full range
+            ovly.send_message("/party/media/volume", get_float(msg.value))
+        elif msg.type == "control_change" and msg.control in audio_controls:
+            # full range
+            atem.send_message(f"/atem/mini/{audio_controls[msg.control]}/gain",
+                              get_float(msg.value) * 110. - 100.)
+        else:
+            print(msg)
+        stdout.flush()
+
+if __name__ == "__main__":
+    apc = get_controller('APC mini mk2:APC mini mk2 APC mini mk2 Contr')
+    if not apc:
+        print("MIDI Controller not found")
+        exit(1)
+    
+    event_loop(*apc)

+ 10 - 0
pyproject.toml

@@ -0,0 +1,10 @@
+[project]
+name = "pad"
+version = "0.1.0"
+description = "trigger OSC messages with APCmini launchapd buttons and knobs"
+readme = "README.md"
+requires-python = ">=3.13"
+dependencies = [
+    "mido>=1.3.3",
+    "python-osc>=1.9.3",
+]