その4までで、一応窓関数も使った高速フーリエ変換が実現できました。しかし実際にオーディオのリアルタイム解析などで使おうとすると、このままでは使いにくいし効率も悪いので、後々使いやすい形に実装しなおします。
なにが問題?
窓関数を使ったりしていく過程で、いままで作ったプログラムの流れを細かく見ていくと、
- 入力信号を入れるための配列を用意する
- 入力信号を準備する
- 窓用の配列を用意する
- 窓を作る
- 窓をあてはめた後の信号をいれるための配列を用意する
- 入力信号に窓をあてはめる
- フーリエ変換に使うための複素数の配列を用意する
- 入力信号を複素数の配列に変換する
- フーリエ変換の設定をする
- フーリエ変換する
- フーリエ変換の結果を解析する
- フーリエ変換の設定を破棄する
- 上で使ったメモリを破棄する
と、結構長い処理になってしまいました。FFTを使う場合は、大抵リアルタイムに音を処理したい場合だと思うのですが、このままだと使いにくいしメモリの使用効率も悪いので今のうちに最適化しておきたいと思います。
目標は?
可読性があって効率の良いプログラム
わざわざ通常の離散フーリエ変換ではなく高速フーリエ変換を使うのは、パフォーマンスが最も重用だからです。使いやすくても遅くなってしまっては意味がありません。
vDSPを使うのはアセンブラを書くのに比べて可読性が高いからです。可読性も失われるべきではありません。
従って、可読性があって効率の良い実装にしなければいけません。
どうする?
Objective-Cのクラスにしておきましょう
vDSPはOS XかiOSでしか使えません。ゲームやマルチメディア系のパフォーマンスが重要視されるライブラリでは、CやC++が使われるのが一般的ですが、OS XやiOS上ではC/C++のクラスよりもObjective-Cのクラスの方が使い勝手がよいことが多いので、Objective-Cのクラスにします。
Objective-CはC/C++に比べて数%くらい遅い、という話を聞く事もありますが、計算量が多くパフォーマンスに影響が有る部分はvDSP内でアセンブラで実装されるので、インターフェイス部分がObjective-Cになったことで失われるパフォーマンスの差は無視できるレベルです。それよりは可読性と再利用性を上げましょう。
メモリの再利用
窓関数も使ったFFTは多くのメモリを必要とします。処理の度に素直にメモリの割り当て/解放を繰り返していては無駄が多いので、これらのメモリは再利用するようにしましょう。
窓の使い回し
窓関数を使って作った窓は同じ長さの配列であれば、基本的には毎回同じ配列が生成されます。なので、この生成も一回だけするようにしましょう。
FFT設定の使い回し
基本的にはFFTの設定も毎回設定しなおす必要はないので、一度作ったら使い回すようにしましょう。
具体的な実装
長くなったので、具体的な実装はまた次の記事にて。