#[must_use]
pub fn spectral_flatness(magnitude: &[f32]) -> f32 {
if magnitude.is_empty() {
return 0.0;
}
let min_magnitude = 1e-10_f32;
let valid_magnitudes: Vec<f32> = magnitude.iter().map(|&m| m.max(min_magnitude)).collect();
let log_sum: f32 = valid_magnitudes.iter().map(|&m| m.ln()).sum();
let geometric_mean = (log_sum / valid_magnitudes.len() as f32).exp();
let arithmetic_mean: f32 = valid_magnitudes.iter().sum::<f32>() / valid_magnitudes.len() as f32;
if arithmetic_mean > 0.0 {
geometric_mean / arithmetic_mean
} else {
0.0
}
}
#[must_use]
pub fn spectral_flatness_db(magnitude: &[f32]) -> f32 {
let flatness = spectral_flatness(magnitude);
if flatness > 0.0 {
10.0 * flatness.log10()
} else {
-100.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spectral_flatness() {
let noise = vec![1.0; 100];
let flatness = spectral_flatness(&noise);
assert!((flatness - 1.0).abs() < 0.01);
let mut tone = vec![0.01; 100];
tone[50] = 1.0;
let flatness = spectral_flatness(&tone);
assert!(flatness >= 0.0 && flatness < 1.0);
}
#[test]
fn test_flatness_db() {
let magnitude = vec![1.0; 100];
let db = spectral_flatness_db(&magnitude);
assert!((db - 0.0).abs() < 0.1); }
}