77template <
typename SampleType =
float>
class LpgT {
95 static constexpr SampleType
kMinFcHz = SampleType{10};
96 static constexpr SampleType
kMaxFcHz = SampleType{20000};
104 static constexpr SampleType
kMaxDroop = SampleType{0.95};
112 const SampleType v = std::clamp(velocity, SampleType{0}, SampleType{1});
117 filter_.prepare(sample_rate);
118 sample_rate_ = filter_.sample_rate();
119 update_coefficients_();
126 decay_ms_ = std::clamp(ms, 1.0, 5000.0);
127 update_coefficients_();
131 rise_ms_ = std::clamp(ms, 0.05, 100.0);
132 update_coefficients_();
140 colour_ = std::clamp(
c, SampleType{0}, SampleType{1});
150 std::swap(min_hz, max_hz);
153 log_range_ = std::log(fc_max_ / fc_min_);
158 droop_ = std::clamp(d, SampleType{0},
kMaxDroop);
164 pulse_level_ = std::clamp(level, SampleType{0}, SampleType{1});
165 pulse_remaining_ = pulse_samples_;
171 gate_ = std::clamp(level, SampleType{0}, SampleType{1});
175 control_ = SampleType{0};
176 gate_ = SampleType{0};
177 pulse_level_ = SampleType{0};
178 pulse_remaining_ = 0;
188 const SampleType amplitude = control_ * std::sqrt(control_);
199 const SampleType filtered = filter_.process_lowpass(input);
200 const SampleType gain = amplitude + colour_ * (SampleType{1} - amplitude);
201 return filtered * gain;
214 return commanded_cutoff_hz_;
223 static SampleType normalize_cutoff_endpoint_(SampleType hz, SampleType nan_fallback) {
229 void command_cutoff_() {
230 const SampleType filter_control = SampleType{1} - colour_ * (SampleType{1} - control_);
231 const SampleType cutoff = fc_min_ * std::exp(log_range_ * filter_control);
232 filter_.set_cutoff(std::min(cutoff, SampleType{0.25} * sample_rate_));
233 commanded_cutoff_hz_ = filter_.cutoff();
236 void update_coefficients_() {
237 rise_coeff_ = coeff_for_(rise_ms_);
238 fall_coeff_ = coeff_for_(decay_ms_);
239 pulse_samples_ =
static_cast<long long>(
240 std::lround(
kStrikePulseMs * 0.001 *
static_cast<double>(sample_rate_)));
241 if (pulse_samples_ < 1)
245 SampleType coeff_for_(
double ms)
const {
246 const double samples = std::max(1.0e-6, ms * 0.001 *
static_cast<double>(sample_rate_));
247 return static_cast<SampleType
>(1.0 - std::exp(-1.0 / samples));
250 void advance_control_() {
251 SampleType target = gate_;
252 if (pulse_remaining_ > 0) {
253 target = std::max(target, pulse_level_);
256 if (target > control_) {
257 control_ += rise_coeff_ * (target - control_);
262 const SampleType coeff =
263 fall_coeff_ * (SampleType{1} - droop_ * (SampleType{1} - control_));
264 control_ += coeff * (target - control_);
266 control_ = std::clamp(
snap_to_zero(control_), SampleType{0}, SampleType{1});
269 TptFilterT<SampleType> filter_{};
270 SampleType sample_rate_ = SampleType{48000};
271 SampleType control_ = SampleType{0};
273 SampleType gate_ = SampleType{0};
274 SampleType pulse_level_ = SampleType{0};
275 SampleType rise_coeff_ = SampleType{0};
276 SampleType fall_coeff_ = SampleType{0};
277 long long pulse_remaining_ = 0;
278 long long pulse_samples_ = 144;
283 SampleType log_range_ =
static_cast<SampleType
>(5.703782474656201);
284 SampleType colour_ = SampleType{0.5};
Vactrol-modelled low-pass gate.
Definition lpg.hpp:77
static constexpr float kVcaExponent
Definition lpg.hpp:91
SampleType cutoff_hz() const
Compatibility spelling for the cutoff telemetry.
Definition lpg.hpp:218
static constexpr float kMaxDroop
Definition lpg.hpp:104
void set_range_hz(SampleType min_hz, SampleType max_hz)
The effective ceiling is additionally capped at sample_rate / 4 when the cutoff command is applied — ...
Definition lpg.hpp:146
SampleType commanded_cutoff_hz() const
Last cutoff command applied to the owned filter.
Definition lpg.hpp:213
static constexpr float kVelocityCurve
Definition lpg.hpp:107
void reset()
Definition lpg.hpp:174
SampleType control() const
Conditioned cell state, 0..1 — the value both the amplitude and the cutoff are derived from.
Definition lpg.hpp:206
static constexpr float kDefaultFcMinHz
Definition lpg.hpp:93
SampleType process(SampleType input)
Definition lpg.hpp:183
static constexpr float kDefaultFcMaxHz
Definition lpg.hpp:94
static constexpr double kRiseMs
Definition lpg.hpp:80
static SampleType velocity_to_strike(SampleType velocity)
Map a 0..1 velocity to a strike level whose loudness and brightness rise together the way a struck ob...
Definition lpg.hpp:111
static constexpr float kMinFcHz
Definition lpg.hpp:95
static constexpr float kDefaultDroop
Definition lpg.hpp:103
void set_droop(SampleType d)
Definition lpg.hpp:157
void strike(SampleType level)
Ping the cell.
Definition lpg.hpp:163
static constexpr float kMaxFcHz
Definition lpg.hpp:96
static constexpr double kDefaultDecayMs
Definition lpg.hpp:97
static constexpr double kStrikePulseMs
Definition lpg.hpp:88
void set_rise_ms(double ms)
Definition lpg.hpp:130
void set_decay_ms(double ms)
Dark-recovery time.
Definition lpg.hpp:125
void prepare(SampleType sample_rate)
Definition lpg.hpp:116
void set_colour(SampleType c)
0 = pure VCA (the filter stays wide open), 1 = pure filter (the gain stays at unity),...
Definition lpg.hpp:137
void set_gate(SampleType level)
Sustained control level, for idiom 4.
Definition lpg.hpp:170
Definition additive_bank.hpp:218
@ c
Definition spectrum_trace.hpp:35
T snap_to_zero(T x)
Returns 0 if |x| < snap_threshold, else x.
Definition denormal.hpp:45
LpgT< float > Lpg
Definition lpg.hpp:288
LpgT< double > Lpg64
Definition lpg.hpp:289
First-order topology-preserving transform (TPT) filter.