#![cfg_attr(not(feature = "std"), no_std)]
use core::fmt;
use core::marker::PhantomData;
use dasp_frame::Frame;
use dasp_ring_buffer as ring_buffer;
use dasp_sample::{FloatSample, Sample};
#[derive(Clone)]
pub struct Rms<F, S>
where
F: Frame,
S: ring_buffer::Slice<Element = F::Float>,
{
frame: PhantomData<F>,
window: ring_buffer::Fixed<S>,
square_sum: F::Float,
}
impl<F, S> Rms<F, S>
where
F: Frame,
S: ring_buffer::Slice<Element = F::Float>,
{
pub fn new(ring_buffer: ring_buffer::Fixed<S>) -> Self {
Rms {
frame: PhantomData,
window: ring_buffer,
square_sum: Frame::EQUILIBRIUM,
}
}
pub fn reset(&mut self)
where
S: ring_buffer::SliceMut,
{
for sample_square in self.window.iter_mut() {
*sample_square = Frame::EQUILIBRIUM;
}
self.square_sum = Frame::EQUILIBRIUM;
}
#[inline]
pub fn window_frames(&self) -> usize {
self.window.len()
}
#[inline]
pub fn next(&mut self, new_frame: F) -> F::Float
where
S: ring_buffer::SliceMut,
{
self.next_squared(new_frame).map(|s| s.sample_sqrt())
}
#[inline]
pub fn next_squared(&mut self, new_frame: F) -> F::Float
where
S: ring_buffer::SliceMut,
{
let new_frame_square = new_frame.to_float_frame().map(|s| s * s);
let removed_frame_square = self.window.push(new_frame_square);
self.square_sum =
self.square_sum
.add_amp(new_frame_square)
.zip_map(removed_frame_square, |s, r| {
let diff = s - r;
if diff < Sample::EQUILIBRIUM {
Sample::EQUILIBRIUM
} else {
diff
}
});
self.calc_rms_squared()
}
pub fn into_parts(self) -> (ring_buffer::Fixed<S>, S::Element) {
let Rms {
window, square_sum, ..
} = self;
(window, square_sum)
}
pub fn current(&self) -> F::Float {
self.calc_rms_squared().map(|s| s.sample_sqrt())
}
fn calc_rms_squared(&self) -> F::Float {
let num_frames_f = Sample::from_sample(self.window.len() as f32);
self.square_sum.map(|s| s / num_frames_f)
}
}
impl<F, S> fmt::Debug for Rms<F, S>
where
F: Frame,
F::Float: fmt::Debug,
S: fmt::Debug + ring_buffer::Slice<Element = F::Float>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("Rms")
.field("frame", &self.frame)
.field("window", &self.window)
.field("square_sum", &self.square_sum)
.finish()
}
}