パンはパンでも体の向きによらない3Dパンがでーきた

WebAudio Web MIDI API Advent Calendar 2016の19日目です。

 

せっかちな人のためのデモページリンク。スマホ+イヤホンで聞いてグルグル歩き回ってください。
http://aikelab.net/apan/

 

Web Audio APIでは、左右だけでなく3Dのパンが用意されています。つまり音の定位を三次元的に配置することができます。
さて、せっかく3Dパンなので、スマホの加速度センサーを使って、聴く人の体の向きによらず空間的に音が配置されるようにしてみましょう。つまり、たとえばイヤホンで音楽を聴きながら横を向くと、それまで左右から聞こえていた音が前後から聞こえてくる、というようなことです。

 

ソースコードはこんな感じになります。
まず何はなくともオーディオコンテキストの作成。

    var ctx = new (window.AudioContext || window.webkitAudioContext)();

Playerは自作のBufferSourceNodeラッパーです。詳しくはplayer.jsを見てください。

    var player = new Player(ctx, 'sound/apan.mp3');

チャンネルスプリッターノードでステレオソースの左chと右chを別々に取り出します。

    var splitter = ctx.createChannelSplitter(2);

パンナーノードで左chの音と右chの音を三次元空間に配置します。

    var panL = ctx.createPanner();
    panL.panningModel = "HRTF";
    panL.setPosition(-1, 0, 0);

    var panR = ctx.createPanner();
    panR.panningModel = "HRTF";
    panR.setPosition(1, 0, 0);

ノード同士の接続はこんな感じ。

    // player ----splitter
    //               ---- PanL ---- destination
    //               ---- PanR ---- destination
    player.connect(splitter);
    splitter.connect(panL, 0);
    splitter.connect(panR, 1);
    panL.connect(ctx.destination);
    panR.connect(ctx.destination);

パン回転用のsetAngle関数です。角度をラジアンで受け取ってパンナーノードの位置をY軸を中心に回転します。

    var setAngle = function(theta) {
        var sn = Math.sin(theta);
        var cs = Math.cos(theta);
        panL.setPosition(-cs, 0, sn);
        panR.setPosition(cs, 0, -sn);
    } 

加速度センサーのイベント処理。加速度センサーはalpha、beta、gammaの3つの値で表現され、スマホを縦持ちしたときのY軸回転はalphaに対応します。加速度センサーの値は度なのでラジアンに変換してから前述のsetAngle関数に渡しています。

    window.addEventListener("deviceorientation", function(e){
        if (e.alpha) {
            setAngle(e.alpha * Math.PI / 180);
        }
    });

 

デモページはこちらです。
http://aikelab.net/apan/
スマホ+イヤホンで再生します。
こんな姿勢でスマホを持ったままグルグル歩き回ってください。


ソースコードはこちら。
https://github.com/aike/AcceleroPanner