Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
dynamics_contract.hpp
Go to the documentation of this file.
1#pragma once
2
5
7
8#include <algorithm>
9#include <array>
10#include <cmath>
11
12namespace pulp::signal {
13
16
28 public:
29 GainReduction() noexcept = default;
30
32 static GainReduction from_signed_db(double signed_db) noexcept {
33 return GainReduction{std::isnan(signed_db) ? 0.0 : std::max(0.0, -signed_db)};
34 }
35
38 return GainReduction{std::isnan(magnitude_db) ? 0.0 : std::max(0.0, magnitude_db)};
39 }
40
42 double db() const noexcept {
43 return db_;
44 }
45
47 double signed_db() const noexcept {
48 return -db_;
49 }
50
53 double linear_gain() const noexcept {
54 return std::pow(10.0, -db_ / 20.0);
55 }
56
57 private:
58 explicit GainReduction(double db) noexcept : db_(db) {}
59
60 double db_ = 0.0;
61};
62
72template <typename SampleType = float>
73class EnvelopeFollowerT : public BallisticsFilterT<SampleType> {
74 public:
76 using Mode = typename Base::Mode;
77
78 EnvelopeFollowerT() : Base(Base::TimeConvention::exact_10_to_90) {}
79
80 static SampleType coefficient_for_time_ms(SampleType ms, SampleType sample_rate) {
81 return Base::exact_coefficient_for_time_ms(ms, sample_rate);
82 }
83
84 private:
85 template <typename> friend class StereoEnvelopeFollowerT;
86
87 SampleType detector_state() const noexcept {
88 return Base::detector_state();
89 }
90
91 void synchronize_detector_state(SampleType state) noexcept {
93 }
94};
95
98
105template <typename SampleType = float> class StereoEnvelopeFollowerT {
106 public:
108
109 void prepare(SampleType sample_rate) {
110 left_.prepare(sample_rate);
111 right_.prepare(sample_rate);
112 }
113
114 void set_attack_ms(SampleType ms) {
115 left_.set_attack_ms(ms);
116 right_.set_attack_ms(ms);
117 }
118
119 void set_release_ms(SampleType ms) {
120 left_.set_release_ms(ms);
121 right_.set_release_ms(ms);
122 }
123
124 void set_mode(Mode mode) {
125 left_.set_mode(mode);
126 right_.set_mode(mode);
127 }
128
129 void set_link(SampleType amount) {
130 if (!std::isfinite(static_cast<double>(amount)))
131 return;
132 const SampleType next = std::clamp(amount, SampleType{0}, SampleType{1});
133 if (next == SampleType{1} && link_ != SampleType{1})
134 synchronize_linked_state();
135 link_ = next;
136 }
137
138 SampleType link() const noexcept {
139 return link_;
140 }
141
142 std::array<SampleType, 2> process(SampleType left, SampleType right) {
143 if (link_ == SampleType{0})
144 return {left_.process(left), right_.process(right)};
145
146 if (!std::isfinite(static_cast<double>(left)) ||
147 !std::isfinite(static_cast<double>(right))) {
148 reset();
149 return {SampleType{0}, SampleType{0}};
150 }
151
152 const SampleType left_magnitude = std::abs(left);
153 const SampleType right_magnitude = std::abs(right);
154 const SampleType shared = std::max(left_magnitude, right_magnitude);
155 const SampleType left_detector = left_magnitude + link_ * (shared - left_magnitude);
156 const SampleType right_detector = right_magnitude + link_ * (shared - right_magnitude);
157 const SampleType left_envelope = left_.process(left_detector);
158 const SampleType right_envelope = right_.process(right_detector);
159 if (link_ == SampleType{1}) {
160 const SampleType linked_state =
161 std::max(left_.detector_state(), right_.detector_state());
162 left_.synchronize_detector_state(linked_state);
163 right_.synchronize_detector_state(linked_state);
164 const SampleType linked_envelope = std::max(left_envelope, right_envelope);
165 return {linked_envelope, linked_envelope};
166 }
167 return {left_envelope, right_envelope};
168 }
169
170 std::array<SampleType, 2> current() const {
171 return {left_.current(), right_.current()};
172 }
173
174 void reset() {
175 left_.reset();
176 right_.reset();
177 }
178
179 private:
180 void synchronize_linked_state() noexcept {
181 const SampleType linked_state =
182 std::max(left_.detector_state(), right_.detector_state());
183 left_.synchronize_detector_state(linked_state);
184 right_.synchronize_detector_state(linked_state);
185 }
186
187 EnvelopeFollowerT<SampleType> left_{};
188 EnvelopeFollowerT<SampleType> right_{};
189 SampleType link_ = SampleType{1};
190};
191
194
195} // namespace pulp::signal
Peak/RMS envelope follower with independent attack and release curves.
static SampleType exact_coefficient_for_time_ms(SampleType ms, SampleType sample_rate)
Definition ballistics_filter.hpp:146
TimeConvention
Definition ballistics_filter.hpp:34
Mode
Definition ballistics_filter.hpp:33
float detector_state() const noexcept
Definition ballistics_filter.hpp:161
void synchronize_detector_state(SampleType state) noexcept
Definition ballistics_filter.hpp:165
Discoverable name for Pulp's established peak/RMS envelope follower.
Definition dynamics_contract.hpp:73
friend class StereoEnvelopeFollowerT
Definition dynamics_contract.hpp:85
static SampleType coefficient_for_time_ms(SampleType ms, SampleType sample_rate)
Definition dynamics_contract.hpp:80
EnvelopeFollowerT()
Definition dynamics_contract.hpp:78
BallisticsFilterT< SampleType > Base
Definition dynamics_contract.hpp:75
typename Base::Mode Mode
Definition dynamics_contract.hpp:76
double linear_gain() const noexcept
The equivalent linear amplitude multiplier in [0, 1]; infinite attenuation maps to zero.
Definition dynamics_contract.hpp:53
double signed_db() const noexcept
The equivalent gain value for addition in the dB domain.
Definition dynamics_contract.hpp:47
double db() const noexcept
Non-negative attenuation magnitude in decibels.
Definition dynamics_contract.hpp:42
static GainReduction from_signed_db(double signed_db) noexcept
Adapt a signed gain value, where attenuation is negative dB.
Definition dynamics_contract.hpp:32
GainReduction() noexcept=default
static GainReduction from_magnitude_db(double magnitude_db) noexcept
Adapt an attenuation magnitude, where reduction is positive dB.
Definition dynamics_contract.hpp:37
Two-channel envelope follower with a detector-domain stereo link.
Definition dynamics_contract.hpp:105
void reset()
Definition dynamics_contract.hpp:174
void set_mode(Mode mode)
Definition dynamics_contract.hpp:124
typename EnvelopeFollowerT< SampleType >::Mode Mode
Definition dynamics_contract.hpp:107
std::array< SampleType, 2 > current() const
Definition dynamics_contract.hpp:170
void set_release_ms(SampleType ms)
Definition dynamics_contract.hpp:119
SampleType link() const noexcept
Definition dynamics_contract.hpp:138
void set_attack_ms(SampleType ms)
Definition dynamics_contract.hpp:114
std::array< SampleType, 2 > process(SampleType left, SampleType right)
Definition dynamics_contract.hpp:142
void set_link(SampleType amount)
Definition dynamics_contract.hpp:129
void prepare(SampleType sample_rate)
Definition dynamics_contract.hpp:109
Definition additive_bank.hpp:218
StereoEnvelopeFollowerT< float > StereoEnvelopeFollower
Definition dynamics_contract.hpp:192
@ right
Definition multi_channel_meter.hpp:67
@ left
Definition multi_channel_meter.hpp:66
float magnitude_db(const BiquadT< SampleType > &filter, double freq_hz, double sample_rate)
Ask a live filter what it looks like: the magnitude, in dB, that this BiquadT is applying at freq_hz ...
Definition frequency_response.hpp:130
EnvelopeFollowerT< double > EnvelopeFollower64
Definition dynamics_contract.hpp:97
@ independent
X and Y advance only on their own clocks and never on each other's.
Definition cartesian_walk.hpp:20
EnvelopeFollowerT< float > EnvelopeFollower
Definition dynamics_contract.hpp:96
StereoEnvelopeFollowerT< double > StereoEnvelopeFollower64
Definition dynamics_contract.hpp:193
DynamicsStereoLink
Shared policy for dynamics processors with two detector channels.
Definition dynamics_contract.hpp:15
@ peak_linked
Definition dynamics_contract.hpp:15
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48