Web MIDI API + WebSocket による遠隔演奏の実験

Web MIDI API と WebSocket を利用した遠隔 MIDI ライブ演奏のサンプルウェブアプリを作りました。イメージ図は次の通りです。WebSocket には Node.js の Socket.IO を利用しました。

遠隔演奏アプリの説明図

クライアントがアプリと MIDI 楽器を繋ぎ演奏すると、同じアプリに繋がれた他のクライアントにも Node.js サーバーを通して、リアルタイムに MIDI メッセージが送られます。メッセージを受信した各クライアントは自身と接続された MIDI 楽器を鳴らします。複数クライアントが同時に演奏すれば、同時に情報が送られ、遠隔演奏セッションが実現できます。

サーバーコード

app.js

まずは node.js のフロント部のコードです。midi.listen(io) を呼び出し、MIDI を送受信する WebSocket のイベントハンドラーを設定しています。

var fs = require('fs');
var http = require('http');
var midi = require('./sockets/midi');

var server = http.createServer();
var io = require('socket.io').listen(server);

// MIDI socket 通信設定
midi.listen(io);

server.listen(8080);

sockets/midi.js

受信した MIDI メッセージを他クライアントに配布するmidi.listion(io) を定義します。

var clients = {};

exports.listen = function (io) {

    io.sockets.on('connection', function (socket) {

        socket.emit('connected', {});    

        socket.on('join', function (client) {

            // 接続クライアント情報を格納する
            socket.set('client', client, function () {
                clients[client.name] = socket;
                console.log('join: ' + client.name);
            });

            socket.emit('joined', {});

            // 受信した MIDI message を全クライアントに送る
            socket.on('play', function (data) {
                console.log('play: ' + data);
                socket.broadcast.emit('play', data);
            });
        }); 
    });
};

play イベントにより MIDI メッセージを受信したら、受け取った data を socket.broadcast.emit メソッドでそのまま他クライアントに配信しています。名前は midi.js ですが、MIDI に特化した処理は何もしてません。

クライアントコード

クライアントは接続確立後、requestMIDIAccess API を用いて端末に接続されている MIDI 機器を認識します。MIDI 機器から MIDI メッセージを受信したら、それをサーバーに送信します。逆にサーバーから play イベントにより MIDI メッセージを受信したら、それを MIDI 機器に送信します。

index.html

<script src="//code.jquery.com/jquery.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>

    var input;
    var output;
    var socket;
    var session = {
        name: Math.round(Math.random() * 1000)
    };

    $(document).ready(function () {
            
        socket = io.connect();

        socket.on('connected', function (data) {
            socket.emit('join', session);
        });

        socket.on('joined', function (data) {

            navigator.requestMIDIAccess({sysex:true})
                     .then(scb, ecb);

            function scb (access) {

                input = access.inputs()[0];
                console.log(input);

                output = access.outputs()[0];
                console.log(output);

                // MIDI 機器から受信した message をサーバーに送る
                input.onmidimessage = function (event) {
                    console.log(event.data);
                    socket.emit('play', event.data);
                };

                // サーバーから受信した message を MIDI 機器に送る
                socket.on('play', function (data) {
                    var data = [data[0], data[1], data[2]];
                    console.log('play: ' + data);
                    output.send(data);
                });
            }

            function ecb (error) {
                console.log(error);
            }
        });
    });

</script>

ネットワーク遅延

Node.js をさくら VPS の東京リージョンのサーバーにデプロイし、実験してみました。クライアントも東京にあり、サーバー・クライアント間の物理的な距離は23区内に収まっていると思われますが、若干演奏の遅延が感じられました。

遅延の程度に幅があり、リズム・テンポが受信側で時々不安定になります。 ただ、体感的には遅延時間は遅くとも 0.5 秒未満には収まっている感じで、ほとんど遅延を感じないタイミングもあります。むしろ全体的には意外に遅延しないなーという感想です。

一方通行の遠隔ライブは大丈夫そうですが、複数クライアントによるセッションは、お互いでタイミングを合わせられないので、ちょっと難しそうです。すっごいテンポゆっくりな曲なら大丈夫かもしれません。