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はこちらで。先を越されてた…。