Skip to content

Band Splitter

Tier: Core | ComponentType: 22 | Params: 7

Multiband signal splitter using chained Linkwitz-Riley 4th-order crossover filters. Supports up to 8 bands with per-band gain, solo, and guaranteed flat recombination.

Overview

BandSplitter divides an audio signal into multiple frequency bands using a cascade of CrossoverFilter instances. Each crossover splits its input into a lowpass and highpass output at a specified frequency, using Linkwitz-Riley 4th-order (LR4) topology. Chaining \(N-1\) crossovers produces \(N\) bands.

The key property of LR4 crossovers is allpass summation: when the lowpass and highpass outputs are summed, the result is a perfect allpass filter (flat magnitude, linear phase rotation). This guarantees that recombining all bands produces a flat frequency response — no comb filtering, no cancellation, no level bumps at crossover frequencies.

Each band has independent gain control (in dB) and a solo function that mutes all other bands. In the TestHarness Signal Chain mode, BandSplitter nodes act as multiband processing hubs — the signal splits into frequency bands, each band passes through its own sub-chain of effects, then bands recombine.

File Locations

Path
Header Sources/FolioDSP/include/FolioDSP/Core/BandSplitter.h
Implementation Sources/FolioDSP/src/Core/BandSplitter.cpp
Tests Tests/FolioDSPTests/BandSplitterTests.swift
Bridge Sources/FolioDSPBridge/src/FolioDSPBridge.mm (BandSplitterBridge)

Parameters

Index Name Description Min Max Default Min Default Max Default Unit
0 Crossover 1 First crossover frequency 10.0 22050.0 20.0 20000.0 200.0 Hz
1 Crossover 2 Second crossover frequency 10.0 22050.0 20.0 20000.0 2000.0 Hz
2 Crossover 3 Third crossover frequency 10.0 22050.0 20.0 20000.0 8000.0 Hz
3 Crossover 4 Fourth crossover frequency 10.0 22050.0 20.0 20000.0 12000.0 Hz
4 Crossover 5 Fifth crossover frequency 10.0 22050.0 20.0 20000.0 15000.0 Hz
5 Crossover 6 Sixth crossover frequency 10.0 22050.0 20.0 20000.0 18000.0 Hz
6 Crossover 7 Seventh crossover frequency 10.0 22050.0 20.0 20000.0 20000.0 Hz

Processing Algorithm

The split() function divides the input into bands, applies per-band processing, and tracks levels for visualization.

1. Crossover Cascade

For \(N\) bands, \(N-1\) crossover filters are chained. Each CrossoverFilter uses two cascaded Butterworth biquad sections to achieve LR4 (24 dB/octave) slopes:

\[\text{crossover}[0].\text{process}(x) \to \text{band}[0] \;(\text{LP}),\; h_0 \;(\text{HP})\]
\[\text{crossover}[1].\text{process}(h_0) \to \text{band}[1] \;(\text{LP}),\; h_1 \;(\text{HP})\]
\[\vdots\]
\[\text{crossover}[N\!-\!2].\text{process}(h_{N-3}) \to \text{band}[N\!-\!2] \;(\text{LP}),\; \text{band}[N\!-\!1] \;(\text{HP})\]

The lowpass output of each crossover becomes a band, and the highpass output feeds the next crossover. The final highpass becomes the highest band.

2. LR4 Crossover Properties

Each CrossoverFilter implements a Linkwitz-Riley 4th-order crossover — two cascaded 2nd-order Butterworth filters:

\[H_{\text{LP}}(s) = H_{\text{BW}}^2(s), \quad H_{\text{HP}}(s) = (1 - H_{\text{BW}}(s))^2\]

At the crossover frequency:

\[|H_{\text{LP}}(f_c)| = |H_{\text{HP}}(f_c)| = -6 \;\text{dB}\]
\[H_{\text{LP}}(s) + H_{\text{HP}}(s) = \text{allpass (flat magnitude)}\]

The slope is 24 dB/octave, providing sharp band separation.

3. Per-Band Gain

Each band has an independent gain control in decibels, converted to linear:

\[\text{band}[i] = \text{band}[i] \times 10^{g_i / 20}\]

where \(g_i\) is the gain in dB for band \(i\).

4. Solo

When solo is active (set to a specific band index), all other bands are zeroed:

\[\text{band}[i] = \begin{cases} \text{band}[i] & \text{if } i = \text{solo} \\ 0 & \text{otherwise} \end{cases}\]

Solo is disabled when set to 0xFF (255).

5. Level Tracking

Per-band levels are tracked via exponential smoothing for display:

\[L_i[n] = L_i[n-1] + 0.01 \cdot (|\text{band}[i]| - L_i[n-1])\]

6. Recombination

To recombine the bands into a single output, simply sum all bands:

\[y = \sum_{i=0}^{N-1} \text{band}[i]\]

The LR4 allpass property guarantees this sum is flat when all gains are at 0 dB and no solo is active.

Core Equations

\[\text{split: } x \xrightarrow{\text{LR4}} [\text{band}_0, \text{band}_1, \ldots, \text{band}_{N-1}]\]
\[\text{band}_i' = \text{band}_i \times 10^{g_i/20}\]
\[y = \sum_{i} \text{band}_i'\]

Snapshot Fields

Field Type Range Unit Description
Num Bands Float 1–8 Current number of frequency bands
Crossovers Float[7] 10–22050 Hz Active crossover frequencies
Band Levels Float[8] 0–1 Smoothed amplitude level per band
Band Gains Float[8] -60–60 dB Gain setting per band
Band Solo Uint8 0–8 Solo'd band index (0xFF = no solo)

Implementation Notes

  • Crossover ordering is the caller's responsibility. Crossover frequencies should be in ascending order for meaningful band separation. The component does not sort or validate ordering.
  • Maximum 8 bands (7 crossovers). The MAX_BANDS and MAX_CROSSOVERS constants enforce this limit. All band arrays are fixed-size C arrays, not dynamically allocated.
  • Level tracking uses a simple exponential smoother with coefficient 0.01, giving a display-rate response suitable for bar-graph visualization.
  • Multiband processing in the TestHarness: the audio callback splits the signal, processes each band through its sub-chain, then recombines. Band outputs use a stack-allocated 8-float tuple for real-time safety.
  • combine() is a separate method that sums a band array. It does not apply gain or solo — those are applied during split().
  • No process() method in the traditional sense — BandSplitter uses split() to separate and combine() to recombine. The setParameter / getParameter interface only exposes crossover frequencies; band gains and solo are set via dedicated methods.
  • All crossover frequency parameters use ParamSmoother (smoothed = true) for zipper-free adjustment.
  • All parameters use std::atomic<float> for lock-free thread safety.
  • Snapshot emission is decimated to ~60 fps (every 735 samples at 44.1 kHz).

Equation Summary

split -> [lo..hi] -> per-band -> recombine