JavaScript Advent Calendar 2011(Node.js/WebSocketsコース)に急遽参戦。24日分の投稿になります。
MIDI信号をNodeで読み取り、WebSocketでブラウザに送ることで、MIDIシンセサイザーをキーボードやトラックパッドにかわる入力デバイスにすることができます。過去にもやったネタなのですが、今回はGoogleマップをシンセで動かします。Nodeみたいなイベント駆動系とハードウェアって相性がいいに決まってるじゃないですか。
まずは動画で
要はこういうことなんです。つまみのぐりぐりに連動して、マップのズームが上下していますね。
MIDI信号をNodeで読むには
node-midiモジュールを使用します。インストールは次のコマンドで。
npm install midi
RtMidiという、MIDI信号をリアルタイムに読み取るC++のライブラリをつかっています。
サーバ側
https://github.com/niccolli/MidiMaps/blob/master/midi2socketio.js
input.on('message', function(deltaTime, message) {
// ノブをいじった信号
if(message[0]==176 && message[1]==99){
if(message[2]==0){ // knob X
state = 'X';
}
if(message[2]==2){ // knob Y
state = 'Y';
}
if(message[2]==4){ // knob Z
state = 'Z';
}
}
if(message[0]==176 && message[1]==38){
LSB = message[2];
switch(state){
case 'X':
var temp = MSB*128 + LSB;
value = (temp-500)/5000;
break;
case 'Y':
if(MSB){
value = LSB - 28;
} else {
value = 100 + LSB;
}
value = (value-200)/1000;
break;
case 'Z':
value = LSB/100*20;
break;
default:
break;
}
io.sockets.emit('message', { state: state, value: value });
}
}
一部抜粋で。使用するMIDIシンセサイザーに依存した記述になっています。ここではALESISのMicronを使っています。input.onはMIDI信号の受信をトリガにして呼ばれるコールバックで、前半のifで3つあるノブのどれが回ったかを記録。後半は3つのノブそれぞれ最大最小値が違うので、なんとなくごまかす処理。最後のio.sockets.emitでブラウザに値を送ります。
クライアント側
https://github.com/niccolli/MidiMaps/blob/master/map.html
socket.on('message', function(data){
switch(data.state){
case 'Z':
map.setZoom(Math.round(data.value));
break;
default:
break;
}
});こちらも一部抜粋。GoogleマップのsetZoomにそのままつっこむと、上記の動画のようになるんですね。
やり残し
本当はノブが3つあるので、残り2つのノブで上下左右の移動を実装しようとしたのですが、「WebSocketを受け取る→マップの中心座標から次の中心を調整→setCenter」を受ける度に繰り返すと、中心座標がundefinedになりうまくうごかないんですね。setCenterの処理が終わる前に次のsetCenterが呼ばれるのがいけない気がするのですが、なかなかうまい解決法がみつけられず…。
加えて、node-midiモジュールはいまのところnodeの0.6に対応できていません。C++のコンパイルがあるので、node_events.hがなくなった状態に追いつけていない状態です。ここはなんとかしたいところ。
(追記 2011.12.26 )Nodeが0.5.2以上の場合、node-midiはこちらで。先を越されてた…。