Skip to content

CrossoverFilter

Tier: Core (Internal Utility) | No ComponentType | No Parameters

Linkwitz-Riley 4th-order (LR4) crossover filter using two cascaded Butterworth 2nd-order biquad sections per output.

Overview

CrossoverFilter splits an input signal into complementary low-frequency and high-frequency bands that sum back to the original signal with flat magnitude response. This is the fundamental building block of the BandSplitter, which chains multiple CrossoverFilters to create multiband processing paths.

The design uses the Linkwitz-Riley 4th-order topology: two cascaded 2nd-order Butterworth filters per output. The Butterworth sections each have \(Q = 1/\sqrt{2} \approx 0.707\), and cascading two of them produces a 4th-order (24 dB/octave) slope with the critical property that \(|H_\text{LP}|^2 + |H_\text{HP}|^2 = 1\) --- the low and high outputs sum to unity power at all frequencies, with no coloration or phase anomalies at the crossover point.

This is an internal utility with no ComponentType, no parameter interface, no snapshot, and no bridge class. It is configured and driven entirely by BandSplitter.

File Locations

Path
Header Sources/FolioDSP/include/FolioDSP/Core/CrossoverFilter.h
Implementation Sources/FolioDSP/src/Core/CrossoverFilter.cpp
Tests Tests/FolioDSPTests/CrossoverFilterTests.swift

API / Interface

namespace folio::dsp {

class CrossoverFilter {
public:
    CrossoverFilter() = default;

    /// Configure crossover frequency. Must be called before process().
    void setCrossoverFrequency(float freq, float sampleRate);

    /// Process one sample. Splits input into low and high outputs.
    void process(float input, float& lowOut, float& highOut);

    /// Reset all internal filter states.
    void reset();

private:
    Biquad lowpass1_, lowpass2_;    // Cascaded for low output
    Biquad highpass1_, highpass2_;  // Cascaded for high output
};

}

Algorithm

1. Butterworth Biquad Configuration

When setCrossoverFrequency(freq, sampleRate) is called, all four internal Biquad filters are configured at the same frequency with Butterworth Q:

\[Q_\text{BW} = \frac{1}{\sqrt{2}} \approx 0.7071\]
  • lowpass1_ and lowpass2_: both set to Lowpass type at frequency \(f_c\) with \(Q = Q_\text{BW}\)
  • highpass1_ and highpass2_: both set to Highpass type at frequency \(f_c\) with \(Q = Q_\text{BW}\)

Each Biquad implements the Audio EQ Cookbook lowpass/highpass using Direct Form II Transposed. The Butterworth Q produces a maximally-flat passband (no resonance peak).

2. Signal Processing

For each input sample, two cascaded filter chains produce the split outputs:

\[y_\text{low} = \text{LP}_2(\text{LP}_1(x))\]
\[y_\text{high} = \text{HP}_2(\text{HP}_1(x))\]

Each individual Biquad provides a 2nd-order (12 dB/octave) slope. Cascading two produces a 4th-order (24 dB/octave) slope.

3. Linkwitz-Riley Properties

The LR4 crossover has three essential properties:

Unity magnitude sum. At all frequencies:

\[|H_\text{LP}(f)|^2 + |H_\text{HP}(f)|^2 = 1\]

This means the recombined signal \(y_\text{low} + y_\text{high}\) has perfectly flat magnitude response. No frequency is boosted or attenuated by the splitting process.

-6 dB at crossover. At the crossover frequency \(f_c\):

\[|H_\text{LP}(f_c)| = |H_\text{HP}(f_c)| = -6\ \text{dB} \approx 0.5\]

Each band is at half amplitude at crossover. When summed, the total is 0 dB (not +3 dB as with a Butterworth crossover, which would produce a bump).

In-phase at crossover. Both outputs are in-phase at \(f_c\), so the summation is coherent. The LR4 topology produces an allpass phase response when the two outputs are summed:

\[H_\text{LP}(z) + H_\text{HP}(z) = A(z)\]

where \(A(z)\) is a 4th-order allpass function. The magnitude is exactly 1 at all frequencies; only the phase varies (which is inaudible for a single crossover).

4. Why LR4 (Not Butterworth or Bessel)

Property Butterworth Crossover LR4 Crossover
Magnitude at \(f_c\) -3 dB each, +3 dB summed -6 dB each, 0 dB summed
Sum flatness +3 dB bump at crossover Perfectly flat
Slope 12 dB/oct (2nd order) 24 dB/oct (4th order)
Phase at crossover 90 degree offset In-phase

The LR4 is the standard for multiband audio processing because it is the lowest-order topology that sums to unity with no magnitude ripple.

5. Transfer Function

A single 2nd-order Butterworth lowpass has the transfer function:

\[H_\text{LP}(s) = \frac{\omega_c^2}{s^2 + \frac{\omega_c}{Q_\text{BW}} s + \omega_c^2}\]

The LR4 low output is the square of this:

\[H_\text{LR4,LP}(s) = \left[H_\text{LP}(s)\right]^2\]

Similarly for highpass:

\[H_\text{LR4,HP}(s) = \left[H_\text{HP}(s)\right]^2\]

The key identity is:

\[H_\text{LR4,LP}(s) + H_\text{LR4,HP}(s) = A(s)\]

where \(A(s)\) is a 4th-order allpass.

Implementation Notes

  • Depends on Biquad: CrossoverFilter includes Biquad.h and uses four Biquad instances internally. The Biquad class provides the Audio EQ Cookbook coefficient computation and Direct Form II Transposed processing.
  • Real-time safe: process() is four biquad filter operations (8 multiplies, 8 adds, 4 state updates). No allocations, no branches beyond the biquad coefficient computation (which happens only in setCrossoverFrequency, not per-sample).
  • No interpolation on frequency changes: When setCrossoverFrequency is called, all four biquads update their coefficients immediately. BandSplitter is responsible for smoothing crossover frequency changes if needed.
  • Reset: Clears the internal state of all four biquads, producing silence on both outputs until new input arrives.
  • Frequency range: The crossover frequency should be between approximately 20 Hz and \(f_s/2\). The Biquad coefficient computation handles edge cases, but extreme values near Nyquist may produce numerical artifacts.

Used By

Component Usage
BandSplitter Chains \(N-1\) CrossoverFilters to split a signal into \(N\) frequency bands (up to 8 bands, 7 crossovers)