audio_filters.iir_filter

Classes

IIRFilter

N-Order IIR filter

Module Contents

class audio_filters.iir_filter.IIRFilter(order: int)

N-Order IIR filter Assumes working with float samples normalized on [-1, 1]

Implementation details: Based on the 2nd-order function from https://en.wikipedia.org/wiki/Digital_biquad_filter, this generalized N-order function was made.

Using the following transfer function
\[H(z)=\frac{b_{0}+b_{1}z^{-1}+b_{2}z^{-2}+...+b_{k}z^{-k}} {a_{0}+a_{1}z^{-1}+a_{2}z^{-2}+...+a_{k}z^{-k}}\]
we can rewrite this to
\[y[n]={\frac{1}{a_{0}}} \left(\left(b_{0}x[n]+b_{1}x[n-1]+b_{2}x[n-2]+...+b_{k}x[n-k]\right)- \left(a_{1}y[n-1]+a_{2}y[n-2]+...+a_{k}y[n-k]\right)\right)\]
process(sample: float) float

Calculate \(y[n]\)

>>> filt = IIRFilter(2)
>>> filt.process(0)
0.0
set_coefficients(a_coeffs: list[float], b_coeffs: list[float]) None

Set the coefficients for the IIR filter. These should both be of size order + 1. \(a_0\) may be left out, and it will use 1.0 as default value.

This method works well with scipy’s filter design functions

>>> # Make a 2nd-order 1000Hz butterworth lowpass filter
>>> import scipy.signal
>>> b_coeffs, a_coeffs = scipy.signal.butter(2, 1000,
...                                          btype='lowpass',
...                                          fs=48000)
>>> filt = IIRFilter(2)
>>> filt.set_coefficients(a_coeffs, b_coeffs)

The leading \(a_0\) coefficient may be omitted and defaults to 1.0:

>>> filt = IIRFilter(2)
>>> filt.set_coefficients([-1.9, 0.9], [1.0, -2.0, 1.0])
>>> filt.a_coeffs
[1.0, -1.9, 0.9]

Passing the wrong number of coefficients raises a ValueError:

>>> IIRFilter(2).set_coefficients([1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0])
Traceback (most recent call last):
    ...
ValueError: Expected a_coeffs to have 3 elements for 2-order filter, got 4
>>> IIRFilter(2).set_coefficients([1.0, 2.0, 3.0], [1.0, 2.0])
Traceback (most recent call last):
    ...
ValueError: Expected b_coeffs to have 3 elements for 2-order filter, got 2

A genuinely too-short a_coeffs is reported with its real length rather than after the optional a_0 has been filled in:

>>> IIRFilter(2).set_coefficients([1.0], [1.0, 2.0, 3.0])
Traceback (most recent call last):
    ...
ValueError: Expected a_coeffs to have 3 elements for 2-order filter, got 1
a_coeffs
b_coeffs
input_history
order
output_history