DJ2Go to OSC Bridge
This is script that was mostly written by GPT, then with minor adaptions. The main uses is to use the DJ2Go as an array of buttons and controls to use with Reaper. (Reaper can bind OSC messages to actions.)
#!/usr/bin/env python
import argparse
import rtmidi
import time
from rtmidi.midiutil import open_midiinput
from pythonosc import udp_client
class DJ2Go:
def __init__(self, midi_port_name=None):
# Initialize MIDI input
self.midiin = rtmidi.MidiIn()
ports = self.midiin.get_ports()
if not ports:
raise RuntimeError("No MIDI input ports found!")
# If no specific port, open the first
if midi_port_name is None:
port = 0
else:
port = next((i for i, p in enumerate(ports) if midi_port_name in p), 0)
self.midiin.open_port(port)
print(f"Opened MIDI port: {ports[port]}")
self.midiin.set_callback(self._midi_callback)
def _midi_callback(self, message_data, _time):
msg, delta_time = message_data
# Typical messages are [status, data1, data2]
if len(msg) < 3:
return
status, data1, data2 = msg
# Note On/Off: buttons
if 0x90 <= status <= 0x9F: # Note On (with velocity)
button = data1
value = 1 if data2 > 0 else 0
self.button(button_number=button, value=value)
elif 0x80 <= status <= 0x8F: # Note Off
button = data1
self.button(button_number=button, value=0)
# Control Change (CC): knobs, faders, jogs, etc.
elif 0xB0 <= status <= 0xBF:
cc = data1
val = data2
self.control_change(cc, val)
# Default handler methods (to override)
def button(self, button_number, value):
print(f"Button {button_number}: {value}")
def jogwheel(self, jogwheel, amount):
print(f"Jogwheel {jogwheel}: {amount}")
def rotary(self, rotary, amount):
print(f"Rotary {rotary}: {amount}")
def pot(self, pot, amount):
print(f"Pot {pot}: {amount}")
def fader(self, fader, amount):
print(f"Fader {fader}: {amount}")
def control_change(self, cc, val):
# Example mapping: suppose jogwheels are CC 0x10 and 0x11
print("cc",cc,val)
if cc == 25:
if val >= 64:
amount = -1
elif val == 0:
amount = 0
else:
amount = 1
print(1,amount)
self.jogwheel(jogwheel=0, amount=amount)
elif cc == 24:
if val >= 64:
amount = -1
elif val == 0:
amount = 0
else:
amount = 1
print(2,amount)
self.jogwheel(jogwheel=1, amount=amount)
elif cc == 26:
if val >= 64:
amount = -1
elif val == 0:
amount = 0
else:
amount = 1
print(3,amount)
self.rotary(rotary=0, amount=amount)
elif cc in [ 13, 14 ]:
self.fader(fader=cc-13, amount=val)
elif cc == 10:
self.fader(fader="x", amount=val)
elif cc in [ 8, 23, 11, 9 ]:
idx = [ 8, 23, 11, 9 ].index(cc)
self.pot(pot=idx, amount=val)
else:
print(f"CC {cc} -> {val}")
button_lookup = {
68: "left_pitch_minus",
67: "left_pitch_plus",
64: "left_sync",
101: "left_mon",
51: "left_cue",
59: "left_play",
75: "centre_a",
89: "centre_back",
90: "centre_enter",
52: "centre_b",
70: "right_pitch_minus",
69: "right_pitch_plus",
71: "right_sync",
102: "right_mon",
60: "right_cue",
66: "right_play",
}
class DJ2GoOSC(DJ2Go):
def __init__(self, midi_port_name=None, osc_host="127.0.0.1", osc_port=8000):
super().__init__(midi_port_name)
self.client = udp_client.SimpleUDPClient(osc_host, osc_port)
print(f"Sending OSC to {osc_host}:{osc_port}")
def jogwheel(self, jogwheel, amount):
if amount > 0:
amount = "plus"
elif amount < 0:
amount = "minus"
else:
return
path = f"/dj2go/jog/{jogwheel}/{amount}"
self.client.send_message(path, [])
print(f"OSC: {path}")
def rotary(self, rotary, amount):
if amount > 0:
amount = "plus"
elif amount < 0:
amount = "minus"
else:
return
path = f"/dj2go/rotary/{rotary}/{amount}"
self.client.send_message(path, [])
print(f"OSC: {path}")
def pot(self, pot, amount):
path = f"/dj2go/pot/{pot}"
self.client.send_message(path, amount/127.0)
print(f"OSC: {path} {amount}")
def fader(self, fader, amount):
path = f"/dj2go/fader/{fader}"
self.client.send_message(path, amount/127.0)
print(f"OSC: {path} {amount}")
def button(self, button_number, value):
if button_number in button_lookup:
button_number = button_lookup[button_number]
path = f"/dj2go/button/{button_number}"
self.client.send_message(path, value)
print(f"OSC: {path} {value}")
def main():
parser = argparse.ArgumentParser(
description="Bridge DJ2Go MIDI messages to OSC."
)
parser.add_argument(
"-c", "--controller",
default="DJ2Go",
help="MIDI controller name or partial match (default: DJ2Go)"
)
parser.add_argument(
"-i", "--ip",
default="127.0.0.1",
help="Target OSC IP address (default: 127.0.0.1)"
)
parser.add_argument(
"-p", "--port",
type=int,
default=8000,
help="Target OSC port (default: 8000)"
)
args = parser.parse_args()
print(f"Starting DJ2Go -> OSC bridge...")
print(f"MIDI controller: {args.controller}")
print(f"OSC target: {args.ip}:{args.port}")
bridge = DJ2GoOSC(midi_port_name=args.controller,
osc_host=args.ip,
osc_port=args.port)
try:
while True:
time.sleep(0.1)
except KeyboardInterrupt:
print("\nExiting gracefully...")
if __name__ == "__main__":
main()