5

I have a physical MIDI keyboard that also features some control keys, such as "play" and "stop". When pressed, they send the MIDI codes 115 and 116 respectively over the MIDI bus. Is it possible to hook up these commands to the usual media controls (play and pause) of Linux, so that when I press "play", the playback starts?

Is it furthermore possible to hook up other MIDI keys (e.g., up/down) to their respective keyboard counterparts (e.g., arrow up/down)?

Green绿色
  • 299
  • 2
  • 9
  • Possible? Yes. Easy? No, you'll need to write custom software that listens for events on that MIDI bus, and turns them into keyboard events. At least I am not aware of anything ready-made for this. – dirkt Apr 23 '22 at 09:28
  • I just googled again and found a software for Windows that maps MIDI signals to keyboard strokes. Probably have to write something by myself though. – Green绿色 Apr 23 '22 at 12:29
  • Look up `uinput`, and read up on the ALSA MIDI interface. You don't have to do this in C, e.g. Python has libraries for both. – dirkt Apr 23 '22 at 14:00
  • Ah, I see you already did :-) – dirkt Apr 23 '22 at 14:00
  • Yes, just finished. Was easier than expected, although I'm not entirely happy with it yet. I'd prefer a non-root user solution. – Green绿色 Apr 23 '22 at 14:01
  • Well, if any user could inject keystrokes, that wouldn't be very secure... – dirkt Apr 23 '22 at 14:04
  • Yes, that's right. – Green绿色 Apr 23 '22 at 14:10

1 Answers1

2

In the comments, dirkt suggested to write a custom program. So, I wrote a short working proof of concept script in Python that reads inputs from a MIDI controller and then simulates the required key press. I tested it on Ubuntu 20.04. Unfortunately, it requires superuser privileges to run, otherwise /dev/uinput cannot be opened for writing.

import mido
from evdev import uinput, ecodes as e


def press_playpause(): 
    """Simulate pressing the "play" key"""
    with uinput.UInput() as ui: 
      ui.write(e.EV_KEY, e.KEY_PLAY, 1)
      ui.syn() 


def clear_event_queue(inport):
    """Recent events are stacked up in the event queue 
    and read when opening the port. Avoid processing these
    by clearing the queue.
    """
    while inport.receive(block=False) is not None:
        pass


device_name = mido.get_input_names()[0]  # you may change this line
print("Device name:", device_name)

MIDI_CODE_PLAY = 115
MIDI_VALUE_ON = 127

with mido.open_input(name=device_name) as inport: 
    clear_event_queue(inport)
    print("Waiting for MIDI events...")
    for msg in inport:
        print(msg)
        if (hasattr(msg, "value") 
                and msg.value == MIDI_VALUE_ON
                and hasattr(msg, "control")
                and msg.control == MIDI_CODE_PLAY):
            press_playpause()

Requirements:

Green绿色
  • 299
  • 2
  • 9