Teensy Audio ライブラリ

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 にできたので微分処理なども少し安心してできると思う.

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中