Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
svf.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <cmath>
6#include <cstddef>
7
8namespace pulp::signal {
9
10// State Variable Filter — Topology Preserving Transform (TPT) design.
11// RT contract: setters recompute scalar coefficients; process/reset allocate no
12// memory. Retune at block boundaries if coefficient continuity matters.
13// Provides simultaneous lowpass, highpass, bandpass, and notch outputs.
14// Numerically stable at all frequencies, no cramping at Nyquist.
15template <typename SampleType = float>
16class SvfT {
17public:
18 enum class Mode { lowpass, highpass, bandpass, notch };
19
20 void set_sample_rate(SampleType sr) { sample_rate_ = sr; update(); }
21 void set_frequency(SampleType hz) { freq_ = hz; update(); }
22 void set_resonance(SampleType q) { q_ = q; update(); }
23 void set_mode(Mode m) { mode_ = m; }
24
28 void configure(Mode mode, SampleType sample_rate, SampleType frequency,
29 SampleType resonance) {
30 mode_ = mode;
31 sample_rate_ = sample_rate;
32 freq_ = frequency;
33 q_ = resonance;
34 update();
35 }
36
40 struct Coefficients {
41 SampleType g, k, a1, a2, a3;
42 };
43
44 static Coefficients design(SampleType sample_rate, SampleType frequency, SampleType resonance) {
45 Coefficients c{};
46 c.g = std::tan(SampleType{3.14159265f} * frequency / sample_rate);
47 c.k = SampleType{1.0f} / resonance;
48 c.a1 = SampleType{1.0f} / (SampleType{1.0f} + c.g * (c.g + c.k));
49 c.a2 = c.g * c.a1;
50 c.a3 = c.g * c.a2;
51 return c;
52 }
53
54 SampleType process(SampleType input) {
55 SampleType v3 = input - ic2_;
56 SampleType v1 = a1_ * ic1_ + a2_ * v3;
57 SampleType v2 = ic2_ + a2_ * ic1_ + a3_ * v3;
58 // Snap the two integrator states where subnormals are slow (see
59 // snap_recursion_state()). No-op above 1e-15.
60 ic1_ = snap_recursion_state(SampleType{2.0f} * v1 - ic1_);
61 ic2_ = snap_recursion_state(SampleType{2.0f} * v2 - ic2_);
62
63 switch (mode_) {
64 case Mode::lowpass: return v2;
65 case Mode::highpass: return input - k_ * v1 - v2;
66 case Mode::bandpass: return v1;
67 case Mode::notch: return input - k_ * v1;
68 }
69 return v2;
70 }
71
79 void process(SampleType* buffer, int num_samples) {
80 switch (mode_) {
81 case Mode::lowpass:
82 run_block<Mode::lowpass>(buffer, num_samples);
83 return;
84 case Mode::highpass:
85 run_block<Mode::highpass>(buffer, num_samples);
86 return;
87 case Mode::bandpass:
88 run_block<Mode::bandpass>(buffer, num_samples);
89 return;
90 case Mode::notch:
91 run_block<Mode::notch>(buffer, num_samples);
92 return;
93 }
94 run_block<Mode::lowpass>(buffer, num_samples);
95 }
96
97 void reset() { ic1_ = 0; ic2_ = 0; }
98
99private:
100 SampleType sample_rate_ = SampleType{44100.0f};
101 SampleType freq_ = SampleType{1000.0f};
102 SampleType q_ = SampleType{0.707f};
103 Mode mode_ = Mode::lowpass;
104
105 SampleType g_ = 0, k_ = 0;
106 SampleType a1_ = 0, a2_ = 0, a3_ = 0;
107 SampleType ic1_ = 0, ic2_ = 0;
108
109 template <Mode M> void run_block(SampleType* buffer, int num_samples) {
110 const SampleType a1 = a1_, a2 = a2_, a3 = a3_, k = k_;
111 SampleType ic1 = ic1_, ic2 = ic2_;
112 if (num_samples <= 0)
113 return;
114 for (const auto [begin, end] : snap_chunks(static_cast<std::size_t>(num_samples))) {
115 for (std::size_t i = begin; i < end; ++i) {
116 const SampleType input = buffer[i];
117 const SampleType v3 = input - ic2;
118 const SampleType v1 = a1 * ic1 + a2 * v3;
119 const SampleType v2 = ic2 + a2 * ic1 + a3 * v3;
120 ic1 = SampleType{2.0f} * v1 - ic1;
121 ic2 = SampleType{2.0f} * v2 - ic2;
122 if constexpr (M == Mode::lowpass)
123 buffer[i] = v2;
124 else if constexpr (M == Mode::highpass)
125 buffer[i] = input - k * v1 - v2;
126 else if constexpr (M == Mode::bandpass)
127 buffer[i] = v1;
128 else
129 buffer[i] = input - k * v1;
130 }
131 ic1 = snap_to_zero(ic1);
132 ic2 = snap_to_zero(ic2);
133 }
134 ic1_ = ic1;
135 ic2_ = ic2;
136 }
137
138 void update() {
139 const Coefficients c = design(sample_rate_, freq_, q_);
140 g_ = c.g;
141 k_ = c.k;
142 a1_ = c.a1;
143 a2_ = c.a2;
144 a3_ = c.a3;
145 }
146};
147
150
151} // namespace pulp::signal
Definition svf.hpp:16
void reset()
Definition svf.hpp:97
Mode
Definition svf.hpp:18
@ notch
Definition svf.hpp:18
@ highpass
Definition svf.hpp:18
@ bandpass
Definition svf.hpp:18
@ lowpass
Definition svf.hpp:18
void set_frequency(SampleType hz)
Definition svf.hpp:21
void process(SampleType *buffer, int num_samples)
Process a buffer in place.
Definition svf.hpp:79
void set_resonance(SampleType q)
Definition svf.hpp:22
void set_mode(Mode m)
Definition svf.hpp:23
static Coefficients design(SampleType sample_rate, SampleType frequency, SampleType resonance)
Definition svf.hpp:44
void configure(Mode mode, SampleType sample_rate, SampleType frequency, SampleType resonance)
Install a coherent coefficient set in one prewarp.
Definition svf.hpp:28
SampleType process(SampleType input)
Definition svf.hpp:54
void set_sample_rate(SampleType sr)
Definition svf.hpp:20
@ end
Definition editor_host.hpp:354
Definition additive_bank.hpp:218
@ buffer
A borrowed static buffer with a traversable playhead.
Definition granular.hpp:110
SvfT< float > Svf
Definition svf.hpp:148
@ c
Definition spectrum_trace.hpp:35
@ v2
Medium vibrato.
Definition scanner_vibrato.hpp:24
@ v3
Full vibrato.
Definition scanner_vibrato.hpp:25
@ v1
Shallow vibrato — scanner output only.
Definition scanner_vibrato.hpp:23
T snap_to_zero(T x)
Returns 0 if |x| < snap_threshold, else x.
Definition denormal.hpp:45
@ i
Definition formant_filter_bank.hpp:20
SvfT< double > Svf64
Definition svf.hpp:149
constexpr SnapChunks snap_chunks(std::size_t num_samples) noexcept
Definition denormal.hpp:173
T snap_recursion_state(T x)
Snap a per-sample recursion's state: snap_to_zero() where subnormals are slow, the identity where kSu...
Definition denormal.hpp:84
@ k
Definition input_events.hpp:40
The TPT state-variable coefficients for a sample rate, cutoff and resonance (Q): g = tan(pi fc / fs),...
Definition svf.hpp:40
SampleType a3
Definition svf.hpp:41
SampleType a2
Definition svf.hpp:41
SampleType g
Definition svf.hpp:41
SampleType k
Definition svf.hpp:41
SampleType a1
Definition svf.hpp:41