Dipping my toes in .. (scripting Behringer CMD MM-1)

Post Reply New Topic
RELATED
PRODUCTS

Post

Hello folks,


so, I'm getting started with the API scripting and I'd appreciate some help.

I've got a Behringer CMD MM-1, it's a controller laid out like a DJ mixer. Basically four channels with faders, four knobs and three buttons on each track and then some (a crossfader, yay). It's a powered USB hub, which is nice as well.

The TL;DR question is this: is it true that notes mapped on Bitwig's interface don't pass through the script's onMidi callback?


And here's the longer version:

The buttons send notes and in order to turn their LEDs on and off I need to send the device the corresponding note. I'm able to do that, no problem. But when I map a button in the interface (in the mixer, on a device, anywhere really) the script no longer sends the necessary note to the device. It doesn't do the println() line either, so I figured it just consumes the note.

Is this expected behaviour? Should I do something more?


Here's a few snippets from the script to show what I'm doing.

At the top of the file where script-wide variables are declared, I create an array to hold the status of each LED button:

Code: Select all

var ledNotes = [ 
    //16, 3, 17,                    // left, encoder button, right. they send notes but can't be turned on/off
    18,                             // button under VU meter
    19, 20, 23, 24, 27, 28, 31, 32, // 4 x buttons labeled 1 & 2
    48, 49, 50, 51                  // cue buttons
];
// array of the status of each LED. set them off by default, init() does it on the device.
var noteStatus = [];
ledNotes.forEach(function(note) { noteStatus[note] = 00 });

This is in the init() function, setting all the LEDs off when the script is loaded:

Code: Select all

// this sets the lights off. off being Behringer Orange, that is.
// would be better to read the values from the project, but it is what it is for now.
ledNotes.forEach(function(note) {
  sendMidi(148, note, 00);
});

Then, in the onMidi() function I catch the notes and set the lights on or off. In order to do that, the device wants the same note sent to itself, with value 00 for off and 01 for on (and 02 for blink, but haven't figured a use for that yet).

Code: Select all

// if it's a NoteOn and the note is among the LED notes:
if (isNoteOn(status) &&  ledNotes.indexOf(data1) != -1) {
  if (noteStatus[data1] == 00) {
        println("set it on " + data1 + " " + data2);
        sendMidi(status, data1, 01);
        noteStatus[data1] = 01;
  } else {
        println("set it off " + data1 + " " + data2);
        sendMidi(status, data1, 00);
        noteStatus[data1] = 00;
  }
}

Should I do something after sending the MIDI to the device?

Setting .setShouldConsumeEvents() either true or false on the input doesn't seem to make a difference -- if the note isn't mapped to anything, the LED functions like I want it to, but when I map it, it no longer lights on or off.



I reckon when I get around using the TrackBank I can pass this all, but I'd first like to create a generic script where I can map the controls to where ever I like.


Thanks for any assistance. It also has an endless encoder that sends either value of 63 or 65 and I'd need some help with that too, but more on that later ...




Something that I did manage to get working nicely is the stereo Vu meter that it has, and this is how I got it to work (in stereo), in case someone wants to set up a stereo meter on the masterbus. I think the meter may have potential for other signaling purposes too ..


At the top of the file I declare:

Code: Select all

var showVu = 1;       // 1 for yes, please make it flashy, 0 for no
var vuPeak = false;   // true for peak value, false for RMS
var vuSum = false;    // show the sum of left & right instead of both separatedly. true to do that, false not
In the init() I do this. The .addVuMeterObserver() returns only one value, so I need to create it for both the left and the right channel and then tell the observing function which channel it's following ..

Code: Select all

masterTrack = host.createMasterTrack(0); 
if (showVu == 1) {
 // 15 is the number of LEDs available in the MM-1's Vu meter
 // 0 is left, 1 is right, -1 is sum of both. vuPeak is set before init()
 // .addVuMeterObserver yields only one result, so we need to make one for both sides.
 if (vuSum == true) {
    masterTrack.addVuMeterObserver(15, -1, vuPeak, function(vu_value) { VuMeter(-1, vu_value); });
 } else {
    masterTrack.addVuMeterObserver(15, 0, vuPeak, function(vu_value) { VuMeter(0, vu_value); }); 
    masterTrack.addVuMeterObserver(15, 1, vuPeak, function(vu_value) { VuMeter(1, vu_value); });
 }
}

Then the VuMeter() function:

Code: Select all

function VuMeter(side, vu_value) {
  // 80 is the left side, 81 the right
    switch(side) {
        case -1: // sum of both: send the same value to both sides.
            host.getMidiOutPort(0).sendMidi(180, 80, vu_value+48);
            host.getMidiOutPort(0).sendMidi(180, 81, vu_value+48);
            break;
        case 0: // left side
            host.getMidiOutPort(0).sendMidi(180, 80, vu_value+48);
            break;
        case 1: // right side
            host.getMidiOutPort(0).sendMidi(180, 81, vu_value+48);
            break;

    }
}
Well actually I just noticed I managed to break it by changing the channel on the device and that 180 means it goes on a specific channel, which the device then ignores as it's working on a different channel. Well not a big problem, need to set it back or do some magic with declaring the correct channel ..

Post

For receiving notes from your controller and sending them to Bitwig you need to do the following:

// Create the port. Note: Store the port, it is unefficient to get it with every call!
this.port = host.getMidiInPort (0);

// Create a note input and tell the script engine to forward the configured MIDI commands automatically to Bitwig:

MyMidiInput.prototype.createNoteInput = function ()
{
var noteInput = this.port.createNoteInput ("My device",
"80????", // Note off
"90????", // Note on
"B040??"); // Sustainpedal
noteInput.setShouldConsumeEvents (false);
return noteInput;
};

// BUT You can still filter or modify received notes with a note map. This is an array with 128 entries. The number of a slot represents the received note number, setting a different number into that slot changes the note sent to Bitwig to that note number, -1 filters the note.

// Creates a map which filters all notes
this.noteMap = initArray (-1, 128);
// Allow note 65
this.noteMap[65] = 65;
// Translate note 70 to 80
this.noteMap[70] = 80;
// Note: You can set this table multiple time, e.g. if you want to have different behaviours in different modes
this.noteInput.setKeyTranslationTable (this.noteMap);

// If you need to use other MIDI commands which should not be forwarded automatically to Bitwig register functions

// For System Exclusive messages
this.port.setSysexCallback (f);
// All other messages
this.port.setMidiCallback (f);

Post

Thanks for chiming in, but I'm sorry I'm a bit thick in the head over here -- you lost me at MyMidiInput.prototype.createNoteInput

I can't figure out what that MyMidiInput refers to? MidiIn ? this.port ? The prototype exists without me creating it?


I was probably too long winded over there and not clear enough, .. I can get notes from the device to Bitwig and back, my problem is that when I map a controller/key to something on the GUI, it no longer passes through the script. I need the script to send the same note back to the hardware, but it no longer does that once the note is mapped to something (say, on a mute button with "Map to Controller or Key" right-click menu item).


I've been building up from the generic scripts and this is how I've had the input working so far:

Code: Select all

host.getMidiInPort(0).setMidiCallback(onMidi);
host.getMidiInPort(0).setSysexCallback(onSysex);

cmdmmone = host.getMidiInPort(0).createNoteInput("CMD MM-1", "??????");
cmdmmone.setShouldConsumeEvents(false);

That's pretty simple but if I understand it correctly your code has the benefit of being more, umm, object-y. Even that this.port variable is already an improvement to what I was doing, as I can now be more relaxed about the MIDI channel and always have the input at hand.

The prototype and MyMidiInput part has me confused now, though. Please bear with me.

Post


Post Reply

Return to “Controller Scripting”