1use super::loudness::{Loudness, LoudnessMeter, TAPS, interpolated, oversampling, polyphase};
2use super::mix::gain;
3use crate::media::{AudioBuffer, Error, Result};
4use std::collections::VecDeque;
5
6const LOOKAHEAD: f64 = 0.005;
7const RELEASE: f64 = 0.08;
8const CENTRE: usize = TAPS / 2;
9
10#[derive(Debug, Clone)]
11pub struct Limiter {
12 channels: usize,
13 ceiling: f32,
14 look: usize,
15 release: f32,
16 bank: Vec<[f32; TAPS]>,
17 buffers: Vec<Vec<f32>>,
18 peaks: Vec<f32>,
19 scratch: Vec<f32>,
20 mins: VecDeque<(u64, f32)>,
21 boxed: Vec<f32>,
22 boxed_sum: f64,
23 boxed_at: usize,
24 released: f32,
25 delay: Vec<f32>,
26 delay_at: usize,
27 step: u64,
28}
29
30impl Limiter {
31 pub fn new(rate: u32, channels: u16, ceiling_db: f64) -> Result<Self> {
32 if rate == 0 || channels == 0 {
33 return Err(Error::Invalid(format!(
34 "limiter at {rate} Hz with {channels} channels"
35 )));
36 }
37 let look = ((rate as f64 * LOOKAHEAD).round() as usize).max(1);
38 let latency = look - 1 + CENTRE;
39 let channels = channels as usize;
40 Ok(Limiter {
41 channels,
42 ceiling: 10f64.powf(ceiling_db / 20.0) as f32,
43 look,
44 release: (1.0 - (-1.0 / (RELEASE * rate as f64)).exp()) as f32,
45 bank: polyphase(oversampling(rate as f64).max(2)),
46 buffers: vec![vec![0.0; TAPS - 1]; channels],
47 peaks: Vec::new(),
48 scratch: vec![0.0; 1024],
49 mins: VecDeque::with_capacity(look + 2),
50 boxed: vec![1.0; look],
51 boxed_sum: look as f64,
52 boxed_at: 0,
53 released: 1.0,
54 delay: vec![0.0; (latency + 1) * channels],
55 delay_at: 0,
56 step: 0,
57 })
58 }
59
60 pub fn latency(&self) -> usize {
61 self.look - 1 + CENTRE
62 }
63
64 pub fn process(&mut self, input: &AudioBuffer, out: &mut AudioBuffer) -> Result<()> {
65 if input.channels as usize != self.channels {
66 return Err(Error::Invalid(format!(
67 "limiter expects {} channels, got {}",
68 self.channels, input.channels
69 )));
70 }
71 out.rate = input.rate;
72 out.channels = input.channels;
73 self.run(&input.samples, out);
74 Ok(())
75 }
76
77 pub fn flush(&mut self, out: &mut AudioBuffer) {
78 out.channels = self.channels as u16;
79 let silence = vec![0.0; self.latency() * self.channels];
80 self.run(&silence, out);
81 }
82
83 fn run(&mut self, input: &[f32], out: &mut AudioBuffer) {
84 let nch = self.channels;
85 let frames = input.len() / nch;
86 out.samples.clear();
87 out.samples.reserve(frames * nch);
88 self.peaks.clear();
89 self.peaks.resize(frames, 0.0);
90 for (c, buffer) in self.buffers.iter_mut().enumerate() {
91 buffer.extend(input.iter().skip(c).step_by(nch).take(frames));
92 peaks(
93 buffer,
94 &self.bank,
95 &mut self.scratch,
96 &mut self.peaks,
97 self.ceiling,
98 );
99 let n = buffer.len();
100 buffer.copy_within(n - (TAPS - 1).., 0);
101 buffer.truncate(TAPS - 1);
102 }
103 let slots = self.delay.len() / nch;
104 for (f, frame) in input.chunks_exact(nch).enumerate() {
105 let p = self.peaks[f];
106 let need = if p > self.ceiling {
107 self.ceiling / p
108 } else {
109 1.0
110 };
111 while self.mins.back().is_some_and(|&(_, g)| g >= need) {
112 self.mins.pop_back();
113 }
114 self.mins.push_back((self.step, need));
115 while self
116 .mins
117 .front()
118 .is_some_and(|&(i, _)| i + (self.look as u64) < self.step)
119 {
120 self.mins.pop_front();
121 }
122 let h = self.mins.front().map_or(1.0, |&(_, g)| g);
123 self.released = h.min(self.released + (1.0 - self.released) * self.release);
124 self.boxed_sum += (self.released - self.boxed[self.boxed_at]) as f64;
125 self.boxed[self.boxed_at] = self.released;
126 self.boxed_at = (self.boxed_at + 1) % self.look;
127 let g = (self.boxed_sum / self.look as f64) as f32;
128 let at = self.delay_at * nch;
129 let oldest = ((self.delay_at + 1) % slots) * nch;
130 self.delay[at..at + nch].copy_from_slice(frame);
131 for c in 0..nch {
132 out.samples.push(self.delay[oldest + c] * g);
133 }
134 self.delay_at = (self.delay_at + 1) % slots;
135 self.step += 1;
136 }
137 }
138}
139
140fn peaks(buffer: &[f32], bank: &[[f32; TAPS]], scratch: &mut [f32], peaks: &mut [f32], floor: f32) {
141 let n = peaks.len();
142 let mut start = 0;
143 while start < n {
144 let len = (n - start).min(scratch.len());
145 let out = &mut scratch[..len];
146 interpolated(&buffer[start..start + len + TAPS - 1], bank, out, floor);
147 for (p, &v) in peaks[start..start + len].iter_mut().zip(out.iter()) {
148 *p = p.max(v);
149 }
150 start += len;
151 }
152}
153
154pub fn normalize_gain_db(measured: &Loudness, target_lufs: f64) -> f64 {
155 if measured.integrated.is_finite() && target_lufs.is_finite() {
156 target_lufs - measured.integrated
157 } else {
158 0.0
159 }
160}
161
162pub fn normalize(buffer: &mut AudioBuffer, target_lufs: f64, ceiling_db: f64) -> Result<Loudness> {
163 let mut first = None;
164 for _ in 0..3 {
165 let mut meter = LoudnessMeter::new(buffer.rate, buffer.channels)?;
166 meter.process(buffer)?;
167 let now = meter.summary();
168 first.get_or_insert(now);
169 let g = normalize_gain_db(&now, target_lufs);
170 if g.abs() < 0.05 && now.true_peak <= ceiling_db + 0.05 {
171 break;
172 }
173 gain(buffer, g as f32);
174 if now.true_peak + g > ceiling_db {
175 limit(buffer, ceiling_db)?;
176 }
177 }
178 first.ok_or_else(|| Error::Invalid("nothing measured".into()))
179}
180
181fn limit(buffer: &mut AudioBuffer, ceiling_db: f64) -> Result<()> {
182 let mut limiter = Limiter::new(buffer.rate, buffer.channels, ceiling_db)?;
183 let mut out = AudioBuffer::new(buffer.rate, buffer.channels);
184 let mut tail = AudioBuffer::new(buffer.rate, buffer.channels);
185 limiter.process(buffer, &mut out)?;
186 limiter.flush(&mut tail);
187 out.samples.extend_from_slice(&tail.samples);
188 let skip = limiter.latency() * buffer.channels as usize;
189 buffer.samples.clear();
190 buffer.samples.extend_from_slice(&out.samples[skip..]);
191 Ok(())
192}