audio_processor_analysis/
running_rms_processor.rs1use std::sync::atomic::{AtomicUsize, Ordering};
25use std::time::Duration;
26
27use audio_garbage_collector::{Handle, Shared, SharedCell};
28use audio_processor_traits::atomic_float::{AtomicFloatRepresentable, AtomicValue};
29use audio_processor_traits::{AudioBuffer, AudioContext, AudioProcessor, Float};
30
31pub type RunningRMSProcessorHandle = RunningRMSProcessorHandleImpl<f32>;
32
33pub struct RunningRMSProcessorHandleImpl<ST: AtomicFloatRepresentable> {
35 window: SharedCell<AudioBuffer<ST::AtomicType>>,
36 running_sums: SharedCell<Vec<ST::AtomicType>>,
37 cursor: AtomicUsize,
38 duration_micros: AtomicUsize,
39}
40
41impl<ST> RunningRMSProcessorHandleImpl<ST>
42where
43 ST: AtomicFloatRepresentable + Float,
44 ST::AtomicType: Send + Sync + Clone + 'static,
45{
46 fn new(gc_handle: &Handle) -> Self {
48 RunningRMSProcessorHandleImpl {
49 window: SharedCell::new(Shared::new(gc_handle, AudioBuffer::empty())),
50 running_sums: SharedCell::new(Shared::new(gc_handle, Vec::new())),
51 cursor: AtomicUsize::new(0),
52 duration_micros: AtomicUsize::new(0),
53 }
54 }
55
56 fn cursor(&self) -> usize {
57 self.cursor.load(Ordering::Relaxed)
58 }
59
60 #[numeric_literals::replace_float_literals(ST::from(literal).unwrap())]
62 fn resize(&self, gc_handle: &Handle, num_channels: usize, num_samples: usize) {
63 self.cursor.store(0, Ordering::Relaxed);
64
65 let mut window = AudioBuffer::empty();
66 window.resize_with(num_channels, num_samples, || ST::AtomicType::from(0.0));
67 self.window.replace(Shared::new(gc_handle, window));
68
69 let mut running_sums = Vec::new();
70 running_sums.resize(num_channels, ST::AtomicType::from(0.0));
71 self.running_sums
72 .replace(Shared::new(gc_handle, running_sums));
73 }
74
75 #[numeric_literals::replace_float_literals(ST::from(literal).unwrap())]
77 pub fn calculate_rms(&self, channel: usize) -> ST {
78 let running_sums = self.running_sums.get();
79 if channel >= running_sums.len() {
80 return 0.0;
81 }
82
83 let sum = running_sums[channel].get().max(0.0);
84 let num_samples = ST::from(self.window.get().num_samples()).unwrap();
85 (sum / num_samples).sqrt()
86 }
87}
88
89pub type RunningRMSProcessor = RunningRMSProcessorImpl<f32>;
90
91pub struct RunningRMSProcessorImpl<ST: AtomicFloatRepresentable + Float> {
98 handle: Shared<RunningRMSProcessorHandleImpl<ST>>,
99 duration_samples: usize,
100 duration: Duration,
101 gc_handle: Handle,
102}
103
104impl<ST> RunningRMSProcessorImpl<ST>
105where
106 ST: AtomicFloatRepresentable + Float + 'static,
107 ST::AtomicType: Send + Sync + Clone + 'static,
108{
109 pub fn new_with_duration(gc_handle: &Handle, duration: Duration) -> Self {
112 let handle = Shared::new(gc_handle, RunningRMSProcessorHandleImpl::new(gc_handle));
113 handle
114 .duration_micros
115 .store(duration.as_micros() as usize, Ordering::Relaxed);
116
117 RunningRMSProcessorImpl {
118 handle,
119 duration_samples: 0,
120 duration,
121 gc_handle: gc_handle.clone(),
122 }
123 }
124
125 pub fn from_handle(handle: Shared<RunningRMSProcessorHandleImpl<ST>>) -> Self {
126 let duration = Duration::from_micros(handle.duration_micros.load(Ordering::Relaxed) as u64);
127 Self {
128 gc_handle: audio_garbage_collector::handle().clone(),
129 handle,
130 duration_samples: 0,
131 duration,
132 }
133 }
134
135 pub fn handle(&self) -> &Shared<RunningRMSProcessorHandleImpl<ST>> {
136 &self.handle
137 }
138}
139
140impl<ST> AudioProcessor for RunningRMSProcessorImpl<ST>
141where
142 ST: AtomicFloatRepresentable + Float + 'static,
143 ST::AtomicType: Send + Sync + Clone + 'static,
144{
145 type SampleType = ST;
146
147 fn prepare(&mut self, context: &mut AudioContext) {
148 let settings = context.settings;
149 self.duration_samples = (settings.sample_rate() * self.duration.as_secs_f32()) as usize;
150 self.handle.resize(
151 &self.gc_handle,
152 settings.output_channels(),
153 self.duration_samples,
154 );
155 }
156
157 fn process(&mut self, _context: &mut AudioContext, buffer: &mut AudioBuffer<Self::SampleType>) {
158 if self.duration_samples == 0 {
159 return;
160 }
161
162 for sample_index in 0..buffer.num_samples() {
163 let running_sums = self.handle.running_sums.get();
164 let window = self.handle.window.get();
165 let mut cursor = self.handle.cursor();
166
167 for channel_index in 0..buffer.num_channels() {
168 let value_slot = window.get(channel_index, cursor);
169 let previous_value = value_slot.get();
170
171 let sample = *buffer.get(channel_index, sample_index);
172 let new_value = sample * sample; value_slot.set(new_value);
174
175 let running_sum_slot = &running_sums[channel_index];
176 let running_sum = running_sum_slot.get() + new_value - previous_value;
177 running_sum_slot.set(running_sum);
178 }
179
180 cursor += 1;
181 if cursor >= self.duration_samples {
182 cursor = 0;
183 }
184 self.handle.cursor.store(cursor, Ordering::Relaxed);
185 }
186 }
187}
188
189#[cfg(test)]
190mod test {
191 use audio_garbage_collector::GarbageCollector;
192 use audio_processor_testing_helpers::assert_f_eq;
193
194 use super::*;
195
196 #[test]
197 fn test_create_handle() {
198 let gc = GarbageCollector::default();
199 let handle = RunningRMSProcessorHandle::new(gc.handle());
200
201 assert_f_eq!(handle.calculate_rms(0), 0.0);
202 }
203
204 #[test]
205 fn test_resize() {
206 let gc = GarbageCollector::default();
207 let handle = RunningRMSProcessorHandle::new(gc.handle());
208
209 handle.resize(gc.handle(), 2, 1000);
210 assert_eq!(handle.window.get().num_channels(), 2);
211 assert_eq!(handle.window.get().num_samples(), 1000);
212 assert_f_eq!(handle.calculate_rms(0), 0.0);
213 assert_f_eq!(handle.calculate_rms(1), 0.0);
214 assert_eq!(handle.cursor(), 0)
215 }
216
217 #[test]
218 fn test_create_running_rms_processor() {
219 let gc = GarbageCollector::default();
220 let mut processor =
221 RunningRMSProcessor::new_with_duration(gc.handle(), Duration::from_millis(10));
222
223 let mut context = AudioContext::default();
224 context.settings.sample_rate = 44100.0;
225 processor.prepare(&mut context);
226
227 assert_eq!(processor.duration_samples, 441);
228 assert_eq!(processor.duration, Duration::from_millis(10));
229 assert_eq!(
230 processor.handle.duration_micros.load(Ordering::Relaxed),
231 10_000
232 );
233 }
234
235 #[test]
236 fn test_create_running_rms_processor_from_handle() {
237 let gc = GarbageCollector::default();
238 let handle = RunningRMSProcessorHandle::new(gc.handle());
239 handle.resize(gc.handle(), 2, 1000);
240 let handle = Shared::new(gc.handle(), handle);
241 let processor = RunningRMSProcessor::from_handle(handle.clone());
242
243 assert_eq!(processor.duration_samples, 0);
244 assert_eq!(processor.duration, Duration::from_micros(0));
245 assert_eq!(processor.handle.duration_micros.load(Ordering::Relaxed), 0);
246 assert_eq!(
247 &*processor.handle().clone() as *const RunningRMSProcessorHandle,
248 &*handle as *const RunningRMSProcessorHandle
249 )
250 }
251
252 #[test]
253 fn test_audio_process_running() {
254 let gc = GarbageCollector::default();
255 let mut processor =
256 RunningRMSProcessor::new_with_duration(gc.handle(), Duration::from_millis(10));
257 let mut test_buffer = AudioBuffer::empty();
258
259 test_buffer.resize_with(2, 1000, || 1.0);
260 let mut context = AudioContext::default();
261 processor.prepare(&mut context);
262 processor.process(&mut context, &mut test_buffer);
263 let rms = processor.handle.calculate_rms(0);
264 assert!(rms > 0.0);
265 }
266}