手軽にできる3Dサウンドを目指してやってみたこと

#動画

[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/IAdq7EOvQ4s/0.jpg)](http://www.youtube.com/watch?v=IAdq7EOvQ4s)

前回までの「[ユニティちゃん歩く走る減衰する](http://qiita.com/tatmos/items/c6b729b3abf294fccbb8)」まで作ってみて、あと足りないところを付けた感じの内容となります。

#アプリ
サンプルのアプリを作って見ました。
ユニティちゃんが音のボールを追いかける様子を眺めるアプリです。
![UnityChanChaseTheBall512.png](https://qiita-image-store.s3.amazonaws.com/0/96808/9f3a13db-3e46-22d5-6336-1bd922b37d29.png)

GooglePlay版：[UnityChanChaseTheBall]
(https://t.co/JXiIR9IKb8)

#ソースのダウンロード
ADX2プロジェクト：[UnityChanChaseTheBall20160515.zip](http://chahix.ddo.jp/~chahi/unity/20160515/UnityChanChaseTheBall20160515.zip)
Unityプロジェクト：[UnityChanChaseTheBall_Simple.zip](http://chahix.ddo.jp/~chahi/unity/20160515/UnityChanChaseTheBall_Simple.zip)
何かの参考にしてもらえれば幸い。

#手軽にできる3Dサウンド、3Dオーディオって何か？

思い浮かぶもの
- HRTF関数（頭部伝達関数）による音像（エフェクト）
- Object based Audio（再生技術）
- Dolby atmos(再生技術）
- ambsonic(録音技術と再生技術）
- 他さまざまなプラグイン
などなど、技術的な問題解決を目指す製品は他のもいろいろあるかと思いますが
上記のようなものの紹介ではなく、

もっと手軽に、
なるべく手間かけずに、
実行時の負荷のかからない方法で、
それらしい感じになるようにデザイン可能なものを目指します。

VRとかハコスコとかみたいなものでのオーディオで3Dをやるときに
やっておくと良さそうな点についての導入編のようなものです。

#使うもの
ゲームエンジンとミドルウェア(UnityとADX2LE)を使ってできる範囲で、
3Dオーディオをデザインしてみようと思い、やってみました。

主に3つの要素になります。
・距離減衰と角度
・遮蔽効果
・反射音

ができるように。

#距離減衰と角度
ここらへんは、UnityでもADX2LE（や他のソリューション）でも同じですが、
２つの座標情報から自動で計算される仕組みがあります。
（OpenALとかと同じ）

リスナーの位置、ソース（音源）の位置、それぞれの向きなどから導き出されます。
基本的にはカメラの位置と向きに合わせてリスナーを置きます。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/30aa9299-7637-817f-7943-1fd7ce04d4b0.png)
[キューの減衰距離を設定]
*さらっと最小距離とありますが、最小距離以下のサウンドの振る舞いは様々で例えば音量が安定するような処理などが初期状態で施されていて、他にも距離や角度に応じてカスタマイズもいろいろできたりとするのですがここでは割愛*

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/8d5493a9-d293-b7ea-c5c6-c025a3320b5e.png)
[ウェーブフォームのパンタイプを設定]

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/1a1e987d-ddd5-97c5-3d4a-5f4844b8835a.png)
[(Unity)CriAtomSourceの3D Positioningをチェックする]

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/561b7a46-c063-857e-1c14-c9b7b9f5d119.png)
[(Unity)CriAtomListenerをカメラにつける]

#遮蔽効果
ローパスフィルターを使うことで、音がこもるような効果を表現できます。
音源とリスナーの位置の間に壁があるかを判定します。

今回は、レイキャストして、距離を計算し、音源までの距離より短い場合は、
何か遮蔽物があるとみなして、音をこもらせてみました。

![ray.png](https://qiita-image-store.s3.amazonaws.com/0/96808/5cb9ac23-5183-1d60-a7e5-55bd89049a0e.png)


どれくらい遮蔽するかは、壁の厚さや、素材など条件から変化させるとより説得力のあるものになります。
スライダーで調整がきくようになっていますので、合ってないと思うところは変化させて気持ちの良いところを探ってみてください。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/b3a67bae-af46-dd4d-904c-f988f047ec73.png)
[トラックのバイクアッドフィルタをローパス、Qを0.71くらいにする]

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/22bb1f2e-f837-01e0-51ba-1a68f4eabd1d.png)
[遮蔽度コントロール用のAISACのバイクアッド-Cofのカーブ]

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/1dd17e5c-b1c1-eee1-dcd0-022e2918a9cc.png)
[(Unity)カメラにスクリプトを追加。遮蔽対象のオブジェクトを指定できるようにしている]

``` OcclusionAudio.cs
        for (int i = 0; i < targets.Length; i++)
        {
            float fracComplete = (Time.time - lastMoveTime [i]) / journeyTime;
            if (fracComplete < 1)
            {
                if (useOcclusionAudio)
                {
                    targets [i].SetAisac("occlusion", Mathf.Lerp(aisacValue [i], aisacNewValue [i] * occlusionGain_, fracComplete));
                } else
                {
                    targets [i].SetAisac("occlusion", 0);
                }
            }
        }
```
[変化をなめらかにするためLerpを使っている]

##遮蔽効果のデザインについて
まわりこみはやってない（=無響室のような遮蔽）

実際は低い音はまわりこんで定位感がなくなったりとか、
穴のある遮蔽物の場合、各穴からの回り込んだ音の方が大きくなるケースなどもあるかもしれません。（ドアとか窓とか）
それらが重要な場合は、地道に計算して音源位置を変化させるとかもいるかもですが、
まぁ、なんとなくそれっぽい感じにはなったかと思います。

（補足：まわりこみは反射音と組み合わせてごまかす）

#反射音

空を飛ぶような世界であれば、上記の処理だけでも十分なのですが、
実際は、床や天井、壁など、音を反射するものがあります。

これをやらないと、壁があっても存在感がなかったり、無響室のような特殊な環境の音となってしまい、
絵と音が合わなくなります。

空間エフェクトで再現する形となります。
今回は、マルチタップディレイ（4タップディレイ）を使って、空間の広さを音に反映させてみました。

空間の広さにばっちりあう空間エフェクトの設定というのは、なかなか簡単には計算できない類のものなので、スライダーで調整がきくようになっています。
(センド量だけ変更される、壁までの距離は固定．．．本当は壁までの距離分いくつかバスを用意したいところ)

遠い壁はリバーブ、近い壁はディレイとかもありかもしれない。
壁が向かい合わせの場合は ディレイ(エコー)のフィードバックを増やすとか。
残響表現のためにDSP書いてしまうとか。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/a08478f7-bed7-7583-56a5-3b8b7a30e3d3.png)
[Delay->Reverbへつないで音の響きを加えている。バンドパスでLo,Hiを削っている]

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/1cc449e1-8ab5-d43d-aa7d-a6a72b2754cc.png)
[反射音コントロール用のAISAC。Main(Dry)とDelay(Wet)へのバスセンド量のカーブ]

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/1dd17e5c-b1c1-eee1-dcd0-022e2918a9cc.png)
[(Unity)カメラにつけたOcclusion Audio（遮蔽）と同じスクリプトで行っている]

```　OcclusionAudio.cs
        for (int i = 0; i < targets.Length; i++)
        {
            if (useReflectionAudio)
            {
                targets [i].SetAisac("reflection", 1f * reflectionGain_);
            } else
            {
                targets [i].SetAisac("reflection", 0);
            }
        }
```
[単純にAISACを設定している。reflectionGain_で効果具合を調整可能にしている]

部屋のような狭い空間、広間、建物の間、開けた場所、山肌、空中
といったような、空間の広さや、音の特徴をなんとなく知っているので、それに合うよういに
パラメータを調整することで、大分よくなるのではと思われます。
上のカテゴリ分のコライダーを用意して、パラメータを設定していくのが地道ですが正解なような気もしました。

(ゲームによっては、四角い部屋の中とかならルームシミュレーションくらいリアルタイムにやる（６面の壁までの距離、壁材質から反響のエフェクトへパラメータ設定など）やっても面白いかもしれませんが、設定はより大変そう・・・）

#あとがき

やってみてわかったこととして、それっぽい効果にするためには調整が大事だということ。
サウンドをシミュレーションするのでもないのであれば、「らしさ」を表現することが大事だと。

狭いエリアだからといって、やたらと反響音がしすぎても音がフランジャーのような状態（現実にも実際そうなる場合もあるけど）になったりするけど、それは好ましくないことが多いです。
やまびこを表現するにも数キロ先に反射オブジェクトを用意するのも現実的ではありませんし、
複雑な反射をすべて計算で行うのも今の所難しい・・・
あらかじめ計算させて半自動でパラメータ割り当てるとか、これから進化していくこととなるかと思いつつ。