tags: reaper osc 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 `/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 ```py #!/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() ```