OnAudioFilterRead関数内でサンプル鳴らす方法。
はじめに
※あとでソースコードも書いてしまおうかと思います。
通常、UnityではメインスレッドでAudioClip~AudioSource~AudioListenerを利用してサウンド出力するが、
特殊なことをしたい場合、OnAudioFilterRead関数にて処理を書くことになる。
シンセサイザーを自作する場合、ここに処理を書く必要が出てくる。
ただ、この関数はあくまでバッファが来た時に読み込むため、メインとは別のスレッドにて処理することになる。
よって、UnityEngineクラス内の関数は実行できない。
音周りではAudio周りの備え付け関数がキックできない。
Update関数上でスケジュールする方法だと下記リンクのようなアプローチになる。
再生自体は可能だし、グリッド周りのタイミングも問題ないんじゃないかと思ってる。
Keijiro’s Dev Log — Unity でシーケンサーを作ってみる (tumblr.com)
とはいうものの、オーディオはバッファを詰め込んで発音する形のため、
delayがどこまで有効か、ってのは正直Update関数の頻度、ひいてはUnity本体に依存するのも事実。
上記方法をUnityで実装するメインの方法として、別のアプローチを模索したい。
情報整理
目的
- OnAudioFilterRead関数内でどうにかしたい(関連する処理の分散防止)
- メイン関数以外でサンプリングデータを取り扱いたい
- オーディオの発音タイミングを正確に取りたい
ただ、以下の点は注意。
- OnAudioFilterRead関数の処理自体時間がかかる場合スローになるしノイズが載る
- 必要があればデータの補間が必要かもしれない
- 最悪プラグイン作成して高速化する必要がある
サンプル再生のためのデータ取得
まずサンプリングデータをどうやって取得するかという話である。
AudioClipからデータを取得できるため、これを使う。
AudioClip-GetData – Unity スクリプトリファレンス (unity3d.com)
これで配列で取得してしまえばメイン関数以外でも取得可能。
ちなみに。
圧縮されたオーディオファイル、オーディオインポーターで Load Type が Decompress on Load に設定されているときサンプルデータを引き出すのみしかできないことに注意してください。
もしこれがそうでない場合、配列はすべてのサンプル値を 0 として返すことになります。
とのことで、サンプル値が0の場合読み込めてないよ、という分岐を組み込んでおくのがいいのかも。
発音
OnAudioFilterRead関数内は、サンプル単位で波形を書き込む形になるため、
本来だとサンプリングレートを揃えたりストレッチした上でデータの書き込みが必要。
ただまぁやらなくても音は書き込めます。
サンプル単位で波形をプラスした上で、OnAudioFilterRead関数の引数(float[])に書き込む形です。
ただ、WAVデータで注意なのは、1点。
- チャンネルごとのデータはまとまっているわけではなく、
「チャンネル1→チャンネル2→チャンネル1→・・・」みたいな書き方になっている。
よって、データを引っこ抜く際にただの線形的な引用ではなく、データ1個飛ばしみたいな形で取得してくる必要あり
これは意外と盲点かもしれない。
ソースコード
時間があれば書く。(今の所色々改造してシンプルな例を作ってないので)