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
- fn normalization ( buffer : & AudioBuffer ) -> f32 {
15
+ // see - <https://webaudio.github.io/web-audio-api/#dom-convolvernode-normalize>
16
+ fn normalize_buffer ( buffer : & AudioBuffer ) -> f32 {
16
17
let gain_calibration = 0.00125 ;
17
18
let gain_calibration_sample_rate = 44100. ;
18
19
let min_power = 0.000125 ;
@@ -116,8 +117,6 @@ pub struct ConvolverNode {
116
117
normalize : AtomicBool ,
117
118
/// The response buffer, nullable
118
119
buffer : Mutex < Option < AudioBuffer > > ,
119
- /// Message bus to the renderer
120
- sender : Sender < ConvolverRendererInner > ,
121
120
}
122
121
123
122
impl AudioNode for ConvolverNode {
@@ -151,33 +150,31 @@ impl ConvolverNode {
151
150
/// Panics when an AudioBuffer is provided via the `ConvolverOptions` with a sample rate
152
151
/// different from the audio context sample rate.
153
152
pub fn new < C : BaseAudioContext > ( context : & C , options : ConvolverOptions ) -> Self {
154
- context. base ( ) . register ( move |registration| {
155
- let ConvolverOptions {
156
- buffer,
157
- disable_normalization,
158
- channel_config,
159
- } = options;
160
-
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 ) ;
153
+ let ConvolverOptions {
154
+ buffer,
155
+ disable_normalization,
156
+ channel_config,
157
+ } = options;
164
158
165
- let renderer = ConvolverRenderer :: new ( receiver) ;
159
+ let node = context. base ( ) . register ( move |registration| {
160
+ let renderer = ConvolverRenderer { inner : None } ;
166
161
167
162
let node = Self {
168
163
registration,
169
164
channel_config : channel_config. into ( ) ,
170
165
normalize : AtomicBool :: new ( !disable_normalization) ,
171
- sender,
172
166
buffer : Mutex :: new ( None ) ,
173
167
} ;
174
168
175
- if let Some ( buffer) = buffer {
176
- node. set_buffer ( buffer) ;
177
- }
178
-
179
169
( node, Box :: new ( renderer) )
180
- } )
170
+ } ) ;
171
+
172
+ // renderer has been sent to render thread, we can send it messages
173
+ if let Some ( buffer) = buffer {
174
+ node. set_buffer ( buffer) ;
175
+ }
176
+
177
+ node
181
178
}
182
179
183
180
/// Get the current impulse response buffer
@@ -199,7 +196,7 @@ impl ConvolverNode {
199
196
200
197
// normalize before padding because the length of the buffer affects the scale
201
198
let scale = if self . normalize ( ) {
202
- normalization ( & buffer)
199
+ normalize_buffer ( & buffer)
203
200
} else {
204
201
1.
205
202
} ;
@@ -219,9 +216,9 @@ impl ConvolverNode {
219
216
. collect ( ) ;
220
217
221
218
let padded_buffer = AudioBuffer :: from ( samples, sample_rate) ;
222
-
223
219
let convolve = ConvolverRendererInner :: new ( padded_buffer) ;
224
- let _ = self . sender . send ( convolve) ; // can fail when render thread shut down
220
+
221
+ self . registration . post_message ( Some ( convolve) ) ;
225
222
226
223
* self . buffer . lock ( ) . unwrap ( ) = Some ( buffer) ;
227
224
}
@@ -304,20 +301,6 @@ impl Fft {
304
301
}
305
302
}
306
303
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
304
struct ConvolverRendererInner {
322
305
num_ir_blocks : usize ,
323
306
h : Vec < Complex < f32 > > ,
@@ -414,6 +397,10 @@ impl ConvolverRendererInner {
414
397
}
415
398
}
416
399
400
+ struct ConvolverRenderer {
401
+ inner : Option < ConvolverRendererInner > ,
402
+ }
403
+
417
404
impl AudioProcessor for ConvolverRenderer {
418
405
fn process (
419
406
& mut self ,
@@ -427,11 +414,6 @@ impl AudioProcessor for ConvolverRenderer {
427
414
let output = & mut outputs[ 0 ] ;
428
415
output. force_mono ( ) ;
429
416
430
- // handle new impulse response buffer, if any
431
- if let Ok ( msg) = self . receiver . try_recv ( ) {
432
- self . inner = Some ( msg) ;
433
- }
434
-
435
417
let convolver = match & mut self . inner {
436
418
None => {
437
419
// no convolution buffer set, passthrough
@@ -455,6 +437,16 @@ impl AudioProcessor for ConvolverRenderer {
455
437
456
438
true
457
439
}
440
+
441
+ fn onmessage ( & mut self , msg : & mut dyn Any ) {
442
+ if let Some ( convolver) = msg. downcast_mut :: < Option < ConvolverRendererInner > > ( ) {
443
+ // Avoid deallocation in the render thread by swapping the convolver.
444
+ std:: mem:: swap ( & mut self . inner , convolver) ;
445
+ return ;
446
+ }
447
+
448
+ log:: warn!( "ConvolverRenderer: Dropping incoming message {msg:?}" ) ;
449
+ }
458
450
}
459
451
460
452
#[ cfg( test) ]
@@ -473,6 +465,36 @@ mod tests {
473
465
assert_eq ! ( & input, & [ 4 , 5 , 6 , 7 , 8 , 9 , 10 , 0 , 0 , 0 ] ) ;
474
466
}
475
467
468
+ #[ test]
469
+ fn test_constructor_options_buffer ( ) {
470
+ let sample_rate = 44100. ;
471
+ let context = OfflineAudioContext :: new ( 1 , 10 , sample_rate) ;
472
+
473
+ let ir = vec ! [ 1. ] ;
474
+ let calibration = 0.00125 ;
475
+ let channel_data = vec ! [ 0. , 1. , 0. , -1. , 0. ] ;
476
+ let expected = vec ! [ 0. , calibration, 0. , -calibration, 0. , 0. , 0. , 0. , 0. , 0. ] ;
477
+
478
+ // identity ir
479
+ let ir = AudioBuffer :: from ( vec ! [ ir; 1 ] , sample_rate) ;
480
+ let options = ConvolverOptions {
481
+ buffer : Some ( ir) ,
482
+ ..ConvolverOptions :: default ( )
483
+ } ;
484
+ let conv = ConvolverNode :: new ( & context, options) ;
485
+ conv. connect ( & context. destination ( ) ) ;
486
+
487
+ let buffer = AudioBuffer :: from ( vec ! [ channel_data; 1 ] , sample_rate) ;
488
+ let src = context. create_buffer_source ( ) ;
489
+ src. connect ( & conv) ;
490
+ src. set_buffer ( buffer) ;
491
+ src. start ( ) ;
492
+
493
+ let output = context. start_rendering_sync ( ) ;
494
+
495
+ assert_float_eq ! ( output. get_channel_data( 0 ) , & expected[ ..] , abs_all <= 1E-6 ) ;
496
+ }
497
+
476
498
fn test_convolve ( signal : & [ f32 ] , impulse_resp : Option < Vec < f32 > > , length : usize ) -> AudioBuffer {
477
499
let sample_rate = 44100. ;
478
500
let context = OfflineAudioContext :: new ( 1 , length, sample_rate) ;
0 commit comments