Skip to content

Commit 95e7cc1

Browse files
b-maorottier
authored andcommitted
refactor: move to message passing API
1 parent 87a74d3 commit 95e7cc1

File tree

2 files changed

+81
-48
lines changed

2 files changed

+81
-48
lines changed

src/node/convolver.rs

Lines changed: 79 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
use super::{AudioNode, ChannelConfig, ChannelConfigOptions, ChannelInterpretation};
1+
use std::any::Any;
2+
use std::sync::atomic::{AtomicBool, Ordering};
3+
use std::sync::{Arc, Mutex};
4+
5+
use realfft::{num_complex::Complex, ComplexToReal, RealFftPlanner, RealToComplex};
6+
27
use crate::buffer::AudioBuffer;
38
use crate::context::{AudioContextRegistration, BaseAudioContext};
49
use crate::render::{AudioParamValues, AudioProcessor, AudioRenderQuantum, RenderScope};
510
use crate::RENDER_QUANTUM_SIZE;
611

7-
use crossbeam_channel::{Receiver, Sender};
8-
use realfft::{num_complex::Complex, ComplexToReal, RealFftPlanner, RealToComplex};
9-
use std::sync::{
10-
atomic::{AtomicBool, Ordering},
11-
Arc, Mutex,
12-
};
12+
use super::{AudioNode, ChannelConfig, ChannelConfigOptions, ChannelInterpretation};
1313

1414
/// Scale buffer by an equal-power normalization
1515
fn normalization(buffer: &AudioBuffer) -> f32 {
@@ -116,8 +116,6 @@ pub struct ConvolverNode {
116116
normalize: AtomicBool,
117117
/// The response buffer, nullable
118118
buffer: Mutex<Option<AudioBuffer>>,
119-
/// Message bus to the renderer
120-
sender: Sender<ConvolverRendererInner>,
121119
}
122120

123121
impl AudioNode for ConvolverNode {
@@ -151,33 +149,39 @@ impl ConvolverNode {
151149
/// Panics when an AudioBuffer is provided via the `ConvolverOptions` with a sample rate
152150
/// different from the audio context sample rate.
153151
pub fn new<C: BaseAudioContext>(context: &C, options: ConvolverOptions) -> Self {
154-
context.base().register(move |registration| {
152+
let node = context.base().register(move |registration| {
155153
let ConvolverOptions {
156154
buffer,
157155
disable_normalization,
158156
channel_config,
159157
} = options;
160158

161-
// Channel to send buffer channels references to the renderer. A capacity of 1
162-
// suffices, it will simply block the control thread when used concurrently
163-
let (sender, receiver) = crossbeam_channel::bounded(1);
159+
// create a dummy convolver to be replaced by a real one without deallocation
160+
let sample_rate = context.base().sample_rate();
161+
let padded_buffer = AudioBuffer::from(vec![vec![0.; 0]; 1], sample_rate);
162+
let convolver = ConvolverRendererInner::new(padded_buffer);
164163

165-
let renderer = ConvolverRenderer::new(receiver);
164+
let renderer = ConvolverRenderer {
165+
convolver,
166+
convolver_set: false,
167+
};
166168

167169
let node = Self {
168170
registration,
169171
channel_config: channel_config.into(),
170172
normalize: AtomicBool::new(!disable_normalization),
171-
sender,
172-
buffer: Mutex::new(None),
173+
buffer: Mutex::new(buffer),
173174
};
174175

175-
if let Some(buffer) = buffer {
176-
node.set_buffer(buffer);
177-
}
178-
179176
(node, Box::new(renderer))
180-
})
177+
});
178+
179+
// audio node has been sent to render thread, we can sent it messages
180+
if let Some(buffer) = node.buffer() {
181+
node.set_buffer(buffer);
182+
}
183+
184+
node
181185
}
182186

183187
/// Get the current impulse response buffer
@@ -219,9 +223,9 @@ impl ConvolverNode {
219223
.collect();
220224

221225
let padded_buffer = AudioBuffer::from(samples, sample_rate);
222-
223226
let convolve = ConvolverRendererInner::new(padded_buffer);
224-
let _ = self.sender.send(convolve); // can fail when render thread shut down
227+
228+
self.registration.post_message(convolve);
225229

226230
*self.buffer.lock().unwrap() = Some(buffer);
227231
}
@@ -304,20 +308,6 @@ impl Fft {
304308
}
305309
}
306310

307-
struct ConvolverRenderer {
308-
receiver: Receiver<ConvolverRendererInner>,
309-
inner: Option<ConvolverRendererInner>,
310-
}
311-
312-
impl ConvolverRenderer {
313-
fn new(receiver: Receiver<ConvolverRendererInner>) -> Self {
314-
Self {
315-
receiver,
316-
inner: None,
317-
}
318-
}
319-
}
320-
321311
struct ConvolverRendererInner {
322312
num_ir_blocks: usize,
323313
h: Vec<Complex<f32>>,
@@ -414,6 +404,11 @@ impl ConvolverRendererInner {
414404
}
415405
}
416406

407+
struct ConvolverRenderer {
408+
convolver: ConvolverRendererInner,
409+
convolver_set: bool,
410+
}
411+
417412
impl AudioProcessor for ConvolverRenderer {
418413
fn process(
419414
&mut self,
@@ -427,19 +422,13 @@ impl AudioProcessor for ConvolverRenderer {
427422
let output = &mut outputs[0];
428423
output.force_mono();
429424

430-
// handle new impulse response buffer, if any
431-
if let Ok(msg) = self.receiver.try_recv() {
432-
self.inner = Some(msg);
425+
// no convolution buffer set, passthrough
426+
if !self.convolver_set {
427+
*output = input.clone();
428+
return !input.is_silent();
433429
}
434430

435-
let convolver = match &mut self.inner {
436-
None => {
437-
// no convolution buffer set, passthrough
438-
*output = input.clone();
439-
return !input.is_silent();
440-
}
441-
Some(convolver) => convolver,
442-
};
431+
let convolver = &mut self.convolver;
443432

444433
// handle tail time
445434
if input.is_silent() {
@@ -455,6 +444,18 @@ impl AudioProcessor for ConvolverRenderer {
455444

456445
true
457446
}
447+
448+
fn onmessage(&mut self, msg: &mut dyn Any) {
449+
if let Some(convolver) = msg.downcast_mut::<ConvolverRendererInner>() {
450+
println!("convolver set");
451+
// Avoid deallocation in the render thread by swapping the convolver.
452+
std::mem::swap(&mut self.convolver, convolver);
453+
self.convolver_set = true;
454+
return;
455+
}
456+
457+
log::warn!("ConvolverRenderer: Dropping incoming message {msg:?}");
458+
}
458459
}
459460

460461
#[cfg(test)]
@@ -473,6 +474,36 @@ mod tests {
473474
assert_eq!(&input, &[4, 5, 6, 7, 8, 9, 10, 0, 0, 0]);
474475
}
475476

477+
#[test]
478+
fn test_constructor_options_buffer() {
479+
let sample_rate = 44100.;
480+
let context = OfflineAudioContext::new(1, 10, sample_rate);
481+
482+
let ir = vec![1.];
483+
let calibration = 0.00125;
484+
let channel_data = vec![0., 1., 0., -1., 0.];
485+
let expected = vec![0., calibration, 0., -calibration, 0., 0., 0., 0., 0., 0.];
486+
487+
// identity ir
488+
let ir = AudioBuffer::from(vec![ir; 1], sample_rate);
489+
let options = ConvolverOptions {
490+
buffer: Some(ir),
491+
..ConvolverOptions::default()
492+
};
493+
let conv = ConvolverNode::new(&context, options);
494+
conv.connect(&context.destination());
495+
496+
let buffer = AudioBuffer::from(vec![channel_data; 1], sample_rate);
497+
let src = context.create_buffer_source();
498+
src.connect(&conv);
499+
src.set_buffer(buffer);
500+
src.start();
501+
502+
let output = context.start_rendering_sync();
503+
504+
assert_float_eq!(output.get_channel_data(0), &expected[..], abs_all <= 1E-6);
505+
}
506+
476507
fn test_convolve(signal: &[f32], impulse_resp: Option<Vec<f32>>, length: usize) -> AudioBuffer {
477508
let sample_rate = 44100.;
478509
let context = OfflineAudioContext::new(1, length, sample_rate);

src/node/waveshaper.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ impl WaveShaperNode {
188188

189189
(node, Box::new(renderer))
190190
})
191+
192+
// @todo - use node.set_curve(curve) here
191193
}
192194

193195
/// Returns the distortion curve

0 commit comments

Comments
 (0)