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);
}