MaxやPuredataを使う理由のうちにマルチチャンネルの制御がしたいということがある人は少なく無いと思います。
一般的な5.1chの実装はVBAPというアルゴリズムで実装されていますが、聴取位置からスピーカーが等距離でないといけなかったり、結構制限が多くて大変です。 そこでもう少しユルいアルゴリズムであるDBAPというのを使ってみようという話です。
パンニングの基本:ILDとITD
人間は左右の耳における音量差と時間差で音源の到来方向を知覚しています。この時の
- 音量差をILD(Inter-Channel Level Difference)
- 時間差をITD(Inter-Channel Time Difference)
と呼んだりします。
一般的なステレオパンナーや次に紹介するVBAPではILDのみを使っています。バイノーラルなどはILDとITDを両方活用したものということが出来ます。
VBAP
マルチチャンネルによく使われているアルゴリズムは基本このVBAPというものです。
VBAPはアールト大のVille Pukki氏の開発したアルゴリズムで、一般的なステレオパンナーを3次元的に拡張したものになります。
Vector base amplitude panning TKK Acoustics / Research / Spatial sound
https://legacy.spa.aalto.fi/research/cat/vbap/
球面上に並んだスピーカー群を三角形分割し、聴取位置から仮想音源位置までのベクトルをその音源を囲む3つのスピーカーまでのベクトルの足しあわせ(基底ベクトルの一次結合)で表現しようという考えです。
すでにMax/Pd共通ソースのエクスターナルオブジェクトが配布されているので割と簡単に使えます。 Pd-extendedには標準でこのvbapエクスターナルが入ってます(vanillaでもfind externalからvbapで検索すると出てきます)。
VBAPでは先述したようにILDのみを使ったパンニング方法なので、すべてのスピーカーから同時に音が届くような前提で考えられています。
そのために発生する問題が2つ。
- スピーカーをすべて同一球面上に置かなければいけない。
- スイートスポットが1点に固定される。
例えば普通の四角い部屋の天井に幾つかスピーカーが配置されてる状態でVBAPを組もうと思うとすでに球面上に位置するポイントがないためにディレイを挟むなどして一工夫する必要があります。
DBAP
以上の問題点を改善するというか、もうちょっと妥協した条件でそれっぽくなるのを目指した(と解釈しました)のがDBAPです。 VBAPが98年に誕生したのに比べてDBAPは論文にまとまったので言えば2009年ぐらいなので、そこそこ新しいもののようです。
空間音響の表現のプロトコルの標準化を図るSpatDIFの中にもDBAPの記述が見られます。
https://redmine.spatdif.org/projects/spatdif/wiki/DBAP_-_Distance_Based_Amplitude_Panning
VBAPに必要な条件は
- 聴取位置
- スピーカー配置(聴取位置から等距離)
- 仮想音源位置(聴取位置から等距離)
の3つです。一方DBAPは、
- スピーカー位置
- 仮想音源位置
の2つの関係のみで音量を決定します。
聴取位置が等距離でないために位相差の干渉などは起こりますがそこに目をつぶればそれなりに移動してる感も出ます。
すでに実装されているものとしては
Jamoma DBAPを考案したBEK(Bergen Center for Electronic Arts)が中心になって作っているらしい?Max/Pd/C++用オーディオフレームワークの中にdbap用のオブジェクトがあります。ただJamoma自体がオーディオプロセッシング用MVCフレームワーク(!?)というかなりクセのあるライブラリのため、DBAPのみを使いたい時に気軽に使えるとも言い難いです。
a-objectシリーズ andré sier氏の制作した大量のエクスターナル群の中にa-dbap2d,a-dbap3dオブジェクトというのがある。最終更新は2007とちょっと古め
Pd用のdbap2d,dbap3dオブジェクト 上のa-objectを元に作られたPd用の実装。
Max上ではnodesオブジェクトを使ったパンニングも思想としては近いですが微妙に違います。 せっかく実装の簡単なアルゴリズムなので、Max上で組んでみるとしましょう。
実装
今回はJamomaのページにある論文をほぼそのまま実装したものですので、より詳しい情報が見たいという方はこちらを参考にするのがよいと思います。
https://jamoma.org/publications/attachments/icmc2009-dbap-rev1.pdf
今回メインでお世話になるのはvexprオブジェクトです。scalarmodeというのをオンにすればスピーカーの数が複数個になってもアウトプットのリストが長くなるだけで動的に対応できるのでとても便利です。
まず、仮想音源とスピーカー位置の距離をそれぞれ算出しましょう。
x,yそれぞれの距離の二乗を足して、平方根を取ります。
距離に反比例して音量が小さくなっていって欲しいので、逆数を取ればよさそうです。
しかし、距離が離れるにつれて減衰していく割合のようなものを自由に変えられると便利です。そこで次のように考えます。 今、距離が2倍になれば音量は1/2、デシベル値に直すと20*log10(1/2)≒-6.02dBです。これをロールオフ(dB/double)というパラメーターと考えるようにしましょう。
ここで逆数というのはマイナス1乗という風にも捉えられますから、1/d = d^(-1) = d^(-6.02/6.02)という風に変形するとd^(-rolloff/20*log10(2))で好きなロールオフに設定することができるようになりました。
ロールオフを3,6,9dBと変化させるとこのようになります。
さて、ここで一つ疑問が浮かびます。スピーカー同士が近いところだけやたら音量が大きくなるのではないでしょうか?はい、そのとおりです。 なので出てきたゲインの係数を一度すべて足し、その数でそれぞれのゲインを割ってあげましょう。
こうすることで「全てのゲインの係数(正確にはエネルギーなのでその2乗)を合計すると1」になります。 というわけで実装はこれだけで完了です。簡単ですね。
さらにvexprをできるだけまとめるとこのように。
基本的な実装はたったこれだけで済みます。シンプル!
オプション1: Spatial Blur(空間広がり)
先程までは音源を点音源と仮定してましたが音源がぼわっと広がるような仮定をする場合はどうでしょう。 この場合は割と簡単で、音源に半径を指定して距離の計算に組み込めば良いだけです。
ちなみに仮想音源とスピーカーの距離が全く同じになるとゲインが無限大に発散するため一応半径を少しだけでも設定しておくのが吉だと思います。
以上。
オプション2:スピーカー領域外の定位(未完)
さて、最後のゲインで割ってあげる処理のおかげで一つ困った問題が出てきます。
スピーカー群から遠ざかるほど音量が全体的に平均的になっていくので、スピーカーに囲まれた領域より外側に定位させることが難しいのです。(というか、そもそもスピーカーより外側に定位させるというのは原理的に無理なんですが仮想音源が遠くから近づいてまた去っていく、のような表現を擬似的に行いたい、という要望はあると思います)
元論文によるとこの場合は「スピーカーの凸包集合に対する最小距離で音量を割ってあげる」とのことです。
凸包?
凸包とは点の集合を紐でぐるっと巻いてやった時にできるような形のことです。 オレンジ色がスピーカー群、緑色の丸が仮想音源だと思ってください。 最小距離は辺に対する垂線の場合もあれば頂点の場合もあるし、そもそも凸包自体を求めるのも結構大変みたいなのでこれについては挫折しました(元論文にも説明だけで実装の数式などは書いてないようで、めんどくさいので挫折したのだと思っています)。
現実的にはスピーカーの配置されてる部屋の形(四角とか円とか)で近似してそこからの距離を出すのがよいのではないかと思います。
オプション3:jsuiでGUIを作ってみる
先ほどのvexprで作ったものはスピーカーの数は動的に変更できますが動かしたい音源が増えるたびに先ほどのオブジェクトを作って対応しないといけません。 poly~で複数可してもよいのですがなんだか冗長な気もしたのでまとめてやる方法はないかと思い、試しに使ってみようと思っていたjsオブジェクトで計算してみることにしました。
ついでにSpatial Blurも実装して、結果としては以下の様な感じで。アウトレットの数をargumentから動的に変更できるのが便利です。
もうちと良くなりそう #maxmsp pic.twitter.com/can3cvtZwA
— 松浦知也/Tomoya Matsuura (@tomoya_nonymous) 2016年6月16日
今回もサンプルパッチ(とjsファイル)を置いておきましたので必要に応じてご活用ください。
https://github.com/tomoyanonymous/maxmsp_dbap_example
余談:活用例
「Synesthesia X1-2.44」の多チャンネル触覚提示のための振動子制御に本サンプルをベースにしたものが使われたようです。
詳細は以下の論文を参考。
https://www.jstage.jst.go.jp/article/tvrsj/27/1/27_51/_article/-char/ja/