1#[allow(unused_imports)]
7use crate::math::FloatMath;
8use crate::pipeline::DspNode;
9
10#[derive(Debug, Clone, Copy)]
12pub struct DynamicsCompressor {
13 threshold_db: f32,
14 ratio: f32,
15 knee_db: f32,
16 makeup_gain_linear: f32,
17 attack_coeff: f32,
18 release_coeff: f32,
19 envelope_db: f32,
20}
21
22impl DynamicsCompressor {
23 pub fn new(
33 threshold_db: f32,
34 ratio: f32,
35 knee_db: f32,
36 attack_s: f32,
37 release_s: f32,
38 makeup_gain_db: f32,
39 sample_rate_hz: f32,
40 ) -> Self {
41 let attack_coeff = (-1.0 / (attack_s.max(1e-5) * sample_rate_hz)).exp();
42 let release_coeff = (-1.0 / (release_s.max(1e-5) * sample_rate_hz)).exp();
43 let makeup_gain_linear = (10.0f32).powf(makeup_gain_db / 20.0);
44
45 Self {
46 threshold_db,
47 ratio: ratio.max(1.0),
48 knee_db: knee_db.max(0.0),
49 makeup_gain_linear,
50 attack_coeff,
51 release_coeff,
52 envelope_db: 0.0,
53 }
54 }
55
56 pub fn process(&mut self, input: f32) -> f32 {
58 let abs_in = input.abs();
59 let input_db = if abs_in > 1e-6 {
60 20.0 * abs_in.log10()
61 } else {
62 -120.0
63 };
64
65 let target_gain_db = if self.knee_db > 0.0
67 && (2.0 * (input_db - self.threshold_db)).abs() <= self.knee_db
68 {
69 let delta = input_db - self.threshold_db + self.knee_db / 2.0;
70 -(1.0 - 1.0 / self.ratio) * delta * delta / (2.0 * self.knee_db)
71 } else if input_db > self.threshold_db {
72 -(input_db - self.threshold_db) * (1.0 - 1.0 / self.ratio)
73 } else {
74 0.0
75 };
76
77 if target_gain_db < self.envelope_db {
79 self.envelope_db = self.attack_coeff * self.envelope_db + (1.0 - self.attack_coeff) * target_gain_db;
81 } else {
82 self.envelope_db = self.release_coeff * self.envelope_db + (1.0 - self.release_coeff) * target_gain_db;
84 }
85
86 let gain_linear = (10.0f32).powf(self.envelope_db / 20.0) * self.makeup_gain_linear;
87 input * gain_linear
88 }
89
90 pub fn reset(&mut self) {
92 self.envelope_db = 0.0;
93 }
94}
95
96impl DspNode<f32> for DynamicsCompressor {
97 #[inline(always)]
98 fn process_sample(&mut self, input: f32) -> f32 {
99 self.process(input)
100 }
101}
102
103#[derive(Debug, Clone, Copy)]
105pub struct NoiseGate {
106 threshold_db: f32,
107 reduction_linear: f32,
108 attack_coeff: f32,
109 release_coeff: f32,
110 envelope_linear: f32,
111}
112
113impl NoiseGate {
114 pub fn new(threshold_db: f32, reduction_db: f32, attack_s: f32, release_s: f32, sample_rate_hz: f32) -> Self {
121 let attack_coeff = (-1.0 / (attack_s.max(1e-5) * sample_rate_hz)).exp();
122 let release_coeff = (-1.0 / (release_s.max(1e-5) * sample_rate_hz)).exp();
123 let reduction_linear = (10.0f32).powf(reduction_db / 20.0);
124
125 Self {
126 threshold_db,
127 reduction_linear,
128 attack_coeff,
129 release_coeff,
130 envelope_linear: 0.0,
131 }
132 }
133
134 pub fn process(&mut self, input: f32) -> f32 {
136 let abs_in = input.abs();
137 let input_db = if abs_in > 1e-6 {
138 20.0 * abs_in.log10()
139 } else {
140 -120.0
141 };
142
143 let target_gain = if input_db >= self.threshold_db {
144 1.0
145 } else {
146 self.reduction_linear
147 };
148
149 if target_gain > self.envelope_linear {
150 self.envelope_linear = self.attack_coeff * self.envelope_linear + (1.0 - self.attack_coeff) * target_gain;
151 } else {
152 self.envelope_linear = self.release_coeff * self.envelope_linear + (1.0 - self.release_coeff) * target_gain;
153 }
154
155 input * self.envelope_linear
156 }
157
158 pub fn reset(&mut self) {
160 self.envelope_linear = 0.0;
161 }
162}
163
164impl DspNode<f32> for NoiseGate {
165 #[inline(always)]
166 fn process_sample(&mut self, input: f32) -> f32 {
167 self.process(input)
168 }
169}