pub const SILENCE_LUFS: f32 = -70.0;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Biquad {
pub b0: f64,
pub b1: f64,
pub b2: f64,
pub a1: f64,
pub a2: f64,
}
pub fn k_weighting(rate: u32) -> [Biquad; 2] {
let rate = rate as f64;
let (f0, gain_db, q) = (1681.974450955533, 3.999843853973347, 0.7071752369554196);
let k = (std::f64::consts::PI * f0 / rate).tan();
let vh = 10f64.powf(gain_db / 20.0);
let vb = vh.powf(0.4996667741545416);
let a0 = 1.0 + k / q + k * k;
let shelf = Biquad {
b0: (vh + vb * k / q + k * k) / a0,
b1: 2.0 * (k * k - vh) / a0,
b2: (vh - vb * k / q + k * k) / a0,
a1: 2.0 * (k * k - 1.0) / a0,
a2: (1.0 - k / q + k * k) / a0,
};
let (f0, q) = (38.13547087602444, 0.5003270373238773);
let k = (std::f64::consts::PI * f0 / rate).tan();
let a0 = 1.0 + k / q + k * k;
let high_pass = Biquad {
b0: 1.0,
b1: -2.0,
b2: 1.0,
a1: 2.0 * (k * k - 1.0) / a0,
a2: (1.0 - k / q + k * k) / a0,
};
[shelf, high_pass]
}
pub struct Meter {
stages: [Biquad; 2],
state: Vec<[[f64; 2]; 2]>,
channels: usize,
channel: usize,
block_frames: usize,
frames: usize,
sum: f64,
blocks: [f64; 4],
next_block: usize,
filled: usize,
peak: f32,
}
impl Meter {
pub fn new(rate: u32, channels: u16) -> Self {
let channels = channels.max(1) as usize;
Meter {
stages: k_weighting(rate),
state: vec![[[0.0; 2]; 2]; channels],
channels,
channel: 0,
block_frames: (rate as usize / 10).max(1),
frames: 0,
sum: 0.0,
blocks: [0.0; 4],
next_block: 0,
filled: 0,
peak: 0.0,
}
}
pub fn sample(&mut self, x: f32) -> Option<f32> {
self.peak = self.peak.max(x.abs());
let mut y = x as f64;
for (stage, z) in self.stages.iter().zip(self.state[self.channel].iter_mut()) {
let out = stage.b0 * y + z[0];
z[0] = stage.b1 * y - stage.a1 * out + z[1];
z[1] = stage.b2 * y - stage.a2 * out;
for v in z.iter_mut() {
if v.abs() < 1e-30 {
*v = 0.0;
}
}
y = out;
}
self.sum += y * y;
self.channel += 1;
if self.channel < self.channels {
return None;
}
self.channel = 0;
self.frames += 1;
if self.frames < self.block_frames {
return None;
}
self.blocks[self.next_block] = self.sum / self.frames as f64;
self.next_block = (self.next_block + 1) % self.blocks.len();
self.filled = (self.filled + 1).min(self.blocks.len());
self.frames = 0;
self.sum = 0.0;
let power = self.blocks[..self.filled].iter().sum::<f64>() / self.filled as f64;
Some((-0.691 + 10.0 * power.log10()) as f32)
}
pub fn take_peak(&mut self) -> f32 {
std::mem::take(&mut self.peak)
}
}