Skip to content

Commit 462afa5

Browse files
committed
Add RMS buffer
1 parent 3b48b88 commit 462afa5

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed

src/utils/buffers/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
pub mod minima_buffer;
22
pub mod peak_buffer;
33
pub mod ring_buffer;
4+
mod rms_buffer;
45
pub mod waveform_buffer;
56

67
use std::ops::{Index, IndexMut};
78

89
pub use minima_buffer::MinimaBuffer;
910
pub use peak_buffer::PeakBuffer;
1011
pub use ring_buffer::RingBuffer;
12+
pub use rms_buffer::RMSBuffer;
1113
pub use waveform_buffer::WaveformBuffer;
1214

1315
pub trait VisualizerBuffer<T>: Index<usize> + IndexMut<usize> {

src/utils/buffers/rms_buffer.rs

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

0 commit comments

Comments
 (0)