Subtitel

A blog by Juri Urbainczyk | Juri on Google+ | Juri on Twitter | Juri on Xing

Sunday, November 22, 2015

node.js and MIDI

jazz-midi (www.thejazzpage.de) is a node.js module which allows access to MIDI interfaces. Midi (musical instrument digital interface) is an international standard for communication between electronical music devices which was invented in the early 80ies. Recently, I have been playing around with jazz-midi quite a bit and I figured I should share my experiences here. Now, first of all opening MIDI devices goes like a breeze:

var outName = MIDI.MidiOutOpen(2);
if (outName) {
  console.log('Opened MIDI-Out port: ', outName); 
} else {
  console.log('Cannot open MIDI-Out port!');
}


This example opens a MIDI out port with the port number 2 (if this one is available). To get a list of available MIDI ports, just use MIDI.MidiOutList() or MIDI.MidiInList() respectively. As easy as this is, I quickly found out that you cannot open multiple ports and then, for instance, receive events from all the ports simultaneously. I tried different ways to circumvent this issue (e.g. calling MidiOutOpen() multiple times) but nothing really worked. That's quite a limitation.

On to the next step, receiving and sending MIDI events:

var inName = MIDI.MidiInOpen( 1, function(t, msg) {   
    if ((msg[0] != 254) && (msg[0] != undefined)) {
        MIDI.MidiOut(msg[0]+midiChannel-1, msg[1], msg[2]);   
    }
});


The above example opens the MIDI in port 1. Whenever a MIDI event comes along, it just triggeres the anonymous callback function which then again sends the MIDI event to the MIDI out port we just opened. The if-statement filters out all the MIDI "active sensing" events, which are generated by many devices just to tell the world that they are alive. Also, take a look at the MidiOut statement. It includes a global variable (bah!) called "midiChannel" which can hold the value 1 to 16 which indicate the "Midi Channel" to send the data to. A MIDI device, such as a synthesizer, will only receive the event, when it listens to the corresponding channel.

The next step, obviously would be to play a note. In MIDI each note is defined by two MIDI events called 'note-on' and 'note-off'. A MIDI note-of must be parametrized with a key value (which defines the pitch) and the key attack velocity (which most of the time defines the volume). Each can have valid values between 0 and 127. A note-off also has to have a key value and a release velocity (which is ignored by most MIDI devices). To play a note, you have to combine a note-on with a corresponding note-off event. The time difference between note-on and note-off defines the duration of the note. All this is accomplished by the following piece of code:

function playNote( note, velocity, length, channel ) {
        MIDI.MidiOut(144+channel-1, note, velocity);
        setTimeout( function() {
                MIDI.MidiOut(144+channel-1, note, 0);
                MIDI.MidiOut(128+channel-1, note, 64); // also send MIDI note off
            },
        length );
}


This sends a note-on on a defined MIDI channel (that's why it's 144+channel-1). And then it creates a timeout which will send the corresponding note-off 'length' milliseconds after the note-on.

Great, now we can play music on our devices triggered by JavaScript. But what happens if something goes awry and notes keep playing on the MIDI device forever? For this scenario MIDI inventend the 'all-notes-off' command, which tells the device to just shut up. Of course, we can send that from JavaScript as well, as the following example shows:

function sendAllNotesOff() {
    //loop over all MIDI channels and send all notes off and all controllers off
    for (var i=0; i<16; i++) {
        MIDI.MidiOut( 176+i, 123, 0 );   
        MIDI.MidiOut( 176+i, 121, 0 );   
    }
}


The following screenshot shows the MIDI events captured by the node.js program when playing on the MIDI keyboard.

MIDI Events
Have fun.

No comments:

Post a Comment