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