music/daw/reaper/ TourBox
So if I close the TourBox Console, I can access the TourBox as a simple usb-serial device. The only issue is finding the correct port.
It seems to be COM5
on Windows, or /dev/ttyS4
under cygwin, and /dev/ttyACM?
under Linux, and /dev/tty...something-weird-about-usb-modem
on a mac.
Once you know that, here's a simple script that turns tourbox events into OSC which you can target to an application.
Obviously, if you are familiar with python, you can do things way more complicated than this.
So I turn the knob, and it sends
tour/knob/cw
to wherever. I don't have bank switching like I do with my nocturn script. Though I could easily send
tour/0/0/knob/cw
instead, using the 0/0
bit to specify which bank of 'virtual tourboxes' the event is coming from.
This bank switching will be a common thread in what I'm doing. The other issue is finding a decent way to
indicate what is mapped to what button. In Reaper, the OSC bindings are stored in
%reaperres%/OSC/reaper-osc-actions.ini
where %reaperres%
is your reaper resource folder (e.g. %APPDATA%/REAPER
on Windows).
My first TourBox→OSC script
#!/usr/bin/env python
import serial # python -m pip install pyserial
import time
from pythonosc import udp_client # python -m pip install python-osc
import random
import time
import sys
import os
import platform
import socket
def get_ip():
# workaround since reaper doesn't receive packets if sent to localhost rather than 192.168.0.x
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
try:
# doesn't even have to be reachable
s.connect(('10.254.254.254', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
port = os.getenv("port",os.getenv("PORT",9000))
try:
port = int(port)
except ValueError:
print(f"Port '{port}' must be an integer")
exit(1)
host = os.getenv("host",os.getenv("HOST",get_ip()))
print(f"{host=} {port=}")
# xxd /dev/ttyACM3
def main():
global client
client = udp_client.SimpleUDPClient(host,port)
com_port = os.getenv("com_port",None)
if com_port is None:
if platform.system() == "Linux":
com_port = "/dev/ttyACM0"
elif platform.system() == "Darwin":
com_port = "weird"
elif "cygwin" in platform.system().lower():
com_port = "/dev/ttyS4"
elif platform.system() == "Windows":
com_port = "COM5:"
else:
print(f"Don't support {platform.system()}")
exit(2)
with serial.Serial(com_port,19200) as ser:
try:
while True:
x = ser.read()
y = ord(x)
print(f"{y:02x}")
handle(y)
except KeyboardInterrupt:
print(f"Ctrl-c")
exit()
def button(n,dirn):
if dirn == 0:
action(f"button_{n:02x}")
def rotary(n,dirn):
if dirn > 0:
return
action('rotary_{n:02x}_{dirn}')
def action(x,args=[]):
print(x,args)
client.send_message(f"tour/{x}",args)
knobs = {
0x04: "knob",
0x0f: "dial",
0x09: "vdial"
}
dpad = {
0x10: "up",
0x11: "down",
0x12: "left",
0x13: "right"
}
def handle(x):
print(f"{x=}")
dirn = x >> 7
x &= 0x7F
kdirn = x >> 4
x &= 0x3f
if x in knobs:
if dirn > 0:
return
action(f"{knobs[x]}/"+("cw" if kdirn > 0 else "ccw"))
elif x in dpad:
if dirn > 0:
return
action(f"dpad/{dpad[x]}")
else:
if dirn > 0:
return
action(f"button/{x:02x}")
if __name__ == "__main__":
main()