use num_traits::{Num, ToPrimitive};
use crate::Adapter;
fn sqrt_newton(value: f64) -> f64 {
if value.is_nan() {
return value;
}
if value.is_infinite() {
return if value.is_sign_positive() {
f64::INFINITY
} else {
0.0
};
}
if value <= 0.0 {
return 0.0;
}
let mut estimate = f64::from_bits((value.to_bits() + (1023_u64 << 52)) >> 1);
for _ in 0..5 {
estimate = 0.5 * (estimate + value / estimate);
}
estimate
}
pub trait AdapterStats<'a, T>: Adapter<'a, T>
where
T: Clone + ToPrimitive + Num + PartialOrd + 'a,
{
fn channel_rms(&self, channel: usize) -> f64 {
let mut square_sum = 0.0;
if self.frames() == 0 || self.channels() == 0 {
return 0.0;
}
for frame in 0..self.frames() {
let sample = self
.read_sample(channel, frame)
.unwrap_or(T::zero())
.to_f64()
.unwrap_or_default();
square_sum += sample * sample;
}
sqrt_newton(square_sum / self.frames() as f64)
}
fn frame_rms(&self, frame: usize) -> f64 {
let mut square_sum = 0.0;
if self.frames() == 0 || self.channels() == 0 {
return 0.0;
}
for channel in 0..self.channels() {
let sample = self
.read_sample(channel, frame)
.unwrap_or(T::zero())
.to_f64()
.unwrap_or_default();
square_sum += sample * sample;
}
sqrt_newton(square_sum / self.channels() as f64)
}
fn channel_min_and_max(&self, channel: usize) -> (T, T) {
let mut min = T::zero();
let mut max = T::zero();
if self.frames() == 0 || self.channels() == 0 {
return (T::zero(), T::zero());
}
for frame in 0..self.frames() {
let sample = self.read_sample(channel, frame).unwrap_or(T::zero());
if sample < min {
min = sample;
} else if sample > max {
max = sample;
}
}
(min, max)
}
fn channel_peak_to_peak(&self, channel: usize) -> f64 {
let (min, max) = self.channel_min_and_max(channel);
max.to_f64().unwrap_or_default() - min.to_f64().unwrap_or_default()
}
fn frame_min_and_max(&self, frame: usize) -> (T, T) {
let mut min = T::zero();
let mut max = T::zero();
if self.frames() == 0 || self.channels() == 0 {
return (T::zero(), T::zero());
}
for channel in 0..self.channels() {
let sample = self.read_sample(channel, frame).unwrap_or(T::zero());
if sample < min {
min = sample;
} else if sample > max {
max = sample;
}
}
(min, max)
}
fn frame_peak_to_peak(&self, frame: usize) -> f64 {
let (min, max) = self.frame_min_and_max(frame);
max.to_f64().unwrap_or_default() - min.to_f64().unwrap_or_default()
}
}
impl<'a, T, U> AdapterStats<'a, T> for U
where
T: Clone + ToPrimitive + Num + PartialOrd + 'a,
U: Adapter<'a, T>,
{
}
#[cfg(test)]
mod tests {
extern crate alloc;
use crate::stats::AdapterStats;
use crate::tests::MinimalAdapter;
use alloc::vec;
#[test]
fn stats_integer() {
let data = vec![1_i32, 1, -1, -1, 1, 1, -1, -1];
let buffer = MinimalAdapter::new_from_vec(data, 2, 4);
assert_eq!(buffer.channel_rms(0), 1.0);
assert_eq!(buffer.channel_min_and_max(0), (-1, 1));
assert_eq!(buffer.channel_peak_to_peak(0), 2.0);
}
#[test]
fn stats_float() {
let data = vec![1.0_f32, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0];
let buffer = MinimalAdapter::new_from_vec(data, 2, 4);
assert_eq!(buffer.channel_rms(0), 1.0);
assert_eq!(buffer.channel_min_and_max(0), (-1.0, 1.0));
assert_eq!(buffer.channel_peak_to_peak(0), 2.0);
}
#[test]
fn stats_frame_integer() {
let data = vec![-1_i32, 1, -1, 1, -1, 1, -1, 1];
let buffer = MinimalAdapter::new_from_vec(data, 2, 4);
assert_eq!(buffer.frame_rms(0), 1.0);
assert_eq!(buffer.frame_min_and_max(0), (-1, 1));
assert_eq!(buffer.frame_peak_to_peak(0), 2.0);
}
#[test]
fn stats_frame_float() {
let data = vec![-1.0_f32, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0];
let buffer = MinimalAdapter::new_from_vec(data, 2, 4);
assert_eq!(buffer.frame_rms(0), 1.0);
assert_eq!(buffer.frame_min_and_max(0), (-1.0, 1.0));
assert_eq!(buffer.frame_peak_to_peak(0), 2.0);
}
#[test]
fn sqrt_newton_accuracy() {
let test_values: [f64; 12] = [
1.0e-12_f64,
1.0e-9_f64,
1.0e-6_f64,
1.0e-3_f64,
0.1_f64,
0.5_f64,
1.0_f64,
2.0_f64,
10.0_f64,
100.0_f64,
1.0e6_f64,
1.0e12_f64,
];
for value in test_values {
let expected = value.sqrt();
let actual = super::sqrt_newton(value);
let rel_err = (actual - expected).abs() / expected.max(1.0);
assert!(
rel_err < 1.0e-12,
"value={value}, expected={expected}, actual={actual}, rel_err={rel_err}"
);
}
}
#[test]
fn sqrt_newton_special_values() {
assert!(super::sqrt_newton(f64::NAN).is_nan());
assert_eq!(super::sqrt_newton(f64::INFINITY), f64::INFINITY);
assert_eq!(super::sqrt_newton(f64::NEG_INFINITY), 0.0);
}
#[test]
fn sqrt_newton_subnormal_value() {
let values = [
f64::from_bits(1),
f64::from_bits(f64::MIN_POSITIVE.to_bits() - 1),
];
for value in values {
let expected = value.sqrt();
let actual = super::sqrt_newton(value);
let rel_err = (actual - expected).abs() / expected.max(1.0);
assert!(
rel_err < 1.0e-12,
"value={value:e}, expected={expected:e}, actual={actual:e}, rel_err={rel_err:e}"
);
}
}
}