#!/usr/bin/env python3
import evdev
import rtmidi
from evdev import ecodes
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import os
import sys
DEVICE_PATH = '/dev/input/by-id/usb-Logitech_Logitech_Dual_Action_DF5CB332-event-joystick'
# ----------------------------
# Corrected human-readable button mappings
# ----------------------------
buttons = {
'A': 61, # physical A triggers MIDI note B
'B': 62, # physical B triggers MIDI note X
'X': 60, # physical X triggers MIDI note A
'Y': 63,
'L': 64,
'R': 65,
'Select': 66,
'Start': 67,
'Up': 68,
'Down': 69,
'Left': 70,
'Right': 71
}
evdev_button_map = {
288: 'A',
289: 'B',
290: 'X',
291: 'Y',
292: 'L',
293: 'R',
296: 'Select',
297: 'Start',
294: 'Up',
295: 'Down',
298: 'Left',
299: 'Right'
}
# ----------------------------
# Axis mappings
# ----------------------------
axes = {
'LX': (10, -32768, 32767),
'LY': (11, -32768, 32767),
'LT': (12, 0, 255),
'RT': (13, 0, 255),
'RX': (14, -32768, 32767),
'RY': (15, -32768, 32767)
}
evdev_axis_map = {0: 'LX', 1: 'LY', 2: 'LT', 5: 'RT', 3: 'RX', 4: 'RY'}
# ----------------------------
# MIDI setup
# ----------------------------
midi_out = rtmidi.MidiOut()
midi_out.open_virtual_port("F310 MIDI")
def scale(value, min_val, max_val):
return int((value - min_val) / (max_val - min_val) * 127)
# ----------------------------
# Main gamepad loop
# ----------------------------
def run():
gamepad = evdev.InputDevice(DEVICE_PATH)
print(f"Listening on {gamepad.name} ({gamepad.path})")
for event in gamepad.read_loop():
if event.type == ecodes.EV_KEY and event.code in evdev_button_map:
label = evdev_button_map[event.code]
note = buttons[label]
if event.value == 1:
midi_out.send_message([0x90, note, 112])
print(f"Button pressed: {label}")
elif event.value == 0:
midi_out.send_message([0x80, note, 0])
elif event.type == ecodes.EV_ABS and event.code in evdev_axis_map:
label = evdev_axis_map[event.code]
cc, min_val, max_val = axes[label]
val = scale(event.value, min_val, max_val)
midi_out.send_message([0xB0, cc, val])
# ----------------------------
# Watchdog for hot-reload
# ----------------------------
class ReloadHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.src_path.endswith(os.path.basename(__file__)):
print("Script changed. Restarting...")
os.execv(sys.executable, ['python3'] + sys.argv)
observer = Observer()
observer.schedule(ReloadHandler(), path='.', recursive=False)
observer.start()
# ----------------------------
# Run forever
# ----------------------------
try:
while True:
run()
except KeyboardInterrupt:
observer.stop()
observer.join()
Friday, August 15, 2025
remaping the f310 to send midi with watchdog for auto reload
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment