An improved key sender. You can send /ch name to this to change the channel name.
The use here is for throwaway bindings. If you use a silly channel name like boing,
then you can add a bunch of temporary shortcuts on using that channel, and then when
done, you can edit <ReaperResourceDir>/OSC/reaper-osc-actions.ini and remove any
lines containing that string (e.g. boing) like
"/rpr/boing/key/g/press" 0 0 40362
e.g. by using GNU sed if you're happy with the command line, save actually even opening and editor:
sed -i '/boing/d' OSC/reaper-osc-actions.ini
Or you can save lines containing that string to a separate file. The beauty of Reaper's config files is that they are simple text files that are easy to manipulate.
Another possible modification is to use some of the keys to switch profiles, sending an osc or udp message to indicate the selected profile.
Code
#!/usr/bin/env python
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from PySide6.QtNetwork import *
from getip import get_ip_for_host
from pythonosc import udp_client
from pythonosc.osc_message import OscMessage
from icecream import ic; ic.configureOutput(includeContext=True)
import sys,os
args = sys.argv[1:]
init_channel = args[0] if len(args) > 0 else os.getenv("HHCHAN","999")
host = "hostname"
port = 9999
default_inport = 2901 # receive osc message to change profile
class SendKey:
def __init__(self,host,port,chan=999):
self.host = host
self.port = port
self.ip = get_ip_for_host(self.host)
self.client = udp_client.SimpleUDPClient(self.ip,self.port)
self.setChannel(chan)
def send(self,k):
addr = f"/hh/{self.chan}/key/{k}"
self.client.send_message(addr,[])
def setChannel(self,chan):
chan = str(chan).replace("/","")
if len(chan) == 0:
return
self.ch-an = chan
print(f"Channel now {chan}")
class Win(QWidget):
def __init__(self,content="Key To OSC",inport=default_inport):
super().__init__()
self.sendkey = SendKey("behemoth",2808,chan=init_channel)
self.resize(640,400)
self.label = QLabel(self)
self.label.setText(content)
self.udpSocket = QUdpSocket()
self.udpSocket.readyRead.connect(self.handleUdp)
try:
if not self.udpSocket.bind(QHostAddress.Any,inport):
raise Exception("Address bind failed")
except Exception:
print(f"Failed to bind UDP port {inport}")
exit(1)
app.quit()
print(f"Listening on {inport}")
def handleUdp(self):
while self.udpSocket.hasPendingDatagrams():
datagram = self.udpSocket.receiveDatagram(4096)
data = datagram.data().data()
print(f"Recv ({len(data)}): {data}")
try:
message = OscMessage(data)
except Exception as e:
print(f"#Fail to parse datagram ({e}) : {data}")
self.processMessage(message)
def processMessage(self,message):
addr = message.address
args = message.params
ic(addr,args)
match addr:
case "/ch"|"/channel":
if len(args) > 0:
return self.sendkey.setChannel(args[0])
else:
return self.sendkey.setChannel("_")
case "/quit":
return app.quit()
case _:
print(f"Unrecognised {addr} {args}")
return
def keyPressEvent(self,event):
key = event.key()
key = QKeySequence(key).toString().lower()
if not key in "abcdefghijklmnopqrstuvwxyz0123456789" and\
not key in ["left","right","up","down"] and\
not key in "!\"£$%^&*()_+-=[]{};:'#@~,./<>?\\|`¬":
return
modifiers = event.modifiers()
if modifiers & Qt.ShiftModifier:
key = "S-"+key
if modifiers & Qt.MetaModifier:
key = "M-"+key
if modifiers & Qt.ControlModifier:
key = "C-"+key
if modifiers & Qt.AltModifier:
key = "A-"+key
if key == "C-q":
app.quit()
print(f"Key: {key} to {self.sendkey.chan}")
self.sendkey.send(key)
app = QApplication([])
win = Win()
win.show()
app.exec()