• 1
    Like
  • 0
    Comment

イントロダクション

とりあえず, IIRFilterNode なんてものがあるからとりあえず使ってみたい ...
という方向けに書いてみました.

BiquadFilterNode との違い

Web Audio API では, 仕様が策定された初期のころから, BiquadFilterNode というフィルタを
使うための Node が定義されていました. BiquadFilterNode は, フィルタの複雑な数学的処理をその内部に隠蔽 (ブラックボックス化) した, 抽象度の高いハイレベルな API と言えます.

例えば, Low-Pass Filter の数学的処理を知らなくても, BiquadFilterNode の使い方を知れば, Low-Pass Filter を使うことができました.

これに対して, IIRFilterNode を使いこなすには, ある程度のフィルタの数学的処理を理解していないと, 有効に活用することは難しいでしょう. しかし, BiquadFilterNode ではできない, ある程度の自由度をもってフィルタを実装することが可能になります.

これは, OscillatorNode と ScriptProcessorNode の関係に似ていまいす.
例えば, 矩形波の生成方法を知らなくても, OscillatorNode を使えば簡単に矩形波を
生成できますが, これでは, ブラウザが実装している矩形波しか生成することができません.
しかし, ScriptProcessorNode を使えば, 矩形波の生成方法を理解しておく必要がありますが,
ブラウザに依存せず, ある程度の自由度をもって矩形波を生成することができます.

Low-Pass Filter を IIRFiltreNodeで実装してみる

簡単な例ではありますが, IIRFilterNode の使い方を知る導入として Low-Pass Filter を
IIRFilterNode を使って実装してみます.

もちろん, 単に Low-Pass Filter を使いたいのであれば, BiquadFilterNode を使ってください.

const context = new AudioContext();

const createLPFilter = (fd, Q) => {
    const denominators = [];
    const numerators   = [];

    const fc = Math.tan((Math.PI * fd) / context.sampleRate) / (2 * Math.PI);

    const d = 1 + ((2 * Math.PI * fc) / Q) + (4 * Math.pow(Math.PI, 2) * Math.pow(fc, 2));

    denominators[0] = 1;
    denominators[1] = ((8 * Math.pow(Math.PI, 2) * Math.pow(fc, 2)) - 2) / d;
    denominators[2] = (1 - ((2 * Math.PI * fc) / Q) + (4 * Math.pow(Math.PI, 2) * Math.pow(fc, 2))) / d;

    numerators[0] = (4 * Math.pow(Math.PI, 2) * Math.pow(fc, 2)) / d;
    numerators[1] = (8 * Math.pow(Math.PI, 2) * Math.pow(fc, 2)) / d;
    numerators[2] = (4 * Math.pow(Math.PI, 2) * Math.pow(fc, 2)) / d;

    return context.createIIRFilter(numerators, denominators);
};

IIRFilterNode インスタンスを生成するには, AudioContext インスタンスの createIIRFilter メソッドを利用します. このメソッドは, 2つの引数をとります.

return context.createIIRFilter(numerators, denominators);
Parameter Type Description
feedforward (第1引数) sequence<double> 必須の引数です. IIR フィルターの伝達関数のフィードフォワード (分子) の係数の配列です. この配列の最大の長さは20です. もし全ての値が0の場合, InvalidStateError 例外を発生します. 配列の長さが0または20より大きい場合は NotSupportedError 例外を発生します.
feedback (第2引数) sequence<double> 必須の引数です. IIR フィルターの伝達関数のフィードバック (分母) の係数の配列です. この配列の最大の長さは20です. もし配列の最初の要素が0の場合, InvalidStateError 例外を発生します. もし配列の長さが0または20より大きい場合は NotSupportedError 例外を発生します.

簡単に表現すれば, フィルタの伝達関数は分数の形になっていて, 分子・分母の係数を引数として渡すということです.

そして, その係数は Low-Pass Filter の場合, 以下のようになるというわけです.

    const denominators = [];
    const numerators   = [];

    // fd は, ディジタルフィルタのカットオフ周波数. fc は,アナログフィルタのカットオフ周波数
    // つまり, ここでは, ディジタルフィルタのカットオフ周波数をアナログフィルタのカットオフ周波数に変換しています.
    const fc = Math.tan((Math.PI * fd) / context.sampleRate) / (2 * Math.PI);

    // Q は, クオリティファクタ
    const d = 1 + ((2 * Math.PI * fc) / Q) + (4 * Math.pow(Math.PI, 2) * Math.pow(fc, 2));

    // Low-Pass Filter の分母の係数を算出しています
    denominators[0] = 1;
    denominators[1] = ((8 * Math.pow(Math.PI, 2) * Math.pow(fc, 2)) - 2) / d;
    denominators[2] = (1 - ((2 * Math.PI * fc) / Q) + (4 * Math.pow(Math.PI, 2) * Math.pow(fc, 2))) / d;

    // Low-Pass Filter の分子の係数を算出しています
    numerators[0] = (4 * Math.pow(Math.PI, 2) * Math.pow(fc, 2)) / d;
    numerators[1] = (8 * Math.pow(Math.PI, 2) * Math.pow(fc, 2)) / d;
    numerators[2] = (4 * Math.pow(Math.PI, 2) * Math.pow(fc, 2)) / d;

係数の算出方法に関しては, ある程度の数学の知識が必要になるのでここでは割愛します.
とりあえず, Low-Pass Filter の場合は, このように算出するのかと眺めてもらえば OK です.

IIRFilterNode は AudioNode を継承しているので, インスタンスを生成すれば, あとは connect メソッドを利用して, 他のノードに接続するだけです.

Demo

まとめ

少し駆け足になりましたが, IIRFilterNode の概要と具体的な使い方について解説しました.
フィルタに関して完全に解説するには, ある程度の数学の知識が必要になるので, かなり
はしょっての解説になりましたが, より詳細を知りたい方は, 以下のテキストが非常に参考になります.

C言語ではじめる音のプログラミング―サウンドエフェクトの信号処理

こちらには, Low-Pass Filter だけでなく, High-Pass Filter や Band-Pass Filter などの係数についても記載されています. IIR フィルタについてもわかりやすく解説されているので, ぜひ参考にして IIRFilterNode で遊んでみましょう !!