1use crate::{ProcessCtx, Processor, StateBlob, StateError};
2
3#[derive(Copy, Clone, Debug, PartialEq)]
5pub struct PeakLimiter {
6 threshold: f32,
7 release_ms: f32,
8 envelope: f32,
9 release_coefficient: f32,
10}
11
12impl PeakLimiter {
13 #[must_use]
17 pub fn new(threshold: f32, release_ms: f32) -> Self {
18 Self {
19 threshold: valid_positive(threshold, 1.0),
20 release_ms: valid_positive(release_ms, 50.0),
21 envelope: 0.0,
22 release_coefficient: 0.0,
23 }
24 }
25}
26
27impl Processor for PeakLimiter {
28 fn prepare(&mut self, sample_rate: u32, _max_block: usize) {
29 #[allow(clippy::cast_precision_loss)]
30 let release_samples = self.release_ms * 0.001 * sample_rate.max(1) as f32;
31 self.release_coefficient = libm::expf(-1.0 / release_samples);
32 }
33
34 fn process(&mut self, _ctx: &ProcessCtx, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
35 let frames = inputs
36 .iter()
37 .map(|channel| channel.len())
38 .chain(outputs.iter().map(|channel| channel.len()))
39 .min()
40 .unwrap_or(0);
41 let channels = inputs.len().min(outputs.len());
42 for frame in 0..frames {
43 let peak = inputs[..channels]
44 .iter()
45 .map(|channel| finite_or_zero(channel[frame]).abs())
46 .fold(0.0_f32, f32::max);
47 if !self.envelope.is_finite() {
48 self.envelope = 0.0;
49 }
50 self.envelope = peak.max(self.envelope * self.release_coefficient);
51 if self.envelope < 1.0e-30 {
54 self.envelope = 0.0;
55 }
56 let gain = if self.envelope > self.threshold {
57 self.threshold / self.envelope
58 } else {
59 1.0
60 };
61 for channel in 0..channels {
62 outputs[channel][frame] = finite_or_zero(inputs[channel][frame]) * gain;
63 }
64 }
65 }
66
67 fn reset(&mut self) {
68 self.envelope = 0.0;
69 }
70
71 fn history_samples(&self) -> u32 {
72 crate::processor::settling_samples(f64::from(self.release_coefficient))
73 }
74
75 fn save_state(&self) -> StateBlob {
76 StateBlob::new(self.envelope.to_le_bytes().to_vec())
77 }
78
79 fn load_state(&mut self, state: &StateBlob) -> Result<(), StateError> {
80 let bytes = <[u8; 4]>::try_from(state.as_bytes()).map_err(|_| StateError::Mismatch)?;
81 let envelope = f32::from_le_bytes(bytes);
82 if !envelope.is_finite() || envelope < 0.0 {
83 return Err(StateError::Invalid);
84 }
85 self.envelope = envelope;
86 Ok(())
87 }
88}
89
90fn finite_or_zero(value: f32) -> f32 {
91 if value.is_finite() { value } else { 0.0 }
92}
93
94fn valid_positive(value: f32, fallback: f32) -> f32 {
95 if value.is_finite() && value > 0.0 {
96 value
97 } else {
98 fallback
99 }
100}