1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
//! Root mean square calculation over a signal.
//!
//! The primary type of interest in this module is the [**Rms**](./struct.Rms).

#![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};

/// Iteratively extracts the RMS (root mean square) envelope from a window over a signal of sample
/// `Frame`s.
#[derive(Clone)]
pub struct Rms<F, S>
where
    F: Frame,
    S: ring_buffer::Slice<Element = F::Float>,
{
    /// The type of `Frame`s for which the RMS will be calculated.
    frame: PhantomData<F>,
    /// The ringbuffer of frame sample squares (i.e. `sample * sample`) used to calculate the RMS
    /// per frame.
    ///
    /// When a new frame is received, the **Rms** pops the front sample_square and adds the new
    /// sample_square to the back.
    window: ring_buffer::Fixed<S>,
    /// The sum total of all sample_squares currently within the **Rms**'s `window` ring buffer.
    square_sum: F::Float,
}

impl<F, S> Rms<F, S>
where
    F: Frame,
    S: ring_buffer::Slice<Element = F::Float>,
{
    /// Construct a new **Rms** that uses the given ring buffer as its window.
    ///
    /// The window size of the **Rms** is equal to the length of the given ring buffer.
    ///
    /// ```
    /// use dasp_ring_buffer as ring_buffer;
    /// use dasp_rms::Rms;
    ///
    /// fn main() {
    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
    ///     let mut rms = Rms::new(window);
    ///     rms.next([0.5]);
    /// }
    /// ```
    pub fn new(ring_buffer: ring_buffer::Fixed<S>) -> Self {
        Rms {
            frame: PhantomData,
            window: ring_buffer,
            square_sum: Frame::EQUILIBRIUM,
        }
    }

    /// Zeroes the square_sum and the buffer of the `window`.
    ///
    /// ```
    /// use dasp_ring_buffer as ring_buffer;
    /// use dasp_rms::Rms;
    ///
    /// fn main() {
    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
    ///     let mut rms = Rms::new(window);
    ///     rms.next([0.6]);
    ///     rms.next([0.9]);
    ///     rms.reset();
    ///     assert_eq!(rms.current(), [0.0]);
    /// }
    /// ```
    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;
    }

    /// The length of the window as a number of frames.
    ///
    /// ```
    /// use dasp_ring_buffer as ring_buffer;
    /// use dasp_rms::Rms;
    ///
    /// fn main() {
    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
    ///     let mut rms = Rms::new(window);
    ///     assert_eq!(rms.window_frames(), 4);
    ///     rms.next([0.5]);
    ///     assert_eq!(rms.window_frames(), 4);
    /// }
    /// ```
    #[inline]
    pub fn window_frames(&self) -> usize {
        self.window.len()
    }

    /// The next RMS given the new frame in the sequence.
    ///
    /// The **Rms** pops its front frame and adds the new frame to the back.
    ///
    /// The yielded RMS is the RMS of all frame squares in the `window` after the new frame is
    /// added.
    ///
    /// This method uses `Rms::next_squared` internally and then calculates the square root.
    ///
    /// ```
    /// use dasp_ring_buffer as ring_buffer;
    /// use dasp_rms::Rms;
    ///
    /// fn main() {
    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
    ///     let mut rms = Rms::new(window);
    ///     assert_eq!(rms.next([1.0]), [0.5]);
    ///     assert_eq!(rms.next([-1.0]), [0.7071067811865476]);
    ///     assert_eq!(rms.next([1.0]), [0.8660254037844386]);
    ///     assert_eq!(rms.next([-1.0]), [1.0]);
    /// }
    /// ```
    #[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())
    }

    /// The same as **Rms::next**, but does not calculate the final square root required to
    /// determine the RMS.
    #[inline]
    pub fn next_squared(&mut self, new_frame: F) -> F::Float
    where
        S: ring_buffer::SliceMut,
    {
        // Determine the square of the new frame.
        let new_frame_square = new_frame.to_float_frame().map(|s| s * s);
        // Push back the new frame_square.
        let removed_frame_square = self.window.push(new_frame_square);
        // Add the new frame square and subtract the removed frame square.
        self.square_sum =
            self.square_sum
                .add_amp(new_frame_square)
                .zip_map(removed_frame_square, |s, r| {
                    let diff = s - r;
                    // Don't let floating point rounding errors put us below 0.0.
                    if diff < Sample::EQUILIBRIUM {
                        Sample::EQUILIBRIUM
                    } else {
                        diff
                    }
                });
        self.calc_rms_squared()
    }

    /// Consumes the **Rms** and returns its inner ring buffer of squared frames along with a frame
    /// representing the sum of all frame squares contained within the ring buffer.
    pub fn into_parts(self) -> (ring_buffer::Fixed<S>, S::Element) {
        let Rms {
            window, square_sum, ..
        } = self;
        (window, square_sum)
    }

    /// Calculates the RMS of all frames currently stored within the inner window.
    ///
    /// ```
    /// use dasp_ring_buffer as ring_buffer;
    /// use dasp_rms::Rms;
    ///
    /// fn main() {
    ///     let window = ring_buffer::Fixed::from([[0.0]; 4]);
    ///     let mut rms = Rms::new(window);
    ///     assert_eq!(rms.current(), [0.0]);
    ///     rms.next([1.0]);
    ///     rms.next([1.0]);
    ///     rms.next([1.0]);
    ///     rms.next([1.0]);
    ///     assert_eq!(rms.current(), [1.0]);
    /// }
    /// ```
    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()
    }
}