def initself order int none self order order a0 ak self acoeffs 1 0 0 0 order b0 bk self bcoeffs 1 0 0 0 order xn1 xnk self inputhistory 0 0 self order yn1 ynk self outputhistory 0 0 self order def setcoefficientsself acoeffs listfloat bcoeffs listfloat none if lenacoeffs self order acoeffs 1 0 acoeffs if lenacoeffs self order 1 msg fexpected acoeffs to have self order 1 elements ffor self orderorder filter got lenacoeffs raise valueerrormsg if lenbcoeffs self order 1 msg fexpected bcoeffs to have self order 1 elements ffor self orderorder filter got lenacoeffs raise valueerrormsg self acoeffs acoeffs self bcoeffs bcoeffs def processself sample float float result 0 0 start at index 1 and do index 0 at the end for i in range1 self order 1 result self bcoeffsi self inputhistoryi 1 self acoeffsi self outputhistoryi 1 result result self bcoeffs0 sample self acoeffs0 self inputhistory1 self inputhistory 1 self outputhistory1 self outputhistory 1 self inputhistory0 sample self outputhistory0 result return result 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 a_ 0 a_ k b_ 0 b_ k x n 1 x n k y n 1 y n k 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 calculate y n filt iirfilter 2 filt process 0 0 0 start at index 1 and do index 0 at the end
from __future__ import annotations


class IIRFilter:
    r

    def __init__(self, order: int) -> None:
        self.order = order

        
        self.a_coeffs = [1.0] + [0.0] * order
        
        self.b_coeffs = [1.0] + [0.0] * order

        
        self.input_history = [0.0] * self.order
        
        self.output_history = [0.0] * self.order

    def set_coefficients(self, a_coeffs: list[float], b_coeffs: list[float]) -> None:
        
        if len(a_coeffs) < self.order:
            a_coeffs = [1.0, *a_coeffs]

        if len(a_coeffs) != self.order + 1:
            msg = (
                f"Expected a_coeffs to have {self.order + 1} elements "
                f"for {self.order}-order filter, got {len(a_coeffs)}"
            )
            raise ValueError(msg)

        if len(b_coeffs) != self.order + 1:
            msg = (
                f"Expected b_coeffs to have {self.order + 1} elements "
                f"for {self.order}-order filter, got {len(a_coeffs)}"
            )
            raise ValueError(msg)

        self.a_coeffs = a_coeffs
        self.b_coeffs = b_coeffs

    def process(self, sample: float) -> float:
        
        result = 0.0

        
        for i in range(1, self.order + 1):
            result += (
                self.b_coeffs[i] * self.input_history[i - 1]
                - self.a_coeffs[i] * self.output_history[i - 1]
            )

        result = (result + self.b_coeffs[0] * sample) / self.a_coeffs[0]

        self.input_history[1:] = self.input_history[:-1]
        self.output_history[1:] = self.output_history[:-1]

        self.input_history[0] = sample
        self.output_history[0] = result

        return result