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個飛ばしみたいな形で取得してくる必要あり

これは意外と盲点かもしれない。

ソースコード

時間があれば書く。(今の所色々改造してシンプルな例を作ってないので)