Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
dynamic_eq.hpp
Go to the documentation of this file.
1#pragma once
2
5
8
9#include <algorithm>
10#include <cmath>
11#include <cstddef>
12
13namespace pulp::signal {
14
30template <typename SampleType = float> class DynamicEqBandT {
31 public:
32 static constexpr SampleType min_frequency_hz = SampleType{20};
33 static constexpr SampleType max_sample_rate = SampleType{768000};
34 static constexpr SampleType min_q = SampleType{0.1};
35 static constexpr SampleType max_q = SampleType{12};
36 static constexpr SampleType min_threshold_db = SampleType{-120};
37 static constexpr SampleType max_threshold_db = SampleType{0};
38 static constexpr SampleType min_range_db = SampleType{-24};
39 static constexpr SampleType max_range_db = SampleType{24};
40
41 struct Parameters {
42 SampleType frequency_hz = SampleType{1000};
43 SampleType q = SampleType{1};
44 SampleType threshold_db = SampleType{-24};
45 SampleType range_db = SampleType{-6};
46 SampleType transition_db = SampleType{6};
47 SampleType attack_ms = SampleType{5};
48 SampleType release_ms = SampleType{80};
49 };
50
52 redesign_();
53 }
54
55 void prepare(SampleType sample_rate) {
56 sample_rate_ = valid_sample_rate_(sample_rate) ? sample_rate : SampleType{48000};
57 parameters_ = clamp_(parameters_);
58 detector_.prepare(sample_rate_);
59 detector_.set_attack_ms(parameters_.attack_ms);
60 detector_.set_release_ms(parameters_.release_ms);
61 redesign_();
62 reset();
63 }
64
67 bool set_parameters(Parameters requested) {
68 if (!finite_(requested))
69 return false;
70 requested = clamp_(requested);
71 const bool filter_changed =
72 requested.frequency_hz != parameters_.frequency_hz || requested.q != parameters_.q;
73 parameters_ = requested;
74 detector_.set_attack_ms(parameters_.attack_ms);
75 detector_.set_release_ms(parameters_.release_ms);
76 if (filter_changed)
77 redesign_();
78 return true;
79 }
80
81 Parameters parameters() const noexcept {
82 return parameters_;
83 }
84
85 SampleType process(SampleType input) noexcept {
86 if (!std::isfinite(static_cast<double>(input))) {
87 reset();
88 return SampleType{0};
89 }
90
91 const SampleType band = band_filter_.process(input);
92 if (!std::isfinite(static_cast<double>(band))) {
93 reset();
94 return SampleType{0};
95 }
96
97 detector_level_ = detector_.process(band);
98 const SampleType level_db = detector_.current_db(SampleType{-160});
99 const SampleType over_db = level_db - parameters_.threshold_db;
100 activity_ = std::clamp(over_db / parameters_.transition_db, SampleType{0}, SampleType{1});
101 dynamic_gain_db_ = parameters_.range_db * activity_;
102
103 if (parameters_.range_db == SampleType{0})
104 return input;
105 const SampleType band_gain = std::pow(SampleType{10}, dynamic_gain_db_ / SampleType{20});
106 const SampleType output = input + band * (band_gain - SampleType{1});
107 if (!std::isfinite(static_cast<double>(output))) {
108 reset();
109 return SampleType{0};
110 }
111 return output;
112 }
113
114 bool process_block(SampleType* samples, std::size_t frames) noexcept {
115 if (samples == nullptr && frames != 0)
116 return false;
117 for (std::size_t i = 0; i < frames; ++i)
118 samples[i] = process(samples[i]);
119 return true;
120 }
121
122 void reset() noexcept {
123 band_filter_.reset();
124 detector_.reset();
125 detector_level_ = SampleType{0};
126 activity_ = SampleType{0};
127 dynamic_gain_db_ = SampleType{0};
128 }
129
130 SampleType detector_level() const noexcept {
131 return detector_level_;
132 }
133 SampleType activity() const noexcept {
134 return activity_;
135 }
136 SampleType dynamic_gain_db() const noexcept {
137 return dynamic_gain_db_;
138 }
139 GainReduction gain_reduction() const noexcept {
140 return GainReduction::from_signed_db(static_cast<double>(dynamic_gain_db_));
141 }
143 return band_filter_.coefficients();
144 }
145 SampleType sample_rate() const noexcept {
146 return sample_rate_;
147 }
148 static constexpr int latency_samples() noexcept {
149 return 0;
150 }
151
152 private:
153 static bool valid_sample_rate_(SampleType sample_rate) noexcept {
154 return std::isfinite(static_cast<double>(sample_rate)) &&
156 }
157
158 static bool finite_(const Parameters& p) noexcept {
159 return std::isfinite(static_cast<double>(p.frequency_hz)) &&
160 std::isfinite(static_cast<double>(p.q)) &&
161 std::isfinite(static_cast<double>(p.threshold_db)) &&
162 std::isfinite(static_cast<double>(p.range_db)) &&
163 std::isfinite(static_cast<double>(p.transition_db)) &&
164 std::isfinite(static_cast<double>(p.attack_ms)) &&
165 std::isfinite(static_cast<double>(p.release_ms));
166 }
167
168 Parameters clamp_(Parameters p) const noexcept {
169 const SampleType max_frequency =
170 std::max(min_frequency_hz, sample_rate_ * SampleType{0.45});
171 p.frequency_hz = std::clamp(p.frequency_hz, min_frequency_hz, max_frequency);
172 p.q = std::clamp(p.q, min_q, max_q);
173 p.threshold_db = std::clamp(p.threshold_db, min_threshold_db, max_threshold_db);
174 p.range_db = std::clamp(p.range_db, min_range_db, max_range_db);
175 p.transition_db = std::clamp(p.transition_db, SampleType{0.1}, SampleType{60});
176 p.attack_ms = std::clamp(p.attack_ms, SampleType{0.01}, SampleType{2000});
177 p.release_ms = std::clamp(p.release_ms, SampleType{0.01}, SampleType{2000});
178 return p;
179 }
180
181 void redesign_() {
182 band_filter_.set_coefficients(BiquadT<SampleType>::Type::bandpass, parameters_.frequency_hz,
183 parameters_.q, sample_rate_);
184 }
185
186 BiquadT<SampleType> band_filter_{};
187 EnvelopeFollowerT<SampleType> detector_{};
188 Parameters parameters_{};
189 SampleType sample_rate_ = SampleType{48000};
190 SampleType detector_level_ = SampleType{0};
191 SampleType activity_ = SampleType{0};
192 SampleType dynamic_gain_db_ = SampleType{0};
193};
194
197
198} // namespace pulp::signal
@ bandpass
Definition biquad.hpp:58
One internally keyed dynamic-EQ band.
Definition dynamic_eq.hpp:30
bool set_parameters(Parameters requested)
Apply one coherent parameter set.
Definition dynamic_eq.hpp:67
BiquadCoefficientsT< SampleType > band_coefficients() const noexcept
Definition dynamic_eq.hpp:142
void reset() noexcept
Definition dynamic_eq.hpp:122
static constexpr float min_q
Definition dynamic_eq.hpp:34
bool process_block(SampleType *samples, std::size_t frames) noexcept
Definition dynamic_eq.hpp:114
SampleType detector_level() const noexcept
Definition dynamic_eq.hpp:130
static constexpr float max_sample_rate
Definition dynamic_eq.hpp:33
static constexpr int latency_samples() noexcept
Definition dynamic_eq.hpp:148
static constexpr float min_threshold_db
Definition dynamic_eq.hpp:36
SampleType process(SampleType input) noexcept
Definition dynamic_eq.hpp:85
void prepare(SampleType sample_rate)
Definition dynamic_eq.hpp:55
Parameters parameters() const noexcept
Definition dynamic_eq.hpp:81
static constexpr float max_threshold_db
Definition dynamic_eq.hpp:37
SampleType activity() const noexcept
Definition dynamic_eq.hpp:133
static constexpr float min_frequency_hz
Definition dynamic_eq.hpp:32
static constexpr float min_range_db
Definition dynamic_eq.hpp:38
SampleType dynamic_gain_db() const noexcept
Definition dynamic_eq.hpp:136
GainReduction gain_reduction() const noexcept
Definition dynamic_eq.hpp:139
static constexpr float max_q
Definition dynamic_eq.hpp:35
DynamicEqBandT()
Definition dynamic_eq.hpp:51
float sample_rate() const noexcept
Definition dynamic_eq.hpp:145
static constexpr float max_range_db
Definition dynamic_eq.hpp:39
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
Shared envelope and gain-reduction vocabulary for dynamics processors.
Definition additive_bank.hpp:218
DynamicEqBandT< double > DynamicEqBand64
Definition dynamic_eq.hpp:196
@ i
Definition formant_filter_bank.hpp:20
DynamicEqBandT< float > DynamicEqBand
Definition dynamic_eq.hpp:195
@ p
Definition input_events.hpp:40
Definition biquad.hpp:24
Definition dynamic_eq.hpp:41
SampleType transition_db
Definition dynamic_eq.hpp:46
SampleType range_db
Definition dynamic_eq.hpp:45
SampleType release_ms
Definition dynamic_eq.hpp:48
SampleType frequency_hz
Definition dynamic_eq.hpp:42
SampleType threshold_db
Definition dynamic_eq.hpp:44
SampleType q
Definition dynamic_eq.hpp:43
SampleType attack_ms
Definition dynamic_eq.hpp:47