Page 1 of 1

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

Posted: Sun Mar 26, 2023 3:33 pm
by alex.cherg

Hello fellow Linux musicians,

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.

My question is, is there a JACK plugin that I can use to map the relative encoders to positional controllers, so that Ardour only sees positional controllers (and I can freely use the MIDI learn functionality)?

This plugin's job would be very 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

Thanks for the help!


Re: Map relative encoders (2s complement) to positional controllers

Posted: Sun Mar 26, 2023 3:59 pm
by bluebell

I think it can be done with mididings but I didn't find a sample script.

Function calls look most promising:
https://mididings.github.io/mididings/u ... tion-calls


Re: Map relative encoders (2s complement) to positional controllers

Posted: Sun Mar 26, 2023 4:06 pm
by erlkönig

Maybe you could solve it with
https://x42-plugins.com/x42/x42-midimap


Re: Map relative encoders (2s complement) to positional controllers

Posted: Sat Apr 08, 2023 8:37 am
by alex.cherg

Thanks both for your suggestions! I gave mididings a try, since it looks like a very flexible tool. After some playing around here is the mididings script that solves my problem and correctly maps relative encoders (with 2s complement encoding) to positional controllers. I hope someone else might find it useful as well:

Code: Select all

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))