2011/04/15

FIRの係数を求めるプログラム Javascript

勉強がてらにデジタルフィルタの係数を求めるプログラムをJavaScriptで作ってみた。信号処理の基本的なローパスフィルタになる。フーリエ級数の単純打ち切り法で得た係数に対して、打ち切りの影響を最小限にするためにハミング窓関数をかけている。直線位相特性を持つデジタルフィルタなのでタップ数は奇数。サンプリング周波数とカットオフ周波数を入力すると、カットオフ周波数よりも上の周波数が滑らかに削られるフィルターになる。位相が乱れない優れたフィルタ。数式はこんな感じで、これを元にプログラムを作ってみた。

FIRの係数を求める式。
n はタップのn0, n1, n2・・・のこと。ωはカットオフ周波数。0~πの範囲で設定する。周波数(サンプリング周波数の1/2)の半分ならπ/21/3であればπ/3という具合。

下がハミング窓関数を求める式。

-M < n < M
M(N-1)/2で求めている。このNはタップ総数。
21タップであれば、
M = (21-1)/2 = 10
となる。

これをもとに素直にプログラムにしてみるが、苦労したのはテキストエリアに配列の数値を改行しながら出力する部分だったりした。テキストエリアはマウスでぐぐぐっと広げられるのね。おおっすげ~と思った。作ってからJavaAppletで作ったほうが早かったとも思ったが、まぁJavaScriptの勉強なのでよしとする。

最終的には音の加工を目的にしているので、下のサンプルは音を前提とした数値になってます。

使い方
tap N に作りたいデジタルフィルターのタップ数(奇数)を入力。
Sampling Frequency に処理したいサンプリング周波数を入力。
Cutoff Frequency にカットオフしたい周波数を入力。これ以上の周波数がカットされる。ローパスフィルターなので、低い周波数のみ通過する。
calculate ボタンを押すと計算される。
下のテキストエリアに
タップ数分の係数が出力される。タップ数が多くてテキストエリアに表示しきれなかった場合は、スクロールするか、テキストエリア右下をマウスでドラッグするといくらでも広げられます。



FIR LOW PASS Filter
hamming window

Tap N (odd number)
Sampling Frequency Hz
Cutoff Frequency Hz

Coefficient n


スクリプトはこんなかんじ
<HTML>
<HEAD>
<TITLE>FIR</TITLE>
<script type="text/javascript">
<!--
function calc(){
var tapn = document.form01.elements[0].value;
var sFreq= document.form01.elements[1].value;
var cFreq = document.form01.elements[2].value;
var tapn2 =((tapn-1)/2)+1;
var omega = (Math.PI)/(sFreq/2);
omega = omega*cFreq;
var omegac = 1-(cFreq/(sFreq/2));
var tapArray = new Array(tapn2);
var coeff = 0;
var wHamming = 0;
var coeffWin;

for(i=1; i<tapn2; i++){
coeff = 1/(Math.PI*i)*Math.sin(i*omega);
wHamming = 0.54+0.46*Math.cos((i*Math.PI)/(tapn2-1));
coeffWin = coeff*wHamming;
tapArray[i] = coeffWin;
}

tapArray[0] = 1-omegac;
var listArray ="";

for(i=tapArray.length-1; i>=0; i--){
listArray = listArray + tapArray[i] + ",\n";
}

for(i=1; i<tapArray.length; i++){
listArray = listArray + tapArray[i] + ",\n";
}

document.form01.coefficient.value=listArray;

}
//-->
</script>
</HEAD>
<BODY>
<b>FIR LOW PASS Filter</b><br>
hamming window<br>
<br>
<form name="form01">
Tap N <input type="text" size="3"value="31"> (odd number)<br>
Sampling Frequency <input type="text" size="3"value="44100"> Hz<br>
Cutoff Frequency <input type="text" size="3"value="10000"> Hz<br>
<input type="button" value="calculate" onClick="calc()"><br>
Coefficient n <br>
<textarea name="coefficient"id="translationarea" 
cols="26" rows="20" readonly="readonly"></textarea><br>
</BODY>
</HTML>

自作デジタルフィルタ(Java)に上記で計算した係数入れて確認してみる
手順は、
サンプリング周波数44100HzのホワイトノイズをAudacityで作成してwav保存。このホワイトノイズは0~22050Hzまでの周波数を満遍なく含んでいるので、これを加工することでフィルタ効果がよく分かるというわけ。

係数の計算
タップ数は少な目の21タップ
サンプリング周波数44100Hz
5000Hz以上をカット

自作デジタルフィルタ(Java)
上記で計算した係数をセット
上記wavファイルをフィルタ加工して保存

Audacity
加工したファイルの周波数スペクトルを表示させてみたのが下図。
5000Hzが肩になって、なだらかに落ちている。-40dBぐらい落ちているのでかなり効いているのが分かる。カーブも素直かな。

ついでに11タップでも試してみた。設定の違いはそれだけで他は同じ。結果的には効き方が控えめになって-20dB程度。よりなだらかなカーブになった。用途によってはタップ数少なめの方がいい場合もありそうだ。

さらにタップ数を5001タップまで増やして実行してみた。なるほど理想的な特性に近づきつつある。計算上では5000Hzでストンと落ちる特性が理想となるので。

ハイパス対応も作ってみた。こちらのページ


sound programming 目次はこちら