Posted at

ExoPlayerでアンビソニック+2ch音声の動画を再生する

More than 1 year has passed since last update.

ふとResonance Audio SDKのリファレンスを見ていたら、GvrAudioSurround.SurroundFormatの中にFIRST_ORDER_AMBISONICS_WITH_NON_DIEGETIC_STEREO, SECOND_ORDER_AMBISONICS_WITH_NON_DIEGETIC_STEREO, THIRD_ORDER_AMBISONICS_WITH_NON_DIEGETIC_STEREOという定数があることを発見しました。これを使えばヘッドトラッキングに対応した空間音響とヘッドトラッキングに対応しないステレオ音声を同時に再生するような気がします。

ExoPlayerをResonance Audio SDKと一緒に使うためのGvrAudioProcessorのソースを見てみると、94行目あたりに音声のチャンネル数に応じてGvrAudioSurround.SurroundFormatの定数値を切り替える記述が見つかりました。

しかしここではFIRST_ORDER_AMBISONICS_WITH_NON_DIEGETIC_STEREOなどの定数値は使われていないようです。じゃあ、この定数値を使うようにこのソースをカスタマイズしたらいいんじゃない?ってことで、以下のようにしてみました。

switch (channelCount) {

case 1:
surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_MONO;
break;
case 2:
surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_STEREO;
break;
case 4:
surroundFormat = GvrAudioSurround.SurroundFormat.FIRST_ORDER_AMBISONICS;
break;
case 6:
surroundFormat = GvrAudioSurround.SurroundFormat.FIRST_ORDER_AMBISONICS_WITH_NON_DIEGETIC_STEREO; // FIRST_ORDER_AMBISONICSから変更
break;
case 9:
surroundFormat = GvrAudioSurround.SurroundFormat.SECOND_ORDER_AMBISONICS;
break;
// 11chサポートを追加
case 11:
surroundFormat = GvrAudioSurround.SurroundFormat.SECOND_ORDER_AMBISONICS_WITH_NON_DIEGETIC_STEREO;
break;
case 16:
surroundFormat = GvrAudioSurround.SurroundFormat.THIRD_ORDER_AMBISONICS;
break;
// 18chサポートを追加
case 18:
surroundFormat = GvrAudioSurround.SurroundFormat.THIRD_ORDER_AMBISONICS_WITH_NON_DIEGETIC_STEREO;
break;
default:
throw new UnhandledFormatException(sampleRateHz, channelCount, encoding);
}

他の部分は一切手を付けていません。

このようにして修正したGvrAudioProcessorをExoPlayerに組み込めば、ExoPlayerでアンビソニック+2chの動画(音声)を再生することができるようになります!ExoPlayerへの組み込み方はざっくり言うとこんな感じです。

// フィールドに保持

val gvrAudioProcessor = GvrAudioProcessor() // 先程カスタマイズしたGvrAudioProcessorを使う

// ExoPlayerのインスタンスを作成する。メインスレッドで呼ばなければいけない。
fun createExoPlayer() : SimpleExoPlayer {
val trackSelector = DefaultTrackSelector()
val renderersFactory = object : DefaultRenderersFactory(context) {
override fun buildAudioProcessors(): Array<AudioProcessor> = arrayOf(gvrAudioProcessor)
}

return ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector)
}

// 毎フレームこの処理を行う
fun update() {
// w,x,y,z にユーザーのヘッドトラッキングのQuaternionの値を入れる
gvrAudioProcessor.updateOrientation(w, x, y, z)
}

この方式で再生できるアンビソニック+2chの音声というのは、例えば1次アンビソニックなら先頭4chにアンビソニック音声をWYZXで入れて5chにL、6chにRを入れた合計6chの音声です。2次、3次も同様に、先頭9chにWYZX...を入れて10chにL、11chにRを入れた合計11chの音声、先頭16chにWYZX...を入れて17chにL、18chにRを入れた合計18chの音声になります。

ただし、3次アンビソニック+2chの音声はGalaxy S8で試したところCPU負荷が高すぎるのかブチブチ音が切れてまともに再生されませんでした。