Base class for Infinite Impulse Response (IIR) filters.
This abstract class provides the foundation for all IIR filter implementations
in torchfx. It implements lazy coefficient computation, automatic device
management, and the core filtering operation using torchaudio.functional.lfilter.
IIR filters are recursive digital filters characterized by their transfer
function H(z) = B(z)/A(z), where B(z) are the feedforward (numerator)
coefficients and A(z) are the feedback (denominator) coefficients.
Although the recipe for forward pass needs to be defined within
this function, one should call the Module instance afterwards
instead of this since the former takes care of running the
registered hooks while the latter silently ignores them.
Efficient FIR filter implementation using PyTorch conv1d operation.
The FIR class implements a basic Finite Impulse Response filter that accepts
pre-computed filter coefficients and applies them using PyTorch’s optimized
conv1d operation. This provides GPU-accelerated filtering with automatic
device management and support for mono, stereo, and batched audio processing.
FIR filters are always stable (no feedback) and can provide linear phase response
when designed with symmetric coefficients. This class serves as the base for
DesignableFIR and can also be used directly when you have custom-designed
coefficients from external tools or algorithms.
Parameters:
b (array_like) – FIR filter coefficients (numerator). Can be a Python list, NumPy array,
or any array-like object. The coefficients define the filter’s impulse
response. Length determines filter order (num_taps = len(b)).
Registered buffer containing the flipped filter coefficients in shape [1, 1, K]
where K is the number of taps. The buffer is automatically moved to the input
tensor’s device during forward pass.
Uses PyTorch’s conv1d with grouped convolution where each channel is
filtered independently with the same kernel. This is equivalent to applying
the FIR filter to each channel separately but is much more efficient.
\*\*KernelFlipping**
Coefficients are flipped (reversed) before storage to ensure causal
convolution behavior compatible with scipy.signal.lfilter. This means
the first coefficient in b corresponds to the current sample, the second
to the previous sample, etc.
\*\*PaddingStrategy**
Right-side padding of (K-1) samples is applied where K is the number of taps.
This maintains the original signal length and ensures causal filtering (no
future samples influence past outputs).
\*\*DeviceManagement**
The kernel buffer is automatically moved to match the input tensor’s device
and dtype. No manual device management is required.
\*\*NoGradientComputation**
All filtering operations execute under @torch.no_grad() context since FIR
filters have fixed coefficients (not trainable parameters).
If coefficients are symmetric (b[i] = b[N-1-i]), the filter has linear phase,
meaning constant group delay across all frequencies. This preserves waveform
shape without phase distortion, critical for audio mastering and measurement.
Stability:
FIR filters are always BIBO (Bounded Input Bounded Output) stable because
they have no poles (denominator is always 1). This makes them suitable for
applications where stability is critical.
Computational Complexity:
Time complexity is O(N × K) where N is signal length and K is number of taps.
Space complexity is O(K) for kernel storage plus O(N) for padded input.
References
For comprehensive FIR filter theory and design patterns, see wiki/4.2 FIR Filters.md.
For implementation details and performance characteristics, see the “Implementation
Details” section in wiki/4.2 FIR Filters.md.
>>> importnumpyasnp>>> fromscipy.signalimportfirwin>>>>>> # Design filter using scipy directly>>> taps=firwin(numtaps=101,cutoff=5000,fs=44100,window='hamming')>>>>>> # Use coefficients in torchfx FIR filter>>> fir=FIR(b=taps)>>>>>> # Apply to audio>>> audio=torch.randn(44100)>>> filtered=fir(audio)
Processing stereo audio [C, T]:
>>> # Stereo signal with 2 channels>>> stereo=torch.randn(2,44100)>>>>>> # Filter applies same coefficients to both channels independently>>> fir=FIR(b=[0.25,0.5,0.25])# Simple 3-tap filter>>> filtered_stereo=fir(stereo)>>> print(filtered_stereo.shape)# torch.Size([2, 44100])
Batch processing [B, C, T]:
>>> # Batch of 8 stereo signals>>> batch=torch.randn(8,2,44100)>>>>>> # Apply filter to entire batch>>> fir=FIR(b=[0.2,0.2,0.2,0.2,0.2])>>> filtered_batch=fir(batch)>>> print(filtered_batch.shape)# torch.Size([8, 2, 44100])
GPU acceleration:
>>> # Move input to GPU>>> signal_gpu=torch.randn(44100,device='cuda')>>>>>> # Filter automatically runs on GPU>>> fir=FIR(b=[0.25,0.5,0.25])>>> filtered_gpu=fir(signal_gpu)>>> print(filtered_gpu.device)# cuda:0
>>> # Design Hilbert transformer (90-degree phase shift)>>> # This is a simplified example - real Hilbert needs more taps>>> fromscipy.signalimporthilbertasscipy_hilbert>>> impulse=np.zeros(101)>>> impulse[50]=1>>> analytic=scipy_hilbert(impulse)>>> hilbert_coeffs=np.imag(analytic)>>>>>> # Create FIR filter>>> hilbert_fir=FIR(b=hilbert_coeffs)>>>>>> # Apply to get quadrature component>>> signal=torch.randn(1000)>>> quadrature=hilbert_fir(signal)
>>> # Design two FIR filters>>> fir1=FIR(b=[0.25,0.5,0.25])# First smoothing stage>>> fir2=FIR(b=[0.25,0.5,0.25])# Second smoothing stage>>>>>> # Cascade them (equivalent to convolving coefficients)>>> signal=torch.randn(44100)>>> filtered=fir2(fir1(signal))>>>>>> # Or use pipeline syntax>>> wave=fx.Wave(signal,fs=44100)>>> result=wave|fir1|fir2
Verifying filter stability (always stable for FIR):
>>> # FIR filters are always stable because denominator a = [1.0]>>> fir=FIR(b=[0.1,0.2,0.4,0.2,0.1])>>> print(fir.a)# [1.0] - no feedback, always stable>>>>>> # Even with arbitrary coefficients, FIR is stable>>> unstable_looking=FIR(b=[100,-200,100])>>> print(unstable_looking.a)# Still [1.0] - stable
Although the recipe for forward pass needs to be defined within
this function, one should call the Module instance afterwards
instead of this since the former takes care of running the
registered hooks while the latter silently ignores them.
FIR filter with automatic coefficient design using the window method.
DesignableFIR extends the base FIR class by automatically computing filter
coefficients using the windowed Fourier series method via scipy.signal.firwin.
Instead of providing pre-computed coefficients, you specify the desired filter
characteristics (cutoff frequency, number of taps, window type) and the class
designs the filter for you.
This class is ideal for standard filtering tasks (lowpass, highpass, bandpass,
bandstop) where you don’t need custom coefficient design. The window method
provides intuitive trade-offs between transition bandwidth and stopband attenuation.
Number of filter taps (coefficients). Must be odd for type I FIR filters.
Higher values provide sharper frequency transitions but increase computational
cost and latency. Typical values:
51-101: Fast, moderate selectivity
101-201: Balanced, good selectivity
201-501: Excellent selectivity, higher latency
fs (int or None, optional) – Sampling frequency in Hz. If None, the filter coefficients are not computed
during initialization. The sampling frequency can be set later via the fs
attribute or automatically when used in a pipeline with Wave objects.
Default is None.
Controls the filter type when a single cutoff is provided:
True: Lowpass (gain of 1 at DC, passes zero frequency)
False: Highpass (gain of 0 at DC, blocks zero frequency)
For two cutoff frequencies:
True: Bandstop (notch filter)
False: Bandpass
Default is True (lowpass).
window (WindowType, optional) –
Window function to apply during filter design. Controls the trade-off between
main lobe width (transition bandwidth) and side lobe attenuation (stopband
ripple). Common options:
”hamming”: Good general-purpose window (-43 dB sidelobes)
”hann”: Smooth transitions (-31 dB sidelobes)
”blackman”: Excellent stopband rejection (-58 dB sidelobes)
Computes FIR filter coefficients using scipy.signal.firwin and initializes
the parent FIR class with the computed coefficients. Called automatically
when fs is set and coefficients haven’t been computed yet.
FIR filters typically require 10-100x more taps than equivalent IIR filters
for similar frequency selectivity. However, FIR filters guarantee stability
and can provide linear phase, which IIR filters cannot (except at specific
frequencies).
Odd vs Even Tap Count:
For most filter types, use odd number of taps. Even tap counts can be used
but may not have zero-phase at Nyquist frequency for certain filter types.
Frequency Response Analysis:
Use scipy.signal.freqz to visualize the designed filter’s frequency response:
from scipy.signal import freqz
w, h = freqz(filter.b, worN=8000, fs=filter.fs)
plt.plot(w, 20*np.log10(abs(h)))
References
For comprehensive filter design examples and window function comparisons,
see wiki/4.2 FIR Filters.md.
For API reference and parameter details, see wiki/8.3 torchfx.filter.md.
For guidance on choosing between FIR and IIR filters, see wiki/4 Filters.md.
Examples
Basic lowpass filter design:
>>> importtorch>>> fromtorchfx.filterimportDesignableFIR>>>>>> # Design lowpass at 5 kHz with default Hamming window>>> lpf=DesignableFIR(... cutoff=5000.0,... num_taps=101,... fs=44100,... pass_zero=True,... window="hamming"... )>>>>>> # Apply to mono signal>>> signal=torch.randn(44100)>>> filtered=lpf(signal)>>> print(filtered.shape)# torch.Size([44100])
Highpass filter to remove low-frequency rumble:
>>> # Remove frequencies below 100 Hz>>> hpf=DesignableFIR(... cutoff=100.0,... num_taps=201,# Higher taps for sharp low-frequency cutoff... fs=44100,... pass_zero=False,# Highpass... window="blackman"# High stopband rejection... )>>>>>> # Apply to audio with DC offset and rumble>>> audio=torch.randn(44100)>>> clean=hpf(audio)
>>> # Remove 50-70 Hz hum band (European power line + harmonics)>>> notch=DesignableFIR(... cutoff=[50.0,70.0],... num_taps=301,# High order for narrow notch... fs=44100,... pass_zero=True,# Bandstop... window="blackman"... )>>>>>> # Apply to remove power line interference>>> noisy=torch.randn(44100)>>> clean=notch(noisy)
Comparing different window types:
>>> importmatplotlib.pyplotasplt>>> fromscipy.signalimportfreqz>>> importnumpyasnp>>>>>> # Design same filter with different windows>>> windows={... "hamming":DesignableFIR(5000,101,44100,window="hamming"),... "hann":DesignableFIR(5000,101,44100,window="hann"),... "blackman":DesignableFIR(5000,101,44100,window="blackman")... }>>>>>> # Plot frequency responses>>> forname,firinwindows.items():... w,h=freqz(fir.b,worN=8000,fs=44100)... plt.plot(w,20*np.log10(abs(h)),label=name)>>> plt.xlabel("Frequency (Hz)")>>> plt.ylabel("Magnitude (dB)")>>> plt.legend()>>> plt.grid(True)>>> plt.title("Window Function Comparison")
Exploring the effect of tap count:
>>> # Low, medium, high tap counts>>> fir_51=DesignableFIR(5000,num_taps=51,fs=44100)>>> fir_101=DesignableFIR(5000,num_taps=101,fs=44100)>>> fir_201=DesignableFIR(5000,num_taps=201,fs=44100)>>>>>> # Compare frequency responses>>> fromscipy.signalimportfreqz>>> fortaps,firin[(51,fir_51),(101,fir_101),(201,fir_201)]:... w,h=freqz(fir.b,worN=8000,fs=44100)... plt.plot(w,20*np.log10(abs(h)),label=f"{taps} taps")>>> plt.xlabel("Frequency (Hz)")>>> plt.ylabel("Magnitude (dB)")>>> plt.legend()>>> plt.title("Effect of Tap Count on Transition Bandwidth")
Deferred coefficient computation (fs=None):
>>> # Create filter without computing coefficients>>> fir=DesignableFIR(cutoff=5000,num_taps=101,fs=None)>>> print(fir.b)# None - coefficients not computed yet>>>>>> # Set sampling frequency later>>> fir.fs=44100>>> fir.compute_coefficients()>>> print(fir.bisnotNone)# True - coefficients now computed
Using with torchfx.Wave pipeline (automatic fs configuration):
>>> importtorchfxasfx>>>>>> # Load audio>>> wave=fx.Wave.from_file("audio.wav")# wave.fs = 44100>>>>>> # Create filter without fs - will be set from wave>>> lpf=DesignableFIR(cutoff=8000,num_taps=101,pass_zero=True)>>> print(lpf.fs)# None>>>>>> # Apply using pipe operator - fs set automatically>>> filtered=wave|lpf>>> print(lpf.fs)# 44100 - set from wave>>> filtered.save("filtered.wav")
Anti-aliasing filter before downsampling:
>>> # Downsample from 44100 Hz to 22050 Hz>>> # Design anti-aliasing filter at 0.4 * new_rate>>> anti_alias=DesignableFIR(... cutoff=0.4*22050,# 8820 Hz... num_taps=201,... fs=44100,... pass_zero=True,... window="blackman"# High stopband attenuation... )>>>>>> # Filter then downsample>>> audio=torch.randn(44100)>>> filtered=anti_alias(audio)>>> downsampled=filtered[::2]# Decimate by factor of 2>>> print(downsampled.shape)# torch.Size([22050])
Multi-band processing with multiple DesignableFIR filters:
>>> # Create 3-band crossover>>> low=DesignableFIR(cutoff=200,num_taps=201,fs=44100,pass_zero=True)>>> mid=DesignableFIR(cutoff=[200,2000],num_taps=201,fs=44100,pass_zero=False)>>> high=DesignableFIR(cutoff=2000,num_taps=201,fs=44100,pass_zero=False)>>>>>> # Process each band separately>>> audio=torch.randn(44100)>>> low_band=low(audio)>>> mid_band=mid(audio)>>> high_band=high(audio)>>>>>> # Apply different processing to each band, then recombine>>> # (processing steps omitted for brevity)>>> mixed=low_band+mid_band+high_band
GPU acceleration:
>>> # Create signal on GPU>>> signal_gpu=torch.randn(44100,device="cuda")>>>>>> # Design filter>>> fir=DesignableFIR(5000,num_taps=101,fs=44100,window="hamming")>>>>>> # Filter automatically runs on GPU>>> filtered_gpu=fir(signal_gpu)>>> print(filtered_gpu.device)# cuda:0
Batch processing stereo audio:
>>> # Batch of 8 stereo signals>>> batch=torch.randn(8,2,44100)>>>>>> # Apply same filter to all signals>>> lpf=DesignableFIR(cutoff=5000,num_taps=101,fs=44100)>>> filtered_batch=lpf(batch)>>> print(filtered_batch.shape)# torch.Size([8, 2, 44100])
Verifying linear phase property:
>>> # Design filter>>> fir=DesignableFIR(cutoff=5000,num_taps=101,fs=44100,window="hamming")>>>>>> # Check coefficient symmetry (implies linear phase)>>> importnumpyasnp>>> coeffs=np.array(fir.b)>>> is_symmetric=np.allclose(coeffs,coeffs[::-1])>>> print(is_symmetric)# True - linear phase>>>>>> # Calculate group delay>>> group_delay=(fir.num_taps-1)/(2*fir.fs)>>> print(f"Group delay: {group_delay*1000:.2f} ms")# Constant for all frequencies
Cascading filters for sharper rolloff:
>>> # Two cascaded filters provide steeper rolloff>>> lpf1=DesignableFIR(cutoff=5000,num_taps=101,fs=44100)>>> lpf2=DesignableFIR(cutoff=5000,num_taps=101,fs=44100)>>>>>> # Apply in series>>> signal=torch.randn(44100)>>> filtered=lpf2(lpf1(signal))>>>>>> # Or use pipeline>>> importtorchfxasfx>>> wave=fx.Wave(signal,fs=44100)>>> result=wave|lpf1|lpf2
Butterworth filter with maximally flat passband response.
Butterworth filters provide the smoothest possible passband response with no
ripple in either passband or stopband. They offer a good balance between
frequency selectivity and phase linearity, making them ideal for general-purpose
audio filtering applications.
The filter has a monotonic frequency response with -3 dB gain at the cutoff
frequency. The rolloff steepness is determined by the filter order, with higher
orders providing sharper transitions at the cost of increased phase distortion.
Parameters:
btype (str) – Filter type. One of:
- “lowpass”: Passes frequencies below cutoff
- “highpass”: Passes frequencies above cutoff
- “bandpass”: Passes frequencies within a band
- “bandstop”: Rejects frequencies within a band
cutoff (float) – Cutoff frequency in Hz. This is the -3 dB point for lowpass/highpass filters.
order (int, default=4) – Filter order. Higher orders provide steeper rolloff but increased phase
distortion. The rolloff rate is approximately 6 * order dB per octave.
Typical values: 2-8 for audio applications.
order_scale ({"linear", "db"}, default="linear") – Order scaling mode:
- “linear”: Use order value directly
- “db”: Divide order by 6 (useful for octave-based specifications)
fs (int | None, default=None) – Sampling frequency in Hz. If None, will be set automatically from the
input signal.
a (Sequence[float] | None, default=None) – Pre-computed denominator coefficients. If provided, coefficient computation
is skipped.
b (Sequence[float] | None, default=None) – Pre-computed numerator coefficients. If provided, coefficient computation
is skipped.
The Butterworth filter is designed to have a frequency response magnitude that
is as flat as possible in the passband. The squared magnitude response is:
where n is the filter order and ωc is the cutoff frequency.
For audio applications, orders 2-4 are common for gentle filtering, while
orders 6-8 provide sharper transitions. Very high orders (>12) may exhibit
numerical instability.
Compute the filter’s transfer function coefficients.
This abstract method must be implemented by all AbstractFilter subclasses.
It is responsible for computing the filter’s transfer function coefficients
(typically the numerator b and denominator a coefficients) and storing
them in instance attributes.
The method is called automatically during the first forward() pass when
the _has_computed_coeff property returns False. It should not be called
directly by users in typical usage scenarios.
Verify sampling frequency: Check that self.fs is not None before
attempting to compute coefficients. Raise a ValueError if it is None.
Design filter coefficients: Use SciPy signal processing functions
(e.g., scipy.signal.butter, scipy.signal.cheby1, scipy.signal.firwin)
or custom algorithms to compute filter coefficients based on the filter’s
parameters (cutoff frequency, order, Q factor, etc.).
Store coefficients: Save the computed coefficients in instance attributes
(typically self.a for denominator and self.b for numerator).
Normalize frequencies: For filters that use normalized frequencies
(most SciPy functions), normalize cutoff frequencies by the Nyquist
frequency (fs/2).
Notes
For IIR filters, both a (denominator) and b (numerator) coefficients
are typically stored as sequences or arrays.
For FIR filters, only b (numerator) coefficients are needed, with a
set to [1.0].
Coefficients are initially stored as Python sequences or NumPy arrays and
are later converted to PyTorch tensors during the forward() pass.
This method should be deterministic - calling it multiple times with the
same parameters should produce identical coefficients.
raises ValueError:
If required parameters (especially fs) are not set before coefficient
computation is attempted.
Examples
Implementing compute_coefficients for a Butterworth lowpass filter:
>>> fromscipy.signalimportbutter>>>>>> defcompute_coefficients(self):... '''Compute Butterworth lowpass coefficients.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Normalize cutoff to Nyquist frequency... nyquist=0.5*self.fs... normalized_cutoff=self.cutoff/nyquist... # Design filter using SciPy... self.b,self.a=butter(self.order,normalized_cutoff,btype='low')
Implementing compute_coefficients for a custom shelving filter:
>>> importnumpyasnp>>>>>> defcompute_coefficients(self):... '''Compute high-shelving filter coefficients using biquad formulas.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Calculate angular frequency... omega=2*np.pi*self.cutoff/self.fs... alpha=np.sin(omega)/(2*self.q)... A=self.gain# Linear gain... # Compute biquad coefficients... b0=A*((A+1)+(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha)... b1=-2*A*((A-1)+(A+1)*np.cos(omega))... b2=A*((A+1)+(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha)... a0=(A+1)-(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha... a1=2*((A-1)-(A+1)*np.cos(omega))... a2=(A+1)-(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha... # Normalize by a0 and store... self.b=[b0/a0,b1/a0,b2/a0]... self.a=[1.0,a1/a0,a2/a0]
Implementing compute_coefficients for an FIR filter:
>>> fromscipy.signalimportfirwin>>>>>> defcompute_coefficients(self):... '''Compute FIR filter coefficients using window method.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Use scipy firwin to design FIR filter... self.b=firwin(self.num_taps,self.cutoff,fs=self.fs,... pass_zero=self.pass_zero,window=self.window)... self.a=[1.0]# FIR filters have trivial denominator
This is a convenience class that creates a high-pass Butterworth filter
by automatically setting btype=”highpass”. Provides maximally flat response
in the passband with smooth rolloff.
Parameters:
cutoff (float) – Cutoff frequency in Hz (-3 dB point). Frequencies below this are attenuated.
This is a convenience class that creates a low-pass Butterworth filter
by automatically setting btype=”lowpass”. Provides maximally flat response
in the passband with smooth rolloff.
Parameters:
cutoff (float) – Cutoff frequency in Hz (-3 dB point). Frequencies above this are attenuated.
Chebyshev Type I filter with equalized passband ripple.
Chebyshev Type I filters trade passband ripple for steeper rolloff compared to
Butterworth filters. They provide sharper transitions between passband and
stopband for a given filter order, making them suitable when aggressive
frequency selectivity is needed and some passband ripple is acceptable.
The filter exhibits equalized ripple in the passband (oscillates between 1 and
1-ε where ε is determined by the ripple parameter) and monotonic rolloff in the
stopband.
Parameters:
btype (str) – Filter type. One of:
- “lowpass”: Passes frequencies below cutoff
- “highpass”: Passes frequencies above cutoff
- “bandpass”: Passes frequencies within a band
- “bandstop”: Rejects frequencies within a band
cutoff (float) – Cutoff frequency in Hz. For lowpass/highpass, this is the frequency where
the response exits the ripple band and enters the stopband.
order (int, default=4) – Filter order. Determines both the number of ripples in the passband (order
ripples) and the rolloff steepness. Higher orders provide steeper rolloff.
ripple (float, default=0.1) – Maximum passband ripple in dB. Typical values:
- 0.01 dB: Minimal ripple, closer to Butterworth response
- 0.1 dB: Good balance (default)
- 0.5 dB: Noticeable ripple but very sharp rolloff
- 1.0 dB: Aggressive rolloff, significant passband distortion
fs (int | None, default=None) – Sampling frequency in Hz. If None, will be set automatically from the
input signal.
a (Sequence[float] | None, default=None) – Pre-computed denominator coefficients.
b (Sequence[float] | None, default=None) – Pre-computed numerator coefficients.
Passband: Equalized ripple (oscillates between 0 and -ripple dB)
Stopband: Monotonic rolloff, no ripple
Transition: Sharper than Butterworth for same order
Phase response: More nonlinear than Butterworth
The ripple parameter controls the trade-off between passband flatness and
rolloff steepness. Smaller ripple values produce responses closer to Butterworth,
while larger values provide sharper transitions at the cost of passband
distortion.
For audio applications where passband flatness is critical (e.g., mastering),
use small ripple values (0.01-0.1 dB). For aggressive filtering where some
passband coloration is acceptable (e.g., anti-aliasing), larger ripple values
(0.5-1.0 dB) provide better stopband attenuation.
Compute the filter’s transfer function coefficients.
This abstract method must be implemented by all AbstractFilter subclasses.
It is responsible for computing the filter’s transfer function coefficients
(typically the numerator b and denominator a coefficients) and storing
them in instance attributes.
The method is called automatically during the first forward() pass when
the _has_computed_coeff property returns False. It should not be called
directly by users in typical usage scenarios.
Verify sampling frequency: Check that self.fs is not None before
attempting to compute coefficients. Raise a ValueError if it is None.
Design filter coefficients: Use SciPy signal processing functions
(e.g., scipy.signal.butter, scipy.signal.cheby1, scipy.signal.firwin)
or custom algorithms to compute filter coefficients based on the filter’s
parameters (cutoff frequency, order, Q factor, etc.).
Store coefficients: Save the computed coefficients in instance attributes
(typically self.a for denominator and self.b for numerator).
Normalize frequencies: For filters that use normalized frequencies
(most SciPy functions), normalize cutoff frequencies by the Nyquist
frequency (fs/2).
Notes
For IIR filters, both a (denominator) and b (numerator) coefficients
are typically stored as sequences or arrays.
For FIR filters, only b (numerator) coefficients are needed, with a
set to [1.0].
Coefficients are initially stored as Python sequences or NumPy arrays and
are later converted to PyTorch tensors during the forward() pass.
This method should be deterministic - calling it multiple times with the
same parameters should produce identical coefficients.
raises ValueError:
If required parameters (especially fs) are not set before coefficient
computation is attempted.
Examples
Implementing compute_coefficients for a Butterworth lowpass filter:
>>> fromscipy.signalimportbutter>>>>>> defcompute_coefficients(self):... '''Compute Butterworth lowpass coefficients.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Normalize cutoff to Nyquist frequency... nyquist=0.5*self.fs... normalized_cutoff=self.cutoff/nyquist... # Design filter using SciPy... self.b,self.a=butter(self.order,normalized_cutoff,btype='low')
Implementing compute_coefficients for a custom shelving filter:
>>> importnumpyasnp>>>>>> defcompute_coefficients(self):... '''Compute high-shelving filter coefficients using biquad formulas.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Calculate angular frequency... omega=2*np.pi*self.cutoff/self.fs... alpha=np.sin(omega)/(2*self.q)... A=self.gain# Linear gain... # Compute biquad coefficients... b0=A*((A+1)+(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha)... b1=-2*A*((A-1)+(A+1)*np.cos(omega))... b2=A*((A+1)+(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha)... a0=(A+1)-(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha... a1=2*((A-1)-(A+1)*np.cos(omega))... a2=(A+1)-(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha... # Normalize by a0 and store... self.b=[b0/a0,b1/a0,b2/a0]... self.a=[1.0,a1/a0,a2/a0]
Implementing compute_coefficients for an FIR filter:
>>> fromscipy.signalimportfirwin>>>>>> defcompute_coefficients(self):... '''Compute FIR filter coefficients using window method.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Use scipy firwin to design FIR filter... self.b=firwin(self.num_taps,self.cutoff,fs=self.fs,... pass_zero=self.pass_zero,window=self.window)... self.a=[1.0]# FIR filters have trivial denominator
High-pass Chebyshev Type I filter convenience class.
This is a convenience class that creates a high-pass Chebyshev Type I filter
by automatically setting btype=”highpass”. High-pass filters attenuate
frequencies below the cutoff while passing higher frequencies.
Parameters:
cutoff (float) – Cutoff frequency in Hz. Frequencies below this are attenuated.
order (int, default=4) – Filter order. Higher values provide steeper rolloff.
ripple (float, default=0.1) – Maximum passband ripple in dB.
fs (int | None, default=None) – Sampling frequency in Hz.
Low-pass Chebyshev Type I filter convenience class.
This is a convenience class that creates a low-pass Chebyshev Type I filter
by automatically setting btype=”lowpass”. Low-pass filters attenuate frequencies
above the cutoff while passing lower frequencies.
Parameters:
cutoff (float) – Cutoff frequency in Hz. Frequencies above this are attenuated.
order (int, default=4) – Filter order. Higher values provide steeper rolloff.
ripple (float, default=0.1) – Maximum passband ripple in dB.
fs (int | None, default=None) – Sampling frequency in Hz.
Chebyshev Type II filter with equalized stopband ripple.
Chebyshev Type II filters (also called inverse Chebyshev) have a monotonic
passband response and equalized ripple in the stopband. They offer a compromise
between Butterworth (no ripple anywhere) and Chebyshev Type I (passband ripple),
providing sharper rolloff than Butterworth while maintaining a clean passband.
The filter is characterized by a maximally flat passband (like Butterworth) but
with oscillations in the stopband that allow for steeper rolloff at a given order.
Parameters:
btype (str) – Filter type. One of:
- “lowpass”: Passes frequencies below cutoff
- “highpass”: Passes frequencies above cutoff
- “bandpass”: Passes frequencies within a band
- “bandstop”: Rejects frequencies within a band
cutoff (float) – Cutoff frequency in Hz. This is the frequency where the monotonic passband
response transitions to the oscillating stopband.
order (int, default=4) – Filter order. Higher orders provide steeper rolloff and more stopband
oscillations.
ripple (float, default=0.1) – Minimum stopband attenuation in dB. This specifies how much the stopband
oscillates above the final attenuation level. Typical values:
- 20 dB: Moderate stopband attenuation
- 40 dB: Good stopband attenuation (common default)
- 60 dB: Strong stopband attenuation
- 80 dB: Very strong stopband rejection
fs (int | None, default=None) – Sampling frequency in Hz. If None, will be set automatically from the
input signal.
Stopband: Equalized ripple, oscillates around final attenuation level
Transition: Sharper than Butterworth, less sharp than Type I Chebyshev
Phase response: Nonlinear, but often better than Type I
Chebyshev Type II filters are ideal when you need:
Clean passband (no frequency-dependent gain variations)
Sharper rolloff than Butterworth
Acceptance of stopband ripple (usually inaudible for audio)
The ripple parameter in Type II filters has opposite meaning from Type I:
it specifies the minimum stopband attenuation rather than passband ripple.
Higher ripple values mean better stopband rejection but require steeper
transition bands.
For audio applications, Type II is often preferred over Type I because:
Passband stays clean (no coloration of wanted frequencies)
Stopband ripple is usually irrelevant (already heavily attenuated)
Good compromise between Butterworth and Type I characteristics
Compute the filter’s transfer function coefficients.
This abstract method must be implemented by all AbstractFilter subclasses.
It is responsible for computing the filter’s transfer function coefficients
(typically the numerator b and denominator a coefficients) and storing
them in instance attributes.
The method is called automatically during the first forward() pass when
the _has_computed_coeff property returns False. It should not be called
directly by users in typical usage scenarios.
Verify sampling frequency: Check that self.fs is not None before
attempting to compute coefficients. Raise a ValueError if it is None.
Design filter coefficients: Use SciPy signal processing functions
(e.g., scipy.signal.butter, scipy.signal.cheby1, scipy.signal.firwin)
or custom algorithms to compute filter coefficients based on the filter’s
parameters (cutoff frequency, order, Q factor, etc.).
Store coefficients: Save the computed coefficients in instance attributes
(typically self.a for denominator and self.b for numerator).
Normalize frequencies: For filters that use normalized frequencies
(most SciPy functions), normalize cutoff frequencies by the Nyquist
frequency (fs/2).
Notes
For IIR filters, both a (denominator) and b (numerator) coefficients
are typically stored as sequences or arrays.
For FIR filters, only b (numerator) coefficients are needed, with a
set to [1.0].
Coefficients are initially stored as Python sequences or NumPy arrays and
are later converted to PyTorch tensors during the forward() pass.
This method should be deterministic - calling it multiple times with the
same parameters should produce identical coefficients.
raises ValueError:
If required parameters (especially fs) are not set before coefficient
computation is attempted.
Examples
Implementing compute_coefficients for a Butterworth lowpass filter:
>>> fromscipy.signalimportbutter>>>>>> defcompute_coefficients(self):... '''Compute Butterworth lowpass coefficients.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Normalize cutoff to Nyquist frequency... nyquist=0.5*self.fs... normalized_cutoff=self.cutoff/nyquist... # Design filter using SciPy... self.b,self.a=butter(self.order,normalized_cutoff,btype='low')
Implementing compute_coefficients for a custom shelving filter:
>>> importnumpyasnp>>>>>> defcompute_coefficients(self):... '''Compute high-shelving filter coefficients using biquad formulas.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Calculate angular frequency... omega=2*np.pi*self.cutoff/self.fs... alpha=np.sin(omega)/(2*self.q)... A=self.gain# Linear gain... # Compute biquad coefficients... b0=A*((A+1)+(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha)... b1=-2*A*((A-1)+(A+1)*np.cos(omega))... b2=A*((A+1)+(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha)... a0=(A+1)-(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha... a1=2*((A-1)-(A+1)*np.cos(omega))... a2=(A+1)-(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha... # Normalize by a0 and store... self.b=[b0/a0,b1/a0,b2/a0]... self.a=[1.0,a1/a0,a2/a0]
Implementing compute_coefficients for an FIR filter:
>>> fromscipy.signalimportfirwin>>>>>> defcompute_coefficients(self):... '''Compute FIR filter coefficients using window method.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Use scipy firwin to design FIR filter... self.b=firwin(self.num_taps,self.cutoff,fs=self.fs,... pass_zero=self.pass_zero,window=self.window)... self.a=[1.0]# FIR filters have trivial denominator
High-pass Chebyshev Type II filter convenience class.
This is a convenience class that creates a high-pass Chebyshev Type II filter
by automatically setting btype=”highpass”. Provides clean passband with
stopband ripple.
Parameters:
cutoff (float) – Cutoff frequency in Hz. Frequencies below this are attenuated.
order (int, default=4) – Filter order. Higher values provide steeper rolloff.
ripple (float, default=0.1) – Minimum stopband attenuation in dB.
fs (int | None, default=None) – Sampling frequency in Hz.
Low-pass Chebyshev Type II filter convenience class.
This is a convenience class that creates a low-pass Chebyshev Type II filter
by automatically setting btype=”lowpass”. Provides clean passband with
stopband ripple.
Parameters:
cutoff (float) – Cutoff frequency in Hz. Frequencies above this are attenuated.
order (int, default=4) – Filter order. Higher values provide steeper rolloff.
ripple (float, default=0.1) – Minimum stopband attenuation in dB.
fs (int | None, default=None) – Sampling frequency in Hz.
Elliptic (Cauer) filter with optimal transition bandwidth.
Elliptic filters (also called Cauer filters) achieve the sharpest possible
transition between passband and stopband for a given filter order. They do this
by allowing equalized ripple in both the passband and stopband, making them
optimal when you need maximum frequency selectivity with minimum filter order.
Of all classical IIR filter types, elliptic filters provide:
Steepest rolloff for a given order
Lowest order for given specifications
Most aggressive frequency separation
However, they also have:
Ripple in both passband and stopband
Most nonlinear phase response
Potential for passband coloration
Parameters:
btype (str) – Filter type:
- “lowpass”: Passes frequencies below cutoff
- “highpass”: Passes frequencies above cutoff
- “bandpass”: Passes frequencies within a band
- “bandstop”: Rejects frequencies within a band
cutoff (float) – Cutoff frequency in Hz. This is the edge of the passband where ripple
transitions to stopband.
order (int, default=4) – Filter order. For elliptic filters, even low orders provide steep rolloff.
Typical values: 3-6 for most applications.
passband_ripple (float, default=0.1) – Maximum passband ripple in dB. Controls the amount of gain variation
allowed in the passband. Typical values:
- 0.01 dB: Minimal ripple, closer to Chebyshev Type II
- 0.1 dB: Good balance (default)
- 0.5 dB: More aggressive rolloff, noticeable ripple
- 1.0 dB: Very steep rolloff, significant passband distortion
stopband_attenuation (float, default=40) – Minimum stopband attenuation in dB. Specifies how much the stopband is
attenuated below 0 dB. Typical values:
- 20 dB: Light attenuation
- 40 dB: Good attenuation (default)
- 60 dB: Strong attenuation
- 80+ dB: Very strong rejection
fs (int | None, default=None) – Sampling frequency in Hz.
Increasing order → Steeper transition and more ripples
All three parameters are interdependent in the design
When to Use Elliptic Filters:
✓ Need steepest possible rolloff
✓ Filter order is constrained (computational resources)
✓ Phase linearity is not critical
✓ Can tolerate some passband and stopband ripple
✓ Anti-aliasing before downsampling
✓ Interference rejection with narrow transition bands
When NOT to Use:
✗ Phase linearity is important (use FIR instead)
✗ Passband must be perfectly flat (use Butterworth)
✗ Critical listening applications (use Chebyshev Type II or Butterworth)
Comparison with other filters (for same specifications):
Butterworth: Highest order, no ripple, smoothest response
The elliptic filter design is based on elliptic rational functions (Jacobi
elliptic functions), which allow the equiripple behavior in both bands. The
filter achieves optimal Chebyshev approximation in both passband and stopband
simultaneously.
Caution: Very high stopband attenuation requirements (>80 dB) combined with
tight passband ripple (<0.1 dB) may result in numerical instability or require
high filter orders.
Compute the filter’s transfer function coefficients.
This abstract method must be implemented by all AbstractFilter subclasses.
It is responsible for computing the filter’s transfer function coefficients
(typically the numerator b and denominator a coefficients) and storing
them in instance attributes.
The method is called automatically during the first forward() pass when
the _has_computed_coeff property returns False. It should not be called
directly by users in typical usage scenarios.
Verify sampling frequency: Check that self.fs is not None before
attempting to compute coefficients. Raise a ValueError if it is None.
Design filter coefficients: Use SciPy signal processing functions
(e.g., scipy.signal.butter, scipy.signal.cheby1, scipy.signal.firwin)
or custom algorithms to compute filter coefficients based on the filter’s
parameters (cutoff frequency, order, Q factor, etc.).
Store coefficients: Save the computed coefficients in instance attributes
(typically self.a for denominator and self.b for numerator).
Normalize frequencies: For filters that use normalized frequencies
(most SciPy functions), normalize cutoff frequencies by the Nyquist
frequency (fs/2).
Notes
For IIR filters, both a (denominator) and b (numerator) coefficients
are typically stored as sequences or arrays.
For FIR filters, only b (numerator) coefficients are needed, with a
set to [1.0].
Coefficients are initially stored as Python sequences or NumPy arrays and
are later converted to PyTorch tensors during the forward() pass.
This method should be deterministic - calling it multiple times with the
same parameters should produce identical coefficients.
raises ValueError:
If required parameters (especially fs) are not set before coefficient
computation is attempted.
Examples
Implementing compute_coefficients for a Butterworth lowpass filter:
>>> fromscipy.signalimportbutter>>>>>> defcompute_coefficients(self):... '''Compute Butterworth lowpass coefficients.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Normalize cutoff to Nyquist frequency... nyquist=0.5*self.fs... normalized_cutoff=self.cutoff/nyquist... # Design filter using SciPy... self.b,self.a=butter(self.order,normalized_cutoff,btype='low')
Implementing compute_coefficients for a custom shelving filter:
>>> importnumpyasnp>>>>>> defcompute_coefficients(self):... '''Compute high-shelving filter coefficients using biquad formulas.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Calculate angular frequency... omega=2*np.pi*self.cutoff/self.fs... alpha=np.sin(omega)/(2*self.q)... A=self.gain# Linear gain... # Compute biquad coefficients... b0=A*((A+1)+(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha)... b1=-2*A*((A-1)+(A+1)*np.cos(omega))... b2=A*((A+1)+(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha)... a0=(A+1)-(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha... a1=2*((A-1)-(A+1)*np.cos(omega))... a2=(A+1)-(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha... # Normalize by a0 and store... self.b=[b0/a0,b1/a0,b2/a0]... self.a=[1.0,a1/a0,a2/a0]
Implementing compute_coefficients for an FIR filter:
>>> fromscipy.signalimportfirwin>>>>>> defcompute_coefficients(self):... '''Compute FIR filter coefficients using window method.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Use scipy firwin to design FIR filter... self.b=firwin(self.num_taps,self.cutoff,fs=self.fs,... pass_zero=self.pass_zero,window=self.window)... self.a=[1.0]# FIR filters have trivial denominator
This convenience class creates a high-pass elliptic filter by automatically
setting btype=”highpass”. Provides the steepest possible high-pass rolloff
for a given filter order, with ripple in both passband and stopband.
Parameters:
cutoff (float) – Cutoff frequency in Hz. Frequencies below this are attenuated.
order (int, default=4) – Filter order. Even low orders (3-4) provide steep rolloff.
passband_ripple (float, default=0.1) – Maximum passband ripple in dB.
stopband_attenuation (float, default=40) – Minimum stopband attenuation in dB.
fs (int | None, default=None) – Sampling frequency in Hz.
This convenience class creates a low-pass elliptic filter by automatically
setting btype=”lowpass”. Provides the steepest possible low-pass rolloff
for a given filter order, with ripple in both passband and stopband.
Parameters:
cutoff (float) – Cutoff frequency in Hz. Frequencies above this are attenuated.
order (int, default=4) – Filter order. Even low orders (3-4) provide steep rolloff.
passband_ripple (float, default=0.1) – Maximum passband ripple in dB.
stopband_attenuation (float, default=40) – Minimum stopband attenuation in dB.
fs (int | None, default=None) – Sampling frequency in Hz.
Linkwitz-Riley crossover filter for speaker systems.
Linkwitz-Riley filters are designed specifically for crossover applications where
audio is split into multiple frequency bands for different speakers (e.g., woofer,
tweeter). They are created by cascading two identical Butterworth filters, resulting
in several desirable properties:
-6 dB gain at the cutoff frequency for both high-pass and low-pass sections
Flat magnitude response when high-pass and low-pass outputs are summed
Zero phase difference between outputs at crossover frequency
Complementary magnitude responses
These properties make Linkwitz-Riley filters ideal for multi-way speaker systems,
ensuring smooth transitions between drivers with no amplitude or phase anomalies.
The filter order must be an even integer (2, 4, 8, etc.) because it’s formed by
cascading two Butterworth filters. The effective order is twice that of each
Butterworth stage.
Parameters:
btype (str) – Filter type: “lowpass” or “highpass”. For crossover applications, use
matching pairs of low-pass and high-pass filters at the same frequency.
cutoff (float) – Crossover frequency in Hz. This is where both the low-pass and high-pass
filters will be at -6 dB. Common crossover frequencies:
- 80-120 Hz: Subwoofer crossover
- 200-500 Hz: Woofer-midrange crossover
- 2000-3500 Hz: Midrange-tweeter crossover
order (int, default=4) – Filter order. Must be a positive even integer (2, 4, 8, 12, etc.).
Common orders:
- 2nd order (12 dB/octave): Gentle slope, good phase
- 4th order (24 dB/octave): Standard for many systems
- 8th order (48 dB/octave): Steep slope for problem drivers
order_scale ({"linear", "db"}, default="linear") – Order scaling mode.
fs (int | None, default=None) – Sampling frequency in Hz.
Phase alignment: 0° difference at crossover frequency
Slope: 6 * order dB/octave (e.g., 24 dB/octave for 4th order)
Crossover point: -6 dB for both filters
Advantages over other crossover designs:
No peaks or dips when signals are summed
Excellent phase coherence
Predictable, well-behaved response
Industry-standard for professional audio
Common crossover orders:
2nd order (LR2): 12 dB/octave, wide transition, good for close drivers
4th order (LR4): 24 dB/octave, most popular, good all-around
8th order (LR8): 48 dB/octave, steep isolation, for problem cases
The filter is formed by cascading two Butterworth filters of half the specified
order. For example, a 4th-order LR filter uses two 2nd-order Butterworth filters.
This is implemented by convolving the Butterworth coefficients with themselves.
Physical interpretation: The -6 dB crossover point means each driver contributes
equally to the acoustic output at the crossover frequency, resulting in flat
total response when properly aligned.
The method calculates the coefficients for a Butterworth filter of half the
specified order and then cascades it with itself by convolving the numerator and
denominator coefficients.
This convenience class creates a high-pass Linkwitz-Riley filter by
automatically setting btype=”highpass”. Used in crossover networks to
send high frequencies to tweeters or mid-range drivers.
Parameters:
cutoff (float) – Crossover frequency in Hz (-6 dB point).
order (int, default=4) – Filter order (must be even). Common values: 2, 4, 8.
order_scale ({"linear", "db"}, default="linear") – Order scaling mode.
fs (int | None, default=None) – Sampling frequency in Hz.
This convenience class creates a low-pass Linkwitz-Riley filter by
automatically setting btype=”lowpass”. Used in crossover networks to
send low frequencies to woofers or subwoofers.
Parameters:
cutoff (float) – Crossover frequency in Hz (-6 dB point).
order (int, default=4) – Filter order (must be even). Common values: 2, 4, 8.
order_scale ({"linear", "db"}, default="linear") – Order scaling mode.
fs (int | None, default=None) – Sampling frequency in Hz.
classtorchfx.filter.HiShelving(cutoff, q, gain, gain_scale='linear', fs=None)[source]#
Bases: Shelving
High-frequency shelving filter for treble control.
A high-shelving filter boosts or cuts frequencies above the cutoff frequency.
This filter is commonly used in audio equalization to adjust treble/brightness,
air frequencies, or apply de-emphasis curves. It provides smooth, gradual
transitions similar to analog tone controls.
The filter affects all frequencies above the cutoff with a constant gain in dB,
creating a shelf-like frequency response. The transition steepness is controlled
by the Q parameter.
Parameters:
cutoff (float) – Transition frequency in Hz. Frequencies above this will be boosted or cut.
Common values:
- 2000-4000 Hz: Presence boost/cut
- 6000-8000 Hz: Brightness control
- 10000-12000 Hz: Air/sparkle adjustment
gain (float) – Shelf gain amount. Interpretation depends on gain_scale:
- For “linear”: Linear gain factor (e.g., 2.0 for +6 dB)
- For “db”: Gain in dB (e.g., 6.0 for +6 dB boost, -3.0 for cut)
gain_scale ({"linear", "db"}, default="linear") – How to interpret the gain parameter:
- “linear”: Direct linear gain multiplier
- “db”: Gain in decibels (converted internally to linear)
fs (int | None, default=None) – Sampling frequency in Hz.
High-shelving filters are one of the most commonly used EQ types in audio
production. They’re essential for:
Brightening dull recordings
Taming harsh cymbals or sibilance
Adding presence to vocals
Adjusting overall tonal balance
The filter uses the Audio EQ Cookbook biquad equations, providing accurate
analog-style shelving response. The Q parameter affects how quickly the
transition occurs but doesn’t affect the final shelf gain.
For subtle, musical adjustments, use moderate Q values (0.5-0.707) and moderate
gains (±3 to ±6 dB). Higher Q values create steeper transitions but may sound
less natural.
Compute the filter’s transfer function coefficients.
This abstract method must be implemented by all AbstractFilter subclasses.
It is responsible for computing the filter’s transfer function coefficients
(typically the numerator b and denominator a coefficients) and storing
them in instance attributes.
The method is called automatically during the first forward() pass when
the _has_computed_coeff property returns False. It should not be called
directly by users in typical usage scenarios.
Verify sampling frequency: Check that self.fs is not None before
attempting to compute coefficients. Raise a ValueError if it is None.
Design filter coefficients: Use SciPy signal processing functions
(e.g., scipy.signal.butter, scipy.signal.cheby1, scipy.signal.firwin)
or custom algorithms to compute filter coefficients based on the filter’s
parameters (cutoff frequency, order, Q factor, etc.).
Store coefficients: Save the computed coefficients in instance attributes
(typically self.a for denominator and self.b for numerator).
Normalize frequencies: For filters that use normalized frequencies
(most SciPy functions), normalize cutoff frequencies by the Nyquist
frequency (fs/2).
Notes
For IIR filters, both a (denominator) and b (numerator) coefficients
are typically stored as sequences or arrays.
For FIR filters, only b (numerator) coefficients are needed, with a
set to [1.0].
Coefficients are initially stored as Python sequences or NumPy arrays and
are later converted to PyTorch tensors during the forward() pass.
This method should be deterministic - calling it multiple times with the
same parameters should produce identical coefficients.
raises ValueError:
If required parameters (especially fs) are not set before coefficient
computation is attempted.
Examples
Implementing compute_coefficients for a Butterworth lowpass filter:
>>> fromscipy.signalimportbutter>>>>>> defcompute_coefficients(self):... '''Compute Butterworth lowpass coefficients.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Normalize cutoff to Nyquist frequency... nyquist=0.5*self.fs... normalized_cutoff=self.cutoff/nyquist... # Design filter using SciPy... self.b,self.a=butter(self.order,normalized_cutoff,btype='low')
Implementing compute_coefficients for a custom shelving filter:
>>> importnumpyasnp>>>>>> defcompute_coefficients(self):... '''Compute high-shelving filter coefficients using biquad formulas.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Calculate angular frequency... omega=2*np.pi*self.cutoff/self.fs... alpha=np.sin(omega)/(2*self.q)... A=self.gain# Linear gain... # Compute biquad coefficients... b0=A*((A+1)+(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha)... b1=-2*A*((A-1)+(A+1)*np.cos(omega))... b2=A*((A+1)+(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha)... a0=(A+1)-(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha... a1=2*((A-1)-(A+1)*np.cos(omega))... a2=(A+1)-(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha... # Normalize by a0 and store... self.b=[b0/a0,b1/a0,b2/a0]... self.a=[1.0,a1/a0,a2/a0]
Implementing compute_coefficients for an FIR filter:
>>> fromscipy.signalimportfirwin>>>>>> defcompute_coefficients(self):... '''Compute FIR filter coefficients using window method.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Use scipy firwin to design FIR filter... self.b=firwin(self.num_taps,self.cutoff,fs=self.fs,... pass_zero=self.pass_zero,window=self.window)... self.a=[1.0]# FIR filters have trivial denominator
Return type:
None
classtorchfx.filter.LoShelving(cutoff, q, gain, gain_scale='linear', fs=None)[source]#
Bases: Shelving
Low-frequency shelving filter for bass control.
A low-shelving filter boosts or cuts frequencies below the cutoff frequency.
This filter is commonly used in audio equalization to adjust bass frequencies,
add warmth, reduce muddiness, or apply bass management. It provides smooth,
gradual transitions similar to analog bass/treble controls.
The filter affects all frequencies below the cutoff with a constant gain in dB,
creating a shelf-like frequency response. The transition steepness is controlled
by the Q parameter.
Parameters:
cutoff (float) – Transition frequency in Hz. Frequencies below this will be boosted or cut.
Common values:
- 60-100 Hz: Sub-bass control
- 100-200 Hz: Bass warmth/muddiness
- 200-400 Hz: Low-mid body
- 400-800 Hz: Fullness/boxiness
gain (float) – Shelf gain amount. Interpretation depends on gain_scale:
- For “linear”: Linear gain factor (e.g., 2.0 for +6 dB)
- For “db”: Gain in dB (e.g., 6.0 for +6 dB boost, -3.0 for cut)
gain_scale ({"linear", "db"}, default="linear") – How to interpret the gain parameter:
- “linear”: Direct linear gain multiplier
- “db”: Gain in decibels (converted internally to linear)
fs (int | None, default=None) – The sampling frequency in Hz.
Low-shelving filters are fundamental tools in audio production for:
Adding warmth and body to thin recordings
Reducing muddiness in the low-midrange
Bass management and room correction
Matching tonal balance between tracks
Creating vintage or “warm” sound characteristics
The filter uses the Audio EQ Cookbook biquad equations, providing accurate
analog-style shelving response. The Q parameter controls transition steepness
but doesn’t affect the final shelf gain.
Common Applications:
Mastering: Subtle low-end adjustments (±2 to ±4 dB at 80-150 Hz)
Mixing: Controlling bass buildup (cuts at 100-300 Hz)
Sound design: Creating warmth or thickness
Broadcast: Meeting bass response standards
For musical, natural-sounding results, use moderate Q values (0.5-0.707) and
conservative gains (±3 to ±6 dB). Excessive low-frequency boost can cause
distortion, speaker damage, or headroom issues.
Compute the filter’s transfer function coefficients.
This abstract method must be implemented by all AbstractFilter subclasses.
It is responsible for computing the filter’s transfer function coefficients
(typically the numerator b and denominator a coefficients) and storing
them in instance attributes.
The method is called automatically during the first forward() pass when
the _has_computed_coeff property returns False. It should not be called
directly by users in typical usage scenarios.
Verify sampling frequency: Check that self.fs is not None before
attempting to compute coefficients. Raise a ValueError if it is None.
Design filter coefficients: Use SciPy signal processing functions
(e.g., scipy.signal.butter, scipy.signal.cheby1, scipy.signal.firwin)
or custom algorithms to compute filter coefficients based on the filter’s
parameters (cutoff frequency, order, Q factor, etc.).
Store coefficients: Save the computed coefficients in instance attributes
(typically self.a for denominator and self.b for numerator).
Normalize frequencies: For filters that use normalized frequencies
(most SciPy functions), normalize cutoff frequencies by the Nyquist
frequency (fs/2).
Notes
For IIR filters, both a (denominator) and b (numerator) coefficients
are typically stored as sequences or arrays.
For FIR filters, only b (numerator) coefficients are needed, with a
set to [1.0].
Coefficients are initially stored as Python sequences or NumPy arrays and
are later converted to PyTorch tensors during the forward() pass.
This method should be deterministic - calling it multiple times with the
same parameters should produce identical coefficients.
raises ValueError:
If required parameters (especially fs) are not set before coefficient
computation is attempted.
Examples
Implementing compute_coefficients for a Butterworth lowpass filter:
>>> fromscipy.signalimportbutter>>>>>> defcompute_coefficients(self):... '''Compute Butterworth lowpass coefficients.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Normalize cutoff to Nyquist frequency... nyquist=0.5*self.fs... normalized_cutoff=self.cutoff/nyquist... # Design filter using SciPy... self.b,self.a=butter(self.order,normalized_cutoff,btype='low')
Implementing compute_coefficients for a custom shelving filter:
>>> importnumpyasnp>>>>>> defcompute_coefficients(self):... '''Compute high-shelving filter coefficients using biquad formulas.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Calculate angular frequency... omega=2*np.pi*self.cutoff/self.fs... alpha=np.sin(omega)/(2*self.q)... A=self.gain# Linear gain... # Compute biquad coefficients... b0=A*((A+1)+(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha)... b1=-2*A*((A-1)+(A+1)*np.cos(omega))... b2=A*((A+1)+(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha)... a0=(A+1)-(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha... a1=2*((A-1)-(A+1)*np.cos(omega))... a2=(A+1)-(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha... # Normalize by a0 and store... self.b=[b0/a0,b1/a0,b2/a0]... self.a=[1.0,a1/a0,a2/a0]
Implementing compute_coefficients for an FIR filter:
>>> fromscipy.signalimportfirwin>>>>>> defcompute_coefficients(self):... '''Compute FIR filter coefficients using window method.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Use scipy firwin to design FIR filter... self.b=firwin(self.num_taps,self.cutoff,fs=self.fs,... pass_zero=self.pass_zero,window=self.window)... self.a=[1.0]# FIR filters have trivial denominator
Parametric equalizer with bell-shaped frequency response.
A parametric EQ is a bell-shaped filter (also called a peaking filter) that
boosts or cuts a specific frequency range. It’s the most versatile and commonly
used type of EQ in music production, audio mastering, and live sound reinforcement,
providing precise control over frequency, bandwidth, and gain.
Unlike shelving filters that affect all frequencies above or below a cutoff,
parametric EQs create a localized boost or cut centered at a specific frequency,
with the bandwidth controlled by the Q parameter. This makes them ideal for
surgical frequency adjustments, tonal shaping, and corrective equalization.
The filter is implemented using the Audio EQ Cookbook biquad equations,
providing accurate analog-style peaking response.
Parameters:
frequency (float) – Center frequency in Hz where the peak or dip occurs. This is the frequency
that receives the maximum boost or cut. Common applications:
- 100-250 Hz: Body, warmth, or muddiness control
- 250-500 Hz: Fullness or boxiness
- 500-2000 Hz: Presence, honk, or nasality
- 2000-5000 Hz: Clarity, definition, or harshness
- 5000-10000 Hz: Brilliance, air, or sibilance
- 10000+ Hz: Sparkle or extreme high-frequency detail
q (float) – Quality factor (Q) controlling the bandwidth of the filter. Higher Q
values result in narrower, more focused adjustments. The bandwidth in Hz
is approximately frequency / Q. Typical values:
- 0.3-0.5: Very wide, gentle, musical (subtle tonal shaping)
- 0.7-1.0: Wide, natural (general tonal adjustments)
- 1.0-2.0: Moderate, focused (typical mixing applications)
- 2.0-5.0: Narrow, surgical (problem frequency removal)
- 5.0-10.0: Very narrow (feedback suppression, notch-like)
- 10.0+: Extremely narrow (surgical removal)
gain (float) – Gain in dB at the center frequency. Positive values boost, negative
values cut. Typical values:
- ±1-2 dB: Subtle, transparent adjustments
- ±3-6 dB: Moderate, audible changes
- ±6-12 dB: Aggressive, obvious EQ
- ±12+ dB: Extreme correction (use with caution)
fs (int | None, default=None) – Sampling frequency in Hz.
Start with narrow Q to identify problem frequencies, then widen for musical result
Use cuts more than boosts (subtractive EQ philosophy)
Lower frequencies generally need wider Q (lower Q values)
Higher frequencies can use narrower Q without sounding unnatural
A/B compare frequently to avoid over-EQing
Use multiple gentle bands rather than one extreme adjustment
The parametric EQ uses the following biquad equations from the Audio EQ Cookbook:
ω₀ = 2π * frequency / fs
α = sin(ω₀) / (2 * Q)
A = 10^(gain/20)
b₀ = 1 + α * A
b₁ = -2 * cos(ω₀)
b₂ = 1 - α * A
a₀ = 1 + α / A
a₁ = -2 * cos(ω₀)
a₂ = 1 - α / A
Caution: Very high Q values (>10) can cause ringing artifacts and may sound
unnatural. Very large gain values (>12 dB) can introduce distortion and
reduce headroom. Always monitor levels after applying EQ.
A notch filter (also called a band-stop or band-reject filter) attenuates a
narrow band of frequencies centered around a specified frequency. This is ideal
for removing tonal interference, hum, resonances, or feedback frequencies
without affecting the surrounding spectrum.
The filter creates a sharp dip in the frequency response at the notch frequency,
with the width of the notch controlled by the Q parameter.
Parameters:
cutoff (float) – Notch center frequency in Hz. This frequency will be maximally attenuated.
Common applications:
- 50 Hz: Mains hum (Europe, Australia)
- 60 Hz: Mains hum (North America, Japan)
- 100/120 Hz: Second harmonic of mains hum
- Any resonant frequency or feedback tone
q (float) – Quality factor determining the notch width. Higher Q values result in
narrower notches that affect fewer adjacent frequencies. The notch
bandwidth in Hz is approximately cutoff / Q. Typical values:
- 5-10: Wide notch, affects broader frequency range
- 10-30: Moderate notch (common for hum removal)
- 30+: Very narrow notch, surgical removal
fs (int | None, default=None) – Sampling frequency in Hz.
>>> # First identify the feedback frequency (e.g., 2350 Hz)>>> notch=fx.filter.Notch(cutoff=2350,q=20,fs=44100)>>> wave=fx.Wave.from_file("live_recording.wav")>>> no_feedback=wave|notch
Remove multiple hum harmonics:
>>> # Remove 60 Hz fundamental and 120 Hz harmonic>>> notch1=fx.filter.Notch(cutoff=60,q=30,fs=44100)>>> notch2=fx.filter.Notch(cutoff=120,q=30,fs=44100)>>> wave=fx.Wave.from_file("humming.wav")>>> clean=wave|notch1|notch2
De-essing (though specialized de-essers are often better)
The Q parameter is critical:
Too low: Notch is wide and affects too many frequencies, causing
audible coloration
Too high: Notch may not be wide enough to fully remove the problem
frequency, especially if it varies slightly
For hum removal, Q values of 20-30 are typical. For surgical removal of
specific tones, Q values up to 50-100 can be used, but be aware that very
high Q filters can ring or become unstable.
The notch filter is implemented using scipy.signal.iirnotch, which designs
a second-order IIR notch filter with maximum attenuation at the specified
frequency.
Caution: Overuse of notch filters can make audio sound unnatural. Use
sparingly and only when necessary. Consider addressing the source of the
problem (ground loops, shielding, etc.) rather than filtering.
Compute the filter’s transfer function coefficients.
This abstract method must be implemented by all AbstractFilter subclasses.
It is responsible for computing the filter’s transfer function coefficients
(typically the numerator b and denominator a coefficients) and storing
them in instance attributes.
The method is called automatically during the first forward() pass when
the _has_computed_coeff property returns False. It should not be called
directly by users in typical usage scenarios.
Verify sampling frequency: Check that self.fs is not None before
attempting to compute coefficients. Raise a ValueError if it is None.
Design filter coefficients: Use SciPy signal processing functions
(e.g., scipy.signal.butter, scipy.signal.cheby1, scipy.signal.firwin)
or custom algorithms to compute filter coefficients based on the filter’s
parameters (cutoff frequency, order, Q factor, etc.).
Store coefficients: Save the computed coefficients in instance attributes
(typically self.a for denominator and self.b for numerator).
Normalize frequencies: For filters that use normalized frequencies
(most SciPy functions), normalize cutoff frequencies by the Nyquist
frequency (fs/2).
Notes
For IIR filters, both a (denominator) and b (numerator) coefficients
are typically stored as sequences or arrays.
For FIR filters, only b (numerator) coefficients are needed, with a
set to [1.0].
Coefficients are initially stored as Python sequences or NumPy arrays and
are later converted to PyTorch tensors during the forward() pass.
This method should be deterministic - calling it multiple times with the
same parameters should produce identical coefficients.
raises ValueError:
If required parameters (especially fs) are not set before coefficient
computation is attempted.
Examples
Implementing compute_coefficients for a Butterworth lowpass filter:
>>> fromscipy.signalimportbutter>>>>>> defcompute_coefficients(self):... '''Compute Butterworth lowpass coefficients.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Normalize cutoff to Nyquist frequency... nyquist=0.5*self.fs... normalized_cutoff=self.cutoff/nyquist... # Design filter using SciPy... self.b,self.a=butter(self.order,normalized_cutoff,btype='low')
Implementing compute_coefficients for a custom shelving filter:
>>> importnumpyasnp>>>>>> defcompute_coefficients(self):... '''Compute high-shelving filter coefficients using biquad formulas.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Calculate angular frequency... omega=2*np.pi*self.cutoff/self.fs... alpha=np.sin(omega)/(2*self.q)... A=self.gain# Linear gain... # Compute biquad coefficients... b0=A*((A+1)+(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha)... b1=-2*A*((A-1)+(A+1)*np.cos(omega))... b2=A*((A+1)+(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha)... a0=(A+1)-(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha... a1=2*((A-1)-(A+1)*np.cos(omega))... a2=(A+1)-(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha... # Normalize by a0 and store... self.b=[b0/a0,b1/a0,b2/a0]... self.a=[1.0,a1/a0,a2/a0]
Implementing compute_coefficients for an FIR filter:
>>> fromscipy.signalimportfirwin>>>>>> defcompute_coefficients(self):... '''Compute FIR filter coefficients using window method.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Use scipy firwin to design FIR filter... self.b=firwin(self.num_taps,self.cutoff,fs=self.fs,... pass_zero=self.pass_zero,window=self.window)... self.a=[1.0]# FIR filters have trivial denominator
Return type:
None
classtorchfx.filter.AllPass(cutoff, q, fs=None)[source]#
All-pass filter for phase manipulation without magnitude change.
An all-pass filter passes all frequencies with unity magnitude (no amplitude
change) but introduces frequency-dependent phase shift. These filters are
primarily used for phase correction, creating reverb/delay effects, and
designing crossover networks with linear phase response.
While the magnitude response is flat across all frequencies, the phase response
varies with frequency, which can be used to align phase between different signal
paths or create time-domain dispersion effects.
Parameters:
cutoff (float) – Center frequency in Hz where maximum phase shift occurs. The phase shift
is frequency-dependent, with maximum shift at this frequency.
q (float) – Quality factor controlling the rate of phase change. Higher Q values result
in more rapid phase transitions around the center frequency.
fs (int | None, default=None) – Sampling frequency in Hz.
Crossover networks: Aligning phase between drivers
Reverb design: Creating dense reflections and diffusion
Effect chains: Adding subtle movement and depth
Room correction: Compensating for acoustic phase distortions
Implementation note: This uses scipy.signal.iirpeak which creates a peaking
filter structure. For a true all-pass filter, ensure the gain is set appropriately
(typically handled internally).
Caution: While all-pass filters don’t change magnitude, they do affect the time-
domain response and can introduce pre-ringing or post-ringing artifacts. Use
judiciously in critical listening applications.
Compute the filter’s transfer function coefficients.
This abstract method must be implemented by all AbstractFilter subclasses.
It is responsible for computing the filter’s transfer function coefficients
(typically the numerator b and denominator a coefficients) and storing
them in instance attributes.
The method is called automatically during the first forward() pass when
the _has_computed_coeff property returns False. It should not be called
directly by users in typical usage scenarios.
Verify sampling frequency: Check that self.fs is not None before
attempting to compute coefficients. Raise a ValueError if it is None.
Design filter coefficients: Use SciPy signal processing functions
(e.g., scipy.signal.butter, scipy.signal.cheby1, scipy.signal.firwin)
or custom algorithms to compute filter coefficients based on the filter’s
parameters (cutoff frequency, order, Q factor, etc.).
Store coefficients: Save the computed coefficients in instance attributes
(typically self.a for denominator and self.b for numerator).
Normalize frequencies: For filters that use normalized frequencies
(most SciPy functions), normalize cutoff frequencies by the Nyquist
frequency (fs/2).
Notes
For IIR filters, both a (denominator) and b (numerator) coefficients
are typically stored as sequences or arrays.
For FIR filters, only b (numerator) coefficients are needed, with a
set to [1.0].
Coefficients are initially stored as Python sequences or NumPy arrays and
are later converted to PyTorch tensors during the forward() pass.
This method should be deterministic - calling it multiple times with the
same parameters should produce identical coefficients.
raises ValueError:
If required parameters (especially fs) are not set before coefficient
computation is attempted.
Examples
Implementing compute_coefficients for a Butterworth lowpass filter:
>>> fromscipy.signalimportbutter>>>>>> defcompute_coefficients(self):... '''Compute Butterworth lowpass coefficients.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Normalize cutoff to Nyquist frequency... nyquist=0.5*self.fs... normalized_cutoff=self.cutoff/nyquist... # Design filter using SciPy... self.b,self.a=butter(self.order,normalized_cutoff,btype='low')
Implementing compute_coefficients for a custom shelving filter:
>>> importnumpyasnp>>>>>> defcompute_coefficients(self):... '''Compute high-shelving filter coefficients using biquad formulas.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Calculate angular frequency... omega=2*np.pi*self.cutoff/self.fs... alpha=np.sin(omega)/(2*self.q)... A=self.gain# Linear gain... # Compute biquad coefficients... b0=A*((A+1)+(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha)... b1=-2*A*((A-1)+(A+1)*np.cos(omega))... b2=A*((A+1)+(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha)... a0=(A+1)-(A-1)*np.cos(omega)+2*np.sqrt(A)*alpha... a1=2*((A-1)-(A+1)*np.cos(omega))... a2=(A+1)-(A-1)*np.cos(omega)-2*np.sqrt(A)*alpha... # Normalize by a0 and store... self.b=[b0/a0,b1/a0,b2/a0]... self.a=[1.0,a1/a0,a2/a0]
Implementing compute_coefficients for an FIR filter:
>>> fromscipy.signalimportfirwin>>>>>> defcompute_coefficients(self):... '''Compute FIR filter coefficients using window method.'''... ifself.fsisNone:... raiseValueError("Sampling frequency must be set")... # Use scipy firwin to design FIR filter... self.b=firwin(self.num_taps,self.cutoff,fs=self.fs,... pass_zero=self.pass_zero,window=self.window)... self.a=[1.0]# FIR filters have trivial denominator