dsPIC33FJ64GP802 (16) --- 1/12 オクターブバンド・フィルタを使った「グライコ」風表示 (1)

 1/12 オクターブバンド・フィルタを多数並べて、オーディオ用グラフィック・イコライザの「バー」風の表示をさせてみました。
 表示は、下の記事の Adafruit 1.8 インチ TFT LCD シールド互換のシールドを装着した Arduino で行い、1/12 オクターブバンド・フィルタを実装した dsPIC33FJ64GP802 とは I2C で接続しています。

1.8 インチ TFT LCD シールド (2) - シンセ・アンプラグド

 表示の様子を下に示します。 (写真をクリックするとモーション GIF による動画が表示されます。)

LCD_bar2_320x240_fps10.gif

 「ピーク・ホールド表示」も付けました。
 dsPIC 側のプログラムの概略をブロック・ダイアグラムとして表現したものを下に示します。

 まだ、外部からの信号を A/D で受けるようにはなっておらず、内部でリニア周波数スイープする 12 kHz サンプリングの正弦波を発生させています。
 C4 (130.81 Hz) から C7 (1046.5 Hz) までの 3 オクターブ 37 鍵分の 1/12 オクターブバンド・フィルタを実装しています。 ここでオクターブ表記は、MIDI ノート番号「0」の音 (8.176 Hz) を「C0」として表しています。 いわゆる「中央 C」(261.6 Hz) はこの表記では「C5」となります。
 鍵数を 37 にしているのは、使用している LCD の長辺が 160 ピクセルなので、鍵数を増やすとバーの幅が狭くなりすぎて見にくくなるためです。 dsPIC 自体の負荷率としては 66 % 程度で、まだ余裕があります。
 入力サンプルは 32 サンプルごとに区切り、ブロック化 (ベクトル化) してフィルタに渡します。 DSP ライブラリの IIRCanonic() 関数にこの入力サンプル・ベクトルを渡すと、フィルタ後の信号として、同じ長さ (32) の出力サンプル・べクトルが返されます。
 この処理は、12 [kHz] / 32 = 375 [Hz] のレートで、全 37 鍵分のすべてについて適用します。
 続いて、フィルタ後のサンプル・ベクトルについて「信号パワー」の計算と、2 を底とする対数への変換を行います。 「パワー」の計算は、DSP ライブラリの「VectorPower()」関数を改造した関数により、サンプル・ベクトルの各要素に渡る 2 乗平均を求めています。
 375 Hz 以下の周波数の入力信号に対しては、平均を取る期間が信号の 1 周期に満たないため、出力に変動を生じます。 A/D 入力版のプログラムを作成して、実使用状態で表示の変動が目立つようであれば、さらに IIR フィルタにより平滑することも考えています。
 2 を底とする対数の計算には、dsPIC3x / PIC24 の「FBCL」(Find Bit Change from Left side) 命令を利用しています。 これは、16 ビット・レジスタに収められている数値を左側 (MSB 側) から 1 ビットずつ見て行って、値が変化 (0 → 1 あるいは 1 → 0) するビット位置 (0 ~ -15) を求めるものです。
 数値を 2 進浮動小数点表示した場合の指数部に相当します。 デシベルで言えば、6 dB 刻みで 0 dB ~ -96 dB の範囲となります。
 FBCL 命令の適用前に、2 乗平均により「パワー」を求めているので、結果は 3 dB 刻みで 0 dB ~ -48 dB の範囲となります。 FBCL 命令の結果に「15」を足し、「15」が 0 dB、「0」が -48 dB に対応するようにしています。
 この処理を 37 鍵分繰り返して 37 個の 4 ビット数値を集めます。 これを I2C を介して Arduino 側に送るのですが、Arduino の I2C ライブラリでは、データ・バッファは 32 バイト分しか用意されていないので、1 バイトに 4 ビット数値を 2 個詰め込んで送っています。