Teensy 4 でオーディオ信号処理を行うにはAudioライブラリを用いるのが簡便でよいが,以下のような制限がある.
(1) 処理が16bit整数のみ
(2) FIRフィルタのタップ数が200まで
(3) SDRで必要となる処理が用意されていない(直交・極変換,微分,平方根など)
など.
まず,2048タップのFIRフィルタを作ろうとして (2) の制限に引っかかった.
そこで,タップ数の少ないFIRフィルタを複数用いそれらを適切な Delay を通して駆動し,
出力をMIXする方法を試してみた(下図).

128タップのFIRフィルタを16個並べて2048タップのFIRフィルタを構成している.これはうまく動作していて問題は無いように思えたが,動作確認を続けていくうちに,信号レベルが低下してくるとバックグラウンドノイズではない別のノイズが生成されてくることに気づいた(信号レベルが大きいときは気にならない).これは以前に16bit整数処理のIIRフィルタ(8次)の動作確認したときと同様の現象で,このFIRフィルタもおそらくアンダーフローを起こしていると思われた(IIRのときよりは顕著でないが).
となるとAudioライブラリは16bit整数処理のみなので,このライブラリ使う限り改善は望めそうにない.そこで Audioライブラリでは I2S と queue のみを使用し,信号処理はコードで書くことにする(下図).


queue_send から信号をコードに取り込み,処理したあと queue_ret に信号を返す.
信号処理には CMSIS DSP Software Library で用意されている関数を用いる.信号処理関数が豊富に,かつ float で処理する関数も用意されているのでそれらを使えば上述のノイズの問題は解消できるはず.
ということで,以下のようなコード作成して動作確認をしてみた.
(なお,コードを見やすくするために実際に動作確認できた状態から抜粋して示しています.このままでおそらく動作するとは思いますが保証はしません.間違いのご指摘は歓迎いたしますが「動作しない」というクレームはご遠慮願います.)
//#include <arm_math.h> #include <Audio.h> AudioInputI2S i2s_IN; AudioMixer4 mixer_IN; AudioRecordQueue queue_send; AudioPlayQueue queue_ret; AudioOutputI2S i2s_OUT; AudioConnection patchCord1(i2s_IN, 0, mixer_IN, 0); AudioConnection patchCord2(i2s_IN, 1, mixer_IN, 1); AudioConnection patchCord3(mixer_IN, queue_send); AudioConnection patchCord4(queue_ret, 0, i2s_OUT, 0); AudioConnection patchCord5(queue_ret, 0, i2s_OUT, 1); #define NtapFIR 2048 float32_t fircoeffs[ NtapFIR ]; float32_t firState[ NtapFIR + 2*AUDIO_BLOCK_SAMPLES - 1 ]; arm_fir_instance_f32 firEQ; float32_t sigf_IN [AUDIO_BLOCK_SAMPLES]; float32_t sigf_OUT[AUDIO_BLOCK_SAMPLES]; int16_t sig16[AUDIO_BLOCK_SAMPLES]; #include <FlexiTimer2.h> //Signal process -------------------- void sig_proc() { int i; int16_t *p_sig; if(queue_send.available() != 0){ //INPUT p_sig = queue_send.readBuffer(); for(i=0; i<AUDIO_BLOCK_SAMPLES; i++) sigf_IN[i]=(float32_t)p_sig[i]; //FIR arm_fir_f32( &firEQ, sigf_IN, sigf_OUT, AUDIO_BLOCK_SAMPLES ); //OUTPUT for(i=0; i<AUDIO_BLOCK_SAMPLES; i++) sig16[i]=(int16_t)(sigf_OUT[i]); queue_ret.play(sig16, AUDIO_BLOCK_SAMPLES); queue_send.freeBuffer(); } } //-------------------------------------- void setup() { arm_fir_init_f32( &firEQ, NtapFIR, fircoeffs, firState, AUDIO_BLOCK_SAMPLES ); //Set freq. response to flat (Impulse) for(int i=0; i<NtapFIR; i++) fircoeffs[i] = 0; fircoeffs[1024] = 1.0f; //Reset CODEC pinMode(22, OUTPUT); digitalWrite(22, LOW); delay(100); digitalWrite(22, HIGH); AudioMemory(100); mixer_IN.gain(0, 1.0f); mixer_IN.gain(1, 1.0f); mixer_IN.gain(2, 0.0f); mixer_IN.gain(3, 0.0f); // call "sig_proc" every 2ms FlexiTimer2::set(2, 1.0/1000, sig_proc); FlexiTimer2::start(); queue_send.begin(); } //------------------------------------------ void loop() { //Set fircoeffs[] }
動作確認の結果 32bit float 処理にすることでノイズが消えた.やはり16bit整数処理が問題だったようだ.
AudioライブラリはGUIでシステムを構築できるので取っつきやすいが,結局コードで書いた方がfloat 処理ができるしFIRフィルタの処理も1行で済むので楽(設定はいろいろ必要だがそれはAudioライブラリも同じ).
あとは I2S のブロックが16bitでなく32bitでインターフェースできれば,CODECのダイナミックレンジを十分に活かせるのだが…
とはいえ内部処理は 32bit float にできたので微分処理なども少し安心してできると思う.