65template <
typename SampleType =
float>
70 SampleType
ratio = SampleType{4.0f};
92 hold_db_ = envelope_db_;
100 configure_sidechain_filter();
101 configure_lookahead_buffer();
109 sidechain_hpf_hz_ = std::max(SampleType{0.0f}, hz);
110 configure_sidechain_filter();
120 lookahead_ms_ = std::clamp(ms, SampleType{0.0f}, SampleType{50.0f});
121 configure_lookahead_buffer();
127 return static_cast<int>(
128 std::round(lookahead_ms_ * SampleType{0.001f} * sample_rate_));
146 if (!std::isfinite(input) || !std::isfinite(sidechain)) {
148 return SampleType{0};
152 const SampleType detector = sidechain_hpf_active_
153 ? sidechain_hpf_.process(sidechain)
163 envelope_db_ =
snap_to_zero(envelope_db_ + attack_coeff_ * (hold_db_ - envelope_db_));
169 const SampleType gain_linear =
db_to_gain(envelope_db_ + params_.makeup_db);
174 if (lookahead_samples_ > 0) {
175 lookahead_buffer_.push(input);
176 const SampleType delayed =
177 lookahead_buffer_.read(
static_cast<SampleType
>(lookahead_samples_));
178 return delayed * gain_linear;
180 return input * gain_linear;
200 run_chunks(
buffer, sidechain, num_samples);
207 return attack_coeff_;
210 return release_coeff_;
221 envelope_db_ = SampleType{0.0f};
222 hold_db_ = SampleType{0.0f};
223 sidechain_hpf_.reset();
224 lookahead_buffer_.reset();
235 const SampleType magnitude = std::max(std::abs(detector), SampleType{1e-10f});
236 if constexpr (std::is_same_v<SampleType, float>)
239 return SampleType{20.0f} * std::log10(magnitude);
247 const SampleType knee = params.knee_db;
248 const SampleType threshold = params.threshold_db;
249 const SampleType slope = SampleType{1.0f} / params.ratio - SampleType{1.0f};
250 const SampleType above = (input_db - threshold) * slope;
252 if (knee <= SampleType{0.0f}) {
254 return input_db <= threshold ? SampleType{0.0f} : above;
258 const SampleType half_knee = knee / SampleType{2.0f};
259 const SampleType x = input_db - threshold + half_knee;
260 const SampleType in_knee = slope * x * x / (SampleType{2.0f} * knee);
261 const SampleType upper = input_db > threshold + half_knee ? above : in_knee;
262 return input_db < threshold - half_knee ? SampleType{0.0f} : upper;
268 if (time_ms <= SampleType{0.0f})
269 return SampleType{1.0f};
270 return SampleType{1.0f} -
271 std::exp(SampleType{-1.0f} / (time_ms * SampleType{0.001f} * sample_rate));
279 const SampleType coeff = (target < envelope) ?
attack :
release;
280 return envelope + coeff * (target - envelope);
290 return target < released ? target : released;
295 if constexpr (std::is_same_v<SampleType, float>)
298 return std::pow(SampleType{10.0f}, db / SampleType{20.0f});
303 SampleType sample_rate_ = SampleType{44100.0f};
304 SampleType envelope_db_ = SampleType{0.0f};
306 SampleType hold_db_ = SampleType{0.0f};
312 SampleType sidechain_hpf_hz_ = SampleType{0.0f};
313 bool sidechain_hpf_active_ =
false;
314 BiquadT<SampleType> sidechain_hpf_{};
316 SampleType lookahead_ms_ = SampleType{0.0f};
317 int lookahead_samples_ = 0;
318 DelayLineT<SampleType> lookahead_buffer_{};
320 void configure_sidechain_filter() {
321 sidechain_hpf_active_ =
322 sidechain_hpf_hz_ > SampleType{0.0f} && sample_rate_ > SampleType{0.0f};
323 if (sidechain_hpf_active_) {
324 sidechain_hpf_.set_coefficients(
326 sidechain_hpf_hz_, SampleType{0.7071068f}, sample_rate_);
327 sidechain_hpf_.reset();
330 void configure_lookahead_buffer() {
331 lookahead_samples_ =
static_cast<int>(
332 std::round(lookahead_ms_ * SampleType{0.001f} * sample_rate_));
333 if (lookahead_samples_ > 0) {
335 lookahead_buffer_.prepare(lookahead_samples_ + 1);
336 lookahead_buffer_.reset();
340 void update_ballistics() {
350 void run_chunks(SampleType*
buffer,
const SampleType* sidechain,
int num_samples) {
351 if (num_samples <= 0)
354 for (
const auto [begin, end] :
snap_chunks(
static_cast<std::size_t
>(num_samples))) {
355 const int n =
static_cast<int>(
end - begin);
356 SampleType*
x =
buffer + begin;
357 const SampleType* sc = sidechain + begin;
362 constexpr SampleType kMaxFinite = std::numeric_limits<SampleType>::max();
364 for (
int i = 0;
i <
n; ++
i)
365 non_finite |=
static_cast<unsigned>(!(std::abs(x[
i]) <= kMaxFinite)) |
366 static_cast<unsigned>(!(std::abs(sc[
i]) <= kMaxFinite));
368 for (
int i = 0;
i <
n; ++
i)
373 if (sidechain_hpf_active_) {
374 for (
int i = 0;
i <
n; ++
i)
375 level[
i] = sidechain_hpf_.process(sc[
i]);
376 for (
int i = 0;
i <
n; ++
i)
379 for (
int i = 0;
i <
n; ++
i)
389 const SampleType
attack = attack_coeff_;
390 const SampleType
release = release_coeff_;
393 SampleType
hold = hold_db_;
394 for (
int i = 0;
i <
n; ++
i) {
401 for (
int i = 0;
i <
n; ++
i) {
408 const SampleType makeup = params_.makeup_db;
409 if (lookahead_samples_ > 0) {
410 const SampleType
delay =
static_cast<SampleType
>(lookahead_samples_);
411 for (
int i = 0;
i <
n; ++
i)
413 for (
int i = 0;
i <
n; ++
i) {
414 lookahead_buffer_.push(x[
i]);
415 x[
i] = lookahead_buffer_.read(
delay) * level[
i];
418 for (
int i = 0;
i <
n; ++
i)
431template <
typename SampleType =
float>
435 threshold_ = std::pow(SampleType{10.0f}, db / SampleType{20.0f});
436 threshold_mute_ = std::isinf(db) && db < SampleType{0};
445 if (!std::isfinite(input)) {
447 return SampleType{0};
450 SampleType abs_input = std::abs(input);
453 if (abs_input > envelope_)
454 envelope_ = abs_input;
456 SampleType coeff = SampleType{1.0f} -
457 std::exp(SampleType{-1.0f} /
458 (release_ms_ * SampleType{0.001f} * sample_rate_));
462 envelope_ =
snap_to_zero(envelope_ + coeff * (abs_input - envelope_));
467 (envelope_ > threshold_) ? threshold_ / envelope_ : SampleType{1.0f};
472 for (
int i = 0;
i < num_samples; ++
i)
482 std::numeric_limits<double>::infinity());
483 const SampleType gain =
484 (envelope_ > threshold_) ? threshold_ / envelope_ : SampleType{1};
485 if (!(gain > SampleType{0}))
487 std::numeric_limits<double>::infinity());
489 SampleType{20} * std::log10(gain));
492 void reset() { envelope_ = SampleType{0.0f}; }
495 SampleType threshold_ = SampleType{1.0f};
496 SampleType release_ms_ = SampleType{50.0f};
497 SampleType sample_rate_ = SampleType{44100.0f};
498 SampleType envelope_ = SampleType{0.0f};
499 bool threshold_mute_ =
false;
@ highpass
Definition biquad.hpp:58
Definition compressor.hpp:66
SampleType process_with_sidechain(SampleType input, SampleType sidechain)
Process one sample with an explicit sidechain detector input.
Definition compressor.hpp:140
int latency_samples() const
Latency reported to the host = lookahead in samples (0 when off).
Definition compressor.hpp:126
void process_with_sidechain(SampleType *buffer, const SampleType *sidechain, int num_samples)
Process a block with a separate sidechain input.
Definition compressor.hpp:198
SampleType release_coefficient() const
Definition compressor.hpp:209
SampleType gain_reduction_db() const
Definition compressor.hpp:213
static SampleType db_to_gain(SampleType db)
Linear gain for a gain change in dB (FastMath for float).
Definition compressor.hpp:294
static SampleType gain_computer_db(SampleType input_db, const Params ¶ms)
The static curve: gain change in dB (<= 0) for a detector level in dB under params (threshold,...
Definition compressor.hpp:246
static SampleType branching_step(SampleType envelope, SampleType target, SampleType attack, SampleType release)
One step of the branching ballistics: the envelope (gain change in dB) moves toward target with the a...
Definition compressor.hpp:277
void process(SampleType *buffer, int num_samples)
Process a block in place; the buffer is also the sidechain.
Definition compressor.hpp:185
SampleType process(SampleType input)
Process one sample.
Definition compressor.hpp:133
SampleType attack_coefficient() const
Envelope smoothing coefficients in force for the current parameters and sample rate: the per-sample s...
Definition compressor.hpp:206
SampleType lookahead_ms() const
Definition compressor.hpp:123
GainReduction gain_reduction() const noexcept
Canonical non-negative gain-reduction magnitude for shared meters.
Definition compressor.hpp:216
void set_sidechain_hpf_hz(SampleType hz)
Set the sidechain high-pass cutoff (Hz).
Definition compressor.hpp:108
void reset()
Definition compressor.hpp:220
void set_sample_rate(SampleType sr)
Definition compressor.hpp:97
static SampleType decoupled_hold(SampleType hold, SampleType target, SampleType release)
The decoupled ballistics' peak hold on gain changes (<= 0 dB): it jumps to any deeper reduction at on...
Definition compressor.hpp:288
void set_ballistics(CompressorBallistics ballistics) noexcept
Select how gain reduction is smoothed (see CompressorBallistics).
Definition compressor.hpp:86
static SampleType detector_level_db(SampleType detector)
Detector level in dB for one sample: 20 log10(|x|), floored at -200 dB.
Definition compressor.hpp:234
SampleType sidechain_hpf_hz() const
Definition compressor.hpp:112
CompressorBallistics ballistics() const noexcept
Definition compressor.hpp:94
void set_lookahead_ms(SampleType ms)
Set the lookahead in milliseconds.
Definition compressor.hpp:119
static SampleType ballistic_coefficient(SampleType time_ms, SampleType sample_rate)
One-pole smoothing coefficient for a time constant in ms: 1 - exp(-1 / (tau fs)), or 1 (instant) for ...
Definition compressor.hpp:267
void set_params(const Params &p)
Definition compressor.hpp:79
Canonical gain-reduction meter value.
Definition dynamics_contract.hpp:27
static GainReduction from_signed_db(double signed_db) noexcept
Adapt a signed gain value, where attenuation is negative dB.
Definition dynamics_contract.hpp:32
static GainReduction from_magnitude_db(double magnitude_db) noexcept
Adapt an attenuation magnitude, where reduction is positive dB.
Definition dynamics_contract.hpp:37
Definition compressor.hpp:432
void set_release_ms(SampleType ms)
Definition compressor.hpp:438
void set_threshold_db(SampleType db)
Definition compressor.hpp:434
void reset()
Definition compressor.hpp:492
GainReduction gain_reduction() const noexcept
Definition compressor.hpp:476
void set_sample_rate(SampleType sr)
Definition compressor.hpp:439
void process(SampleType *buffer, int num_samples)
Definition compressor.hpp:471
SampleType process(SampleType input)
Definition compressor.hpp:441
Shared envelope and gain-reduction vocabulary for dynamics processors.
Scalar math helpers for real-time audio DSP.
Definition additive_bank.hpp:218
CompressorT< double > Compressor64
Definition compressor.hpp:426
@ buffer
A borrowed static buffer with a traversable playhead.
Definition granular.hpp:110
@ release
Definition envelope.hpp:62
@ delay
Definition envelope.hpp:57
@ hold
Definition envelope.hpp:59
@ attack
Definition envelope.hpp:58
constexpr std::size_t kSnapChunkSamples
Samples a block path runs between denormal snaps of its recursive state.
Definition denormal.hpp:113
T snap_to_zero(T x)
Returns 0 if |x| < snap_threshold, else x.
Definition denormal.hpp:45
@ i
Definition formant_filter_bank.hpp:20
LimiterT< float > Limiter
Definition compressor.hpp:502
constexpr SnapChunks snap_chunks(std::size_t num_samples) noexcept
Definition denormal.hpp:173
CompressorT< float > Compressor
Definition compressor.hpp:425
LimiterT< double > Limiter64
Definition compressor.hpp:503
CompressorBallistics
How CompressorT smooths the gain reduction its static curve asks for, in the log (dB) domain after th...
Definition compressor.hpp:42
@ branching
Definition compressor.hpp:42
@ decoupled
Definition compressor.hpp:42
@ non_finite
Definition formant_filter_bank.hpp:26
@ n
Definition input_events.hpp:40
@ x
Definition input_events.hpp:41
Definition compressor.hpp:68
SampleType ratio
Definition compressor.hpp:70
SampleType threshold_db
Definition compressor.hpp:69
SampleType attack_ms
Definition compressor.hpp:73
SampleType release_ms
Definition compressor.hpp:74
SampleType knee_db
Definition compressor.hpp:75
SampleType makeup_db
Definition compressor.hpp:76
static float db_to_gain_accurate(float db) noexcept
10^(dB / 20), within exp10_accurate()'s bound.
Definition fast_math.hpp:555
static float gain_to_db_accurate(float gain) noexcept
20 log10(gain) for a finite, positive, normal gain (clamp to a floor first); within log10_accurate()'...
Definition fast_math.hpp:550