alpha2_audio/
compressor.rs1#![allow(clippy::cast_precision_loss)]
2
3use crate::{ProcessCtx, Processor, StateBlob, StateError};
4
5pub const MAX_RATIO: f32 = 100.0;
7
8#[derive(Clone, Debug)]
10pub struct Compressor {
11 threshold_db: f32,
12 ratio: f32,
13 attack_ms: f32,
14 release_ms: f32,
15 lookahead_ms: f32,
16 envelope: f32,
17 attack_coefficient: f32,
18 release_coefficient: f32,
19 threshold_amplitude: f32,
22 delay: [Vec<f32>; 2],
23 cursor: usize,
24 latency: u32,
25 bypass: bool,
26}
27
28impl Compressor {
29 #[must_use]
31 pub fn new(
32 threshold_db: f32,
33 ratio: f32,
34 attack_ms: f32,
35 release_ms: f32,
36 lookahead_ms: f32,
37 ) -> Self {
38 Self {
39 threshold_db: finite(threshold_db, -12.0).clamp(-96.0, 0.0),
40 ratio: finite(ratio, 4.0).clamp(1.0, MAX_RATIO),
41 attack_ms: positive(attack_ms, 10.0),
42 release_ms: positive(release_ms, 100.0),
43 lookahead_ms: finite(lookahead_ms, 0.0).clamp(0.0, 100.0),
44 envelope: 0.0,
45 attack_coefficient: 0.0,
46 release_coefficient: 0.0,
47 threshold_amplitude: 0.0,
48 delay: [Vec::new(), Vec::new()],
49 cursor: 0,
50 latency: 0,
51 bypass: false,
52 }
53 }
54
55 pub fn set_bypass(&mut self, bypass: bool) {
57 self.bypass = bypass;
58 }
59}
60
61impl Processor for Compressor {
62 fn prepare(&mut self, sample_rate: u32, _max_block: usize) {
63 let rate = sample_rate.max(1) as f32;
64 self.attack_coefficient = libm::expf(-1.0 / (self.attack_ms * 0.001 * rate));
65 self.release_coefficient = libm::expf(-1.0 / (self.release_ms * 0.001 * rate));
66 self.threshold_amplitude = libm::exp10f(self.threshold_db / 20.0) * (1.0 - 1.0e-4);
69 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
70 {
71 self.latency = (self.lookahead_ms * 0.001 * rate).round() as u32;
72 }
73 let size = usize::try_from(self.latency).unwrap_or(0).max(1);
74 self.delay = [vec![0.0; size], vec![0.0; size]];
75 self.cursor = 0;
76 }
77
78 fn process(&mut self, _ctx: &ProcessCtx, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
79 let channels = inputs.len().min(outputs.len()).min(2);
80 let frames = inputs
81 .iter()
82 .take(channels)
83 .map(|input| input.len())
84 .chain(outputs.iter().take(channels).map(|output| output.len()))
85 .min()
86 .unwrap_or(0);
87 if self.delay[0].is_empty() {
88 return;
89 }
90 for frame in 0..frames {
91 let peak = inputs[..channels]
92 .iter()
93 .map(|channel| finite(channel[frame], 0.0).abs())
94 .fold(0.0_f32, f32::max);
95 if !self.envelope.is_finite() {
96 self.envelope = 0.0;
97 }
98 let coefficient = if peak > self.envelope {
99 self.attack_coefficient
100 } else {
101 self.release_coefficient
102 };
103 self.envelope = coefficient * self.envelope + (1.0 - coefficient) * peak;
104 if self.envelope < 1.0e-30 {
107 self.envelope = 0.0;
108 }
109 let reduction_db = if self.envelope <= self.threshold_amplitude {
113 0.0
114 } else {
115 let level_db = 20.0 * libm::log10f(self.envelope.max(1.0e-12));
116 if level_db > self.threshold_db {
117 (self.threshold_db + (level_db - self.threshold_db) / self.ratio) - level_db
118 } else {
119 0.0
120 }
121 };
122 let gain = if self.bypass || reduction_db == 0.0 {
123 1.0
124 } else {
125 libm::exp10f(reduction_db / 20.0)
126 };
127 for channel in 0..channels {
128 let input = finite(inputs[channel][frame], 0.0);
129 let delayed = if self.latency == 0 {
130 input
131 } else {
132 let delayed = self.delay[channel][self.cursor];
133 self.delay[channel][self.cursor] = input;
134 finite(delayed, 0.0)
135 };
136 outputs[channel][frame] = delayed * gain;
137 }
138 if self.latency != 0 {
139 self.cursor = (self.cursor + 1) % self.delay[0].len();
140 }
141 }
142 }
143
144 fn reset(&mut self) {
145 self.envelope = 0.0;
146 self.cursor = 0;
147 for channel in &mut self.delay {
148 channel.fill(0.0);
149 }
150 }
151 fn latency_samples(&self) -> u32 {
152 self.latency
153 }
154 fn history_samples(&self) -> u32 {
155 if self.ratio <= 1.0 {
156 return 0;
157 }
158 crate::processor::settling_samples(f64::from(
159 self.attack_coefficient.max(self.release_coefficient),
160 ))
161 }
162 fn save_state(&self) -> StateBlob {
163 let mut bytes = Vec::with_capacity(16 + self.delay.iter().map(Vec::len).sum::<usize>() * 4);
164 bytes.extend_from_slice(&self.envelope.to_le_bytes());
165 bytes.extend_from_slice(&(u64::try_from(self.cursor).unwrap_or(0)).to_le_bytes());
166 bytes.extend_from_slice(&self.latency.to_le_bytes());
167 for channel in &self.delay {
168 for value in channel {
169 bytes.extend_from_slice(&value.to_le_bytes());
170 }
171 }
172 StateBlob::new(bytes)
173 }
174 fn load_state(&mut self, state: &StateBlob) -> Result<(), StateError> {
175 let bytes = state.as_bytes();
176 let expected = 16 + self.delay.iter().map(Vec::len).sum::<usize>() * 4;
177 if bytes.len() != expected {
178 return Err(StateError::Mismatch);
179 }
180 let envelope = f32::from_le_bytes(bytes[0..4].try_into().expect("fixed range"));
181 let cursor = usize::try_from(u64::from_le_bytes(
182 bytes[4..12].try_into().expect("fixed range"),
183 ))
184 .ok();
185 let latency = u32::from_le_bytes(bytes[12..16].try_into().expect("fixed range"));
186 if latency != self.latency {
187 return Err(StateError::Mismatch);
188 }
189 let values = bytes[16..]
190 .as_chunks::<4>()
191 .0
192 .iter()
193 .map(|word| f32::from_le_bytes(*word));
194 if !envelope.is_finite()
195 || envelope < 0.0
196 || cursor.is_none_or(|value| value >= self.delay[0].len())
197 || values.clone().any(|value| !value.is_finite())
198 {
199 return Err(StateError::Invalid);
200 }
201 self.envelope = envelope;
202 self.cursor = cursor.unwrap_or(0);
203 for (target, value) in self.delay.iter_mut().flatten().zip(values) {
204 *target = value;
205 }
206 Ok(())
207 }
208}
209
210fn finite(value: f32, fallback: f32) -> f32 {
211 if value.is_finite() { value } else { fallback }
212}
213fn positive(value: f32, fallback: f32) -> f32 {
214 let value = finite(value, fallback);
215 if value > 0.0 { value } else { fallback }
216}