Friday, August 15, 2025

remaping the f310 to send midi with watchdog for auto reload


#!/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()


No comments:

Post a Comment