Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
noise_gate.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <cmath>
7#include <algorithm>
8
9namespace pulp::signal {
10
11// Noise gate / expander
12// Attenuates signal below threshold with configurable attack/release.
13//
14// RT contract: setters, process paths, and reset are scalar-only and allocate
15// no memory.
16template <typename SampleType = float>
18public:
19 struct Params {
20 SampleType threshold_db = SampleType{-40.0f};
21 SampleType ratio = SampleType{10.0f}; // Expansion ratio (10:1 = gate)
22 SampleType attack_ms = SampleType{0.5f};
23 SampleType release_ms = SampleType{50.0f};
24 SampleType range_db = SampleType{-80.0f}; // Max attenuation
25 };
26
27 NoiseGateT() { recompute_coeffs(); } // coeffs match the default params up front
28
29 void set_params(const Params& p) {
30 params_.threshold_db =
31 std::isfinite(p.threshold_db) ? p.threshold_db : SampleType{-40.0f};
32 params_.ratio = std::isfinite(p.ratio)
33 ? std::max(SampleType{1.0f}, p.ratio)
34 : SampleType{1.0f};
35 params_.attack_ms =
36 std::isfinite(p.attack_ms)
37 ? std::max(SampleType{0.0f}, p.attack_ms)
38 : SampleType{0.0f};
39 params_.release_ms =
40 std::isfinite(p.release_ms)
41 ? std::max(SampleType{0.0f}, p.release_ms)
42 : SampleType{0.0f};
43 params_.range_db =
44 std::isfinite(p.range_db)
45 ? std::min(SampleType{0.0f}, p.range_db)
46 : SampleType{-80.0f};
47 recompute_coeffs();
48 }
49
50 void set_sample_rate(SampleType sr) {
51 sample_rate_ = (std::isfinite(sr) && sr > SampleType{0.0f})
52 ? sr
53 : SampleType{44100.0f};
54 recompute_coeffs();
55 }
56
57 SampleType process(SampleType input) {
58 SampleType abs_in = std::abs(input);
59 if (abs_in < SampleType{1e-10f}) abs_in = SampleType{1e-10f};
60 SampleType input_db = SampleType{20.0f} * std::log10(abs_in);
61
62 SampleType gain_db = SampleType{0.0f};
63 if (input_db < params_.threshold_db) {
64 // Expansion: apply ratio below threshold
65 SampleType below = params_.threshold_db - input_db;
66 gain_db = -below * (params_.ratio - SampleType{1.0f});
67 gain_db = std::max(gain_db, params_.range_db);
68 }
69
70 // Envelope follower — coefficients depend only on the attack/release times
71 // and the sample rate, so they are cached in the setters (bit-identical to
72 // recomputing the two exp() per sample, minus the per-sample transcendentals).
73 SampleType coeff = (gain_db < envelope_db_) ? attack_coeff_ : release_coeff_;
74 envelope_db_ += coeff * (gain_db - envelope_db_);
75
76 // Clamp to prevent overflow
77 envelope_db_ = std::max(envelope_db_, params_.range_db);
78 // Snap the recursive envelope: a fully-open gate settles toward 0 dB and
79 // otherwise stalls in the denormal range with no FTZ guard. No-op above
80 // 1e-15.
81 envelope_db_ = snap_to_zero(envelope_db_);
82
83 SampleType gain_linear =
84 std::pow(SampleType{10.0f}, envelope_db_ / SampleType{20.0f});
85 return input * gain_linear;
86 }
87
88 void process(SampleType* buffer, int num_samples) {
89 if (buffer == nullptr || num_samples <= 0)
90 return;
91
92 for (int i = 0; i < num_samples; ++i)
93 buffer[i] = process(buffer[i]);
94 }
95
97 GainReduction gain_reduction() const noexcept {
98 return GainReduction::from_signed_db(static_cast<double>(envelope_db_));
99 }
100
101 void reset() { envelope_db_ = SampleType{0.0f}; }
102
103private:
104 // Envelope attack/release one-pole coefficients, recomputed only when the
105 // params or sample rate change (never per sample). Expression is identical to
106 // the former inline computation, so results are bit-for-bit unchanged.
107 void recompute_coeffs() {
108 attack_coeff_ = params_.attack_ms > SampleType{0.0f}
109 ? SampleType{1.0f} -
110 std::exp(SampleType{-1.0f} /
111 (params_.attack_ms * SampleType{0.001f} * sample_rate_))
112 : SampleType{1.0f};
113 release_coeff_ = params_.release_ms > SampleType{0.0f}
114 ? SampleType{1.0f} -
115 std::exp(SampleType{-1.0f} /
116 (params_.release_ms * SampleType{0.001f} * sample_rate_))
117 : SampleType{1.0f};
118 }
119
120 Params params_;
121 SampleType sample_rate_ = SampleType{44100.0f};
122 SampleType envelope_db_ = SampleType{0.0f};
123 SampleType attack_coeff_ = SampleType{1.0f};
124 SampleType release_coeff_ = SampleType{1.0f};
125};
126
129
130} // namespace pulp::signal
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
Definition noise_gate.hpp:17
void process(SampleType *buffer, int num_samples)
Definition noise_gate.hpp:88
SampleType process(SampleType input)
Definition noise_gate.hpp:57
NoiseGateT()
Definition noise_gate.hpp:27
void reset()
Definition noise_gate.hpp:101
void set_sample_rate(SampleType sr)
Definition noise_gate.hpp:50
GainReduction gain_reduction() const noexcept
Canonical non-negative attenuation magnitude for shared meters.
Definition noise_gate.hpp:97
void set_params(const Params &p)
Definition noise_gate.hpp:29
Shared envelope and gain-reduction vocabulary for dynamics processors.
Definition additive_bank.hpp:218
@ buffer
A borrowed static buffer with a traversable playhead.
Definition granular.hpp:110
NoiseGateT< double > NoiseGate64
Definition noise_gate.hpp:128
NoiseGateT< float > NoiseGate
Definition noise_gate.hpp:127
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
Definition noise_gate.hpp:19
SampleType release_ms
Definition noise_gate.hpp:23
SampleType threshold_db
Definition noise_gate.hpp:20
SampleType range_db
Definition noise_gate.hpp:24
SampleType attack_ms
Definition noise_gate.hpp:22
SampleType ratio
Definition noise_gate.hpp:21