今日はコード自体は書かずに、サウンドフォント再生を VSTi 化するにあたり出来そうな最適化について考えたりしていた。
VSTi では出力は 32bit float で行う必要があるので、そもそも 16bit int でサンプルが格納されているサウンドフォントの内部フォーマットとの相性はよくない(とはいっても2倍のファイルサイズになるのも悩みものなのでサウンドフォントとしては16bitでいいと思う)。
そのため出力するまでには必ずどこかで変換しなければならない。この場合 16bit のサンプルを 32768 で割ることで float で扱う -1.0 〜 1.0 の範囲へ変換できる。
変換するタイミング。
サウンドフォントでの発音は複数の音が重なって発音される事などによって簡単に 0db を超える可能性があって、16bit の状態でそれぞれの音のミックスまで行い、最後に float に変換なんて事をしてしまうと32768を超えた分のデータが悲しいことになるのが容易に想像付く。
だから少なくともミックスに使うバッファは 16bit 以上でないといけないと思うので、そうなるとミックス時には変換済みのデータが準備されている必要が出てきて、変換するタイミングはサウンドフォントから波形を読み取って加工する前、つまり一番初めがいいように思える。
最適化も視野に入れて考えてみると、初めに変換するという事はミックスする音の数だけ変換する必要があって、整数型から浮動小数点数型への変換コストが気になる。
ミックス用のバッファは 32bit int で用意して、32bit int のテンポラリバッファにサンプルレート変換済みデータを生成後ミックスして、最後に 32bit int から 32bit float へ変換した方がイメージ的には効率的な感じがしている。
この場合音量の調節なども整数演算で出来そう。
もっと素直に組むなら最初の時点で 32bit float へ変換してミックスしていく。この場合最後の変換自体が必要なくなる。
これはこれでそれなりにパフォーマンス面で悪くなさそうな気がするけど、SSE ではほとんどコードを書いた事もないので技術面で心配もある(MMX もそんなに使ってないけど……)。
発音数が少ないなら後者、多くなってくると前者の方が速くなっていきそうかなあ…。
組まずにあーだこーだ言ってても無駄そうな事ぐらいはわかった。
あと今回サウンドフォント読み込み処理を作るにあたりサウンドフォントのディスクストリーミングをする事をひとつの目標にしていて、その処理をいかに効率的に出来るかが重要になってきそう。
経験上ディスクからデータを読む場合大きいブロックで一気に要求した方がパフォーマンスを稼げたケースが多かったので、書き出し先バッファサイズの2倍〜8倍ぐらいのバッファを準備してなるべく一気に必要なデータを読み出せるようにしたい(等倍じゃないのは、リサンプルによってデータが縮められる可能性があるため。2倍なら1オクターブ分ピッチを上げたデータが一度に読み出せる)。
それとは関係ないけど、最適化関係の情報を調べていると必ずといっていいほどへるみさんのサイトに何度も出会う。
凄い人だ。