Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
tpt_filter.hpp
Go to the documentation of this file.
1#pragma once
2
6
8
9#include <algorithm>
10#include <cmath>
11#include <cstddef>
12
13namespace pulp::signal {
14
30template <typename SampleType = float> class TptFilterT {
31 public:
32 TptFilterT() = default;
33
34 static constexpr SampleType kMinCutoffHz = SampleType{1};
35 static constexpr SampleType kMaxCutoffFraction = SampleType{0.49};
36
39 static constexpr SampleType kMinSampleRate = SampleType{4};
40
41 void prepare(SampleType sample_rate) {
42 sample_rate_ = std::isfinite(sample_rate) && sample_rate >= kMinSampleRate ? sample_rate
44 cutoff_ = std::clamp(cutoff_, kMinCutoffHz, sample_rate_ * kMaxCutoffFraction);
45 update_coefficient();
46 }
47
49 void set_cutoff(SampleType hz) {
50 if (std::isnan(hz))
51 hz = cutoff_;
52 cutoff_ = std::clamp(hz, kMinCutoffHz, sample_rate_ * kMaxCutoffFraction);
53 update_coefficient();
54 }
55
56 SampleType cutoff() const {
57 return cutoff_;
58 }
59 SampleType sample_rate() const {
60 return sample_rate_;
61 }
62
68 SampleType process_lowpass(SampleType input) {
69 const SampleType d = input - state_;
70 const SampleType lp = g_ * d + state_;
71 // Snap the integrator state where subnormals are slow (see
72 // snap_recursion_state()). No-op above 1e-15.
73 state_ = snap_recursion_state(two_g_ * d + state_);
74 return lp;
75 }
76
78 SampleType process_highpass(SampleType input) {
79 return input - process_lowpass(input);
80 }
81
83 SampleType process_allpass(SampleType input) {
84 SampleType lp = process_lowpass(input);
85 return SampleType{2.0f} * lp - input;
86 }
87
98 void process_lowpass(SampleType* buffer, std::size_t num_samples) {
99 run_block(buffer, num_samples, [](SampleType, SampleType lp) { return lp; });
100 }
101
103 void process_highpass(SampleType* buffer, std::size_t num_samples) {
104 run_block(buffer, num_samples, [](SampleType in, SampleType lp) { return in - lp; });
105 }
106
108 void process_allpass(SampleType* buffer, std::size_t num_samples) {
109 run_block(buffer, num_samples,
110 [](SampleType in, SampleType lp) { return SampleType{2.0f} * lp - in; });
111 }
112
114 struct Outputs {
116 };
117
118 Outputs process(SampleType input) {
119 const SampleType d = input - state_;
120 const SampleType lp = g_ * d + state_;
121 state_ = snap_recursion_state(two_g_ * d + state_); // see process_lowpass()
122 SampleType hp = input - lp;
123 SampleType ap = lp - hp; // = 2*lp - input
124 return {lp, hp, ap};
125 }
126
127 void reset() {
128 state_ = SampleType{0.0f};
129 }
130
131 private:
132 // The state sits apart from the coefficients the per-sample path reads.
133 // When the caller's output pointer may alias the filter, every call reloads
134 // the coefficients and the state from memory; next to a coefficient, the
135 // state was fetched with it in one paired load that overlapped the
136 // previous call's state store, which cannot be store-forwarded, so each
137 // call waited for that store to complete.
138 SampleType state_ = SampleType{0.0f};
139 SampleType sample_rate_ = SampleType{44100.0f};
140 SampleType cutoff_ = SampleType{1000.0f};
141 SampleType g_ = SampleType{0.0f};
142 SampleType two_g_ = SampleType{0.0f};
143
144 void update_coefficient() {
145 constexpr SampleType pi = SampleType{3.14159265358979323846};
146 SampleType wd = SampleType{2.0f} * pi * cutoff_;
147 SampleType wa =
148 (SampleType{2.0f} * sample_rate_) * std::tan(wd / (SampleType{2.0f} * sample_rate_));
149 g_ = wa / (SampleType{2.0f} * sample_rate_ + wa);
150 two_g_ = SampleType{2.0f} * g_;
151 }
152
153 template <typename Output>
154 void run_block(SampleType* buffer, std::size_t num_samples, Output output) {
155 const SampleType g = g_;
156 const SampleType two_g = two_g_;
157 SampleType s = state_;
158 for (const auto [begin, end] : snap_chunks(num_samples)) {
159 for (std::size_t i = begin; i < end; ++i) {
160 const SampleType input = buffer[i];
161 const SampleType d = input - s;
162 const SampleType lp = g * d + s;
163 s = two_g * d + s;
164 buffer[i] = output(input, lp);
165 }
166 s = snap_to_zero(s);
167 }
168 state_ = s;
169 }
170};
171
174
175} // namespace pulp::signal
First-order TPT (trapezoidal integration) filter.
Definition tpt_filter.hpp:30
Outputs process(SampleType input)
Definition tpt_filter.hpp:118
static constexpr float kMaxCutoffFraction
Definition tpt_filter.hpp:35
SampleType process_highpass(SampleType input)
Process and return the highpass output.
Definition tpt_filter.hpp:78
void reset()
Definition tpt_filter.hpp:127
SampleType process_allpass(SampleType input)
Process and return the allpass output.
Definition tpt_filter.hpp:83
SampleType process_lowpass(SampleType input)
Process and return the lowpass output.
Definition tpt_filter.hpp:68
SampleType cutoff() const
Definition tpt_filter.hpp:56
void process_highpass(SampleType *buffer, std::size_t num_samples)
Process a buffer in place, replacing it with the highpass output.
Definition tpt_filter.hpp:103
void prepare(SampleType sample_rate)
Definition tpt_filter.hpp:41
static constexpr float kMinCutoffHz
Definition tpt_filter.hpp:34
static constexpr float kMinSampleRate
Definition tpt_filter.hpp:39
void process_allpass(SampleType *buffer, std::size_t num_samples)
Process a buffer in place, replacing it with the allpass output.
Definition tpt_filter.hpp:108
float sample_rate() const
Definition tpt_filter.hpp:59
void process_lowpass(SampleType *buffer, std::size_t num_samples)
Process a buffer in place, replacing it with the lowpass output.
Definition tpt_filter.hpp:98
void set_cutoff(SampleType hz)
Set cutoff frequency in Hz. Safe to modulate per-sample.
Definition tpt_filter.hpp:49
@ end
Definition editor_host.hpp:354
@ d
Definition pitch.hpp:22
@ g
Definition pitch.hpp:27
constexpr double pi
Definition fir_design.hpp:164
Definition additive_bank.hpp:218
@ buffer
A borrowed static buffer with a traversable playhead.
Definition granular.hpp:110
TptFilterT< float > TptFilter
Definition tpt_filter.hpp:172
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
TptFilterT< double > TptFilter64
Definition tpt_filter.hpp:173
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
@ s
Definition input_events.hpp:41
Process and return all three outputs at once.
Definition tpt_filter.hpp:114
SampleType allpass
Definition tpt_filter.hpp:115
SampleType lowpass
Definition tpt_filter.hpp:115
SampleType highpass
Definition tpt_filter.hpp:115