Control MIDI Mapping programmatically

BenCello's icon

Hello Maxers,


A little meta question here...
Is it possible to control Max MIDI mapping with messages?
In particular, I would like to be able to enable/disable specific mappings with another action but not with the mouse and the Mapping Management window (typically another MIDI control hence meta).
Do you know if that is possible?

Thank you very much!


PS: I did not know about the [mappings] object before posting but it seems that this object does not provide insight in the mappings. Only overall controls (display, read/write,...). Or is there some hidden messages?

D.Singer's icon

Hi Bencello,

As far as I know, there are no implemented methods to control mapped object parameters programmatically (the one you see in mappings), for me, this whole mappings system feels very incomplete...

I had the same issue in my project and my solution was to use javascript on node.js, to load the .maxmap file, edit its content (since its just a json data), write it to disk and load it back to mappings, this way I can control parameters programically. But it's not very fast.

This code allows you to load your .maxmap file (user argument read, same as for mappings object), then use arguments like this:

active PARAMETER VALUE

example: active slider1 1 slider2 0

this will enable the slider with the param name slider1 (it's also a scripting name) to enable it to control the object with midi controller and slider2 to disable it, this can be done on the fly with multiple objects.

Hope this helps.

Cheers.

save this on the JS file and load it into node.script object

const fs = require('fs');
const Max = require('max-api');

let filePath, jsonData;

//feed a message active with data
Max.addHandler("active", (...data) => {
    data.reduce((acc, val, index, array) => {
        if (index % 2 === 0) {
            const name = array[index];
            const value = array[index + 1];
            acc.parameter_map.midi[name].active = value;
        }
        return acc;
    }, jsonData);
    write(); //write file
});

//read file
Max.addHandler("read", (loc) => {
    filePath = loc.startsWith("/") ? loc : loc.split(":").slice(1).join(":");//remove drive name
    fs.readFile(filePath, 'utf8', (err, data) => {
        if (err) {
          Max.post('Error reading file:', err);
          return;
        }
      
        try {
          jsonData = JSON.parse(data);
        } catch (error) {
          Max.post('Error parsing JSON:', error);
        }
      });
})

function write() {
    const jsonString = JSON.stringify(jsonData, null, 2);
    fs.writeFile(filePath, jsonString, 'utf8', (err) => {
        if (err) {
            Max.post('Error writing file:', err);
          return;
        }
        // Max.post('File has been saved successfully.');
        Max.outlet('read',filePath);
    });
}
TFL's icon

I see the builtin mapping feature more as a convenience tool to quickly control your patch with a midi device than as a full-fledged mapping system. For dynamic mapping, I would basically build the MIDI interface myself.

Map all of your physical controls to static dials, then use these dials as your starting point for the mapping system. I would expose all paramaters you want to take control of to pattr (using either pattr objects or scripting name + "parameter mode enable" on) and dynamically change the routing by changing the @bindto attribute.

If you're working in standalone Max, you can take advantage of the Paramater mode. When you enable it, you can set the type to float (or other), then define a range, and you can retrive those using pattr.
Here is a simple example.

Max Patch
Copy patch and select New From Clipboard in Max.

It feels a bit whacky at the beginning but I found this approach quite useful and very flexible lately

BenCello's icon

Hello,


Thank you for your answers!
My post is about 9 months old so I am not sure nowadays why I wanted to do this... I probably managed my way around this issue at the time.

But anyway, I like your javascript solution, @D.SINGER! Thx
And from you patch, @TFL, I discovered the @invisible attribute from [pattr] object that I did not know! This is going to be very useful, cheers!

Best,
Benjamin