Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
layers.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <pulp/signal/svf.hpp>
7
8#include <algorithm>
9#include <cmath>
10
11namespace pulp::signal::drum {
12
32public:
33 void prepare(double sample_rate) {
34 sample_rate_ = sample_rate;
35 envelope_.set_sample_rate(sample_rate);
36 filter_.prepare(static_cast<float>(sample_rate));
37 apply_cutoff();
38 reset();
39 }
40
41 void reset() {
42 envelope_.reset();
43 filter_.reset();
44 }
45
46 void set_level(double level) {
47 level_ = std::max(level, 0.0);
48 if (level_ == 0.0) reset();
49 }
50 void set_cutoff_hz(double hz) {
51 cutoff_hz_ = std::clamp(hz, 20.0, 20000.0);
52 apply_cutoff();
53 }
54 void set_decay_ms(double ms) {
55 decay_ms_ = std::clamp(ms, 0.05, 200.0);
56 envelope_.set_decay_time_constant_ms(decay_ms_);
57 }
58
59 double level() const { return level_; }
60 double cutoff_hz() const { return cutoff_hz_; }
61
64 void trigger(double brightness = 1.0) {
65 if (level_ <= 0.0) return;
66 brightness_ = std::max(brightness, 0.01);
67 apply_cutoff();
68 // The filter starts clean on every hit: a strike is a fresh event, and
69 // leaving state behind would make a hit depend on how long ago the
70 // previous one was.
71 filter_.reset();
72 envelope_.set_attack_ms(0.0);
73 envelope_.set_decay_time_constant_ms(decay_ms_);
74 envelope_.trigger();
75 }
76
82 bool is_active() const { return level_ > 0.0 && envelope_.is_active(); }
83
85 double process(double noise) {
86 if (!envelope_.is_active() || level_ <= 0.0) return 0.0;
87 const double filtered = filter_.process_lowpass(static_cast<float>(noise));
88 return filtered * envelope_.process() * level_;
89 }
90
91private:
92 void apply_cutoff() {
93 filter_.set_cutoff(static_cast<float>(
94 std::min(cutoff_hz_ * brightness_, 0.49 * sample_rate_)));
95 }
96
97 double sample_rate_ = 44100.0;
98 double level_ = 0.0;
99 double cutoff_hz_ = 4000.0;
100 double decay_ms_ = 2.0;
101 double brightness_ = 1.0;
102 DecayEnvelope64 envelope_;
103 TptFilter filter_;
104};
105
116public:
117 void prepare(double sample_rate) {
118 envelope_.set_sample_rate(sample_rate);
119 reset();
120 }
121
122 void reset() { envelope_.reset(); }
123
124 void set_level(double level) {
125 level_ = std::max(level, 0.0);
126 if (level_ == 0.0) reset();
127 }
128 void set_decay_ms(double ms) {
129 decay_ms_ = std::clamp(ms, 0.5, 4000.0);
130 envelope_.set_decay_t60_ms(decay_ms_);
131 }
132
133 double level() const { return level_; }
134
135 void trigger() {
136 if (level_ <= 0.0) return;
137 envelope_.set_attack_ms(0.2);
138 envelope_.set_decay_t60_ms(decay_ms_);
139 envelope_.trigger();
140 }
141
143 bool is_active() const { return level_ > 0.0 && envelope_.is_active(); }
144
147 double process(double coloured) {
148 if (!envelope_.is_active() || level_ <= 0.0) return 0.0;
149 return coloured * envelope_.process() * level_;
150 }
151
152private:
153 double level_ = 0.0;
154 double decay_ms_ = 60.0;
155 DecayEnvelope64 envelope_;
156};
157
166public:
167 void prepare(double sample_rate) {
168 sample_rate_ = sample_rate > 0.0 ? sample_rate : sample_rate_;
169 filter_.set_sample_rate(static_cast<float>(sample_rate_));
170 filter_.set_mode(Svf::Mode::bandpass);
171 apply();
172 reset();
173 }
174
175 void reset() {
176 filter_.reset();
177 ring_level_ = 0.0;
178 }
179
180 void set_level(double level) {
181 level_ = std::max(level, 0.0);
182 if (level_ == 0.0) reset();
183 }
184 void set_frequency_hz(double hz) {
185 frequency_hz_ = std::clamp(hz, 20.0, 20000.0);
186 apply();
187 }
188 void set_resonance(double q) {
189 resonance_ = std::clamp(q, 1.0, 30.0);
190 apply();
191 }
192
193 double level() const { return level_; }
194 double frequency_hz() const { return frequency_hz_; }
195 double resonance() const { return resonance_; }
196 bool is_ringing() const {
197 return level_ > 0.0 && ring_level_ > kSilenceLevel;
198 }
199
200 double process(double excitation) {
201 if (level_ <= 0.0) {
202 ring_level_ = 0.0;
203 return excitation;
204 }
205 const double resonance = static_cast<double>(
206 filter_.process(static_cast<float>(excitation)));
207 ring_level_ =
208 std::max(std::fabs(resonance), ring_level_ * kLevelDecay);
209 return excitation + level_ * resonance;
210 }
211
212private:
213 void apply() {
214 filter_.set_frequency(static_cast<float>(
215 std::min(frequency_hz_, 0.49 * sample_rate_)));
216 filter_.set_resonance(static_cast<float>(resonance_));
217 }
218
219 static constexpr double kSilenceLevel = 1.0e-6;
220 static constexpr double kLevelDecay = 0.999;
221
222 double sample_rate_ = 44100.0;
223 double level_ = 0.0;
224 double frequency_hz_ = 180.0;
225 double resonance_ = 12.0;
226 double ring_level_ = 0.0;
227 Svf filter_;
228};
229
239class SubLayer {
240public:
241 void prepare(double sample_rate) {
242 sample_rate_ = sample_rate > 0.0 ? sample_rate : sample_rate_;
243 reset();
244 }
245
246 void reset() { phase_ = 0.0; }
247
248 void set_level(double level) { level_ = std::max(level, 0.0); }
249 double level() const { return level_; }
250
251 void trigger() { phase_ = 0.0; }
252
254 double process(double body_hz, double body_envelope) {
255 if (level_ <= 0.0) return 0.0;
256 phase_ += 0.5 * body_hz / sample_rate_;
257 if (phase_ >= 1.0) phase_ -= std::floor(phase_);
258 return std::sin(2.0 * 3.14159265358979323846 * phase_) * body_envelope * level_;
259 }
260
261private:
262 double sample_rate_ = 44100.0;
263 double level_ = 0.0;
264 double phase_ = 0.0;
265};
266
267} // namespace pulp::signal::drum
void set_frequency(SampleType hz)
Definition svf.hpp:21
void set_resonance(SampleType q)
Definition svf.hpp:22
void set_cutoff(SampleType hz)
Set cutoff frequency in Hz. Safe to modulate per-sample.
Definition tpt_filter.hpp:49
The transient: a burst of noise through a lowpass, decaying in a few milliseconds.
Definition layers.hpp:31
void set_cutoff_hz(double hz)
Definition layers.hpp:50
void set_level(double level)
Definition layers.hpp:46
void trigger(double brightness=1.0)
Starts a click.
Definition layers.hpp:64
void reset()
Definition layers.hpp:41
double process(double noise)
Next sample. noise is a raw white sample from the voice's generator.
Definition layers.hpp:85
double level() const
Definition layers.hpp:59
double cutoff_hz() const
Definition layers.hpp:60
bool is_active() const
A silent layer is never active.
Definition layers.hpp:82
void prepare(double sample_rate)
Definition layers.hpp:33
void set_decay_ms(double ms)
Definition layers.hpp:54
The air a strike moves: a longer, coloured noise layer under the body.
Definition layers.hpp:115
void trigger()
Definition layers.hpp:135
double process(double coloured)
Next sample.
Definition layers.hpp:147
void prepare(double sample_rate)
Definition layers.hpp:117
void reset()
Definition layers.hpp:122
double level() const
Definition layers.hpp:133
bool is_active() const
A silent layer is never active – see the note on ClickLayer.
Definition layers.hpp:143
void set_decay_ms(double ms)
Definition layers.hpp:128
void set_level(double level)
Definition layers.hpp:124
A high-Q body resonance mixed back into an excitation path.
Definition layers.hpp:165
void set_resonance(double q)
Definition layers.hpp:188
void reset()
Definition layers.hpp:175
double resonance() const
Definition layers.hpp:195
void set_level(double level)
Definition layers.hpp:180
double level() const
Definition layers.hpp:193
double frequency_hz() const
Definition layers.hpp:194
void prepare(double sample_rate)
Definition layers.hpp:167
void set_frequency_hz(double hz)
Definition layers.hpp:184
bool is_ringing() const
Definition layers.hpp:196
double process(double excitation)
Definition layers.hpp:200
A sine an octave below the body, gated by the body's own envelope.
Definition layers.hpp:239
void set_level(double level)
Definition layers.hpp:248
void reset()
Definition layers.hpp:246
void trigger()
Definition layers.hpp:251
double level() const
Definition layers.hpp:249
void prepare(double sample_rate)
Definition layers.hpp:241
double process(double body_hz, double body_envelope)
Next sample at half of body_hz, scaled by the body's envelope.
Definition layers.hpp:254
Definition clap.hpp:13
SvfT< float > Svf
Definition svf.hpp:148
TptFilterT< float > TptFilter
Definition tpt_filter.hpp:172
DecayEnvelopeT< double > DecayEnvelope64
Definition decay_envelope.hpp:201
First-order topology-preserving transform (TPT) filter.