Get Data On MIDI Controller Mapping For Param


is it possible for a script to get data about a MIDI controller hardware control that is mapped to a particular script param?

I ask because I’d like my script to be able to echo back MIDI CCs so that MIDI controller hardware like the FaderFox EC4 can stay in sync with the current param values when it’s changed locally on the Norns, or recalled from a preset.

To be able to do this, my script would ideally need to be able to tell, within the callback function for a param, the MIDI device, channel CC no. etc. of an external physical controller mapped to the param.

It would be great if this feature was built into Norns, actually (for OSC, as well as MIDI).

1 Like

yes, you can inspect mappings and vport assignments.

here’s what a mapping looks like:

p = 'rec_level' -- e.g.
map =[p]
accum	false
ch	1
value	0
out_hi	128
in_hi	127
out_lo	1
dev	2
in_lo	0
cc	24

the field dev refers to a numerical index in midi.vports. that’s where you can inspect the device itself:

dev = midi.vports[]

the device table mostly consists of the assigned handler functions, but also the device name and an opaque handle to the low-level device data. (which isn’t useful to lua)

connected	true
start	function: 0x418f18
note_on	function: 0x419078
song_position	function: 0x481a50
name	nanoKEY Studio
program_change	function: 0x418ed0
device	table: 0x60d110
note_off	function: 0x47fd28
key_pressure	function: 0x418d28
song_select	function: 0x481a68
clock	function: 0x4197b8
pitchbend	function: 0x418bc8
stop	function: 0x4800f0
channel_pressure	function: 0x418d70
continue	function: 0x4594f0
send	function: 0x471d50
cc	function: 0x47ff10

it should be fine to do these lookups in the param handler, they shouldn’t invoke any costly functions.

mmm… looking at the use case specifically, i’m so far not able to retreive a useful value for the last incoming (unmapped) value from the map data, which is what i expected from the value field. would like to figure that out without reversing the parameter scaling function, which has a cost. will poke at it a minute, we may have a small bug there if the value field is supposed to be doing this.

nevermind. of course, you need to perform some kind of scaling back to a MIDI byte anway, because the whole point is that the param can change elsewhere… 1 moment

here’s a basically untested mockup of what i suppose it would look like:

-- something to know which CC channel to echo on
echo_cc_map = {
    rec_level = {ch=1, cc=60},
    pre_level = {ch=1, cc=61}

-- call this from a param action function with the param ID
echo_param = function(id, value)
    local map =[id]
    local dev = midi.vports[]
    local cc = echo_cc_map[id]
    local p = params.params[params.lookup[id]]

    -- this gets a little annoying; 
    -- there isn't really a clean API for reversing a parameter mapping.
    -- here i'm going to just assume that the param is a `control`,
    -- which provides both normalization and un-normalization.
    -- (supporting other types presently means manually reversing the map scaling functions,
    -- which are implemented in menu/params.lua)
    local norm_val = 0
    if p.t == 'control' then
        norm_val = p.unmap_value(value)
       -- TODO: other param types
    local tx ={
        ch = echo_cc_map[id].ch,
        cc = echo_cc_map[id].cc,
        -- or just: cc = cc,
        val = util.linlin(0, 1, 0, 127, norm_val)

ok last update sorry. i don’t have any of these controllers which support bidirectional binding. wihtout digging into documentation, just going to assume that in fact they are set up to receive on the same cc(/channel) that they send on. so in fact instead of the static mapping shown above you would use the CC number retreived from the mapping data. (shown in comment)


Norns is a gift to humankind. Just needed to express it. Have a nice day :slight_smile:

1 Like

This is great thank you very much, @zebra!

I’m bidding on a FaderFox EC4 on eBay at the moment, so hoping to have one of those to test this on soon.

My understanding is that this box receives MIDI CCs on the same channel it sends them on, to update the values on it’s display. I’ll be able to test the details once I have one on my desk.

I’d probably have CC feedback as an option in my script(s), rather than have it turned on all the time, as it’s only really useful for controllers with encoders (rather than pots with a fixed range). It might cause problems with some MIDI controller hardware, too.

I’m also thinking that there may be issues when banging params (ie when a preset is loaded). If all params are sent at the same time, that might cause buffer overflows on the controller (or even at the Norns end).