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
+
2
7
use crate :: buffer:: AudioBuffer ;
3
8
use crate :: context:: { AudioContextRegistration , BaseAudioContext } ;
4
9
use crate :: render:: { AudioParamValues , AudioProcessor , AudioRenderQuantum , RenderScope } ;
5
10
use crate :: RENDER_QUANTUM_SIZE ;
6
11
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 } ;
13
13
14
14
/// Scale buffer by an equal-power normalization
15
15
fn normalization ( buffer : & AudioBuffer ) -> f32 {
@@ -116,8 +116,6 @@ pub struct ConvolverNode {
116
116
normalize : AtomicBool ,
117
117
/// The response buffer, nullable
118
118
buffer : Mutex < Option < AudioBuffer > > ,
119
- /// Message bus to the renderer
120
- sender : Sender < ConvolverRendererInner > ,
121
119
}
122
120
123
121
impl AudioNode for ConvolverNode {
@@ -151,33 +149,39 @@ impl ConvolverNode {
151
149
/// Panics when an AudioBuffer is provided via the `ConvolverOptions` with a sample rate
152
150
/// different from the audio context sample rate.
153
151
pub fn new < C : BaseAudioContext > ( context : & C , options : ConvolverOptions ) -> Self {
154
- context. base ( ) . register ( move |registration| {
152
+ let node = context. base ( ) . register ( move |registration| {
155
153
let ConvolverOptions {
156
154
buffer,
157
155
disable_normalization,
158
156
channel_config,
159
157
} = options;
160
158
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) ;
164
163
165
- let renderer = ConvolverRenderer :: new ( receiver) ;
164
+ let renderer = ConvolverRenderer {
165
+ convolver,
166
+ convolver_set : false ,
167
+ } ;
166
168
167
169
let node = Self {
168
170
registration,
169
171
channel_config : channel_config. into ( ) ,
170
172
normalize : AtomicBool :: new ( !disable_normalization) ,
171
- sender,
172
- buffer : Mutex :: new ( None ) ,
173
+ buffer : Mutex :: new ( buffer) ,
173
174
} ;
174
175
175
- if let Some ( buffer) = buffer {
176
- node. set_buffer ( buffer) ;
177
- }
178
-
179
176
( 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
181
185
}
182
186
183
187
/// Get the current impulse response buffer
@@ -219,9 +223,9 @@ impl ConvolverNode {
219
223
. collect ( ) ;
220
224
221
225
let padded_buffer = AudioBuffer :: from ( samples, sample_rate) ;
222
-
223
226
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) ;
225
229
226
230
* self . buffer . lock ( ) . unwrap ( ) = Some ( buffer) ;
227
231
}
@@ -304,20 +308,6 @@ impl Fft {
304
308
}
305
309
}
306
310
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
-
321
311
struct ConvolverRendererInner {
322
312
num_ir_blocks : usize ,
323
313
h : Vec < Complex < f32 > > ,
@@ -414,6 +404,11 @@ impl ConvolverRendererInner {
414
404
}
415
405
}
416
406
407
+ struct ConvolverRenderer {
408
+ convolver : ConvolverRendererInner ,
409
+ convolver_set : bool ,
410
+ }
411
+
417
412
impl AudioProcessor for ConvolverRenderer {
418
413
fn process (
419
414
& mut self ,
@@ -427,19 +422,13 @@ impl AudioProcessor for ConvolverRenderer {
427
422
let output = & mut outputs[ 0 ] ;
428
423
output. force_mono ( ) ;
429
424
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 ( ) ;
433
429
}
434
430
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 ;
443
432
444
433
// handle tail time
445
434
if input. is_silent ( ) {
@@ -455,6 +444,18 @@ impl AudioProcessor for ConvolverRenderer {
455
444
456
445
true
457
446
}
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
+ }
458
459
}
459
460
460
461
#[ cfg( test) ]
@@ -473,6 +474,36 @@ mod tests {
473
474
assert_eq ! ( & input, & [ 4 , 5 , 6 , 7 , 8 , 9 , 10 , 0 , 0 , 0 ] ) ;
474
475
}
475
476
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
+
476
507
fn test_convolve ( signal : & [ f32 ] , impulse_resp : Option < Vec < f32 > > , length : usize ) -> AudioBuffer {
477
508
let sample_rate = 44100. ;
478
509
let context = OfflineAudioContext :: new ( 1 , length, sample_rate) ;
0 commit comments