dasp_rms/
lib.rs

1//! Root mean square calculation over a signal.
2//!
3//! The primary type of interest in this module is the [**Rms**](./struct.Rms).
4
5#![cfg_attr(not(feature = "std"), no_std)]
6
7use core::fmt;
8use core::marker::PhantomData;
9use dasp_frame::Frame;
10use dasp_ring_buffer as ring_buffer;
11use dasp_sample::{FloatSample, Sample};
12
13/// Iteratively extracts the RMS (root mean square) envelope from a window over a signal of sample
14/// `Frame`s.
15#[derive(Clone)]
16pub struct Rms<F, S>
17where
18    F: Frame,
19    S: ring_buffer::Slice<Element = F::Float>,
20{
21    /// The type of `Frame`s for which the RMS will be calculated.
22    frame: PhantomData<F>,
23    /// The ringbuffer of frame sample squares (i.e. `sample * sample`) used to calculate the RMS
24    /// per frame.
25    ///
26    /// When a new frame is received, the **Rms** pops the front sample_square and adds the new
27    /// sample_square to the back.
28    window: ring_buffer::Fixed<S>,
29    /// The sum total of all sample_squares currently within the **Rms**'s `window` ring buffer.
30    square_sum: F::Float,
31}
32
33impl<F, S> Rms<F, S>
34where
35    F: Frame,
36    S: ring_buffer::Slice<Element = F::Float>,
37{
38    /// Construct a new **Rms** that uses the given ring buffer as its window.
39    ///
40    /// The window size of the **Rms** is equal to the length of the given ring buffer.
41    ///
42    /// ```
43    /// use dasp_ring_buffer as ring_buffer;
44    /// use dasp_rms::Rms;
45    ///
46    /// fn main() {
47    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
48    ///     let mut rms = Rms::new(window);
49    ///     rms.next([0.5]);
50    /// }
51    /// ```
52    pub fn new(ring_buffer: ring_buffer::Fixed<S>) -> Self {
53        Rms {
54            frame: PhantomData,
55            window: ring_buffer,
56            square_sum: Frame::EQUILIBRIUM,
57        }
58    }
59
60    /// Zeroes the square_sum and the buffer of the `window`.
61    ///
62    /// ```
63    /// use dasp_ring_buffer as ring_buffer;
64    /// use dasp_rms::Rms;
65    ///
66    /// fn main() {
67    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
68    ///     let mut rms = Rms::new(window);
69    ///     rms.next([0.6]);
70    ///     rms.next([0.9]);
71    ///     rms.reset();
72    ///     assert_eq!(rms.current(), [0.0]);
73    /// }
74    /// ```
75    pub fn reset(&mut self)
76    where
77        S: ring_buffer::SliceMut,
78    {
79        for sample_square in self.window.iter_mut() {
80            *sample_square = Frame::EQUILIBRIUM;
81        }
82        self.square_sum = Frame::EQUILIBRIUM;
83    }
84
85    /// The length of the window as a number of frames.
86    ///
87    /// ```
88    /// use dasp_ring_buffer as ring_buffer;
89    /// use dasp_rms::Rms;
90    ///
91    /// fn main() {
92    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
93    ///     let mut rms = Rms::new(window);
94    ///     assert_eq!(rms.window_frames(), 4);
95    ///     rms.next([0.5]);
96    ///     assert_eq!(rms.window_frames(), 4);
97    /// }
98    /// ```
99    #[inline]
100    pub fn window_frames(&self) -> usize {
101        self.window.len()
102    }
103
104    /// The next RMS given the new frame in the sequence.
105    ///
106    /// The **Rms** pops its front frame and adds the new frame to the back.
107    ///
108    /// The yielded RMS is the RMS of all frame squares in the `window` after the new frame is
109    /// added.
110    ///
111    /// This method uses `Rms::next_squared` internally and then calculates the square root.
112    ///
113    /// ```
114    /// use dasp_ring_buffer as ring_buffer;
115    /// use dasp_rms::Rms;
116    ///
117    /// fn main() {
118    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
119    ///     let mut rms = Rms::new(window);
120    ///     assert_eq!(rms.next([1.0]), [0.5]);
121    ///     assert_eq!(rms.next([-1.0]), [0.7071067811865476]);
122    ///     assert_eq!(rms.next([1.0]), [0.8660254037844386]);
123    ///     assert_eq!(rms.next([-1.0]), [1.0]);
124    /// }
125    /// ```
126    #[inline]
127    pub fn next(&mut self, new_frame: F) -> F::Float
128    where
129        S: ring_buffer::SliceMut,
130    {
131        self.next_squared(new_frame).map(|s| s.sample_sqrt())
132    }
133
134    /// The same as **Rms::next**, but does not calculate the final square root required to
135    /// determine the RMS.
136    #[inline]
137    pub fn next_squared(&mut self, new_frame: F) -> F::Float
138    where
139        S: ring_buffer::SliceMut,
140    {
141        // Determine the square of the new frame.
142        let new_frame_square = new_frame.to_float_frame().map(|s| s * s);
143        // Push back the new frame_square.
144        let removed_frame_square = self.window.push(new_frame_square);
145        // Add the new frame square and subtract the removed frame square.
146        self.square_sum =
147            self.square_sum
148                .add_amp(new_frame_square)
149                .zip_map(removed_frame_square, |s, r| {
150                    let diff = s - r;
151                    // Don't let floating point rounding errors put us below 0.0.
152                    if diff < Sample::EQUILIBRIUM {
153                        Sample::EQUILIBRIUM
154                    } else {
155                        diff
156                    }
157                });
158        self.calc_rms_squared()
159    }
160
161    /// Consumes the **Rms** and returns its inner ring buffer of squared frames along with a frame
162    /// representing the sum of all frame squares contained within the ring buffer.
163    pub fn into_parts(self) -> (ring_buffer::Fixed<S>, S::Element) {
164        let Rms {
165            window, square_sum, ..
166        } = self;
167        (window, square_sum)
168    }
169
170    /// Calculates the RMS of all frames currently stored within the inner window.
171    ///
172    /// ```
173    /// use dasp_ring_buffer as ring_buffer;
174    /// use dasp_rms::Rms;
175    ///
176    /// fn main() {
177    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
178    ///     let mut rms = Rms::new(window);
179    ///     assert_eq!(rms.current(), [0.0]);
180    ///     rms.next([1.0]);
181    ///     rms.next([1.0]);
182    ///     rms.next([1.0]);
183    ///     rms.next([1.0]);
184    ///     assert_eq!(rms.current(), [1.0]);
185    /// }
186    /// ```
187    pub fn current(&self) -> F::Float {
188        self.calc_rms_squared().map(|s| s.sample_sqrt())
189    }
190
191    fn calc_rms_squared(&self) -> F::Float {
192        let num_frames_f = Sample::from_sample(self.window.len() as f32);
193        self.square_sum.map(|s| s / num_frames_f)
194    }
195}
196
197impl<F, S> fmt::Debug for Rms<F, S>
198where
199    F: Frame,
200    F::Float: fmt::Debug,
201    S: fmt::Debug + ring_buffer::Slice<Element = F::Float>,
202{
203    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
204        f.debug_struct("Rms")
205            .field("frame", &self.frame)
206            .field("window", &self.window)
207            .field("square_sum", &self.square_sum)
208            .finish()
209    }
210}
OSZAR »