Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
transient_designer.hpp
Go to the documentation of this file.
1#pragma once
2
5
7
8#include <algorithm>
9#include <cmath>
10
11namespace pulp::signal {
12
24template <typename SampleType = float> class TransientDesignerT {
25 public:
26 static constexpr SampleType kMinShapeDb = SampleType{-24};
27 static constexpr SampleType kMaxShapeDb = SampleType{24};
28
29 void prepare(SampleType sample_rate) {
30 if (!std::isfinite(static_cast<double>(sample_rate)) || !(sample_rate > SampleType{0}))
31 sample_rate = SampleType{44100};
32 fast_.prepare(sample_rate);
33 slow_.prepare(sample_rate);
34 apply_detector_times_();
35 reset();
36 }
37
38 void set_attack_db(SampleType db) noexcept {
39 if (std::isfinite(static_cast<double>(db)))
40 attack_db_ = std::clamp(db, kMinShapeDb, kMaxShapeDb);
41 }
42
43 void set_sustain_db(SampleType db) noexcept {
44 if (std::isfinite(static_cast<double>(db)))
45 sustain_db_ = std::clamp(db, kMinShapeDb, kMaxShapeDb);
46 }
47
48 void set_fast_attack_ms(SampleType ms) {
49 set_time_(ms, fast_attack_ms_, true, true);
50 }
51 void set_fast_release_ms(SampleType ms) {
52 set_time_(ms, fast_release_ms_, true, false);
53 }
54 void set_slow_attack_ms(SampleType ms) {
55 set_time_(ms, slow_attack_ms_, false, true);
56 }
57 void set_slow_release_ms(SampleType ms) {
58 set_time_(ms, slow_release_ms_, false, false);
59 }
60
61 SampleType process(SampleType input) noexcept {
62 if (!std::isfinite(static_cast<double>(input))) {
63 reset();
64 return SampleType{0};
65 }
66
67 const SampleType fast = fast_.process(input);
68 const SampleType slow = slow_.process(input);
69 const SampleType scale = std::max({fast, slow, SampleType{1.0e-12}});
70 contrast_ = std::clamp((fast - slow) / scale, SampleType{-1}, SampleType{1});
71
72 const SampleType attack_amount = std::max(contrast_, SampleType{0});
73 const SampleType sustain_amount = std::max(-contrast_, SampleType{0});
74 gain_db_ = attack_db_ * attack_amount + sustain_db_ * sustain_amount;
75
76 // Preserve an exact transparent path while still advancing detector
77 // state, so automation can enter from neutral without a stale onset.
78 if (attack_db_ == SampleType{0} && sustain_db_ == SampleType{0})
79 return input;
80 const SampleType output = input * std::pow(SampleType{10}, gain_db_ / SampleType{20});
81 if (!std::isfinite(static_cast<double>(output))) {
82 reset();
83 return SampleType{0};
84 }
85 return output;
86 }
87
88 void process(const SampleType* input, SampleType* output, int num_samples) noexcept {
89 if (input == nullptr || output == nullptr || num_samples <= 0)
90 return;
91 for (int i = 0; i < num_samples; ++i)
92 output[i] = process(input[i]);
93 }
94
95 void reset() noexcept {
96 fast_.reset();
97 slow_.reset();
98 contrast_ = SampleType{0};
99 gain_db_ = SampleType{0};
100 }
101
102 SampleType attack_db() const noexcept {
103 return attack_db_;
104 }
105 SampleType sustain_db() const noexcept {
106 return sustain_db_;
107 }
108 SampleType fast_attack_ms() const noexcept {
109 return fast_attack_ms_;
110 }
111 SampleType fast_release_ms() const noexcept {
112 return fast_release_ms_;
113 }
114 SampleType slow_attack_ms() const noexcept {
115 return slow_attack_ms_;
116 }
117 SampleType slow_release_ms() const noexcept {
118 return slow_release_ms_;
119 }
120 SampleType contrast() const noexcept {
121 return contrast_;
122 }
123 SampleType gain_db() const noexcept {
124 return gain_db_;
125 }
126 static constexpr int latency_samples() noexcept {
127 return 0;
128 }
129
130 private:
131 void set_time_(SampleType ms, SampleType& stored, bool fast, bool attack) {
132 if (!std::isfinite(static_cast<double>(ms)))
133 return;
134 stored = std::max(ms, SampleType{0.01});
135 auto& follower = fast ? fast_ : slow_;
136 if (attack)
137 follower.set_attack_ms(stored);
138 else
139 follower.set_release_ms(stored);
140 }
141
142 void apply_detector_times_() {
143 fast_.set_attack_ms(fast_attack_ms_);
144 fast_.set_release_ms(fast_release_ms_);
145 slow_.set_attack_ms(slow_attack_ms_);
146 slow_.set_release_ms(slow_release_ms_);
147 }
148
149 EnvelopeFollowerT<SampleType> fast_{};
150 EnvelopeFollowerT<SampleType> slow_{};
151 SampleType attack_db_ = SampleType{0};
152 SampleType sustain_db_ = SampleType{0};
153 SampleType fast_attack_ms_ = SampleType{0.5};
154 SampleType fast_release_ms_ = SampleType{20};
155 SampleType slow_attack_ms_ = SampleType{15};
156 SampleType slow_release_ms_ = SampleType{120};
157 SampleType contrast_ = SampleType{0};
158 SampleType gain_db_ = SampleType{0};
159};
160
163
164} // namespace pulp::signal
void set_attack_ms(float ms)
Definition ballistics_filter.hpp:46
void set_release_ms(float ms)
Definition ballistics_filter.hpp:53
Separates attack and sustain by comparing exact-timing peak envelopes.
Definition transient_designer.hpp:24
void set_sustain_db(SampleType db) noexcept
Definition transient_designer.hpp:43
void reset() noexcept
Definition transient_designer.hpp:95
static constexpr float kMaxShapeDb
Definition transient_designer.hpp:27
void set_fast_attack_ms(SampleType ms)
Definition transient_designer.hpp:48
void process(const SampleType *input, SampleType *output, int num_samples) noexcept
Definition transient_designer.hpp:88
void set_attack_db(SampleType db) noexcept
Definition transient_designer.hpp:38
SampleType slow_release_ms() const noexcept
Definition transient_designer.hpp:117
SampleType fast_attack_ms() const noexcept
Definition transient_designer.hpp:108
SampleType gain_db() const noexcept
Definition transient_designer.hpp:123
static constexpr float kMinShapeDb
Definition transient_designer.hpp:26
static constexpr int latency_samples() noexcept
Definition transient_designer.hpp:126
SampleType process(SampleType input) noexcept
Definition transient_designer.hpp:61
SampleType contrast() const noexcept
Definition transient_designer.hpp:120
void prepare(SampleType sample_rate)
Definition transient_designer.hpp:29
void set_slow_release_ms(SampleType ms)
Definition transient_designer.hpp:57
SampleType sustain_db() const noexcept
Definition transient_designer.hpp:105
SampleType slow_attack_ms() const noexcept
Definition transient_designer.hpp:114
void set_fast_release_ms(SampleType ms)
Definition transient_designer.hpp:51
SampleType fast_release_ms() const noexcept
Definition transient_designer.hpp:111
void set_slow_attack_ms(SampleType ms)
Definition transient_designer.hpp:54
SampleType attack_db() const noexcept
Definition transient_designer.hpp:102
Shared envelope and gain-reduction vocabulary for dynamics processors.
Definition additive_bank.hpp:218
TransientDesignerT< float > TransientDesigner
Definition transient_designer.hpp:161
@ attack
Definition envelope.hpp:58
@ i
Definition formant_filter_bank.hpp:20
TransientDesignerT< double > TransientDesigner64
Definition transient_designer.hpp:162