fn catmull_rom(p0: f32, p1: f32, p2: f32, p3: f32, t: f32) -> f32 {
let t2 = t * t;
let t3 = t2 * t;
0.5 * ((2.0 * p1)
+ (-p0 + p2) * t
+ (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2
+ (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3)
}
pub fn true_peak(samples: &[f32]) -> f32 {
if samples.is_empty() { return 0.0; }
let n = samples.len();
let mut max = 0.0f32;
for i in 0..n {
max = max.max(samples[i].abs());
if i + 1 < n {
let p0 = if i > 0 { samples[i - 1] } else { samples[i] };
let p1 = samples[i];
let p2 = samples[i + 1];
let p3 = if i + 2 < n { samples[i + 2] } else { samples[i + 1] };
for k in 1..4 {
let t = k as f32 / 4.0;
let interp = catmull_rom(p0, p1, p2, p3, t);
max = max.max(interp.abs());
}
}
}
max
}
pub fn rms_level(samples: &[f32]) -> f32 {
if samples.is_empty() { return 0.0; }
let sum_sq: f32 = samples.iter().map(|s| s * s).sum();
(sum_sq / samples.len() as f32).sqrt()
}
pub fn rms_level_checked(samples: &[f32]) -> (f32, bool) {
if samples.is_empty() { return (0.0, false); }
let mut sum_sq: f32 = 0.0;
let mut corrupted = false;
for &s in samples {
if !s.is_finite() { corrupted = true; continue; }
sum_sq += s * s;
}
((sum_sq / samples.len() as f32).sqrt(), corrupted)
}
pub fn phase_correlation(left: &[f32], right: &[f32]) -> f32 {
let n = left.len().min(right.len());
if n == 0 { return 0.0; }
let mut sum_lr = 0.0f64;
let mut sum_ll = 0.0f64;
let mut sum_rr = 0.0f64;
for i in 0..n {
let l = left[i] as f64;
let r = right[i] as f64;
sum_lr += l * r;
sum_ll += l * l;
sum_rr += r * r;
}
let denom = (sum_ll * sum_rr).sqrt();
if denom < 1e-12 { return 0.0; }
(sum_lr / denom) as f32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn true_peak_finds_inter_sample() {
let samples = vec![0.0, 0.95, 0.95, 0.0];
let peak = true_peak(&samples);
assert!(peak > 0.95, "Expected inter-sample peak > 0.95, got {}", peak);
}
#[test]
fn true_peak_of_silence_is_zero() {
let samples = vec![0.0; 1024];
assert_eq!(true_peak(&samples), 0.0);
}
#[test]
fn true_peak_full_scale() {
let samples = vec![0.0, 1.0, 0.0];
assert!((true_peak(&samples) - 1.0).abs() < 0.01);
}
#[test]
fn rms_of_sine() {
let samples: Vec<f32> = (0..44100).map(|i| {
(i as f32 * 2.0 * std::f32::consts::PI * 440.0 / 44100.0).sin()
}).collect();
let rms = rms_level(&samples);
assert!((rms - 0.707).abs() < 0.01);
}
#[test]
fn rms_of_silence_is_zero() {
assert_eq!(rms_level(&[0.0; 1024]), 0.0);
}
#[test]
fn phase_correlation_mono_is_one() {
let signal: Vec<f32> = (0..1024).map(|i| (i as f32 * 0.1).sin()).collect();
let corr = phase_correlation(&signal, &signal);
assert!((corr - 1.0).abs() < 0.01);
}
#[test]
fn phase_correlation_inverted_is_negative_one() {
let signal: Vec<f32> = (0..1024).map(|i| (i as f32 * 0.1).sin()).collect();
let inverted: Vec<f32> = signal.iter().map(|s| -s).collect();
let corr = phase_correlation(&signal, &inverted);
assert!((corr - (-1.0)).abs() < 0.01);
}
#[test]
fn phase_correlation_uncorrelated_near_zero() {
let a: Vec<f32> = (0..4096).map(|i| (i as f32 * 0.1).sin()).collect();
let b: Vec<f32> = (0..4096).map(|i| (i as f32 * 0.1731).sin()).collect();
let corr = phase_correlation(&a, &b);
assert!(corr.abs() < 0.2);
}
#[test]
fn rms_level_detects_nan() {
let mut buf = vec![0.5f32; 100];
buf[50] = f32::NAN;
let (_, corrupted) = rms_level_checked(&buf);
assert!(corrupted);
}
#[test]
fn rms_level_clean_buffer() {
let buf: Vec<f32> = (0..100).map(|i| (i as f32 * 0.1).sin()).collect();
let (rms, corrupted) = rms_level_checked(&buf);
assert!(!corrupted);
assert!(rms > 0.0);
}
#[test]
fn rms_level_detects_inf() {
let mut buf = vec![0.5f32; 100];
buf[10] = f32::INFINITY;
let (_, corrupted) = rms_level_checked(&buf);
assert!(corrupted);
}
}