Hello WebMIDI

October 30th, 2016. Tagged: (x)HTML(5), JavaScript, Music, WebAudio

Inspired by this talk by Jean-Philippe Côté I saw at Web Unleashed in Toronto last month, I thought I should dust off the old midi cable.

MIDI?

MIDI is a protocol that various music and lighting devices use to talk to each other. Believe it or not v1.0 of the spec from 1983 is still the one in use, remaining largely unchanged. In a world where we don't know if we're using JavaScript, ECMAScript7, ES2016 or ES.Next, this is... interesting development. Or lack of. Looks like it just works. It's all about sending messages between devices. I admit the content of the messages in not the frieldliest, but it seems to be doing the job.

And now - there's WebMIDI! Supported in Chrome and Opera proper (no flags) and via a polyfill everywhere else.

So let's see how to use it...

Off the ground

You need:

  • Device with MIDI out, any old keyboard or pad of some sorts
  • MIDI to USB cable (under $10 on Amazon), unless your device speaks midi over USB or your computer has a MIDI (send me photo!)

navigator.requestMIDIAccess() is where it all starts. It returns a promise, which resolves with MIDIAccess object that has a map of MIDI inputs (you can have more than one)

Here's how I get the first (and only, in my case) input suffering through modern APIs with their promises and their iterators and buh-humbah-grumble-grubmle:

navigator
  .requestMIDIAccess()
  .then(
    midi => 
      FIRST = midi.inputs.values().next().value
  );

Now if you inspect FIRST you'll see it has a curious property - onmidimessage.

screen-shot-2016-10-29-at-10-51-00-pm

As you can expect, you signup for those events and start exploring them.

Listen...

  FIRST.addEventListener(
   'midimessage', 
   msg => console.log(msg.data)
  )

The data seems to be the most promising piece of the msg event object.

Here's what happens when I play C as soft as I can:

[144, 48, 33]
[128, 48, 64]

The two events seem to be "start" (144) and "stop" (128), the note is 48 and 33 is the velocity. 64... no idea and too lazy to look up specs.

Isn't that the beauty of web programming? I dunno what I'm doing (did not RTFM) but I can already build stuff just by trying things out and happily hacking.

Then I play the same C as loud as I can:

[144, 48, 122]
[128, 48, 64]

yup, 144 is start, 48 is C and 122 is the velocity (how hard I hit)

Playing soft and loud D seems to coincide with the hypothesis.

[144, 50, 70]
[128, 50, 64]
[144, 50, 120]
[128, 50, 64]

Thanks!

Thanks for watching and reading, now let's hack on some music stuff!

Quick update - tried with electronic drums, works like a charm

navigator.requestMIDIAccess().then(midi => I = midi.inputs);
I.values().next().value.onmidimessage = msg => console.log(msg.data)

Here's a slow closing of the hi-hat:

[185, 4, 4]
[185, 4, 8]
[185, 4, 12]
[185, 4, 16]
[185, 4, 21]
[185, 4, 26]
[185, 4, 30]
[185, 4, 35]
[185, 4, 42]
[185, 4, 49]
[185, 4, 55]
[185, 4, 59]
[185, 4, 63]
[185, 4, 68]
[185, 4, 73]
[185, 4, 79]
[185, 4, 83]
[185, 4, 90]

Closed hi-hat hit

[185, 4, 90]
[153, 22, 89]
[137, 22, 64]

And slightly open

[185, 4, 81]
[153, 26, 65]
[137, 26, 64]

Snare proper:

[153, 38, 98]
[137, 38, 64]

Snare rim:

[153, 37, 35]
[137, 37, 64]

Rim shot (both skin/mesh and rim)

[153, 40, 127]
[137, 40, 64]

Another quick update: control surface gone bananas

A control surface is a big mouse replacement when you work with music software and there are a lot of channels (tracks, e.g. guitar, bass, lead vocals, backing vocals...). So instead of reaching with the mouse to various virtual knobs why not work with real knobs and even twist more than one at a time. And guess what - control surfaces also use MIDI.

So after logging a few MIDI messaged to the console, it was obvious that messages starting with 144 (like note ON for keyboards) are for touch controls. The second bit is which touch control (there are about a hundred on this device) and then a value between 0 (off), 127 (on) and sometimes values in between to mean blinking.

As for the faders, this control surface has 9 with numbers 224 to 232 and values 0 to 127.

So all that's left is to write two random functions to control random knobs and faders with a setTimeout and make the thing dance and blink and go crazy.

Unlike the previous examples where I used MIDI input, this here is the midi output. In other words the browser sends a MIDI message to the device and the device reacts. For some reason I couldn't make this output work with the keyboard, but here it totally worked.

navigator.requestMIDIAccess().then(midi => O = midi.outputs);

// thanks stackoverflow
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

// random touch (on/off lights)
function rando144() {
  return [144, getRandomInt(0, 100), getRandomInt(120, 127)];
}
var a = setInterval(() => O.values().next().value.send(rando144()), 100)

// random fader
function rando() {
  var channel = getRandomInt(224, 232); 
  var value = getRandomInt(0, 127); 
  return [channel, 0, value];
}
var b = setInterval(() => O.values().next().value.send(rando()), 200)

And the video result:

Tell your friends about this post: Facebook, Twitter, Google+

Sorry, comments disabled and hidden due to excessive spam. Working on restoring the existing comments...

Meanwhile, hit me up on twitter @stoyanstefanov