We'll use the same method as before, javascript objects, to split up an NRPN change into 4 CC packets to send back out to the device. Here's a snapshot of the Max device with the addition of the javascript object
js nrpn_to_cc.js
:We'll go through the path really quickly...
MIDI comes in through
midiin
and into midiparse
which filters different types of MIDI data to each of it's different outlet. Outlet 2 forwards all CC messages, so we connect that directly to js nrpn_filter.js
to filter out NRPN messages. Any non-NRPN related CC messages are sent to outlet 0, which are displayed in the two number objects control change ctrl
and value
. When we receive 2-4 CC changes that represent an NRPN change we forward the index and value out of outlet 1, which are displayed in the two number objects NRPN change index
and value
.After we've unpacked the CC data and taken a look at it, it's repacked and send to midiformat inlet 2, which takes a list of two values and turns it into a MIDI CC event. We do the same with the NRPN data but we have to translate it into the correct MIDI values first in
js nrpn_to_cc.js
, which sends out 4 CC messages for each NRPN change. midiformat
then sends a raw MIDI stream to midiout
and to a print
object which spits out the raw MIDI to the main Max window for debugging.The code in
nrpn_to_cc.js
is much simpler than the filter so we'll just start with pseudocode:When we receive 2 values
Split value 0 into two 7 bit values
Send value 0 high 7 bits as a CC change to CC#99
Send value 0 low 7 bits as a CC change to CC#98
Split value 1 into two 7 bit values
Send value 1 high 7 bits as a CC change to CC#38
Send value 1 low 7 bits as a CC change to CC#6
Pretty simple! Since we already have the values we don't need to do a lot of voodoo to deal with timing. We could handle Running Status here and optimize the MIDI stream quite a bit but for now we'll just make sure we can reconstruct the NRPN packets to send out. The only real interesting part of this code for the budding code junkie is bitwise operators, so we'll go over those quick, give the full code and call it a day.
To get the high and low 7 bits we can either do some convoluted math to convert from base 2 to base 10 or just use bit operators to do it lickity split. Going back to our first example we'll use the value 572:
572 = 0x023C = 0b 00 0010 0011 1100
First we'll get the bottom 7 bits. We use the bit operator AND to grab certain bits from the number and ignore the rest. To demonstrate we'll do a couple bit operations...
First AND against all ones:
00 0010 0011 1100 | |
AND | 11 1111 1111 1111 |
= | 00 0010 0011 1100 |
And second AND against all zeros:
00 0010 0011 1100 | |
AND | 00 0000 0000 0000 |
= | 00 0000 0000 0000 |
So we'll do this for the lower 7 bits:
00 0010 0011 1100 | 0x023C | |
AND | 00 0000 0111 1111 | 0x007F |
= | 00 0000 0011 1100 | 0x003C |
And for the higher 7 bits:
00 0010 0011 1100 | 0x023C | |
AND | 11 1111 1000 0000 | 0x3F80 |
= | 00 0010 0000 0000 | 0x0200 |
0b00 0010 0000 0000 >> 7 = 0b00 0000 0000 0001
0x0200 >> 7 = 0x0001
Now everything is ready to send out! Here's the code for the javascript object:
// MIDI CC constants
const CC_DATA_MSB = 6;
const CC_DATA_LSB = 38;
const CC_NRPN_LSB = 98;
const CC_NRPN_MSB = 99;
// inlets and outlets
inlets = 1;
outlets = 1;
// global variables and arrays
var nrpn_index; // Save last known rpn index
var data;
var output; // Up to 8 Bytes to send
// Maxobj variables for scripting
var nrpn_index_lsb;
var nrpn_index_msb;
var data_lsb;
var data_msb;
// methods start here
// list - expects an RPN 14 bit Index + 14 bit Value
function list(val)
{
if(arguments.length==2)
{
nrpn_index = arguments[0];
data = arguments[1];
nrpn_index_lsb = nrpn_index & 0x007F;
nrpn_index_msb = (nrpn_index & 0x3F8) >> 7 ;
data_lsb = data & 0x007F;
data_msb = (data & 0x3F8) >> 7;
outlet(0, CC_NRPN_MSB, nrpn_index_msb);
outlet(0, CC_NRPN_LSB, nrpn_index_lsb);
outlet(0, CC_DATA_MSB, data_msb);
outlet(0, CC_DATA_LSB, data_lsb);
}
}
thanks for these js objects. just what i needed :)
ReplyDeleteHiya chris,
ReplyDeleteI left a message on gearslutz about being interested in doing some of this. I mentioned there that i was using Java to do all my midi in and out. I only just start exploring the midi api and i found a bug with Mac and midi - when ableton is open accessing the underlying midi system crashes the java part of the app. To get around this i was using some 3rd party java API's to access the midi system. This bypasses ableton and hence allows Sysex. I would be interested in your thoughts on this and if this would be a good way to go (bypassing ableton for the sysex and possibly nrpn). I spose it does give you the flexibility to choose to send midi to ableton or out via the midi system.
cheers
kle
data_msb = (data & 0x3F8) >> 7;
ReplyDeleteA zero is missing: (data & 0x3F80), without it it'll be only capable of 1024 as the highest value.