Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
envelope.hpp
Go to the documentation of this file.
1#pragma once
2
43
46
47#include <algorithm>
48#include <cmath>
49#include <cstdint>
50
51namespace pulp::signal {
52
64
65namespace detail {
66
72 long long pos = 0;
73 long long length = 0;
74
75 void restart(long long len) {
76 pos = 0;
77 length = len;
78 }
79
80 float progress() const {
81 if (length <= 0) return 1.0f;
82 return static_cast<float>(pos) / static_cast<float>(length);
83 }
84
85 bool advance() { return ++pos >= length; }
86};
87
88inline long long envelope_ms_to_samples(double ms, double sample_rate) {
89 return static_cast<long long>(std::lround(std::max(0.0, ms) * 0.001 * sample_rate));
90}
91
99template <typename SampleType = float>
101public:
103
104 static constexpr int kMaxLoops = 128;
105
106 void prepare(double sample_rate) {
107 sample_rate_ = sample_rate > 0.0 ? sample_rate : 1.0;
108 update_lengths_();
109 reset();
110 }
111
112 void set_delay_ms(double ms) { delay_ms_ = std::max(0.0, ms); update_lengths_(); }
113 void set_attack_ms(double ms) { attack_ms_ = std::max(0.0, ms); update_lengths_(); }
114 void set_hold_ms(double ms) { hold_ms_ = std::max(0.0, ms); update_lengths_(); }
115 void set_decay_ms(double ms) { decay_ms_ = std::max(0.0, ms); update_lengths_(); }
116 void set_release_ms(double ms) { release_ms_ = std::max(0.0, ms); update_lengths_(); }
117
118 void set_attack_curve(float c) { attack_curve_ = std::clamp(c, -1.0f, 1.0f); }
119 void set_decay_curve(float c) { decay_curve_ = std::clamp(c, -1.0f, 1.0f); }
120 void set_release_curve(float c) { release_curve_ = std::clamp(c, -1.0f, 1.0f); }
121
122 void set_sustain(SampleType level) {
123 sustain_ = std::clamp(level, SampleType{0}, SampleType{1});
124 }
125
126 void set_has_sustain(bool on) { has_sustain_ = on; }
127 void set_gate_driven(bool on) { gate_driven_ = on; }
128
129 void set_loop(bool on, int count) {
130 loop_ = on;
131 loop_count_ = std::clamp(count, 0, kMaxLoops);
132 }
133
134 void reset() {
135 stage_ = Stage::idle;
136 level_ = SampleType{0};
137 peak_ = SampleType{1};
138 start_ = SampleType{0};
139 release_start_ = SampleType{0};
140 loops_done_ = 0;
141 clock_.restart(0);
142 }
143
144 void trigger(SampleType velocity) {
145 peak_ = std::clamp(velocity, SampleType{0}, SampleType{1});
146 loops_done_ = 0;
147 begin_shape_();
148 }
149
150 void release() {
151 if (!gate_driven_ || stage_ == Stage::idle || stage_ == Stage::release) return;
152 release_start_ = level_;
153 stage_ = Stage::release;
154 clock_.restart(release_samples_);
155 if (release_samples_ <= 0) {
156 // Straight to idle, not through finish_(): a release is the end of
157 // the note, and finish_() would loop a looping envelope — a
158 // note-off would start it over.
159 stage_ = Stage::idle;
160 level_ = SampleType{0};
161 clock_.restart(0);
162 }
163 }
164
165 SampleType next() {
166 level_ = level_at_();
167 advance_();
168 return level_;
169 }
170
171 SampleType current() const { return level_; }
172 Stage stage() const { return stage_; }
173 bool active() const { return stage_ != Stage::idle; }
174 int loops_completed() const { return loops_done_; }
175
176private:
177 void update_lengths_() {
178 delay_samples_ = envelope_ms_to_samples(delay_ms_, sample_rate_);
179 attack_samples_ = envelope_ms_to_samples(attack_ms_, sample_rate_);
180 hold_samples_ = envelope_ms_to_samples(hold_ms_, sample_rate_);
181 decay_samples_ = envelope_ms_to_samples(decay_ms_, sample_rate_);
182 release_samples_ = envelope_ms_to_samples(release_ms_, sample_rate_);
183 }
184
185 long long shape_length_() const {
186 return delay_samples_ + attack_samples_ + hold_samples_ + decay_samples_;
187 }
188
189 SampleType sustain_level_() const {
190 return has_sustain_ ? sustain_ * peak_ : SampleType{0};
191 }
192
193 void begin_shape_() {
194 start_ = level_;
195 enter_delay_();
196 }
197
198 void enter_delay_() {
199 if (delay_samples_ <= 0) {
200 enter_attack_();
201 return;
202 }
203 stage_ = Stage::delay;
204 clock_.restart(delay_samples_);
205 }
206
207 void enter_attack_() {
208 if (attack_samples_ <= 0) {
209 level_ = peak_;
210 enter_hold_();
211 return;
212 }
213 stage_ = Stage::attack;
214 clock_.restart(attack_samples_);
215 }
216
217 void enter_hold_() {
218 if (hold_samples_ <= 0) {
219 enter_decay_();
220 return;
221 }
222 stage_ = Stage::hold;
223 clock_.restart(hold_samples_);
224 }
225
226 void enter_decay_() {
227 if (decay_samples_ <= 0) {
228 level_ = sustain_level_();
229 enter_after_decay_();
230 return;
231 }
232 stage_ = Stage::decay;
233 clock_.restart(decay_samples_);
234 }
235
236 void enter_after_decay_() {
237 // Looping skips the sustain hold: an envelope that stops to wait for a
238 // note-off cannot also be a cycling modulation source.
239 if (has_sustain_ && !loop_) {
240 stage_ = Stage::sustain;
241 clock_.restart(0);
242 return;
243 }
244 finish_();
245 }
246
247 void finish_() {
248 // A shape with no length at all cannot be looped — that is an infinite
249 // loop, not a modulation source.
250 if (loop_ && shape_length_() > 0) {
251 ++loops_done_;
252 if (loop_count_ == 0 || loops_done_ < loop_count_) {
253 start_ = level_;
254 enter_delay_();
255 return;
256 }
257 }
258 stage_ = Stage::idle;
259 level_ = SampleType{0};
260 clock_.restart(0);
261 }
262
263 SampleType level_at_() const {
264 const float p = clock_.progress();
265 switch (stage_) {
266 case Stage::delay:
267 // A fresh trigger enters the delay at zero because `start_` is
268 // zero. A retrigger holds the level it captured instead:
269 // snapping to zero for the delay and then resuming the attack
270 // from `start_` would be two full-scale steps, which is the
271 // opposite of retrigger-from-current-level.
272 return start_;
273 case Stage::attack:
274 return start_ + (peak_ - start_)
275 * static_cast<SampleType>(curve_rise(p, attack_curve_));
276 case Stage::hold:
277 return peak_;
278 case Stage::decay: {
279 const SampleType floor_level = sustain_level_();
280 return floor_level + (peak_ - floor_level)
281 * static_cast<SampleType>(curve_fall(p, decay_curve_));
282 }
283 case Stage::sustain:
284 return sustain_level_();
285 case Stage::release:
286 return release_start_ * static_cast<SampleType>(curve_fall(p, release_curve_));
287 case Stage::idle:
288 default:
289 return SampleType{0};
290 }
291 }
292
293 void advance_() {
294 switch (stage_) {
295 case Stage::delay:
296 if (clock_.advance()) enter_attack_();
297 return;
298 case Stage::attack:
299 if (clock_.advance()) enter_hold_();
300 return;
301 case Stage::hold:
302 if (clock_.advance()) enter_decay_();
303 return;
304 case Stage::decay:
305 if (clock_.advance()) enter_after_decay_();
306 return;
307 case Stage::release:
308 if (clock_.advance()) {
309 stage_ = Stage::idle;
310 level_ = SampleType{0};
311 clock_.restart(0);
312 }
313 return;
314 case Stage::sustain:
315 case Stage::idle:
316 default:
317 return;
318 }
319 }
320
321 EnvelopeStageClock clock_{};
322 double sample_rate_ = 48000.0;
323 double delay_ms_ = 0.0;
324 double attack_ms_ = 10.0;
325 double hold_ms_ = 0.0;
326 double decay_ms_ = 100.0;
327 double release_ms_ = 200.0;
328 long long delay_samples_ = 0;
329 long long attack_samples_ = 480;
330 long long hold_samples_ = 0;
331 long long decay_samples_ = 4800;
332 long long release_samples_ = 9600;
333 SampleType level_ = SampleType{0};
334 SampleType peak_ = SampleType{1};
335 SampleType start_ = SampleType{0};
336 SampleType release_start_ = SampleType{0};
337 SampleType sustain_ = SampleType{0.7};
338 float attack_curve_ = 0.0f;
339 float decay_curve_ = 0.0f;
340 float release_curve_ = 0.0f;
341 int loop_count_ = 0;
342 int loops_done_ = 0;
343 Stage stage_ = Stage::idle;
344 bool has_sustain_ = true;
345 bool gate_driven_ = true;
346 bool loop_ = false;
347};
348
349} // namespace detail
350
359template <typename SampleType = float>
360class ArT {
361public:
362 void prepare(double sample_rate) {
363 engine_.set_has_sustain(true);
364 engine_.set_sustain(SampleType{1});
365 engine_.set_gate_driven(true);
366 engine_.set_hold_ms(0.0);
367 engine_.set_decay_ms(0.0);
368 engine_.prepare(sample_rate);
369 }
370
371 void set_attack_ms(double ms) { engine_.set_attack_ms(ms); }
372 void set_release_ms(double ms) { engine_.set_release_ms(ms); }
373 void set_attack_curve(float c) { engine_.set_attack_curve(c); }
374 void set_release_curve(float c) { engine_.set_release_curve(c); }
375
379 void set_curve(double c) {
380 const float curve = static_cast<float>(std::clamp(c, 0.0, 1.0));
381 engine_.set_attack_curve(-curve);
382 engine_.set_release_curve(curve);
383 }
384
387 void gate(bool on) {
388 if (on == gate_) return;
389 gate_ = on;
390 if (on) engine_.trigger(SampleType{1});
391 else engine_.release();
392 }
393
396 void gate_on() { gate(true); }
397 void gate_off() { gate(false); }
398
399 void reset() {
400 engine_.reset();
401 gate_ = false;
402 }
403
404 SampleType next() { return engine_.next(); }
405 SampleType current() const { return engine_.current(); }
406 SampleType value() const { return current(); }
407 bool active() const { return engine_.active(); }
408 EnvelopeStage stage() const { return engine_.stage(); }
409
410private:
412 bool gate_ = false;
413};
414
417
425template <typename SampleType = float>
426class AdT {
427public:
428 void prepare(double sample_rate) {
429 engine_.set_has_sustain(false);
430 engine_.set_gate_driven(false);
431 engine_.set_hold_ms(0.0);
432 engine_.prepare(sample_rate);
433 }
434
435 void set_attack_ms(double ms) { engine_.set_attack_ms(ms); }
436 void set_decay_ms(double ms) { engine_.set_decay_ms(ms); }
437 void set_attack_curve(float c) { engine_.set_attack_curve(c); }
438 void set_decay_curve(float c) { engine_.set_decay_curve(c); }
439
440 void set_curve(double c) {
441 const float curve = static_cast<float>(std::clamp(c, 0.0, 1.0));
442 engine_.set_attack_curve(-curve);
443 engine_.set_decay_curve(curve);
444 }
445
447 void set_loop(bool on, int count = 0) { engine_.set_loop(on, count); }
448
449 void trigger(SampleType velocity = SampleType{1}) { engine_.trigger(velocity); }
450 void gate_on() { trigger(); }
451 void gate_off() {}
452 void reset() { engine_.reset(); }
453
454 SampleType next() { return engine_.next(); }
455 SampleType current() const { return engine_.current(); }
456 SampleType value() const { return current(); }
457 bool active() const { return engine_.active(); }
458 int loops_completed() const { return engine_.loops_completed(); }
459 EnvelopeStage stage() const { return engine_.stage(); }
460
461private:
463};
464
467
475template <typename SampleType = float>
476class AhdT {
477public:
478 void prepare(double sample_rate) {
479 engine_.set_has_sustain(false);
480 engine_.set_gate_driven(false);
481 engine_.prepare(sample_rate);
482 }
483
484 void set_attack_ms(double ms) { engine_.set_attack_ms(ms); }
485 void set_hold_ms(double ms) { engine_.set_hold_ms(ms); }
486 void set_decay_ms(double ms) { engine_.set_decay_ms(ms); }
487 void set_attack_curve(float c) { engine_.set_attack_curve(c); }
488 void set_decay_curve(float c) { engine_.set_decay_curve(c); }
489 void set_curve(double c) {
490 const float curve = static_cast<float>(std::clamp(c, 0.0, 1.0));
491 engine_.set_attack_curve(-curve);
492 engine_.set_decay_curve(curve);
493 }
494 void set_loop(bool on, int count = 0) { engine_.set_loop(on, count); }
495
496 void trigger(SampleType velocity = SampleType{1}) { engine_.trigger(velocity); }
497 void gate_on() { trigger(); }
498 void gate_off() {}
499 void reset() { engine_.reset(); }
500
501 SampleType next() { return engine_.next(); }
502 SampleType current() const { return engine_.current(); }
503 SampleType value() const { return current(); }
504 bool active() const { return engine_.active(); }
505 int loops_completed() const { return engine_.loops_completed(); }
506 EnvelopeStage stage() const { return engine_.stage(); }
507
508private:
510};
511
514
528template <typename SampleType = float>
529class DahdsrT {
530public:
531 void prepare(double sample_rate) {
532 engine_.set_has_sustain(true);
533 engine_.set_gate_driven(true);
534 engine_.prepare(sample_rate);
535 }
536
537 void set_delay_ms(double ms) { engine_.set_delay_ms(ms); }
538 void set_attack_ms(double ms) { engine_.set_attack_ms(ms); }
539 void set_hold_ms(double ms) { engine_.set_hold_ms(ms); }
540 void set_decay_ms(double ms) { engine_.set_decay_ms(ms); }
541 void set_sustain(SampleType level) { engine_.set_sustain(level); }
542 void set_release_ms(double ms) { engine_.set_release_ms(ms); }
543
544 void set_attack_curve(float c) { engine_.set_attack_curve(c); }
545 void set_decay_curve(float c) { engine_.set_decay_curve(c); }
546 void set_release_curve(float c) { engine_.set_release_curve(c); }
547
548 void set_curve(double c) {
549 const float curve = static_cast<float>(std::clamp(c, 0.0, 1.0));
550 engine_.set_attack_curve(-curve);
551 engine_.set_decay_curve(curve);
552 engine_.set_release_curve(curve);
553 }
554
555 void set_loop(bool on, int count = 0) { engine_.set_loop(on, count); }
556
557 void note_on(SampleType velocity = SampleType{1}) { engine_.trigger(velocity); }
558 void note_off() { engine_.release(); }
559 void gate_on() { note_on(); }
560 void gate_off() { note_off(); }
561 void reset() { engine_.reset(); }
562
563 SampleType next() { return engine_.next(); }
564 SampleType current() const { return engine_.current(); }
565 SampleType value() const { return current(); }
566 bool active() const { return engine_.active(); }
567 int loops_completed() const { return engine_.loops_completed(); }
568 EnvelopeStage stage() const { return engine_.stage(); }
569
570private:
572};
573
576
585template <typename SampleType = float>
586class ModEnvT {
587public:
588 void prepare(double sample_rate) {
589 engine_.set_has_sustain(false);
590 engine_.set_gate_driven(false);
591 engine_.prepare(sample_rate);
592 }
593
594 void set_delay_ms(double ms) { engine_.set_delay_ms(ms); }
595 void set_attack_ms(double ms) { engine_.set_attack_ms(ms); }
596 void set_hold_ms(double ms) { engine_.set_hold_ms(ms); }
597 void set_decay_ms(double ms) { engine_.set_decay_ms(ms); }
598 void set_release_ms(double ms) { engine_.set_release_ms(ms); }
599 void set_sustain(SampleType level) { engine_.set_sustain(level); }
600 void set_attack_curve(float c) { engine_.set_attack_curve(c); }
601 void set_decay_curve(float c) { engine_.set_decay_curve(c); }
602 void set_curve(double c) {
603 const float curve = static_cast<float>(std::clamp(c, 0.0, 1.0));
604 engine_.set_attack_curve(-curve);
605 engine_.set_decay_curve(curve);
606 engine_.set_release_curve(curve);
607 }
608 void set_loop(bool on, int count = 0) { engine_.set_loop(on, count); }
609
611 void set_depth(SampleType d) { depth_ = std::clamp(d, SampleType{-1}, SampleType{1}); }
612
613 void trigger(SampleType velocity = SampleType{1}) {
614 legacy_scaled_next_ = false;
615 engine_.set_has_sustain(false);
616 engine_.set_gate_driven(false);
617 engine_.trigger(velocity);
618 }
619 void gate_on() {
620 legacy_scaled_next_ = true;
621 engine_.set_has_sustain(true);
622 engine_.set_gate_driven(true);
623 engine_.trigger(SampleType{1});
624 }
625 void gate_off() { engine_.release(); }
626 void reset() { engine_.reset(); }
627
629 SampleType next() {
630 const SampleType level = engine_.next();
631 return legacy_scaled_next_ ? level * depth_ : level;
632 }
633
636 SampleType modulation() const { return engine_.current() * depth_; }
637
638 SampleType current() const { return engine_.current(); }
639 SampleType value() const {
640 return legacy_scaled_next_ ? engine_.current() * depth_ : engine_.current();
641 }
642 bool active() const { return engine_.active(); }
643 EnvelopeStage stage() const { return engine_.stage(); }
644
645private:
647 SampleType depth_ = SampleType{1};
648 bool legacy_scaled_next_ = false;
649};
650
653
670template <typename SampleType = float>
672public:
674 static constexpr SampleType kDefaultFastMs = SampleType{2};
676 static constexpr SampleType kDefaultSlowMs = SampleType{40};
677
678 void prepare(SampleType sample_rate) {
679 // RMS rather than peak on both followers: a peak follower fast enough
680 // to sit on an attack also tracks the waveform ripple of any low tone,
681 // which reads as a permanent 20% transient on a held bass note. RMS
682 // averages the ripple out without slowing the attack response.
685 fast_.prepare(sample_rate);
686 slow_.prepare(sample_rate);
687 apply_times_();
688 }
689
690 void set_fast_ms(SampleType ms) {
691 fast_ms_ = std::max(SampleType{0.01}, ms);
692 apply_times_();
693 }
694
695 void set_slow_ms(SampleType ms) {
696 slow_ms_ = std::max(SampleType{0.01}, ms);
697 apply_times_();
698 }
699
700 void reset() {
701 fast_.reset();
702 slow_.reset();
703 }
704
705 SampleType process(SampleType input) {
706 const SampleType magnitude = std::abs(input);
707 const SampleType f = fast_.process(magnitude);
708 const SampleType s = slow_.process(magnitude);
709 // The floor only engages in near-silence, well below any level where a
710 // transient means anything, so it does not break scale invariance in
711 // the range the detector is actually used in.
712 const SampleType denominator = std::max(s, SampleType{1e-9});
713 return std::max(SampleType{0}, (f - s) / denominator);
714 }
715
716private:
717 void apply_times_() {
718 // Symmetric ballistics on both followers: the detector compares two
719 // time scales, and an asymmetric follower would make the comparison
720 // depend on whether the signal happens to be rising.
721 fast_.set_attack_ms(fast_ms_);
722 fast_.set_release_ms(fast_ms_);
723 slow_.set_attack_ms(slow_ms_);
724 slow_.set_release_ms(slow_ms_);
725 }
726
727 BallisticsFilterT<SampleType> fast_{};
728 BallisticsFilterT<SampleType> slow_{};
729 SampleType fast_ms_ = kDefaultFastMs;
730 SampleType slow_ms_ = kDefaultSlowMs;
731};
732
735
736} // namespace pulp::signal
Peak/RMS envelope follower with independent attack and release curves.
Attack-Decay one-shot, trigger-driven.
Definition envelope.hpp:426
SampleType value() const
Definition envelope.hpp:456
bool active() const
Definition envelope.hpp:457
SampleType next()
Definition envelope.hpp:454
void set_attack_ms(double ms)
Definition envelope.hpp:435
void set_curve(double c)
Definition envelope.hpp:440
void set_attack_curve(float c)
Definition envelope.hpp:437
void gate_on()
Definition envelope.hpp:450
void set_decay_ms(double ms)
Definition envelope.hpp:436
void trigger(SampleType velocity=SampleType{1})
Definition envelope.hpp:449
void reset()
Definition envelope.hpp:452
int loops_completed() const
Definition envelope.hpp:458
EnvelopeStage stage() const
Definition envelope.hpp:459
void set_loop(bool on, int count=0)
Definition envelope.hpp:447
SampleType current() const
Definition envelope.hpp:455
void set_decay_curve(float c)
Definition envelope.hpp:438
void prepare(double sample_rate)
Definition envelope.hpp:428
void gate_off()
Definition envelope.hpp:451
Attack-Hold-Decay one-shot, trigger-driven.
Definition envelope.hpp:476
SampleType current() const
Definition envelope.hpp:502
void reset()
Definition envelope.hpp:499
void set_curve(double c)
Definition envelope.hpp:489
void prepare(double sample_rate)
Definition envelope.hpp:478
void gate_off()
Definition envelope.hpp:498
void set_attack_curve(float c)
Definition envelope.hpp:487
void set_attack_ms(double ms)
Definition envelope.hpp:484
void gate_on()
Definition envelope.hpp:497
bool active() const
Definition envelope.hpp:504
SampleType next()
Definition envelope.hpp:501
void trigger(SampleType velocity=SampleType{1})
Definition envelope.hpp:496
SampleType value() const
Definition envelope.hpp:503
void set_loop(bool on, int count=0)
Definition envelope.hpp:494
EnvelopeStage stage() const
Definition envelope.hpp:506
void set_hold_ms(double ms)
Definition envelope.hpp:485
int loops_completed() const
Definition envelope.hpp:505
void set_decay_curve(float c)
Definition envelope.hpp:488
void set_decay_ms(double ms)
Definition envelope.hpp:486
Attack-Release, gate-driven.
Definition envelope.hpp:360
void set_attack_ms(double ms)
Definition envelope.hpp:371
void gate_on()
Convenience spelling with exactly the same level-before-advance sample ordering as gate(true).
Definition envelope.hpp:396
bool active() const
Definition envelope.hpp:407
void set_release_ms(double ms)
Definition envelope.hpp:372
SampleType next()
Definition envelope.hpp:404
SampleType current() const
Definition envelope.hpp:405
void reset()
Definition envelope.hpp:399
SampleType value() const
Definition envelope.hpp:406
void gate_off()
Definition envelope.hpp:397
EnvelopeStage stage() const
Definition envelope.hpp:408
void gate(bool on)
Gate edges drive the envelope; repeated calls with the same state are no-ops, so this can be called e...
Definition envelope.hpp:387
void prepare(double sample_rate)
Definition envelope.hpp:362
void set_release_curve(float c)
Definition envelope.hpp:374
void set_curve(double c)
Round-2 compatibility curve: 0 is linear and 1 is the strongest capacitor-like attack/release pair.
Definition envelope.hpp:379
void set_attack_curve(float c)
Definition envelope.hpp:373
void set_attack_ms(SampleType ms)
Definition ballistics_filter.hpp:46
void set_release_ms(SampleType ms)
Definition ballistics_filter.hpp:53
@ rms
Definition ballistics_filter.hpp:33
Delay-Attack-Hold-Decay-Sustain-Release with per-stage curves and velocity.
Definition envelope.hpp:529
void set_release_curve(float c)
Definition envelope.hpp:546
void set_decay_curve(float c)
Definition envelope.hpp:545
void gate_on()
Definition envelope.hpp:559
void reset()
Definition envelope.hpp:561
void set_hold_ms(double ms)
Definition envelope.hpp:539
EnvelopeStage stage() const
Definition envelope.hpp:568
void set_attack_ms(double ms)
Definition envelope.hpp:538
void set_sustain(SampleType level)
Definition envelope.hpp:541
void set_delay_ms(double ms)
Definition envelope.hpp:537
SampleType current() const
Definition envelope.hpp:564
void set_decay_ms(double ms)
Definition envelope.hpp:540
void set_release_ms(double ms)
Definition envelope.hpp:542
SampleType next()
Definition envelope.hpp:563
bool active() const
Definition envelope.hpp:566
void set_attack_curve(float c)
Definition envelope.hpp:544
void note_off()
Definition envelope.hpp:558
void prepare(double sample_rate)
Definition envelope.hpp:531
void set_curve(double c)
Definition envelope.hpp:548
int loops_completed() const
Definition envelope.hpp:567
void set_loop(bool on, int count=0)
Definition envelope.hpp:555
void gate_off()
Definition envelope.hpp:560
void note_on(SampleType velocity=SampleType{1})
Definition envelope.hpp:557
SampleType value() const
Definition envelope.hpp:565
Delay-Attack-Hold-Decay modulation envelope with a signed depth.
Definition envelope.hpp:586
void reset()
Definition envelope.hpp:626
void set_curve(double c)
Definition envelope.hpp:602
void set_depth(SampleType d)
Signed, -1..+1. Applied by modulation(), not by next().
Definition envelope.hpp:611
void set_decay_ms(double ms)
Definition envelope.hpp:597
void set_release_ms(double ms)
Definition envelope.hpp:598
void set_attack_ms(double ms)
Definition envelope.hpp:595
SampleType next()
Raw unipolar level, 0..1.
Definition envelope.hpp:629
SampleType modulation() const
The last next() scaled by the signed depth — the value to hand a modulation destination.
Definition envelope.hpp:636
bool active() const
Definition envelope.hpp:642
void set_loop(bool on, int count=0)
Definition envelope.hpp:608
void set_attack_curve(float c)
Definition envelope.hpp:600
void set_delay_ms(double ms)
Definition envelope.hpp:594
void set_sustain(SampleType level)
Definition envelope.hpp:599
void gate_on()
Definition envelope.hpp:619
SampleType value() const
Definition envelope.hpp:639
SampleType current() const
Definition envelope.hpp:638
void prepare(double sample_rate)
Definition envelope.hpp:588
void set_hold_ms(double ms)
Definition envelope.hpp:596
EnvelopeStage stage() const
Definition envelope.hpp:643
void set_decay_curve(float c)
Definition envelope.hpp:601
void gate_off()
Definition envelope.hpp:625
void trigger(SampleType velocity=SampleType{1})
Definition envelope.hpp:613
Level-independent transient detector: the normalized excess of a fast envelope follower over a slow o...
Definition envelope.hpp:671
static constexpr float kDefaultSlowMs
Definition envelope.hpp:676
void set_fast_ms(SampleType ms)
Definition envelope.hpp:690
void set_slow_ms(SampleType ms)
Definition envelope.hpp:695
void reset()
Definition envelope.hpp:700
static constexpr float kDefaultFastMs
Definition envelope.hpp:674
void prepare(SampleType sample_rate)
Definition envelope.hpp:678
SampleType process(SampleType input)
Definition envelope.hpp:705
The DAHDSR superset state machine.
Definition envelope.hpp:100
void set_attack_curve(float c)
Definition envelope.hpp:118
void set_hold_ms(double ms)
Definition envelope.hpp:114
int loops_completed() const
Definition envelope.hpp:174
void set_loop(bool on, int count)
Definition envelope.hpp:129
void set_delay_ms(double ms)
Definition envelope.hpp:112
Stage stage() const
Definition envelope.hpp:172
SampleType current() const
Definition envelope.hpp:171
void trigger(SampleType velocity)
Definition envelope.hpp:144
void set_release_ms(double ms)
Definition envelope.hpp:116
void set_has_sustain(bool on)
Definition envelope.hpp:126
void set_release_curve(float c)
Definition envelope.hpp:120
static constexpr int kMaxLoops
Definition envelope.hpp:104
EnvelopeStage Stage
Definition envelope.hpp:102
void set_decay_ms(double ms)
Definition envelope.hpp:115
void release()
Definition envelope.hpp:150
void set_gate_driven(bool on)
Definition envelope.hpp:127
SampleType next()
Definition envelope.hpp:165
void set_sustain(SampleType level)
Definition envelope.hpp:122
void prepare(double sample_rate)
Definition envelope.hpp:106
void set_decay_curve(float c)
Definition envelope.hpp:119
bool active() const
Definition envelope.hpp:173
void reset()
Definition envelope.hpp:134
void set_attack_ms(double ms)
Definition envelope.hpp:113
The small modular utilities every patch reaches for between a modulation source and its destination: ...
long long envelope_ms_to_samples(double ms, double sample_rate)
Definition envelope.hpp:88
Definition additive_bank.hpp:218
float curve_fall(float p, float curve)
Shape a falling stage: 1 -> 0 over p.
Definition mod_tools.hpp:70
ModEnvT< float > ModEnv
Definition envelope.hpp:651
@ c
Definition spectrum_trace.hpp:35
EnvelopeStage
Stage of any envelope in this header.
Definition envelope.hpp:55
@ sustain
Definition envelope.hpp:61
@ release
Definition envelope.hpp:62
@ decay
Definition envelope.hpp:60
@ delay
Definition envelope.hpp:57
@ hold
Definition envelope.hpp:59
@ idle
Definition envelope.hpp:56
@ attack
Definition envelope.hpp:58
AdT< double > Ad64
Definition envelope.hpp:466
ModEnvT< double > ModEnv64
Definition envelope.hpp:652
AhdT< float > Ahd
Definition envelope.hpp:512
DahdsrT< double > Dahdsr64
Definition envelope.hpp:575
float curve_rise(float p, float curve)
Shape a rising stage: 0 -> 1 over p, with curve in [-1, +1].
Definition mod_tools.hpp:65
@ e
Definition formant_filter_bank.hpp:20
ArT< double > Ar64
Definition envelope.hpp:416
AhdT< double > Ahd64
Definition envelope.hpp:513
TransientDetectorT< float > TransientDetector
Definition envelope.hpp:733
AdT< float > Ad
Definition envelope.hpp:465
TransientDetectorT< double > TransientDetector64
Definition envelope.hpp:734
ArT< float > Ar
Definition envelope.hpp:415
DahdsrT< float > Dahdsr
Definition envelope.hpp:574
@ p
Definition input_events.hpp:40
Sample counter for one envelope stage.
Definition envelope.hpp:71
bool advance()
Definition envelope.hpp:85
float progress() const
Definition envelope.hpp:80
long long pos
Definition envelope.hpp:72
long long length
Definition envelope.hpp:73
void restart(long long len)
Definition envelope.hpp:75