|
| 1 | +use nih_plug::buffer::Buffer; |
| 2 | +use std::ops::{Index, IndexMut}; |
| 3 | + |
| 4 | +use super::{RingBuffer, VisualizerBuffer}; |
| 5 | + |
| 6 | +/// Stores RMS amplitudes over time. |
| 7 | +/// |
| 8 | +/// This buffer keeps track of the windowed root mean squared amplitudes of a |
| 9 | +/// signal. |
| 10 | +/// |
| 11 | +/// It needs to be provided a sample rate after initialization - do this inside your |
| 12 | +/// [`initialize()`](nih_plug::plugin::Plugin::initialize)` function! |
| 13 | +#[derive(Clone, Default)] |
| 14 | +pub struct RMSBuffer { |
| 15 | + buffer: RingBuffer<f32>, |
| 16 | + /// The duration of RMS values that the buffer captures, in s (example: 10.0) |
| 17 | + duration: f32, |
| 18 | + /// The time window in which the RMS is calculated, in ms (example: 300.0) |
| 19 | + rms_duration: f32, |
| 20 | + |
| 21 | + /// The sample rate (example: 44100.0) |
| 22 | + sample_rate: f32, |
| 23 | + /// The current time |
| 24 | + t: f32, |
| 25 | + /// The squared sum accumulator - When a sample gets enqueued, its squared value |
| 26 | + /// is added into this. When it gets removed, its squared value is removed from |
| 27 | + /// here. |
| 28 | + sum_acc: f32, |
| 29 | + /// The time it takes (in samples) for an RMS value to get enqueued |
| 30 | + sample_delta: f32, |
| 31 | + /// The buffer of squared sums - This is needed so that the squared samples can |
| 32 | + /// be removed from the `sum_acc` |
| 33 | + squared_buffer: RingBuffer<f32>, |
| 34 | +} |
| 35 | + |
| 36 | +impl RMSBuffer { |
| 37 | + /// Creates a new RMSBuffer |
| 38 | + /// |
| 39 | + /// * `size` - The length of the buffer in samples |
| 40 | + /// * `duration` - The duration (in seconds) of the RMS data inside the buffer, in seconds |
| 41 | + /// * `rms_duration` - The duration of each RMS window, in milliseconds |
| 42 | + pub fn new(size: usize, duration: f32, rms_duration: f32) -> Self { |
| 43 | + Self { |
| 44 | + buffer: RingBuffer::<f32>::new(size), |
| 45 | + duration, |
| 46 | + rms_duration, |
| 47 | + |
| 48 | + // These values will be needed internally. |
| 49 | + sample_delta: 0.0, |
| 50 | + t: 0.0, |
| 51 | + sum_acc: 0.0, |
| 52 | + sample_rate: 0.0, |
| 53 | + squared_buffer: RingBuffer::<f32>::new(0), |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + pub fn set_sample_rate(&mut self, sample_rate: f32) { |
| 58 | + self.sample_rate = sample_rate; |
| 59 | + self.update(); |
| 60 | + } |
| 61 | + |
| 62 | + fn update(&mut self) { |
| 63 | + self.sample_delta = |
| 64 | + ((self.sample_rate as f64 * self.duration as f64) / self.buffer.len() as f64) as f32; |
| 65 | + |
| 66 | + let rms_size = (self.sample_rate as f64 * (self.rms_duration as f64 / 1000.0)) as usize; |
| 67 | + self.squared_buffer.resize(rms_size); |
| 68 | + |
| 69 | + self.clear(); |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +impl Index<usize> for RMSBuffer { |
| 74 | + type Output = f32; |
| 75 | + |
| 76 | + fn index(&self, index: usize) -> &Self::Output { |
| 77 | + &self.buffer[index] |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +impl IndexMut<usize> for RMSBuffer { |
| 82 | + fn index_mut(&mut self, index: usize) -> &mut Self::Output { |
| 83 | + &mut self.buffer[index] |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +impl VisualizerBuffer<f32> for RMSBuffer { |
| 88 | + fn enqueue(self: &mut Self, value: f32) { |
| 89 | + let squared_value = value * value; |
| 90 | + |
| 91 | + self.sum_acc -= self.squared_buffer.tail(); |
| 92 | + self.squared_buffer.enqueue(squared_value); |
| 93 | + self.sum_acc += squared_value; |
| 94 | + |
| 95 | + self.t -= 1.0; |
| 96 | + |
| 97 | + if self.t <= 0.0 { |
| 98 | + let rms = (self.sum_acc / self.squared_buffer.len() as f32).sqrt(); |
| 99 | + if rms.is_nan() { |
| 100 | + self.buffer.enqueue(0.0); |
| 101 | + } else { |
| 102 | + self.buffer.enqueue(rms); |
| 103 | + } |
| 104 | + self.t += self.sample_delta |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + fn enqueue_buffer(self: &mut Self, buffer: &mut Buffer, channel: Option<usize>) { |
| 109 | + match channel { |
| 110 | + Some(channel) => { |
| 111 | + for sample in buffer.as_slice()[channel].into_iter() { |
| 112 | + self.enqueue(*sample); |
| 113 | + } |
| 114 | + } |
| 115 | + None => { |
| 116 | + for sample in buffer.iter_samples() { |
| 117 | + self.enqueue( |
| 118 | + (1. / (&sample).len() as f32) * sample.into_iter().map(|x| *x).sum::<f32>(), |
| 119 | + ); |
| 120 | + } |
| 121 | + } |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + fn clear(self: &mut Self) { |
| 126 | + self.sum_acc = 0.0; |
| 127 | + self.t = self.sample_delta; |
| 128 | + self.buffer.clear(); |
| 129 | + self.squared_buffer.clear(); |
| 130 | + } |
| 131 | + |
| 132 | + fn grow(self: &mut Self, size: usize) { |
| 133 | + self.clear(); |
| 134 | + self.buffer.grow(size); |
| 135 | + } |
| 136 | + |
| 137 | + fn shrink(self: &mut Self, size: usize) { |
| 138 | + self.clear(); |
| 139 | + self.buffer.shrink(size); |
| 140 | + } |
| 141 | + |
| 142 | + fn len(self: &Self) -> usize { |
| 143 | + self.buffer.len() |
| 144 | + } |
| 145 | +} |
0 commit comments