DSPark 1.6.1
Header-only audio DSP framework in pure C++20 — zero dependencies
Loading...
Searching...
No Matches
DCBlocker.h File Reference

Removes DC offset from audio signals with configurable filter order. More...

#include "../Core/DspMath.h"
#include "../Core/Biquad.h"
#include "../Core/AudioSpec.h"
#include "../Core/AudioBuffer.h"
#include "../Core/StateBlob.h"
#include <algorithm>
#include <array>
#include <atomic>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <numbers>
#include <vector>
Include dependency graph for DCBlocker.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

class  dspark::DCBlocker< T >
 DC blocking filter with configurable Butterworth order (1-10). More...
 
struct  dspark::DCBlocker< T >::OnePoleState
 
struct  dspark::DCBlocker< T >::SectionState
 
struct  dspark::DCBlocker< T >::Section
 Second-order section: b0 * (1 - z^-1)^2 / (1 + a1 z^-1 + a2 z^-2). More...
 

Namespaces

namespace  dspark
 Main namespace for the DSPark framework.
 

Detailed Description

Removes DC offset from audio signals with configurable filter order.

Order 1 uses a lightweight 1-pole high-pass filter (2 multiplies, 2 additions per sample). Orders 2-10 use cascaded Butterworth biquad high-pass stages with predefined Q values for maximally-flat passband response.

Numerical design (why the core is double regardless of the sample type): a DC blocker parks its poles a hair inside the unit circle - 1 - |z| is about pi*fc/(Q*fs), i.e. 1.2e-4 at 5 Hz / 192 kHz and 2.9e-5 at 768 kHz. The denominator evaluated at DC, 1 + a1 + a2 = (2 - 2*cos w0)/a0, is then a cancellation of terms of size 2 landing on 2.7e-8, well under the float resolution of the terms it is made of (1.2e-7). Measured on the float cascade this replaced, at 5 Hz: that sum rounded to 4.77e-7 at 44.1 kHz (6 % off) and to EXACTLY 0 at 192 kHz and 768 kHz, which puts a realised pole right ON the unit circle - from there every rounding error of the recursion is integrated forever instead of decaying and the "DC blocker" sources DC of its own: -0.72 measured at 768 kHz order 2 on a DC-free -6 dBFS tone (output peaking at 1.42), and a realised DC gain of -53.7 at order 4. In double the same cancellation carries an absolute error of about 2e-16, so the poles land within 1e-8 (relative) of the design instead of on the unit circle. Scalar double throughput matches float on x86-64 and ARM64, so the whole filter core runs in double and only the buffer I/O is T

  • measured slightly cheaper than the float cascade it replaces, because the per-channel state now lives in registers for the length of a block.

The DC zero is structural on top of that: the numerator of both topologies is applied as a difference of the input history ((1 - z^-1) for the 1-pole, (1 - z^-1)^2 for the biquads, which is the exact RBJ high-pass numerator since b1 = -2*b0 and b2 = b0). A constant input therefore drives the recursion with an exact zero in IEEE arithmetic, whatever the coefficients round to, so steady DC always decays to true zero.

Dependencies: Core/DspMath.h, Core/Biquad.h, Core/AudioSpec.h, Core/AudioBuffer.h, Core/StateBlob.h.

Threading: prepare() belongs to the setup thread; the processing calls and reset() to the audio thread. setOrder()/setCutoff() are safe from any thread (atomics, applied lazily at the top of the next processing call); non-finite cutoffs are ignored. getState()/setState() are setup/UI thread.

Definition in file DCBlocker.h.