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() } }