●FIRフィルタで折り返しノイズを除去する方法

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 X680x0 の ADPCM はご存知のとおり 15.6kHz (正確には 15.625kHz)です。
 従ってサンプリング定理により表現可能な最高周波数は 7.8125 kHz です。

 かたや、今時の音声データは 44.1kHz か 48.0kHz ですので、7.8125kHz〜
 22.05/24.0kHzの領域は ADPCM では再生できません。

 ここで、単に再生できないだけであれば良いのですが・・・もし、何も
 せずに単にサンプリング周波数を変換すると、この表現できない領域の
 音のせいで耳障りなノイズが発生してしまいます。

 X680x0上ではま〜きゅり〜ゆにっとでも付けてないと聞けませんけれど、
 16kHz正弦波 を 本CD の\PCMS\ORIGINAL.WAV に入れてるので、まずは、
 Windows等で聞いてみてください。

 ・・・多分、ほとんど何も聞こえないと思います。

 16kHz の正弦波となると、人によっては聞こえない領域ですし、出力
 機器によっても出てこなくなりますから、これで正解です。

 問題は、これを単純にサンプリング周波数 15.625kHz に落とした時。

 論より証拠、次を聞いてみてください。

◎単に15.625kHz に変換した16kHz正弦波

 かなり耳障りな音が聞こえるはずです。このサンプリング周波数では
 表現しきれない領域の音が、このようにノイズとなって現れるのです。
 これがいわゆる折り返しノイズです。

 これを避けるためには、この場合 15.625kHz のサンプリング周波数で
 再現できない領域、つまり 7.8125kHz 以上の音をカットしてあげれば
 良いわけです。ローパスフィルタですね。

 次は、オリジナルの 16KHZ正弦波に FIRフィルタで 7.8125kHz 以上の
 周波数帯域をカットしたものです。

◎FIRフィルタ適用後15.625kHzに変換した16kHz正弦波

 いかがでしょう、ほとんど何も聞こえなくなったと思います。

 X680x0 の ADPCM高音質化改造(68kΩの抵抗3つを、40kΩ位に交換して
 高域を伸ばす)を行っていると顕著ですが、そうでなくても、本来は
 存在しなかったノイズが出てくるので、結構困り物です。

 サンプリング周波数変換には、デジタル・フィルタは欠かせないと、
 そういうお話でした。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●FIR デジタルフィルタ概要

 FIRデジタルフィルタは、現在の値を含む前後 n 個の値を用いて、
 それぞれにフィルタ係数を掛けた値の総合計として出力を得ます。

 3次(2次+現在値)の FIRフィルタの場合、時刻 t の値を x(t)とし、
 フィルタ係数を f(t) で表した場合、出力 Y(t) は次になります。

   Y(t) = f(-1)*x(t-1) + f(0)*x(t) + f(+1)*x(t+1)

 フィルタ係数は、元のデータのサンプリング周波数と、ローパス/
 ハイパス/バンドパス etc. のフィルタ形態、カットオフ周波数、
 減衰帯域の広さ、リップル成分の許容量、阻止帯域での減衰量(db)
 などのパラメータで変わります。

 一般的には、フィルタの次数が高い方が精度の良いフィルタに
 なります。300次くらいあるとカットオフ周波数でかなり綺麗な
 減衰をしてくれます。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●FIRフィルタのプログラム的なこと

 FIRフィルタは、プログラム的には、単なる実数の積和演算です。

 下記の例のように、フィルタ係数が 101個の配列である場合で、
 元のデータは DATANUM 個ある場合を考えてみましょう。

 積和演算の際に注目データの前後『フィルタ係数の個数/2』だけ
 参照しますので、データ用領域を DATANUM + 100個 分用意して
 0 クリアしておき、50個スキップしたところからデータを書いて
 おくと、大体こんな感じで処理できるでしょう。

   // フィルタ係数の配列(実数)
   double f[101] = {
    0.012835e-3,
    -1.2303252,
     ...
    0.3303252
   };

   short x[DATANUM + 100]; // 前後50ずつ見る為x[50]〜x[DATANUM+50-1]が有効値
   short output[DATANUM];  // フィルタ適用後の結果格納領域
   int i;
   int didx;
   int fidx;
   double y;

   // データの有効個数だけループで処理
   for ( i = 0; i < DATANUM; i++ )
   {
    // データ位置からフィルタ係数の個数分を積和演算
    for ( y = 0, fidx = 0, didx = i; fidx < 101; fidx++,didx++ )
    {
     y += f[fidx] * x[didx];
    }

    // ここで y の値が、出力値
    output[i] = (short)y;
   }

 要は、注目するサンプルの前後幾つかのデータを使って、フィルタ
 係数で積和処理をかけた結果が出力となります。移動平均みたいな
 ものです。

 フィルタ係数を計算するのは面倒ですが、ありがたい事にフィルタ設計
 ツールを公開してくれたり、Web上で即計算できるサイトを作られている
 方々もいらっしゃいますので、感謝しつつ使わせていただきます。

 筆者は、テクノクリエートさんが公開されているフィルタ設計ツールを
 使わせていただきました。この場を借りて感謝申し上げます。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●フィルタ設計ツールの使い方

 テクノクリエートさんのフィルタ設計ツール使い方のメモです。

 1. filter.exe を起動
 2. ツール->通過帯域の設定 にて、通過帯域入力画面を開く
 3. フィルタ計算のパラメータを指定

  Type of Filter : Low Pass Filter
  Filter Length : 301 (300次のフィルタの場合、中央の1つ分を加え301)
  Outband Attn  : 60 (60dB 落ちるという指定)

 4. Cut Off Frequency (カットオフ周波数)を指定

  ここで指定する周波数は、遷移帯域の中心周波数を、元のサンプリング
  周波数で割った値です。アナログフィルタのように 3db 減衰点の指定
  ではありません。

  X680x0 の ADPCM は、最大15.625kHzであり、表現可能な最高周波数は
  7.8125kHz。そのまま 7.8125kHz を指定すると、次のようになります。

  [例] 48000 Hz -> 15625 Hz
     7812.5 / 48000 = 0.162760416

  [例] 44100 Hz -> 15625 Hz
     7812.5 / 44100 = 0.177154195

  [例] 22050 Hz -> 15625 Hz
     7812.5 / 22050 = 0.354308390

  [例] 24000 Hz -> 15625 Hz
     7812.5 / 24000 = 0.325520833

 以上で計算を実行すると、目的の応答を示すフィルタ係数が作られます。
 ファイルメニューから、C ソースに読み込める形で保存できますので、
 とても便利させていただきました。 

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●参考文献他

 株式会社テクノクリエート
 http://www.techno-create.com/others/
 Others > ダウンロード > フィルタ設計ツール Filter Ver.0.12

(EOF)