Unityで音にフィルタをかける

f:id:hirasho0:20190730200158g:plain

こんにちは。技術部平山です。

今日は実行時に音にフィルタをかけることで、 元々のデータをゲームの状況に合わせて変化させる方法について書きます。 私も実際の製品でフィルタを活用して作り込んだ経験まではないので、 初歩的なことしか書けませんが、 皆さまがこの分野に興味を持つきっかけになればうれしいです。

ソースコードはGithubに置いてあります。残念ながらWebGLではフィルタが動きませんので、 今回はWebGL版は用意しておりません。エディタにてご確認ください。

サンプル

今回用意したサンプルは、Unityが用意しているフィルタをOn/Offして、 どんなふうに変わるのかを体験するためのものです。

f:id:hirasho0:20190730200217p:plain

BGMトグルでBGMのOn/Offを切り換え、Playを押すと効果音が鳴ります。

Unityが用意しているフィルタは、

の6つです。確証はありませんが、ハードウェア、もしくはC++の高速な実装によって 実装されていると思いますので、C#で自力で書くよりも遥かに軽い負荷で 使えるのではないかと思います(調べてません)。

サンプルでは、chorus、distortion、echo、reverbの4つについては デフォルトのパラメータで単にOn/Offできるだけとし、 lowPassとhighPassについては周波数のみ設定できるようにしました。

lowPassの周波数設定が4000Hzであれば、「4000Hzより下はそのまま通し、上を削る」 という意味になります。ただ、高音が消えてなくなるわけではありません。 「削る」という程度と思ってください。 highPassは逆に「4000Hzより上は通し、下を削る」となります。 こちらも綺麗に削れるわけではありませんが、低音が減ります。

画面左下に周波数ごとの音の大きさがグラフで出ますので、 lowPassやhighPassの設定でどう変わるか見てみてください。

また、エフェクトではありませんが、ピッチや音量もいじれるようにしておきました。

動機

ちょっと机を叩いてみてください。 何度か叩けば、毎回違う音が鳴ります。

また、同じ音でも、風呂場で鳴るのと、 屋外で鳴るのとでは、聴こえ方は違ってきます。

こういうバリエーションがないと、毎回同じ音が鳴る安っぽい感じの ゲームになってしまうのです。 気にしない人は気にしないですし、 開発早期から音を入れて詰めていくことがなかなかできなかったりもしますが、 私個人としてはもうちょっと気を使いたいなあという気持ちがあります。

とはいえ、バリエーションを出すためにデータを沢山用意するのは 良くありません。手間がかかりますし、容量が増えてお客さんに迷惑をかけます。 近年のゲーム開発で容量を圧迫する原因になることが多いのが音声なのですが、 ただでも数十あるいは数百メガバイトになってしまう音声に、 バリエーションを用意するのはほとんど不可能です。

仮にできたとしても、ホールでしゃべっているのか、屋外なのか、 といった差異をデータの段階でつけておくとあまりにも柔軟性が削がれてしまいます。 同じ台詞をいろんなところで使いたいでしょう?

そこを計算でどうにかするのが、フィルタなのです。

それぞれの基本的な使い所

では、各フィルタについて簡単に触れてみましょう。

reverb

一番使いやすいのは、このreverb(残響、リバーブ)ではないでしょうか。

風呂場、体育館、屋外、といったいくつかの場面に応じて パラメータを決めておいて、それを実行時に設定するだけです。 銃声なんかには効果が大きいですよね。

lowPass

高音を削るフィルタです。こもった音になります。

高い音は直進してあまり回り込まない上に、 重い物に吸収されやすいので、 壁の向こうの音は低音に比べて高音が削れています。

例えばドアの向こうから銃声がする、という場合、高音を削っておくと それっぽくなるでしょう。 これによって、ゲームで遊ぶ人に「敵は壁の向こうだ」 という情報を与えることになります。 そしてドアが開いたらフィルタを切って音をクリアにします。 緊張感増しますよね。 視覚情報だけでなく聴覚情報でもゲームの状況を伝えられれば、 より一層体験が豊かになるわけです。

また、スピーカーのように向きがある音源の場合、 こちらを向いていない時には高音が削れます。 回りこまないからです。うまく使えば自分が音源に対してどういう向きか、 といった情報も音に込められるかもしれません。

さらに、高い音ほど地面や空気で減衰しやすいため、遠くなるほど高音が削れます。 どの距離で花火を見ているかによってフィルタの設定を変えれば、 元の花火の音が一つしかなくても、距離による聴こえ方の違いを出せるでしょう。

highPass

低音を削るフィルタです。

かなり下の方(例えば80Hz以下とか)だけを削ると、 風や振動などのノイズを削る効果があります。

現実の物理現象としては、 高い音ほど減衰しやすいので、 「低音だけ削られる物理現象」というのはありません。 そんなものがあれば、集合住宅の防音はもっと楽な問題だったことでしょう。

しかしこれを逆手に取って意図的に低音を削ることで 相対的に高音を強くし、 「音源がより近く感じられるように味付けする」 といった使い方はできるのかもしれません。 とはいえそうなってくると音の素人がやるべきことではなく、 サウンドの専門家に調整をお願いしたいところです。

また、「ショボいスピーカー」で鳴らした感じを出す時にも 使えるでしょう。スピーカーはそれぞれ周波数特性というのを持っていて、 低音が鳴らないものも多くあります。 音データに低音が入っていても、そもそも鳴らせないわけです。 今サンプルの確認用に安いイヤホンを使っているのですが、 こいつは低音が元々ロクに鳴らないので、 イマイチフィルタの効果がわからない状態です。

ちなみに、ハイパスフィルタをかけた音を聞くと私は 商店街で鳴っている音楽を思い出します。 また、ふと気になってトランペットスピーカーで検索して周波数特性を見てみたら、 200Hzから、となっていました。 普通に男性の声の基底周波数が削られてしまうレベルです。 運動会のスピーカーの声を思い出してみれば、 どことなくキンキンして耳障りな印象があります。 低音が削られて高音だけが残っているせいなのでしょう。

というわけで、このフィルタは、そういったスピーカーの差異を表現するのに使える ように思います。

chorus,distortion,echo

これらの使い方は私にはあまりピンと来ないので、 サウンドの専門家にお願いしたい所です。

distortionに関しては「出力かけすぎて溢れちゃった」ということなので、 「音質の悪いスピーカーから出てる感」を出す時には使えるかもしれませんし、 echoは文字通りエコーですので、広い屋外で遠くに山がある状態で銃撃戦や爆発音、 というケースであれば使えるのかもしれません。 距離が遠いのでlowPassも併用すると良いのでしょう。 是非製品で試してみたいものです。

実装

今回は実装について触れることは何もありません。 必要なコンポーネントをつけて、パラメータを設定するだけです。

ただ一つ注意があります。フィルタをつける対象としては、 AudioSourceAudioListener の二つがあるということです。 例えば、「プレイヤーと音源の間にドアがある」というような場合、 フィルタはどちらにつけるのが良いのでしょうか?

AudioListenerにつけると全てのAudioSourceから出た音に同じフィルタがかかってしまうので、 「正面のドアの向こうに音源A、同じ部屋の中に音源B」 というようなケースではAudioSourceにつける必要があります。 しかし、音源の数だけフィルタがかかれば、負荷も音源の数に比例してかかるでしょう。 風呂場のシーンで全ての音源が部屋の中にあるのであれば、 AudioListenerにつけて終わりにする方が良いのではないでしょうか。

しかしながら、そういった実装の詳細については経験がありませんので、 別の情報源を探していただくのが良いかと思います。

なお、AudioMixer というものもありまして、 ここでもフィルタと同じことができます。AudioListnerで設定するよりも きめ細かい制御が可能です。BGMやクリック音のように「世界の中で鳴っているわけではない音」 と、効果音のような「世界の中で鳴っている音」をブレンドすることを考えると、 AudioListnerで一括、というのは問題がありますので、 こちらを使う方が良いでしょう。 ゼニガメブログさんの記事 が非常に参考になりました。

おわりに

「Unityにはこんなフィルタが用意されているよ!」 という紹介をしてみました。

実際に製品でどう使うかに関しては今後の開発の中で試していきたいと思っていますが、 基本的には「こんな状況ではこんなパラメータ」 というのを複数サウンドの方に用意していただいて、 それを実行時にうまく補間しながら設定していく、 ということになるかと思います。 lowPassフィルタに関しては対応する物理現象がはっきりしているので、 もしかしたら実際の測定データや理論に基いて設定できるかもしれませんが、 ゲームはウソをついてナンボな所もありますので、 最終的な味付けは専門家にやって頂く方が良い結果になるでしょう。

ところで、今回は扱いませんでしたが、 リアルタイムにサウンドに行う処理として重要なものに、空間定位があります。

音がどこで鳴っているかによって、左スピーカーと右スピーカーの ボリュームを変えるのがパンニング です。音の方向感を出します。一番基本になる処理です。

また、左スピーカーと右スピーカーでわずかに時間をズラすことで、 左耳と右耳に音が届くタイミングがズレる効果を表現したりします。 前や後ろから来た音は、左右の耳までの距離が同じなのでズレませんが、 左右方向から来た音は左右の耳までの距離が異なるのでズレるのです。

スピーカーが2つでない場合もあり、そういう場合はさらに複雑になります。 取り組むとなかなか面白い分野です。 3Dなら絶対必要だと私は思うのですが、2Dゲームであっても画面にキャラが二人いるなら、 定位を行って、どちらから話しかけられているかがわかるようにしたいですよね。

ちなみに、私は片耳が聴こえない障碍持ちでして、 過去の仕事でサウンド制御を担当した時は、 左右のヘッドホンを交代交代で壊れていない右耳に当てて 正しく定位できているか確認したりしていました。 ちょっと大変なので、今回は省略した次第です。

それに、スマホってスピーカーがモノラルのケースが多いんですよね。 ヘッドホンをつけてゲームをしていただけるなら良いのですが、 スマホゲームの場合、お客さんにそれを期待するのは難しいものがあるでしょう。