雲仙のドッグフードとして作っているシンセサイザーの続きです。VFOの後ろにDC Blockerを置きました。
今回作ったVCOはDC成分をそのまま出力しています。三角波の場合、DC成分は振幅のちょうど中点ですので計算に簡単に取り除くことができます。矩形波の場合もあらかじめデューティー比がわかっているので計算で取り除くことができます。しかし、後で手を加えたときに再計算をするのが面倒なのでDC Blockerをかけました。
DC Blocker
DC Blockerは単純な作りです。構造は二段に分かれており、前段では入力信号と1サンプル遅延信号の間で差分を取ります。DC成分は遅延しても同じですのでこれによってDC成分が消えます。数学的には、サンプル間隔を無限小にすることで微分回路と見ることが出来ます。後段では1サンプル遅延信号をフィードバックして加算することで、積分を行います。
微分と積分をタンデムにつなぐと、伝達関数は1になります。しかし、数値計算では理想積分系は不安定になりますのでごくわずか特性を落として使っています。詳細は後述のリンク先を参照すると良いでしょう。
DC Blockerは非常にシンプルなコードになります。
/*
* Related Link
* https://www.dsprelated.com/freebooks/filters/DC_Blocker.html
* https://ccrma.stanford.edu/~jos/fp/DC_Blocker.html
*/
DCBlocker::DCBlocker( uint32_t blockSize ) : amakusa::AbstractFilter ( blockSize )
{
this->x_last = 0;
this->y_last = 0;
}
void DCBlocker::run( float32_t *pSrc, float32_t *pDst, uint32_t blockSize )
{
// if the parameter is non-zero, take it. If the parameter is zero, use default.
if ( blockSize == 0 )
blockSize = this->blockSize;
for ( int i = 0; i < blockSize; i++ )
{
// y = x - x * z^-1 + 0.995 * y * z^-1
pDst[ i ] = pSrc[ i ] - this->x_last + 0.995f * this->y_last;
this->x_last = pSrc[ i ];
this->y_last = pDst[ i ];
}
}
また、Monophonic::run()の中ではVFO型とDC Blocker型のオブジェクトのメソッドをタンデムに動かしています。
// Run all signal processing.
void Monophonic::run(
float out_buffer[], // place to write the right output samples
unsigned int block_size // block size [sample]
)
{
// place the signal processing coce here
this->vfo->run( work_buf_a, block_size );
// blocking DC.
this->dc_blocker->run( work_buf_a, work_buf_b );
// applying filter.
this->sv_filter->run( work_buf_b, out_buffer );
} // End of run()