use crate::compute_rms;
pub fn crest_factor(samples: &[f32]) -> f32 {
if samples.is_empty() {
return 0.0;
}
let peak = samples.iter().map(|&x| x.abs()).fold(0.0_f32, f32::max);
let rms = compute_rms(samples);
if rms > 0.0 {
peak / rms
} else {
0.0
}
}
#[must_use]
pub fn crest_factor_db(samples: &[f32]) -> f32 {
let crest = crest_factor(samples);
if crest > 0.0 {
20.0 * crest.log10()
} else {
-100.0
}
}
pub fn crest_factor_over_time(samples: &[f32], window_size: usize) -> Vec<f32> {
if window_size == 0 || samples.len() < window_size {
return vec![];
}
samples
.windows(window_size)
.step_by(window_size / 2) .map(crest_factor)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_crest_factor() {
let samples: Vec<f32> = (0..1000)
.map(|i| (2.0 * std::f32::consts::PI * i as f32 / 100.0).sin())
.collect();
let crest = crest_factor(&samples);
assert!((crest - 1.414).abs() < 0.1);
}
#[test]
fn test_crest_factor_db() {
let samples = vec![1.0, -1.0, 1.0, -1.0];
let crest_db = crest_factor_db(&samples);
assert!(crest_db >= 0.0); }
#[test]
fn test_crest_over_time() {
let samples = vec![1.0; 1000];
let crest_track = crest_factor_over_time(&samples, 100);
assert!(!crest_track.is_empty());
for &crest in &crest_track {
assert!((crest - 1.0).abs() < 0.01);
}
}
}