Hacking a RigKontrol 2 as a MODEP controller

I’ve been messing around with MODEP for a while and I eventually decided to make it suitable for live performing.

I needed a good audio card (possibly with HI-Z input) and a MIDI pedalboard to control the effects. I bumped into this old piece of hardware that I got from a friend and I thought BINGO! That’s perfect! But… I didn’t know the MIDI implementation of this pedalboard was such a nightmare! In fact it has NO MIDI IMPLEMENTATION!

After spending a couple of days unsuccesfully trying to get some MIDI signal out of the RigControl, I realized that the footswitches emulates a USB keyboard and the volume controller emulate a USB joystick (Yes you got it right…) Essentially, pressing a footswitch RigKontrol sends a number (1 to 7) like if it was pressed on the keyboard. Operating the foot controller, acts like if you were moving a joystick on the X axis.

So I wrote a little software that grabs these events and sends MIDI messages to MODEP accordingly. I hate Python, but in the end I decided to go for it, because it seemed to be the simplest solution for such a small program.

The good thing is that IT WORKS!

All you have to do is launch the program then you can let the MODEP effects’ controls learn the MIDI message sent by the pedalboard. You can manually run the program via SSH, but I strongly suggest for you to make it auto-run at boot using systemd (as a service).

Here’s the code, in case you’re interested in it:

#!/usr/bin/python

import time
import rtmidi
import evdev

#
## Definizione dei 7 controlli a pulsante
## Stato, N. CC, Valore off, Valore on
#
aFootControls = [
        [False, 20, 0, 127],
        [False, 21, 0, 127],
        [False, 22, 0, 127],
        [False, 23, 0, 127],
        [False, 24, 0, 127],
        [False, 25, 0, 127],
        [False, 26, 0, 127]]

#
## Definizione dei 3 controlli a pedale
## CC, Valore minimo ingresso, Valore massimo in ingresso, valore minimo rimappato, valore massimo rimappato
#
aVariableControls = [
        [27, 190, 1500, 0, 127],
        [28, 190, 1600, 0, 127],
        [29, 190, 1600, 0, 127]]


# Funione di rimappatura del valore letto dal controller ai valori MIDI
def remap(val, from_min, from_max, to_min, to_max):
        nNewVal = (to_max - to_min)*(val - from_min) / (from_max - from_min) + to_min
        if nNewVal < to_min:
                nNewVal = to_min

        if nNewVal > to_max:
                nNewVal = to_max

        return(nNewVal)

#print(evdev.util.list_devices())

device = evdev.InputDevice('/dev/input/event0')

#print(device)

# Apre la porta Midi
midiout = rtmidi.MidiOut()
available_ports = midiout.get_ports()

if available_ports:
    midiout.open_port(0)
else:
    midiout.open_virtual_port("GuidoPort")

with midiout:
    aMessage = [0xB0, 0, 0] # channel 1, middle C, velocity 112
    try:
        for event in device.read_loop():
            # Premuto un bottone sulla pedaliera
            if event.type == evdev.ecodes.EV_KEY:
                keyevent = evdev.categorize(event)
                if int(event.value) <> 1:
                  continue

                # Controlla che il bottone sia nel range dei 7 bottoni della pedaliera
                nButton = int(event.code) - 2

                if nButton < 0 or nButton > 6:
                  continue

                # Assegna al messaggio il numero di CC MIDI associato al bottone
                aMessage[1] = aFootControls[nButton][1]

                # inverte lo stato del bottone
                aFootControls[nButton][0] = not aFootControls[nButton][0]

                # Prepara il valore da inviate
                if aFootControls[nButton][0]:
                   aMessage[2] = aFootControls[nButton][3]
                else:
                   aMessage[2] = aFootControls[nButton][2]

                # Invio del messaggio MIDI
                midiout.send_message(aMessage)

            elif event.type == evdev.ecodes.EV_ABS:
                absevent = evdev.categorize(event)
                if absevent.event.code > len(aVariableControls):
                        continue

                nNewVal = remap(absevent.event.value, aVariableControls[absevent.event.code][1], aVariableControls[absevent.event.code][2],
                                aVariableControls[absevent.event.code][3], aVariableControls[absevent.event.code][4]);

                if absevent.event.code < 0 or absevent.event.code > 2:
                  continue

                # Assegna al messaggio il numero di CC MIDI associato al bottone
                aMessage[1] = aVariableControls[absevent.event.code][0]

                aMessage[2] = nNewVal

                # Invio del messaggio MIDI
                midiout.send_message(aMessage)

    except (EOFError, KeyboardInterrupt):
      print('Fine')

del midiout

This software sends CC #20 to #26 for the footswitches and CC #27 to #t29 for the avriable controller, as specified in the following matrices:

aFootControls = [
        [False, 20, 0, 127],
        [False, 21, 0, 127],
        [False, 22, 0, 127],
        [False, 23, 0, 127],
        [False, 24, 0, 127],
        [False, 25, 0, 127],
        [False, 26, 0, 127]]

aVariableControls = [
        [27, 190, 1500, 0, 127],   # Volume pedal
        [28, 190, 1600, 0, 127],   # pedal input 1
        [29, 190, 1600, 0, 127]]   # pedal input 2

The second matrix specifies the CC #, the minimum read value, the maximum read value, the minimum MIDI value to be sent, the maximum MIDI value to be sent. The mapping of these values is performed by the remap() funcion.

NOTE: I couldn’t test the input pedals 1 & 2, not having any pedal to plug! :slight_smile:

Now, the bad thing is that I could only grab signals out of the RigKontrol, but I could not reverse engineer the way the device accepts signals back into it, so I wasn’t able to turn on the LEDs on the footswitches. In other words you don’t have any visual feedback on the status of a pedal which is pretty annoying. I thought I could add some external LEDs, perhaps driven by the GPIO ports, but I didn’t worked it out yet. I’ll see.

Please feel free to ask questions.

Hi everybody! After almost one month of fine tuning I’m pretty satisfied with the pedalboard. I eventually decided to replace the pedalboard LEDs with Neopixels driven by the Raspi. I had fun! :slight_smile:

Here is a demo.

2 Likes

Hi there, great hackery project. I have your script working on a Pi 4. Although more work to do to get midi data into PD. Do you have any more info on how you set about getting your LEDs to work?

1 Like

Hello @nburge! Thanks, I’m happy I could help you. This is the very first version of my code, you can find the latest version here:

The new version allows you to have 2 banks of switches to be chosen holding the switch #3 (as shown in the video). Holding switch #2 you can choose among 6 pedalboards preset.

As fo the LEDs… I haven’t used the built in LEDs, but replaced them with WS2812b (so called NEOPIXELS). Unassembling the RK2, it’s easy to remove the red plastic that cover the original LEDs. Once you remove it, you have room to put the new Neopixels in place. Then I drilled a small hole on the plastic side of RK, and connected them to my RPi GPIOs, you only need 3 wires (VCC, GND and DATA).

Please don’t hesitate to contact me should you have more questions…

Happy hacking! :wink:

1 Like

Hi there, making progress, but could you please give me some pointers as regards the neopixel module (part of circuitpython from adafruit is that correct?)…I’d like to install the module in a virtualenv. Just wanted to check I’ve got the right module.

Yes, please follow the guide on Adafruit site on how to make Neopixels work on RPi. Here’s the link: https://learn.adafruit.com/neopixels-on-raspberry-pi You have to install a few Python libraries.

I’ve used a WS2812b LED strip bought on Amazon, cut the strip down to separate the single LEDs, then glued them on the RK2 chassis, in place of the original ones.

Thanks for the tips.

I’ve been trying to get the python script to load at bootup using systemd, but something stops it loading even though when logged on as patch I can run the script from a terminal without any problem…Have you had any luck with this?

Ciao @nburge, that sounds strange to me. Neopixel library requires root privileges, but I guess you already know that, so if you are able to manually run the script from the terminal, I can’t see any point in starting it at boot.

I copied the script to /usr/local/bin folder an used chmod 0755 to make it executable. That directory is already in the execution path, so you should be able to run it from the trerminal without specifying the whole path. Then you have to create a .service file in the directory /etc/systemd/system to make it usable from systemd. Here’s the content of /etc/systemd/system/rkcapture.service

[Unit]
Description=RigKontrol Capture MIDI interface
After=jack.service
StartLimitIntervalSec=0
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
User=root
ExecStart=/usr/local/bin/RigKontrolCapture.py

[Install]
WantedBy=multi-user.target

You should be able to run it using sudo service rkcapture start. Please check out more documentation about systemd, as I’m not a master at that! :slight_smile:

Let me know if you could get through.

Here’s the output running sudo service rkcapture status on my RPi

Hmmmm this is what I get. Same as before… as you can see the only thing I changed was the name of the pi script to reflect the name I have given mine. I think it’s a python3 question and the modules it requires needing to run as root? I’m quite a novice with python.

rig_kontrol.service - rig_kontrol Capture MIDI interface
Loaded: loaded (/etc/systemd/system/rig_kontrol.service; enabled; vendor preset:
Active: activating (auto-restart) (Result: exit-code) since Sat 2021-02-06 16:08:
Process: 1104 ExecStart=/usr/local/bin/rig_kontrol.py (code=exited, status=1/FAILU
Main PID: 1104 (code=exited, status=1/FAILURE)

…then I changed the user from “root” to “patch” and things moved on a bit further…

rig_kontrol.service - rig_kontrol Capture MIDI interface
Loaded: loaded (/etc/systemd/system/rig_kontrol.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2021-02-06 16:15:24 GMT; 602ms ago
Main PID: 1196 (rig_kontrol.py)
Tasks: 1 (limit: 2075)
Memory: 5.5M
CGroup: /system.slice/rig_kontrol.service
└─1196 /usr/bin/python3 /usr/local/bin/rig_kontrol.py

Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: midiout = rtmidi.MidiOut()
Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: AttributeError: module ‘rtmidi’ has no attribute ‘MidiOut’
Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: Exception ignored in: <function InputDevice.del at 0x75f2f228>
Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: Traceback (most recent call last):
Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: File “/home/patch/.local/lib/python3.7/site-packages/evdev/device.py”, line 160, in del
Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: File “/home/patch/.local/lib/python3.7/site-packages/evdev/device.py”, line 305, in close
Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: File “/home/patch/.local/lib/python3.7/site-packages/evdev/eventio_async.py”, line 55, in close
Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: File “/usr/lib/python3.7/asyncio/events.py”, line 726, in get_event_loop_policy
Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: File “/usr/lib/python3.7/asyncio/events.py”, line 719, in _init_event_loop_policy
Feb 06 16:15:24 patchbox rig_kontrol.py[1196]: ImportError: sys.meta_path is None, Python is likely shutting down

Where the actual script is located on the system? Can you manually run it without specifying the path?

Yes, I just type rig_kontrol.py and it starts to execute but fails with the following output…(The script is in /usr/local/bin).

patch@patchbox:~$ rig_kontrol.py
Traceback (most recent call last):
File “/usr/local/bin/rig_kontrol.py”, line 173, in
midiout = rtmidi.MidiOut()
AttributeError: module ‘rtmidi’ has no attribute ‘MidiOut’
Exception ignored in: <function InputDevice.del at 0x75ea7270>
Traceback (most recent call last):
File “/home/patch/.local/lib/python3.7/site-packages/evdev/device.py”, line 160, in del
File “/home/patch/.local/lib/python3.7/site-packages/evdev/device.py”, line 305, in close
File “/home/patch/.local/lib/python3.7/site-packages/evdev/eventio_async.py”, line 55, in close
File “/usr/lib/python3.7/asyncio/events.py”, line 726, in get_event_loop_policy
File “/usr/lib/python3.7/asyncio/events.py”, line 719, in _init_event_loop_policy
ImportError: sys.meta_path is None, Python is likely shutting down

It seems to be a problem with loading rtmidi ??..

yes, in fact rtmidi seems to have disappeared from alsa altogether, so that would explain why the rig kontrol script won’t run I suppose, but trying to re-install it with “sudo pip3 install rtmidi” just tells me that all requirements are satisfied…

Patchbox is a bit strange to me in that with other raspberry pi systems the default user (and sudoer) is always user pi and when you first run it you can access the pi account with the usual password “raspberry”, and then go on to change the password, make new users etc. But in patchbox the pi user is still there but not accesible. None of the usual passwords work. Would this be the reason why python seems to get in such a mess? Or rather why I am in such a mess…

Try to remove it from pip3 and reinstall it as a normal package with sudo apt install python3-rtmidi… In my system I have installed that way.

IT WORKS!!! Using systemd from the /etc/systemd/system folder and as root it seems to do the trick. I got the leds all installed and working this morning as well. Thanks for your support.

There’s only a symlink from /home/pi to /home/patch. This is done, because some applications or scripts meant for Raspberry Pi assume that’s the home directory. :slight_smile: The pi user does not exist. Otherwise, the ‘patch’ user is completely equivalent to pi, and also you start with a default password which you get to change during the wizard. The password does not impact software in any way.

Thank you for the explanation. Slowly things are making sense, perhaps this could be added to the documentation for users accustomed to other distros?