らくとあいすの備忘録

twitter : lactoice251

アンビソニックスを利用して広がり感のあるオブジェクト音を作る in VRChat

要点

はじめに

前に書いた記事『VRの世界に音を存在させる』からはや2年近くが立ち、VRの世界の音や、音に関わる表現はますます豊かになっています。*1

このような発展の中において、オブジェクトに紐づく音については、まだまだ踏み込む余地があるのではないかと考えていました。 特に、単にオブジェクトに音がくっついているというだけではなくて、その音が世界に存在しているようにするにはどうすれば良いのかという部分についてです。

シンプルな例

複雑な状況を考える前に、まずは (Unity/VRChatの) オブジェクトに音を紐づけるシンプルな例から考えてみます。

  1. 追従先のオブジェクトを用意する
  2. オブジェクトの子にAudioSourceを配置する

これでオブジェクトに音が追従します。ですが、ここで気にしておかなければならないのはAudioSourceの設定であり、適切に立体音響再生される設定を行う必要があります。ここで立体音響再生とは、すなわち音源と自身の頭部の相対的な位置関係から決まる頭部インパルス応答 (Head-Related Impulse Response; HRIR) の効果を取り込んだ再生です。これについては前の記事と内容が重複するため詳細は割愛しますが、要点としては次の3点です。

  1. 音が左 (右) にあると左 (右) から音が大きく聴こえる : Inter Level Difference (ILD)
  2. 音が左 (右) にあると左 (右) からの音が先に聴こえる : Inter Time Difference (ITD)
  3. 音の位置に応じて頭や耳で遮蔽される周波数帯域が変化する : Spectral Cue

こうした効果を取り入れるのはなかなか難しい話ではありますが、例えばVRChatでは「VRC Spatial Audio Source」というこの効果を簡単に取り入れられるコンポーネントがあるのでありがたく使います*2

シンプルな例における課題

シンプルな例における課題は、例えば残響感のある音を得ようと思ったときに現れます。 音源にリバーブを適用してそれを単一のAudio Sourceに割り当てた場合、音はぼやけますが広がりのある音にはなりません。

  • 正面位置から再生されるドライな音源
  • 正面位置から再生される、リバーブ適用後にダウンミックスされた音源

残響音は、(少なくとも現実の空間の場合) 音源から発生した音が部屋や環境に跳ね返り、音源とは異なる様々な方向から耳に入ってくることで生じます。 従って、残響音らしい音の広がりを得るためには、反射音の方向から到来する無数の音を用意する必要があり、単一の音源だけでは再現が難しいという課題にあたります。

この課題に対して、実際に壁の反射を考慮して動的に残響を変化させる仕組みは取り入れられつつあります。

しかし、音楽等の高品質な音響が求められる用途に耐えうる残響を、リアルタイムに変化するプレイヤー位置と音源位置を考慮しながら生成することは依然として困難です。

加えて、VRChat上で音響伝搬シミュレーションを動作させるには追加の多くの課題をクリアしなければなりません。鏡像法のような単純なアルゴリズムを適用するとして、ナイーブにAudioSourceを鏡像においていく方法ではすぐに音源数の上限に達してしまいます。したがって、再生する音自体を合成して単一の音源を用いてステレオ再生することの方が比較的現実的です。プレイヤーの位置と音源位置、壁の吸音特性を考慮して、鏡像法等を用いて仮想音源の位置と適用するフィルタを決め、各方向ごとに用意した何らかのHRTFを畳み込んだ音を加算した音を作り、それをAudioSourceに流し込むなどの方法が取れると思われます。VRChat上での音響合成の例があることを踏まえると、GPUに上手く送ってやれば出来ないこともない...気がします...。

別の方法としては、HRIR適用済みの全てのオブジェクト音が混ざった状態の出力に対して最後段で残響を付加するという方法が考えられます。 これは、UnityではAudio Reverb Filterを用いて実現できます。ただし、この残響は音源方向や距離とは無関係に適用されるものであり、残響レベルを上げるほどに音の立体感が失われていくという状況に直面します。 別の問題として (これはVRChatにおける問題ですが) 利用出来るReverb (Reverb Filter) の質として、音楽用途に充分ではないという点も併せて課題となります。

Reverbの質については、VRChatで解決不可能な問題かというと考える余地はあります。IRのデータを何らかの形式で用意して、それをリアルタイムに畳み込む処理を適用することで、Audio Reverb Filterには無い、自由な残響効果を付与できると思います。ですが、そうしたものをリアルタイムにかつ複数音源に対して実現したものはまだ見たことがないので、やってみたらやっぱり困難があった...ということもあるかもしれません。

チャンネルベース (サラウンド) の利用

比較的少数の音源を用いて、広がりのある音場を作る方法として、チャンネルベースの音の利用があります。 ステレオ再生はこの最も基本的なものです。ステレオ再生では、2つの音源を用いてその音圧差や位相差をコントロールすることで、あたかも音が広がっているようにきこえたり、音源が存在しない中央から音が鳴っているように感じたりすることが出来ます。 ただし、それでも二つのスピーカーより外にある音 (たとえば後ろの壁からの反響のような音) をステレオ再生のみによって再現することは困難です。

サラウンドは、ステレオを超えてより広がりのある音を得ることをひとつの動機として導入されるものです。例えば、5.1chサラウンドでは、受聴者の正面と前方左右、後方左右の5つの音源と、低音専用の1つの音源の計6チャンネルの音源で構成されます。より音源を増やした構成では、例えば受聴者を取り囲む7つの音源、低音専用の1つの音源、上部の4つの音源を組み合わせた7.1.4chサラウンドなどがあります。

サラウンドを利用した広がりのある残響音

サラウンドを活用することで、ある方向で鳴っている音の直接音と、プレイヤーを取り囲むように到来する残響音の両方を再現することが可能になります。

  • 正面位置から再生される音に対して、サラウンドを利用した広がりのある残響音を付加*3

ただし、オブジェクト音を鳴らす手段としてサラウンドを直接利用出来ない点には注意が必要です。 これは、例えばプレイヤーの近傍を通り過ぎる飛行物から音を鳴らす状況を考えた場合、その左右が入れ替わるタイミングはプレイヤーの前を通り過ぎた瞬間であるべきですが、サラウンドでは一律してあるタイミングで左右にパンを送ることしか出来ないためこういったことは実現出来ません。 より一般的に言えば、サラウンドの音源が配置された表面上の音の再現は可能ですが、その表面を超えてプレイヤーに近づけたり、プレイヤー自身が音源の囲む空間から移動したりする状況は扱うことが出来ません。(VRの文脈でよりくだけて言えば、3DoFと6DoFの違いにおおむね対応します。)

もう一つ注意すべき点は、サラウンド音響では (多くの方式において) チャンネル数を節約するために空間的に対象ではない音源配置を採用しています。 従って、十分な音像定位の解像度が得られるのは、より密に音源が配置された方向 (すなわち前方正面) であるため、(表面の音に限ったとしても) オブジェクトを明確に定位させられる領域は限定されます。

シーンベース (アンビソニックス) の利用

シーンベースの再生方式とは、プレイヤーを取り巻く空間全体、特にプレイヤーを覆う球面上の物理的な音場を記録・再生する方式です*4 *5。 アンビソニックスは、球面調和関数展開と呼ばれる方法を用いて、球面上の音場を特定の空間的なパターンの重ね合わせとして展開します*6。これはちょうど、フーリエ変換と似た操作であり、(必ずしも正確な表現ではありませんが) 空間的な音場のパターンを空間的な周波数の低いなだらかな変動成分と、空間的な周波数の高い細かな変動成分とに分けるような操作です。展開の0番目の次数は無指向の成分であり、1番目の次数はX, Y, Z方向に対応する指向性の成分ですが、この0, 1次の成分のみを用いて球面上の音場を表現する方式をFirst Order Ambisonics (FOA) と呼びます。FOAは空間的な解像度こそ高くはありませんが、サラウンドとは異なり、全天球に対して対称的であり、全ての方向に音を配置することが可能です。したがって、解像度はぼやけていても良いが、空間全体への広がりが必要な残響音への利用は有望と考えられます。

アンビソニックスを併用した広がり感のあるオブジェクト音

空間上のある一点から鋭い定位の音を再生できるオブジェクトベースの音に対して、広がりのある残響音を付加する方法として、アンビソニックスを組み合わせることは良い効果が期待出来ます。これは、アンビソニックス (特に1次アンビソニックス) は、空間解像度は必ずしも高くないが、少ないチャンネル数で全天球のあらゆる方向からの音を再生出来るという点に優れているからです。実際、オブジェクトベースとアンビソニックスの組み合わせ*7 *8や、アンビソニックス形式のリバーブ*9*10も活用されつつあります。 一方、VR音楽ライブ等においてはまだ利用例が乏しく、今後活用が期待される領域だと思います。 以下では、VRChatでのタイムラインに基づくライブでの実装を例にオブジェクト音とアンビソニックスに基づく残響音の併用方法について解説します。 なお、関連するUnity/Reaper用のスクリプト等はこちらで公開しています。

基本方針

オブジェクト音とアンビソニックスに基づく残響音を併用する基本方針は次の通りです:

  • 音を発するオブジェクトの位置をUnity上で記録する
  • 記録された音源位置をDAWに取り込む
  • 対応する位置の音源を想定した残響音をアンビソニックス形式で書き出す
  • Unity (VRChat) 上でアンビソニックスとオブジェクト音 (w/ HRIR) を同時に再生する

この方式では、全てのオブジェクト音に対する残響音成分がアンビソニックス上に重ね合わされてレンダリングされることになり、オブジェクト数が増えても単一のアンビソニックスによってこれらの情報を統合することが出来ます。

Unity上での音源位置の記録

Unity上での音源位置の記録は各オブジェクトの座標等の情報を一定間隔でCSVに書き込むことによって行います。 ここでは以下のような2つのオブジェクトのアニメーションを作成し、座標を記録しました。

注意すべきポイントとして、アンビソニックスではある中心位置を仮定したときの全天球上で音をレンダリングするため、座標の記録は絶対座標ではなく、あるリスニングポイントを仮定したときの相対的な極座標 (方向+距離) で記録されることが望ましいです。実際に記録されたCSVファイルは以下のような形式です。

time,x,y,z,azimuth,elevation,distance
0.0,-3.02,2.40,0.14,0.74,0.55,0.03
0.1,-3.02,2.40,0.19,0.74,0.55,0.03
0.2,-3.00,2.40,0.43,0.73,0.55,0.03
...

元音源の分解・残響音の付加・振り分け

オブジェクト音に対する残響音を作りだす最もストレートな方法は、対象とする空間を仮定し、音源位置と受聴地点の情報をもとにその空間における残響音を計算することです。このような目的のためには、例えばIEM Room Encoderのようなプラグインを用いることが出来ます。

IEM Room Encoder

ただし、ここで元音源 (source) として適用するべき音は無響室で収録されたモノラル音源である点に注意が必要です。 多くのSFXや音楽的な音は元々響きの成分を含んだステレオの素材として作られており、このような音源素材を使う場合には上記のような方法は必ずしも適していません。

そこで今回は、響きを含んだステレオの元音源にも対応出来る方法として次の図のような処理手順を採用しています。

元音源からモノラルのオブジェクト音とアンビソニックスの残響音を作りだす
この方法では、まず元音源を任意のチャンネルのサラウンド (今回は7.1.4chを採用) に拡張します。 拡張の方法としては、追加のリバーブを付加せずに疑似的にチャンネル数を拡張出来るNugen Halo Upmixや、サラウンドリバーブの付加によってチャンネル数を拡張出来るCinematic Room等のサラウンドリバーブの利用が考えられます。 ここで得られたCenterのチャンネルをモノラルのオブジェクト音、それ以外の音をアンビソニックスに割り当てる残響音成分として分配します。 サラウンド表現からアンビソニックス表現への変換には、IEMのMulti Encoderを利用しました。
IEM Multi Encoder
この方式における利点は、ステレオの音源を使ってオブジェクト音とそのアンビソニックスでの残響音を作りだせることであり、ステレオの領域で好みのリバーブプラグインを利用することも可能になります。一方で、今回の方法はRoom Encoder等の部屋の形状に基づくシミュレーションとは異なり、特定の部屋の構造を想定していません。したがって、あくまでも適用したリバーブによって暗黙的に何らかの部屋形状や空間の大きさが規定されることになるという点には注意が必要です。さらに、アンビソニックスに割り当てられる音は必ずしも残響音と呼べるもののみになるわけではないため、音像をタイトに絞りたい場合にも適さない可能性があります。逆に、オブジェクトの音を点ではなくもう少し広がりのある大きな発音体として見せたい場合にはむしろ効果的な状況が生まれるはずです。

ReaScriptを用いたCSVを中間ファイルとするUnityとDAWの連携

音源位置に応じた残響音の再現は、前節で述べたような方法を考える場合DAW上で各種プラグインを利用して行えることが望ましいです *11。 この時、各プラグインのパラメータは音源位置に応じてオートメーションされる必要があります。CSVで記録された音源位置 (方向+距離) をDAW上のオートメーションに取り込む方法として、例えば一度MIDI CC信号に変換するなどの方法を取ることが出来ると思いますが、ここではスクリプト経由で直接CSVからオートメーションに情報を反映することにします。

スクリプト経由でのCSVの読み込みとオートメーションの作成のため、Reaper というDAWを利用します。 Reaperでは、ReaScriptと呼ばれるスクリプト機能 (lua) を用いて、各種操作を自動化出来ます。スクリプトの詳細については、こちらで公開したので割愛しますが、ポイントとなる部分をいくつか抜粋します。

CSVファイルをラインごとに読み取って、配列に格納する:

local csv_points = {}
for line in csv_file:lines() do
    local time, x, y, z, azimuth, elevation, distance = line:match("([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+)")
    time = tonumber(time)
    azimuth = tonumber(azimuth)
    elevation = tonumber(elevation)
    distance = tonumber(distance)
    if time and azimuth and elevation and distance then
        table.insert(csv_points, {time = time, azimuth = azimuth, elevation = elevation, distance = distance})
    end
end
csv_file:close()

配列に格納されたCSV情報 (csv_points) をある指定のトラックのエンベロープ (オートメーション) にセットする:

function EnvelopeUtils.insert_points_from_csv(envelope, csv_points, envFunc)
    for _, point in ipairs(csv_points) do
        local value = EnvelopeUtils.csv_to_env(envFunc, point.azimuth, point.elevation, point.distance)
        if value then
            reaper.InsertEnvelopePoint(envelope, point.time, value, 0, 0, false, true)
        end
    end
    reaper.Envelope_SortPoints(envelope)
end

ここで、EnvelopeUtils.csv_to_envは角度や距離の値からエンベロープの値 (0-1) に変換するための関数であり、音源種類や適用先プラグインに応じて決定されるenvFuncを引数として決定されます。envFuncの指定は具体的には次のような全体設定用csvファイルを別に用意することによって行いました。

objectName,trackType,envName,envFunc,loadScore,useScore
ClapObject,ReverbAmbi,Master azimuth angle / MultiEncoder,azimuth,0,0
ClapObject,ReverbAmbi,Master elevation angle / MultiEncoder,elevation,0,0
ClapObject,Source,Mix / ValhallaVintageVerb,dist2verb,0,0
...

このファイルでは、適用するオブジェクトの名前、そのオブジェクト用のトラックの中で、どのトラック (直接音用 / 残響音用) のどのFXパラメータにエンベロープを設定するか、そしてどのような関数で角度・距離からエンベロープ (0-1) に変換するかという情報が与えられています。例えば、角度 (azimuth) は上述のIEM MultioEncoderのMaster azimuth angleにそのまま0-1に正規化された値として入力されます。音源自体に適用されるリバーブは、dist2verbの関数に従っておおよそ距離に反比例する振幅でdry/wet比が決められます。

これらの処理を行うことで、次のように各FXのパラメータにエンベロープ (オートメーション) が適用されます。この動画では、直接音も含めてアンビソニックス上に配置したものを、binaural decodingしたステレオを再生しています。

細かい話

1. 動画中で表示しているエンベロープは主に方位角 (azimuth) ですが、エンベロープ中に不連続な点があるのがわかると思います。これは、角度が一回転する地点に対応します。このとき、その区切れ目 (360° -> 0°) で高速な逆回転が起こらないように、360°の点と0°の点を同一時刻に配置する必要があります。このため、エンベロープの値の差分がある閾値を超えた場合に、移動先の値に近い方に回り込んだ値の点を同一時刻に一つ追加するという後処理を追加しています。

2. ReaScriptはほとんどのDAW操作を自動化出来ます。したがって、今回の例においてもルーティング済みのトラックの複製・リネーム・エンベロープ適用などのオブジェクト毎の操作や、クリップ自体の配置などを自動化している他、書き出し/リスニング時のミュート設定なども全トラックに対して一括で変更出来るようにしています。

Unity (VRChat) への取り込み

作成されたモノラルのオブジェクト音と、アンビソニックスの残響音をそれぞれUnityに取り込みます。 アンビソニックスの音のVRChat向けの取り込み方法・再生方法については前回の記事の「2.3 シーンベースの立体音響」に記載したため、ここでは割愛します。記事に記載した適切な方法で、AudioClipの取り込み、AudioSourceの設定を行うことで、VRChatではアンビソニックスの音を頭部の回転に応じたBinaural (Stereo) として再生可能です。一方、オブジェクト音については、上述の「シンプルな例」と同様に単にオブジェクトに追従するAudioSourceから再生されるように設定します。

下記の動画は実際に上記の設定を行ったものをVRChat上で再生したものです。

ここでは、次の3つの設定が比較されています:

  1. 上記の方法で抽出されたモノラルのオブジェクト音とアンビソニックス形式の残響音の組み合わせ
  2. モノラルのオブジェクト音のみ
  3. バーブを適用した後モノラルにダウンミックスした音源をオブジェクト音に使用

これらの比較から、1の方法がオブジェクト音の定位感を損なわずに空間的な広がりのある音を表現出来ていることが分かります。

おわりに

本記事では、オブジェクト音を活用したVRでの音楽ライブ等の用途を主な目的として、空間的な広がり感のある残響音の付加を行う方法について解説した。 空間的な広がり感のある残響音を得るため、全天球にわたる音場を表現することの出来るアンビソニックス形式を利用した。 残響音を作りだす方法としては、Unity上で記録された音源位置を元に作り出された残響音成分を、一度サラウンド形式を介してアンビソニックス形式に変換する方式を採用した。

今回の方法では、部屋形状や材質等の考慮などの厳密なシミュレーションの代わりに音楽的に欲しいリバーブプラグインを使って作られた音を空間的に配置するという方向でのアンビソニックスの活用を行いましたが、もちろん前者のような方法が役立つ状況も多くあると思います。 また、リアルタイムに (あるいはインタラクティブに) 発生するオブジェクト音に対する残響音の付加についてもこの範疇では解決しきっていない問題です。 これらの点もふくめ、この記事で書かれている内容は単なる一例に過ぎませんが、様々なシチュエーションでオブジェクト音を活用していくための一助となれば幸いです。

ところで、現在公開中のこちらのミニパレードでは、アンビソニックスを併用した広がり感のあるオブジェクト音が使われていたりします。この記事を読んで興味を持たれた方は、9/22 (日) 23:00までにぜひ!最後に宣伝でした!