use std::{collections::VecDeque, time::Duration};
use instant::Instant;
pub(super) const LEVEL_INTERVAL: Duration = Duration::from_millis(80);
pub(super) const LEVEL_HISTORY: usize = 256;
const PLAYHEAD_LAG: f64 = 1.75;
const PLAYHEAD_PHASE_GAIN: f64 = 0.1;
const PLAYHEAD_PACE_GAIN: f64 = 0.01;
const PLAYHEAD_MAX_LEAD: f32 = 3.;
const NOISE_FLOOR: f32 = 0.06;
const ATTACK: f32 = 0.85;
const RELEASE: f32 = 0.3;
pub(super) struct LevelMeter {
window: usize,
sum: f64,
count: usize,
smoothed: f32,
levels: VecDeque<f32>,
recorded: u64,
anchor: Option<Instant>,
pace: f64,
}
impl LevelMeter {
pub(super) fn new() -> Self {
Self {
window: window_for(16_000, 1),
sum: 0.,
count: 0,
smoothed: 0.,
levels: VecDeque::with_capacity(LEVEL_HISTORY),
recorded: 0,
anchor: None,
pace: LEVEL_INTERVAL.as_secs_f64(),
}
}
pub(super) fn reset(&mut self, sample_rate: u32, channels: u16) {
*self = Self {
window: window_for(sample_rate, channels),
..Self::new()
};
}
pub(super) fn push(&mut self, samples: &[i16]) -> bool {
self.push_at(samples, Instant::now())
}
pub(super) fn push_at(&mut self, samples: &[i16], now: Instant) -> bool {
let mut recorded = false;
for &sample in samples {
let sample = sample as f64 / i16::MAX as f64;
self.sum += sample * sample;
self.count += 1;
if self.count == self.window {
let raw = gate(level_of_rms((self.sum / self.count as f64).sqrt()));
self.smoothed = smooth(self.smoothed, raw);
if self.levels.len() == LEVEL_HISTORY {
self.levels.pop_front();
}
self.levels.push_back(self.smoothed);
self.sum = 0.;
self.count = 0;
self.recorded += 1;
self.align_playhead(now);
recorded = true;
}
}
recorded
}
fn align_playhead(&mut self, now: Instant) {
let Some(anchor) = self.anchor else {
self.anchor = now.checked_sub(LEVEL_INTERVAL);
return;
};
let error = signed_secs(now, anchor) - self.recorded as f64 * self.pace;
let interval = LEVEL_INTERVAL.as_secs_f64();
let pace = (self.pace + error * PLAYHEAD_PACE_GAIN).clamp(interval / 2., interval * 2.);
let elapsed_levels = signed_secs(now, anchor) / self.pace;
let anchor = shift_instant(now, -elapsed_levels * pace);
self.pace = pace;
self.anchor = Some(shift_instant(anchor, error * PLAYHEAD_PHASE_GAIN));
}
pub(super) fn lead_at(&self, now: Instant) -> Option<f32> {
let anchor = self.anchor?;
let playhead = signed_secs(now, anchor) / self.pace - PLAYHEAD_LAG;
let lead = (self.recorded as f64 - playhead) as f32;
Some(lead.clamp(0., PLAYHEAD_MAX_LEAD))
}
pub(super) fn levels(&self) -> impl ExactSizeIterator<Item = f32> + '_ {
self.levels.iter().copied()
}
}
fn signed_secs(later: Instant, earlier: Instant) -> f64 {
match later.checked_duration_since(earlier) {
Some(elapsed) => elapsed.as_secs_f64(),
None => -earlier.duration_since(later).as_secs_f64(),
}
}
fn shift_instant(instant: Instant, secs: f64) -> Instant {
let by = Duration::from_secs_f64(secs.abs());
if secs >= 0. {
instant + by
} else {
instant.checked_sub(by).unwrap_or(instant)
}
}
fn window_for(sample_rate: u32, channels: u16) -> usize {
let per_second = sample_rate as u128 * channels.max(1) as u128;
((per_second * LEVEL_INTERVAL.as_millis() / 1_000) as usize).max(1)
}
pub(super) fn level_of_rms(rms: f64) -> f32 {
if rms <= 0. {
return 0.;
}
let db = 20. * rms.log10();
((db + 50.) / 50.).clamp(0., 1.) as f32
}
fn gate(level: f32) -> f32 {
if level < NOISE_FLOOR { 0. } else { level }
}
pub(super) fn smooth(previous: f32, raw: f32) -> f32 {
let rate = if raw > previous { ATTACK } else { RELEASE };
previous + (raw - previous) * rate
}
#[cfg(test)]
mod tests {
use super::*;
fn tone(amplitude: i16, len: usize) -> Vec<i16> {
(0..len)
.map(|ix| if ix % 2 == 0 { amplitude } else { -amplitude })
.collect()
}
#[test]
fn one_level_per_interval_across_pushes() {
let mut meter = LevelMeter::new();
meter.reset(16_000, 1);
let window = window_for(16_000, 1);
assert!(!meter.push(&tone(1_000, window / 3)));
assert!(meter.push(&tone(1_000, window)));
assert!(meter.push(&tone(1_000, window + window / 6)));
assert_eq!(meter.levels().len(), 2);
assert!(meter.lead_at(Instant::now()).is_some());
}
#[test]
fn the_window_follows_the_stream_format() {
assert_eq!(window_for(16_000, 1), 1_280);
assert_eq!(window_for(48_000, 2), 7_680);
}
#[test]
fn peaks_rise_fast_and_fall_slowly() {
let up = smooth(0., 1.);
assert!(up >= 0.85);
let down = smooth(1., 0.);
assert!(down >= 0.65, "{down}");
assert!(smooth(down, 0.) < down);
}
#[test]
fn background_noise_reads_as_silence() {
let mut meter = LevelMeter::new();
meter.reset(16_000, 1);
meter.push(&tone(32, window_for(16_000, 1)));
assert_eq!(meter.levels().next(), Some(0.));
}
#[test]
fn level_maps_decibels_to_the_unit_range() {
assert_eq!(level_of_rms(0.), 0.);
assert_eq!(level_of_rms(1.), 1.);
assert!((level_of_rms(0.1) - 0.6).abs() < 0.01);
}
#[test]
fn history_is_bounded() {
let mut meter = LevelMeter::new();
meter.reset(16_000, 1);
meter.push(&tone(8_000, window_for(16_000, 1) * (LEVEL_HISTORY + 10)));
assert_eq!(meter.levels().len(), LEVEL_HISTORY);
}
fn level_at(meter: &mut LevelMeter, at: Instant) {
let window = window_for(16_000, 1);
assert!(meter.push_at(&tone(1_000, window), at));
}
#[test]
fn the_playhead_moves_at_an_even_pace_between_levels() {
let mut meter = LevelMeter::new();
meter.reset(16_000, 1);
let start = Instant::now();
level_at(&mut meter, start);
let lead = |ms: u64| meter.lead_at(start + Duration::from_millis(ms)).unwrap();
assert!((lead(0) - 1.75).abs() < 1e-3, "{}", lead(0));
assert!((lead(40) - 1.25).abs() < 1e-3, "{}", lead(40));
assert!((lead(80) - 0.75).abs() < 1e-3, "{}", lead(80));
assert_eq!(lead(1_000), 0.);
}
#[test]
fn uneven_arrivals_do_not_jerk_the_playhead() {
let mut meter = LevelMeter::new();
meter.reset(16_000, 1);
let start = Instant::now();
level_at(&mut meter, start);
let before = meter.lead_at(start + Duration::from_millis(60)).unwrap();
level_at(&mut meter, start + Duration::from_millis(60));
let after = meter.lead_at(start + Duration::from_millis(60)).unwrap();
assert!((after - before - 1.).abs() < 0.05, "{before} → {after}");
}
#[test]
fn the_playhead_settles_on_the_pace_levels_arrive_at() {
for interval_ms in [72u64, 92] {
let mut meter = LevelMeter::new();
meter.reset(16_000, 1);
let start = Instant::now();
for ix in 0..300u64 {
level_at(&mut meter, start + Duration::from_millis(ix * interval_ms));
}
let last = start + Duration::from_millis(299 * interval_ms);
let half = Duration::from_millis(interval_ms / 2);
let at_arrival = meter.lead_at(last).unwrap();
let between = meter.lead_at(last + half).unwrap();
assert!(
(at_arrival - 1.75).abs() < 0.1,
"{interval_ms} ms: {at_arrival}"
);
assert!((between - 1.25).abs() < 0.1, "{interval_ms} ms: {between}");
}
}
#[test]
fn nothing_leads_before_the_first_level() {
let mut meter = LevelMeter::new();
meter.reset(16_000, 1);
assert_eq!(meter.lead_at(Instant::now()), None);
}
}