THETA Sの全天球配信を強化する(3) 3Dサウンドを再現したい


こんにちは、インフォコム技術企画室のがねこです。前回は3Dサウンドを配信するための自作ガジェットと、処理の仕組みを紹介しました。今回は3Dサウンドを受信して、それをVR映像と合わせて再生するまでの流れをご紹介します。

複数音声の受信

まずWebRTCの通信を行うPeerConnectionを経由して、ステレオ音声×4を受け取ります。残念ながら順不同(※もしかしたらルールがあるかもしれません)なので、人が聞きながらどの位置の音なのかを対応づけます。

receive_multistream.png

各<audio>タグが、前後左右の音に対応しています。

audio_4_around.png

4つのステレオサウンドの配置調整

4つのaudioタグの一つだけを鳴らしながら、それがどの位置の音を人間が聞き分けて、画面上でドラッグ&ドロップして並べ替えます。本当はこのような職人芸は不要にしたかったのですが、今回は間に合いませんでした。

audio_renumber.png

ヘッドトラッキングに合わせた音量調整

Oculus Riftのヘッドトラキングを利用して人の顔の向きを取得し、向きに応じて各<audio>タグの音量を動的に調整します。例えば前後の場合は、対応する<audio>タグを1つだけ鳴らします。

front_back.png

同様に左右の場合も、対応する<audio>タグを1つだけ鳴らします。

left_right.png

そして斜めの場合(つまり、ほとんどの場合)は、対応する2つの<audio>タグの音量を調整して鳴らします。

front_left_right.png

実際の処理は次のようになっています。こちらもメンバーのtokさんが考案、実装してくれました。

// WebVR1.0 APIでHMDを取得する
var vrDisplay;
navigator.getVRDisplays().then(function (displays) {
    if (displays.length === 0) {
        console.log('WebVR API is NOT supported.');
        return;
    }
    vrDisplay = displays[0];
    console.log('WebVR API is supported!!!');
}).catch(function (err) {
    console.error('Could not get VRDisplays', err.stack);
});

function animate() {
    requestAnimationFrame( animate );
    update();
}

function update() {
    // WebVR1.0 APIでHMDが向いている方向を取得
    var pose = vrDisplay.getPose();
    var orientation = pose.orientation;
    // クォータニオンからオイラー角へ変換
    var euler = new THREE.Euler();
    euler.setFromQuaternion(new THREE.Quaternion(orientation[0],orientation[1],orientation[2],orientation[3]), 'ZXY');
    var yaw = euler.y * 180 / Math.PI;

    updateVolumeByHMD(yaw);

    // この後、HMDへの描画処理などが続く
}

function updateVolumeByHMD(yaw) {
    // 水平角の値を調整
    azimuth = yaw;
    if(azimuth > 180) azimuth = 180;
    else if(azimuth < -180) azimuth = -180;

    // 角度別に、ボリュームバランスを調整
    if(azimuth <= 0 && azimuth > -90) { /*  右前  */
        var sectionAzimuth = -1 * azimuth;
        var sectionMax = 90;
        frontAudio.volume = (sectionMax - sectionAzimuth) / sectionMax;
        backAudio.volume = 0;
        leftAudio.volume = 0;
        rightAudio.volume = sectionAzimuth / sectionMax;
    } else if(azimuth <= -90 && azimuth >= -180) { /*  右後  */
        var sectionAzimuth = -1 * (azimuth + 90);
        var sectionMax = 90;
        frontAudio.volume = 0;
        backAudio.volume = sectionAzimuth / sectionMax;
        leftAudio.volume = 0;
        rightAudio.volume = (sectionMax - sectionAzimuth) / sectionMax;
    } else if(azimuth > 90 && azimuth <= 180) { /*  左後  */
        var sectionAzimuth = azimuth - 90;
        var sectionMax = 90;
        frontAudio.volume = 0;
        backAudio.volume = sectionAzimuth / sectionMax;
        leftAudio.volume = (sectionMax - sectionAzimuth) / sectionMax;
        rightAudio.volume = 0;
    } else if(azimuth > 0 && azimuth <= 90) { /*  左前  */
        var sectionAzimuth = azimuth;
        var sectionMax = 90;
        frontAudio.volume = (sectionMax - sectionAzimuth) / sectionMax;
        backAudio.volume = 0;
        leftAudio.volume = sectionAzimuth / sectionMax;
        rightAudio.volume = 0;
    }
}

こうすることで、見ている映像と違和感のない3Dサウンドを視聴者に体験させることができます。

利用環境について

Chromium

ステレオサウンドを扱うためにChromeを利用していますが、ChromeでWebVRを扱うには安定板ではなく、 Chromium のWebVR機能を追加したビルドを利用する必要があります。今回利用したのはこちらになります。

three.js

WebGL + WebVRを手軽に扱うために、three.js を利用しています。

最後に

今回ご紹介した3Dサウンド対応の全天球配信VRを、2016年6月8日~6月10日に行われる AppsJapanWebRTCパビリオンに展示します。Interopに併設されるイベントですので、お越しの際にはぜひ体験してみてください。お待ちしています!

wind_chime_machine.jpg

(自作の風鈴連続鳴らし器。会場で展示される、かもしれません)