use num_traits::{Num, ToPrimitive};
use crate::Adapter;
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() {
square_sum += self
.read_sample(channel, frame)
.unwrap_or(T::zero())
.to_f64()
.unwrap_or_default()
.powi(2);
}
(square_sum / self.frames() as f64).sqrt()
}
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() {
square_sum += self
.read_sample(channel, frame)
.unwrap_or(T::zero())
.to_f64()
.unwrap_or_default()
.powi(2);
}
(square_sum / self.frames() as f64).sqrt()
}
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 {
use crate::stats::AdapterStats;
use crate::tests::MinimalAdapter;
#[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);
}
}