38template <
typename SampleType =
float>
42 if (!std::isfinite(
high) || !std::isfinite(low))
return;
43 high_threshold_ =
high;
44 low_threshold_ = std::min(low,
high - 1
e-9);
50 const double x =
static_cast<double>(input);
51 if (armed_ && x >= high_threshold_) {
55 if (!armed_ && x <= low_threshold_) armed_ =
true;
60 const bool was_high = !armed_;
65 bool high()
const {
return !armed_; }
89template <
typename SampleType =
float>
98 sample_rate_ = sample_rate > 0.0 ? sample_rate : 1.0;
104 refractory_ms_ = std::max(0.0, ms);
105 update_refractory_();
119 if (countdown_ > 0) --countdown_;
120 const bool rising = gate && !prev_;
122 if (!
rising || countdown_ > 0)
return false;
123 countdown_ = refractory_samples_;
130 template <
typename Value>
131 requires(!std::same_as<std::remove_cvref_t<Value>,
bool>)
140 const bool was_high = prev_;
145 bool armed()
const {
return countdown_ == 0; }
148 void update_refractory_() {
149 refractory_samples_ =
150 static_cast<long long>(std::lround(refractory_ms_ * 0.001 * sample_rate_));
153 double sample_rate_ = 48000.0;
155 long long refractory_samples_ = 48;
156 long long countdown_ = 0;
157 SampleType threshold_ = SampleType{0.5};
174template <
typename SampleType =
float>
192 sample_rate_ = sample_rate > 0.0 ? sample_rate : 1.0;
209 length_samples_ =
static_cast<long long>(std::lround(std::max(0.0, samples)));
210 length_ms_ =
static_cast<double>(length_samples_) * 1000.0 / sample_rate_;
215 void set_levels(SampleType low_level, SampleType high_level) {
216 low_level_ = low_level;
217 high_level_ = high_level;
227 if (trigger && length_samples_ > 0) {
228 switch (retrigger_) {
230 if (remaining_ <= 0) remaining_ = length_samples_;
233 remaining_ += length_samples_;
237 remaining_ = length_samples_;
241 if (remaining_ <= 0)
return false;
247 if (detector_.process(trigger)) remaining_ = length_samples_;
248 if (remaining_ <= 0)
return low_level_;
253 bool gate()
const {
return remaining_ > 0; }
257 void update_length_() {
258 length_samples_ =
static_cast<long long>(std::lround(length_ms_ * 0.001 * sample_rate_));
261 double sample_rate_ = 48000.0;
263 long long length_samples_ = 2400;
264 long long remaining_ = 0;
266 HystereticTriggerDetectT<SampleType> detector_{};
267 SampleType low_level_ = SampleType{0};
268 SampleType high_level_ = SampleType{1};
283template <
typename SampleType =
float>
301 if (!trigger)
return false;
302 const bool pass = (counter_ == 0);
303 if (++counter_ >= division_) counter_ = 0;
308 if (!detector_.process(clock))
return false;
309 const bool pass = counter_ == 0;
310 counter_ = (counter_ + 1) % division_;
350 have_period_ =
false;
361 period_ = since_trigger_;
369 if (!have_period_ || multiplier_ <= 1)
return false;
370 if (ticks_emitted_ >= multiplier_)
return false;
371 const long long due = tick_offset_(ticks_emitted_);
372 if (since_trigger_ < due)
return false;
381 long long tick_offset_(
int tick_index)
const {
382 return static_cast<long long>(
383 std::llround(
static_cast<double>(tick_index) *
static_cast<double>(period_)
384 /
static_cast<double>(multiplier_)));
387 long long period_ = 0;
388 long long since_trigger_ = 0;
390 int ticks_emitted_ = 0;
391 bool have_period_ =
false;
392 bool have_first_ =
false;
405template <
typename SampleType =
float>
416template <
typename SampleType =
float>
425 sample_rate_ = sample_rate > 0.0 ? sample_rate : sample_rate_;
426 max_period_samples_ = sample_rate_ *
kMaxPeriodMs * 0.001;
431 if (next == multiple_)
return;
433 if (!(period_ > 0.0) || since_edge_ >= period_) {
438 const double spacing = period_ /
static_cast<double>(multiple_);
439 const int next_index =
static_cast<int>(std::floor(since_edge_ / spacing)) + 1;
440 pending_ = std::max(0, multiple_ - next_index);
441 next_at_ =
static_cast<double>(next_index) * spacing;
456 if (detector_.process(clock)) {
457 const bool usable = have_edge_ && since_edge_ <= max_period_samples_;
458 period_ = usable ? since_edge_ : 0.0;
461 pending_ = period_ > 0.0 ? multiple_ - 1 : 0;
462 next_at_ = period_ /
static_cast<double>(multiple_);
466 while (pending_ > 0 && period_ > 0.0 && since_edge_ >= next_at_) {
468 next_at_ += period_ /
static_cast<double>(multiple_);
476 template <std::
integral Integer>
480 double sample_rate_ = 44100.0;
481 double max_period_samples_ = 44100.0 * 4.0;
483 double since_edge_ = 0.0;
484 double period_ = 0.0;
485 double next_at_ = 0.0;
487 bool have_edge_ =
false;
508template <
typename SampleType =
float>
527 sample_rate_ = sample_rate > 0.0 ? sample_rate : 1.0;
537 const bool idle = next_index_ >= count_;
539 if (
idle) next_index_ = count_;
541 int count()
const {
return count_; }
546 level_first_ =
first;
557 next_index_ = count_;
573 if (next_index_ >= count_)
return hit;
574 if (elapsed_ >= hit_offset_(next_index_)) {
576 hit.index = next_index_;
577 hit.level = level_for_(next_index_);
585 if (detector_.process(trigger)) {
586 remaining_ = count_ - 1;
587 countdown_ = interval_samples_;
590 if (remaining_ <= 0)
return false;
591 if (--countdown_ <= 0) {
593 countdown_ = interval_samples_;
604 bool active()
const {
return next_index_ < count_; }
605 bool busy()
const {
return remaining_ > 0; }
608 void update_interval_() {
609 interval_samples_ =
static_cast<long long>(
610 std::llround(interval_ms_ * 0.001 * sample_rate_));
611 if (interval_samples_ < 1) interval_samples_ = 1;
614 long long hit_offset_(
int index)
const {
615 if (count_ <= 1 || index <= 0)
return 0;
616 const double span =
static_cast<double>(count_ - 1) * spacing_ms_ * 0.001 * sample_rate_;
617 const float p =
static_cast<float>(index) /
static_cast<float>(count_ - 1);
618 return static_cast<long long>(
619 std::llround(span *
static_cast<double>(
stage_curve(p, spacing_curve_))));
622 SampleType level_for_(
int index)
const {
623 if (count_ <= 1)
return level_first_;
624 const SampleType t =
static_cast<SampleType
>(index) /
static_cast<SampleType
>(count_ - 1);
625 return level_first_ + (level_last_ - level_first_) * t;
628 double sample_rate_ = 48000.0;
629 double spacing_ms_ = 60.0;
630 long long elapsed_ = 0;
631 SampleType level_first_ = SampleType{1};
632 SampleType level_last_ = SampleType{1};
633 float spacing_curve_ = 0.0f;
637 long long interval_samples_ = 1;
638 long long remaining_ = 0;
639 long long countdown_ = 0;
640 HystereticTriggerDetectT<SampleType> detector_{};
659template <
typename SampleType =
float>
671 sample_rate_ = sample_rate > 0.0 ? sample_rate : 1.0;
685 delay_samples_ =
static_cast<long long>(std::lround(std::max(0.0, samples)));
686 delay_ms_ =
static_cast<double>(delay_samples_) * 1000.0 / sample_rate_;
700 for (
auto& slot : pending_) {
701 if (slot <= 0)
continue;
702 if (--slot == 0) fired =
true;
705 if (delay_samples_ <= 0) {
707 }
else if (!enqueue_(delay_samples_)) {
715 if (detector_.process(trigger)) countdown_ = delay_samples_;
716 if (countdown_ < 0)
return false;
717 if (countdown_ == 0) {
726 long long dropped()
const {
return dropped_; }
729 bool enqueue_(
long long samples) {
730 for (
auto& slot : pending_) {
739 void update_delay_() {
740 delay_samples_ =
static_cast<long long>(std::lround(delay_ms_ * 0.001 * sample_rate_));
743 std::array<long long, kCapacity> pending_{};
744 double sample_rate_ = 48000.0;
745 double delay_ms_ = 0.0;
746 long long delay_samples_ = 0;
747 long long dropped_ = 0;
748 long long countdown_ = -1;
749 HystereticTriggerDetectT<SampleType> detector_{};
One trigger in, a burst of N triggers out, with a spacing curve and a level ramp.
Definition trigger.hpp:509
void set_interval_ms(double ms)
Definition trigger.hpp:550
static constexpr double kDefaultIntervalMs
Definition trigger.hpp:516
double interval_ms() const
Definition trigger.hpp:554
void set_spacing_ms(double ms)
Definition trigger.hpp:542
static constexpr int kDefaultCount
Definition trigger.hpp:513
void reset()
Definition trigger.hpp:556
static constexpr int kMaxCount
Definition trigger.hpp:512
static constexpr int kMinCount
Definition trigger.hpp:511
static constexpr double kMinIntervalMs
Definition trigger.hpp:514
void set_spacing_curve(float c)
Definition trigger.hpp:543
void set_count(int n)
Changing the count while the burst is idle keeps it idle: next_index_ marks "finished" by sitting at ...
Definition trigger.hpp:536
bool active() const
Definition trigger.hpp:604
void set_levels(SampleType first, SampleType last)
Definition trigger.hpp:545
static constexpr double kMaxIntervalMs
Definition trigger.hpp:515
Hit process(bool trigger)
At most one hit fires per sample.
Definition trigger.hpp:567
void prepare(double sample_rate)
Definition trigger.hpp:526
bool busy() const
Definition trigger.hpp:605
int count() const
Definition trigger.hpp:541
bool process(SampleType trigger)
Definition trigger.hpp:584
long long hit_offset_samples(int index) const
Sample offset of hit index from the burst's trigger.
Definition trigger.hpp:602
Pass every Nth trigger.
Definition trigger.hpp:284
bool process(bool trigger)
Definition trigger.hpp:300
bool process(SampleType clock)
Definition trigger.hpp:307
void set_division(int n)
Definition trigger.hpp:290
int division() const
Definition trigger.hpp:291
static constexpr int kMinDivision
Definition trigger.hpp:286
void reset_phase()
Realign so the next trigger passes.
Definition trigger.hpp:294
static constexpr int kMaxDivision
Definition trigger.hpp:287
void reset()
Definition trigger.hpp:298
static constexpr int kDefaultDivision
Definition trigger.hpp:288
Emit N evenly spaced event triggers per measured input period.
Definition trigger.hpp:339
long long period_samples() const
Measured input period in samples; 0 until two triggers have arrived.
Definition trigger.hpp:378
int multiplier() const
Definition trigger.hpp:344
bool process(bool trigger)
Definition trigger.hpp:355
static constexpr int kMaxMultiplier
Definition trigger.hpp:341
void reset()
Definition trigger.hpp:346
void set_multiplier(int n)
Definition trigger.hpp:343
Trigger in, fixed-length gate out.
Definition trigger.hpp:175
Retrigger
Definition trigger.hpp:181
@ ignore
Drop triggers while the gate is high.
Definition trigger.hpp:186
@ restart
Reset the countdown to full length.
Definition trigger.hpp:184
@ extend
Add another full length to whatever remains.
Definition trigger.hpp:188
static constexpr double kMaxLengthMs
Definition trigger.hpp:178
void set_length_samples(double samples)
Direct sample length, for synced use via units::division_to_samples().
Definition trigger.hpp:208
double length_ms() const
Definition trigger.hpp:201
static constexpr double kMinLengthMs
Definition trigger.hpp:177
bool open() const
Definition trigger.hpp:254
void set_levels(SampleType low_level, SampleType high_level)
Definition trigger.hpp:215
SampleType process(SampleType trigger)
Definition trigger.hpp:246
static constexpr double kDefaultLengthMs
Definition trigger.hpp:179
bool gate() const
Definition trigger.hpp:253
void set_retrigger(Retrigger policy)
Definition trigger.hpp:213
void prepare(double sample_rate)
Definition trigger.hpp:191
void set_length_ms(double ms)
Definition trigger.hpp:197
void reset()
Definition trigger.hpp:220
bool process(bool trigger)
Definition trigger.hpp:226
Hysteretic continuous-CV edge detector.
Definition trigger.hpp:39
bool process(SampleType input)
Definition trigger.hpp:49
bool high() const
Definition trigger.hpp:65
void process(SampleType input, bool &rising, bool &falling)
Definition trigger.hpp:59
void set_thresholds(double high, double low)
Definition trigger.hpp:41
void reset()
Definition trigger.hpp:47
Continuous-signal clock multiplier.
Definition trigger.hpp:417
bool process(SampleType clock)
Definition trigger.hpp:454
static constexpr double kMaxPeriodMs
Definition trigger.hpp:422
int multiple() const
Definition trigger.hpp:443
static constexpr int kDefaultMultiple
Definition trigger.hpp:421
static constexpr int kMaxMultiple
Definition trigger.hpp:420
void reset()
Definition trigger.hpp:445
void set_multiple(int n)
Definition trigger.hpp:429
void prepare(double sample_rate)
Definition trigger.hpp:424
bool process(Integer)=delete
Reject ambiguous integral clock samples; use bool with ClockMultT or an explicit floating-point sampl...
static constexpr int kMinMultiple
Definition trigger.hpp:419
Delay a trigger by a fixed time, through a small fixed-capacity queue.
Definition trigger.hpp:660
static constexpr int kCapacity
Definition trigger.hpp:665
static constexpr double kDefaultDelayMs
Definition trigger.hpp:668
bool process(SampleType trigger)
Definition trigger.hpp:714
long long dropped() const
Triggers discarded because the queue was full since the last reset().
Definition trigger.hpp:726
void set_delay_samples(double samples)
Direct sample delay, for synced use.
Definition trigger.hpp:684
void set_delay_ms(double ms)
Definition trigger.hpp:676
static constexpr double kMinDelayMs
Definition trigger.hpp:666
static constexpr double kMaxDelayMs
Definition trigger.hpp:667
double delay_ms() const
Definition trigger.hpp:680
void reset()
Definition trigger.hpp:689
void prepare(double sample_rate)
Definition trigger.hpp:670
bool process(bool trigger)
Definition trigger.hpp:697
Rising-edge detector with a refractory period.
Definition trigger.hpp:90
bool armed() const
Definition trigger.hpp:145
void reset()
Definition trigger.hpp:112
bool process_signal(SampleType input)
Established single-threshold signal lane.
Definition trigger.hpp:137
void set_threshold(SampleType t)
Threshold for the refractory process_signal() lane.
Definition trigger.hpp:110
void set_refractory_ms(double ms)
Definition trigger.hpp:103
bool process(Value)=delete
Prevent continuous values from silently selecting the decoded-event lane through implicit conversion ...
void process_signal(SampleType input, bool &rising, bool &falling)
Definition trigger.hpp:139
void prepare(double sample_rate)
Definition trigger.hpp:97
bool process(bool gate)
Definition trigger.hpp:118
static constexpr double kDefaultRefractoryMs
Definition trigger.hpp:95
Migration namespace for the unshipped Round-2 spelling whose template arity conflicts with the establ...
Definition mod_matrix.hpp:184
::pulp::signal::ClockMultT ClockMultT
Definition trigger.hpp:406
Definition additive_bank.hpp:218
TriggerDetectT< double > TriggerDetect64
Definition trigger.hpp:162
ClockMultT ClockMult64
Definition trigger.hpp:397
@ first
Definition dither.hpp:23
ClockMultT EventClockMult
Definition trigger.hpp:398
GateGenT< float > GateGen
Definition trigger.hpp:271
SignalClockMultT< float > SignalClockMult
Definition trigger.hpp:491
HystereticTriggerDetectT< float > HystereticTriggerDetect
Definition trigger.hpp:73
ClockDividerT< double > ClockDivider64
Definition trigger.hpp:321
@ falling
Definition rise_fall_generator.hpp:19
@ rising
Definition rise_fall_generator.hpp:19
BurstGenT< float > BurstGen
Definition trigger.hpp:643
constexpr double kTriggerHighThreshold
Definition trigger.hpp:31
@ c
Definition spectrum_trace.hpp:35
@ idle
Definition envelope.hpp:56
ClockMultT EventClockMult64
Definition trigger.hpp:399
TrigDelayT< double > TrigDelay64
Definition trigger.hpp:753
ClockDividerT< float > ClockDivider
Definition trigger.hpp:320
TriggerDetectT< float > TriggerDetect
Definition trigger.hpp:161
float stage_curve(float p, float curve)
The shared stage-shaping law:
Definition mod_tools.hpp:44
@ e
Definition formant_filter_bank.hpp:20
constexpr double kTriggerLowThreshold
Definition trigger.hpp:32
HystereticTriggerDetectT< double > HystereticTriggerDetect64
Definition trigger.hpp:74
TrigDelayT< float > TrigDelay
Definition trigger.hpp:752
ClockMultT ClockMult
Backward-compatible name for the already-decoded event adapter.
Definition trigger.hpp:396
SignalClockMultT< double > SignalClockMult64
Definition trigger.hpp:492
BurstGenT< double > BurstGen64
Definition trigger.hpp:644
GateGenT< double > GateGen64
Definition trigger.hpp:272
Definition trigger.hpp:518
bool fired
Definition trigger.hpp:519
int index
0-based index within the burst; meaningful only when fired.
Definition trigger.hpp:523
SampleType level
Level for this hit, interpolated between the start and end levels.
Definition trigger.hpp:521