●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) |