[TRICK] Map relative encoders (2s complement) to positional controllers

Hello fellow Ardour-ers,

I recently bought the Akai APC key 25 Mk2. I turns out the knobs are relative encoders, rather than positional controllers (i.e. the enc-2 option in Ardour). Sadly, Ardour doesn’t support MIDI learn for relative encoders, which is something I might find myself using.

I wanted to find a JACK/ALSA plugin, which handles the conversion between relative encoders and positional controllers before MIDI reaches Ardour and Ardour sees only positional controller values. This plugin’s job would be simple:

  • keep a 0-127 value representing the controller’s current state

  • on each MIDI encoder event, increase/decrease the current state according to the event, and

  • send the newly updated state as output

After some playing around with mididings, here is my script that does exactly this:

from mididings import *
from mididings.extra import *
from mididings.extra.inotify import AutoRestart
from mididings.event import CtrlEvent

from collections import defaultdict
import numpy as np

config(
    backend='alsa',
    # backend='jack',
)

BIT7_MIN = 0
BIT7_MID = 64
BIT7_MAX = 127
BIT7_MODULO = 128

class CtrlsState:
    def __init__(self):
        self.states = defaultdict(lambda: np.int(BIT7_MID))
ctrls_state = CtrlsState()

def twos_complement_to_signed_7bit(num):
    return ((num + BIT7_MID) % BIT7_MODULO) - BIT7_MID

def process_ctrl(ev):
    if ev.type == CTRL:
        ctrl_key = (ev.port, ev.channel, ev.ctrl)
        ctrl_value = ctrls_state.states[ctrl_key]
        delta_2s = ev.value
        delta = twos_complement_to_signed_7bit(delta_2s)
        ctrl_value += delta
        ctrl_value = np.clip(ctrl_value, BIT7_MIN, BIT7_MAX)
        ctrls_state.states[ctrl_key] = ctrl_value
        return CtrlEvent(ev.port, ev.channel, ev.ctrl, int(ctrl_value))
    else:
        return ev

run(Process(process_ctrl))

I hope that someone else might find that useful as well!

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.