VisualDSP++のライブラリ関数を利用してインターポレーター・クラスを作りました。抽象フィルタークラスで作ったCFilter型のサブクラスです。VisualDSP++のポリフェーズフィルタ・ライブラリ関数は嫌な癖があるのでラッパ・クラスの役割としてこれを遮蔽しました。
プライベートなメソッドとしてreroder()を追加しています。これはコンストラクタに与えたcoeffを変更するものです。coeffはインターポレーターのインパルス応答を格納しています。直感的にはここには通常のFIRフィルタのインパルス応答を収めればよいように思えます。しかし、VisualDSP++のインターポレーター関数は、特殊な並びを要求するため、あらかじめユーザーが並べなおさなければなりません。その並べなおしを行うのがreorder()です。
coeffの並べなおしには嫌な点がいくつかあります。
これらをすべて同時に解決することは出来ません。したがって、完全に何もかも遮蔽することはあきらめて、ある程度プログラマに任せることにしました。具体的にはコンストラクタの最後の引数doReorderによって並べ替えの指示が出来るようにしています。doReorderが1ならば並べ替えます。0がデフォルトで、並べ替えはしません。
また、VisualDSP++のインターポレーターは、coeffの長さではなく、フェーズあたりの係数長を要求します。そこで、コンストラクタに与えたtaps引数を内部で割って使っています。この結果tapsはratioの整数倍でなければならないという縛りがあります。
class CInterpolator:public CFilter{ private: // coeffを並べなおすプライベート関数 virtual void reorder( void ); public: // coeff : インパルス応答。 // taps : 総タップ数。ratioの整数倍 // ratio : オーバーサンプル比(ポリフェーズステージ数) // doReorder : 1のとき、coeffの順序を内部データ型にあわせて並べなおす。 // 0のとき、coeff はfir_interp_fr16の係数に従うと仮定 CInterpolator( const short coeff[], unsigned int taps, unsigned int ratio, bool doReorder = 0 ); virtual void run( short out[], const short in[], unsigned int count ); };
実装は簡単です。runメソッドは単にVisualDSP++の関数を読んでいるだけです。reorder()メソッドは一時領域としてディレイラインを使用しています。
CInterpolator::CInterpolator( const short coeff[], unsigned int taps, unsigned int ratio, bool doReorder ) :CFilter::CFilter( coeff, taps/ratio, ratio ) { if ( doReorder ) this->reorder(); } void CInterpolator::reorder( void ) { short * temp, * coeff; int i, ratio, taps; // ディレイラインを一時領域として使用する temp = this->s->d; coeff = this->s->h; ratio = this->s->l; taps = this->s->k*ratio; // 一般形式のインパルス応答を、fir_decima_fr16用にならべなおす i = 0; for ( int np = 0; np < ratio; np++ ) for ( int nc = 0; nc < taps; nc+= ratio ) temp[ i++ ] = coeff[ np + nc ]; // 作業領域から並べなおし結果をコピー memcpy( coeff, temp, sizeof(short)*taps ); // 作業用に使ったディレイをきれいにする for ( int i=0; i<taps; i++ ) temp[i] = 0; } void CInterpolator::run( short out[], const short in[], unsigned int count ) { fir_interp_fr16(in, out, count, this->s); }