Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
ladder_bank.hpp
Go to the documentation of this file.
1#pragma once
2
5
8
9#include <algorithm>
10#include <cstddef>
11#include <iterator>
12#include <type_traits>
13
14namespace pulp::signal {
15
49template <typename SampleType = float, std::size_t Voices = 16> class LadderBankT {
50 static_assert(Voices > 0, "LadderBankT needs at least one voice");
51
52 public:
54 static constexpr std::size_t voice_count = Voices;
55
57 std::fill(std::begin(cutoff_), std::end(cutoff_), SampleType{1000.0f});
58 }
59
62 void set_sample_rate(SampleType sample_rate) {
63 sample_rate_ = sample_rate;
64 for (std::size_t v = 0; v < Voices; ++v)
65 update(v);
66 }
67
68 SampleType sample_rate() const {
69 return sample_rate_;
70 }
71
72 void set_frequency(std::size_t voice, SampleType hz) {
73 if (voice >= Voices)
74 return;
75 cutoff_[voice] = hz;
76 update(voice);
77 }
78
79 void set_resonance(std::size_t voice, SampleType resonance) {
80 if (voice >= Voices)
81 return;
82 resonance_[voice] = std::clamp(resonance, SampleType{0.0f}, SampleType{1.0f});
83 update(voice);
84 }
85
86 SampleType frequency(std::size_t voice) const {
87 return voice < Voices ? cutoff_[voice] : SampleType{};
88 }
89 SampleType resonance(std::size_t voice) const {
90 return voice < Voices ? resonance_[voice] : SampleType{};
91 }
92
94 SampleType stage_coefficient(std::size_t voice) const {
95 return voice < Voices ? g_[voice] : SampleType{};
96 }
97
100 void process(const SampleType* in, SampleType* out) {
101 SampleType prev[Voices];
102 for (std::size_t v = 0; v < Voices; ++v) {
103 const SampleType input = in[v];
104 const SampleType feedback =
105 resonance_[v] * SampleType{4.0f} * (stage_[3][v] - input * SampleType{0.5f});
106 prev[v] = saturate(input - feedback);
107 }
108 for (int i = 0; i < 4; ++i) {
109 SampleType* stage = stage_[i];
110 SampleType* saturated = saturated_stage_[i];
111 for (std::size_t v = 0; v < Voices; ++v) {
112 stage[v] = detail::snap_to_zero_bits(stage[v] + g_[v] * (prev[v] - saturated[v]));
113 saturated[v] = saturate(stage[v]);
114 prev[v] = saturated[v];
115 }
116 }
117 for (std::size_t v = 0; v < Voices; ++v)
118 out[v] = stage_[3][v];
119 }
120
123 void process_voices(SampleType* const* buffers, std::size_t num_samples) {
124 SampleType frame[Voices];
125 for (std::size_t n = 0; n < num_samples; ++n) {
126 for (std::size_t v = 0; v < Voices; ++v)
127 frame[v] = buffers[v][n];
128 process(frame, frame);
129 for (std::size_t v = 0; v < Voices; ++v)
130 buffers[v][n] = frame[v];
131 }
132 }
133
135 void reset(std::size_t voice) {
136 if (voice >= Voices)
137 return;
138 for (int i = 0; i < 4; ++i) {
139 stage_[i][voice] = SampleType{0};
140 saturated_stage_[i][voice] = SampleType{0};
141 }
142 }
143
145 void reset() {
146 for (std::size_t v = 0; v < Voices; ++v)
147 reset(v);
148 }
149
150 private:
151 // The scalar filter's saturator; for the accurate float saturator, its
152 // bit-operation form, so the per-stage loop over voices vectorizes under
153 // GCC as well as Clang (same values).
154 static SampleType saturate(SampleType x) {
155 if constexpr (std::is_same_v<SampleType, float> &&
158 else
159 return Filter::template saturate<kDefaultLadderSaturation>(x);
160 }
161
162 void update(std::size_t voice) {
163 g_[voice] = Filter::stage_coefficient(cutoff_[voice], sample_rate_);
164 }
165
166 SampleType sample_rate_ = SampleType{44100.0f};
167 SampleType cutoff_[Voices];
168 SampleType resonance_[Voices] = {};
169 SampleType g_[Voices] = {};
170 SampleType stage_[4][Voices] = {};
171 SampleType saturated_stage_[4][Voices] = {};
172};
173
174template <std::size_t Voices = 16> using LadderBank = LadderBankT<float, Voices>;
175
176} // namespace pulp::signal
Voices independent LadderFilterT voices advanced together.
Definition ladder_bank.hpp:49
LadderFilterT< SampleType > Filter
Definition ladder_bank.hpp:53
void process(const SampleType *in, SampleType *out)
Advance every voice by one sample: out[v] = voice v's output for in[v].
Definition ladder_bank.hpp:100
static constexpr std::size_t voice_count
Definition ladder_bank.hpp:54
void set_sample_rate(SampleType sample_rate)
Set the sample rate every voice runs at, and recompute every voice's coefficient for its current freq...
Definition ladder_bank.hpp:62
float resonance(std::size_t voice) const
Definition ladder_bank.hpp:89
float sample_rate() const
Definition ladder_bank.hpp:68
void set_frequency(std::size_t voice, SampleType hz)
Definition ladder_bank.hpp:72
void set_resonance(std::size_t voice, SampleType resonance)
Definition ladder_bank.hpp:79
SampleType frequency(std::size_t voice) const
Definition ladder_bank.hpp:86
void reset()
Clear every voice's state.
Definition ladder_bank.hpp:145
void reset(std::size_t voice)
Clear one voice's state.
Definition ladder_bank.hpp:135
void process_voices(SampleType *const *buffers, std::size_t num_samples)
Advance every voice by num_samples samples in place, one buffer per voice (buffers[v] holds voice v's...
Definition ladder_bank.hpp:123
SampleType stage_coefficient(std::size_t voice) const
The voice's per-stage one-pole coefficient (Filter::stage_coefficient()).
Definition ladder_bank.hpp:94
LadderBankT()
Definition ladder_bank.hpp:56
Definition ladder_filter.hpp:25
static SampleType stage_coefficient(SampleType cutoff_hz, SampleType sample_rate)
Definition ladder_filter.hpp:75
@ Voices
Definition prepare_resources.hpp:26
float tanh_accurate_bits(float x) noexcept
FastMath::tanh_accurate() with its clamps as bit operations; the same value for every input.
Definition fast_math.hpp:743
T snap_to_zero_bits(T x) noexcept
snap_to_zero() as bit operations (see select_bits()).
Definition fast_math.hpp:733
Definition additive_bank.hpp:218
@ feedback
Definition comb_filter.hpp:18
LadderBankT< float, Voices > LadderBank
Definition ladder_bank.hpp:174
@ i
Definition formant_filter_bank.hpp:20
@ accurate
Definition fast_math.hpp:630
constexpr LadderSaturation kDefaultLadderSaturation
Selecting the default ladder saturator at build time:
Definition fast_math.hpp:657