1
+ use std:: any:: Any ;
1
2
use std:: f32:: consts:: PI ;
2
3
use std:: sync:: atomic:: { AtomicU8 , Ordering } ;
3
- use std:: sync:: Arc ;
4
+
5
+ use crossbeam_channel:: { Receiver , Sender } ;
6
+ use float_eq:: float_eq;
7
+ use hrtf:: { HrirSphere , HrtfContext , HrtfProcessor , Vec3 } ;
4
8
5
9
use crate :: context:: { AudioContextRegistration , AudioParamId , BaseAudioContext } ;
6
10
use crate :: param:: { AudioParam , AudioParamDescriptor } ;
@@ -11,10 +15,6 @@ use super::{
11
15
AudioNode , ChannelConfig , ChannelConfigOptions , ChannelCountMode , ChannelInterpretation ,
12
16
} ;
13
17
14
- use crossbeam_channel:: { Receiver , Sender } ;
15
- use float_eq:: float_eq;
16
- use hrtf:: { HrirSphere , HrtfContext , HrtfProcessor , Vec3 } ;
17
-
18
18
/// Spatialization algorithm used to position the audio in 3D space
19
19
#[ derive( Debug , Copy , Clone , PartialEq , Eq , Default ) ]
20
20
pub enum PanningModelType {
@@ -115,6 +115,18 @@ impl Default for PannerOptions {
115
115
}
116
116
}
117
117
118
+ #[ derive( Debug , Copy , Clone ) ]
119
+ enum ControlMessage {
120
+ DistanceModel ( u8 ) ,
121
+ RefDistance ( f64 ) ,
122
+ MaxDistance ( f64 ) ,
123
+ RollOffFactor ( f64 ) ,
124
+ ConeInnerAngle ( f64 ) ,
125
+ ConeOuterAngle ( f64 ) ,
126
+ ConeOuterGain ( f64 ) ,
127
+ // Panning // @todo
128
+ }
129
+
118
130
/// Assert that the channel count is valid for the PannerNode
119
131
/// see <https://webaudio.github.io/web-audio-api/#audionode-channelcount-constraints>
120
132
///
@@ -271,13 +283,13 @@ pub struct PannerNode {
271
283
orientation_x : AudioParam ,
272
284
orientation_y : AudioParam ,
273
285
orientation_z : AudioParam ,
274
- cone_inner_angle : Arc < AtomicF64 > ,
275
- cone_outer_angle : Arc < AtomicF64 > ,
276
- cone_outer_gain : Arc < AtomicF64 > ,
277
- distance_model : Arc < AtomicU8 > ,
278
- ref_distance : Arc < AtomicF64 > ,
279
- max_distance : Arc < AtomicF64 > ,
280
- rolloff_factor : Arc < AtomicF64 > ,
286
+ cone_inner_angle : AtomicF64 ,
287
+ cone_outer_angle : AtomicF64 ,
288
+ cone_outer_gain : AtomicF64 ,
289
+ distance_model : AtomicU8 ,
290
+ ref_distance : AtomicF64 ,
291
+ max_distance : AtomicF64 ,
292
+ rolloff_factor : AtomicF64 ,
281
293
panning_model : AtomicU8 ,
282
294
/// HRTF message bus to the renderer
283
295
sender : Sender < Option < HrtfState > > ,
@@ -333,37 +345,45 @@ impl PannerNode {
333
345
pub fn new < C : BaseAudioContext > ( context : & C , options : PannerOptions ) -> Self {
334
346
let node = context. register ( move |registration| {
335
347
use crate :: spatial:: PARAM_OPTS ;
348
+
349
+ let PannerOptions {
350
+ position_x,
351
+ position_y,
352
+ position_z,
353
+ orientation_x,
354
+ orientation_y,
355
+ orientation_z,
356
+ distance_model,
357
+ ref_distance,
358
+ max_distance,
359
+ rolloff_factor,
360
+ cone_inner_angle,
361
+ cone_outer_angle,
362
+ cone_outer_gain,
363
+ channel_config,
364
+ panning_model,
365
+ } = options;
366
+
336
367
// position params
337
- let ( position_x , render_px) = context. create_audio_param ( PARAM_OPTS , & registration) ;
338
- let ( position_y , render_py) = context. create_audio_param ( PARAM_OPTS , & registration) ;
339
- let ( position_z , render_pz) = context. create_audio_param ( PARAM_OPTS , & registration) ;
340
- position_x . set_value_at_time ( options . position_x , 0. ) ;
341
- position_y . set_value_at_time ( options . position_y , 0. ) ;
342
- position_z . set_value_at_time ( options . position_z , 0. ) ;
368
+ let ( param_px , render_px) = context. create_audio_param ( PARAM_OPTS , & registration) ;
369
+ let ( param_py , render_py) = context. create_audio_param ( PARAM_OPTS , & registration) ;
370
+ let ( param_pz , render_pz) = context. create_audio_param ( PARAM_OPTS , & registration) ;
371
+ param_px . set_value_at_time ( position_x, 0. ) ;
372
+ param_py . set_value_at_time ( position_y, 0. ) ;
373
+ param_pz . set_value_at_time ( position_z, 0. ) ;
343
374
344
375
// orientation params
345
376
let orientation_x_opts = AudioParamDescriptor {
346
377
default_value : 1.0 ,
347
378
..PARAM_OPTS
348
379
} ;
349
- let ( orientation_x , render_ox) =
380
+ let ( param_ox , render_ox) =
350
381
context. create_audio_param ( orientation_x_opts, & registration) ;
351
- let ( orientation_y, render_oy) = context. create_audio_param ( PARAM_OPTS , & registration) ;
352
- let ( orientation_z, render_oz) = context. create_audio_param ( PARAM_OPTS , & registration) ;
353
- orientation_x. set_value_at_time ( options. orientation_x , 0. ) ;
354
- orientation_y. set_value_at_time ( options. orientation_y , 0. ) ;
355
- orientation_z. set_value_at_time ( options. orientation_z , 0. ) ;
356
-
357
- // distance attributes
358
- let distance_model = Arc :: new ( AtomicU8 :: new ( options. distance_model as u8 ) ) ;
359
- let ref_distance = Arc :: new ( AtomicF64 :: new ( options. ref_distance ) ) ;
360
- let max_distance = Arc :: new ( AtomicF64 :: new ( options. max_distance ) ) ;
361
- let rolloff_factor = Arc :: new ( AtomicF64 :: new ( options. rolloff_factor ) ) ;
362
-
363
- // cone attributes
364
- let cone_inner_angle = Arc :: new ( AtomicF64 :: new ( options. cone_inner_angle ) ) ;
365
- let cone_outer_angle = Arc :: new ( AtomicF64 :: new ( options. cone_outer_angle ) ) ;
366
- let cone_outer_gain = Arc :: new ( AtomicF64 :: new ( options. cone_outer_gain ) ) ;
382
+ let ( param_oy, render_oy) = context. create_audio_param ( PARAM_OPTS , & registration) ;
383
+ let ( param_oz, render_oz) = context. create_audio_param ( PARAM_OPTS , & registration) ;
384
+ param_ox. set_value_at_time ( orientation_x, 0. ) ;
385
+ param_oy. set_value_at_time ( orientation_y, 0. ) ;
386
+ param_oz. set_value_at_time ( orientation_z, 0. ) ;
367
387
368
388
// Channel to send a HRTF processor to the renderer. A capacity of 1 suffices, it will
369
389
// simply block the control thread when used concurrently
@@ -376,44 +396,39 @@ impl PannerNode {
376
396
orientation_x : render_ox,
377
397
orientation_y : render_oy,
378
398
orientation_z : render_oz,
379
- distance_model : Arc :: clone ( & distance_model) ,
380
- ref_distance : Arc :: clone ( & ref_distance) ,
381
- max_distance : Arc :: clone ( & max_distance) ,
382
- rolloff_factor : Arc :: clone ( & rolloff_factor) ,
383
- cone_inner_angle : Arc :: clone ( & cone_inner_angle) ,
384
- cone_outer_angle : Arc :: clone ( & cone_outer_angle) ,
385
- cone_outer_gain : Arc :: clone ( & cone_outer_gain) ,
386
- hrtf_state : None ,
387
- receiver,
388
- tail_time_counter : 0 ,
389
- } ;
390
-
391
- let node = PannerNode {
392
- registration,
393
- channel_config : ChannelConfigOptions {
394
- count : 2 ,
395
- count_mode : ChannelCountMode :: ClampedMax ,
396
- interpretation : ChannelInterpretation :: Speakers ,
397
- }
398
- . into ( ) ,
399
- position_x,
400
- position_y,
401
- position_z,
402
- orientation_x,
403
- orientation_y,
404
- orientation_z,
405
399
distance_model,
406
400
ref_distance,
407
401
max_distance,
408
402
rolloff_factor,
409
403
cone_inner_angle,
410
404
cone_outer_angle,
411
405
cone_outer_gain,
406
+ hrtf_state : None ,
407
+ receiver,
408
+ tail_time_counter : 0 ,
409
+ } ;
410
+
411
+ let node = PannerNode {
412
+ registration,
413
+ channel_config : channel_config. into ( ) ,
414
+ position_x : param_px,
415
+ position_y : param_py,
416
+ position_z : param_pz,
417
+ orientation_x : param_ox,
418
+ orientation_y : param_oy,
419
+ orientation_z : param_oz,
420
+ distance_model : AtomicU8 :: new ( distance_model as u8 ) ,
421
+ ref_distance : AtomicF64 :: new ( ref_distance) ,
422
+ max_distance : AtomicF64 :: new ( max_distance) ,
423
+ rolloff_factor : AtomicF64 :: new ( rolloff_factor) ,
424
+ cone_inner_angle : AtomicF64 :: new ( cone_inner_angle) ,
425
+ cone_outer_angle : AtomicF64 :: new ( cone_outer_angle) ,
426
+ cone_outer_gain : AtomicF64 :: new ( cone_outer_gain) ,
412
427
sender,
413
428
panning_model : AtomicU8 :: new ( 0 ) ,
414
429
} ;
415
430
416
- node. set_panning_model ( options . panning_model ) ;
431
+ node. set_panning_model ( panning_model) ;
417
432
418
433
// instruct to BaseContext to add the AudioListener if it has not already
419
434
context. base ( ) . ensure_audio_listener_present ( ) ;
@@ -454,63 +469,78 @@ impl PannerNode {
454
469
}
455
470
456
471
pub fn distance_model ( & self ) -> DistanceModelType {
457
- self . distance_model . load ( Ordering :: SeqCst ) . into ( )
472
+ self . distance_model . load ( Ordering :: Acquire ) . into ( )
458
473
}
459
474
460
475
pub fn set_distance_model ( & self , value : DistanceModelType ) {
461
- self . distance_model . store ( value as u8 , Ordering :: SeqCst ) ;
476
+ let value = value as u8 ;
477
+ self . distance_model . store ( value, Ordering :: Release ) ;
478
+ self . registration
479
+ . post_message ( ControlMessage :: DistanceModel ( value) ) ;
462
480
}
463
481
464
482
pub fn ref_distance ( & self ) -> f64 {
465
- self . ref_distance . load ( Ordering :: SeqCst )
483
+ self . ref_distance . load ( Ordering :: Acquire )
466
484
}
467
485
468
486
pub fn set_ref_distance ( & self , value : f64 ) {
469
- self . ref_distance . store ( value, Ordering :: SeqCst ) ;
487
+ self . ref_distance . store ( value, Ordering :: Release ) ;
488
+ self . registration
489
+ . post_message ( ControlMessage :: RefDistance ( value) ) ;
470
490
}
471
491
472
492
pub fn max_distance ( & self ) -> f64 {
473
- self . max_distance . load ( Ordering :: SeqCst )
493
+ self . max_distance . load ( Ordering :: Acquire )
474
494
}
475
495
476
496
pub fn set_max_distance ( & self , value : f64 ) {
477
- self . max_distance . store ( value, Ordering :: SeqCst ) ;
497
+ self . max_distance . store ( value, Ordering :: Release ) ;
498
+ self . registration
499
+ . post_message ( ControlMessage :: MaxDistance ( value) ) ;
478
500
}
479
501
480
502
pub fn rolloff_factor ( & self ) -> f64 {
481
- self . rolloff_factor . load ( Ordering :: SeqCst )
503
+ self . rolloff_factor . load ( Ordering :: Acquire )
482
504
}
483
505
484
506
pub fn set_rolloff_factor ( & self , value : f64 ) {
485
- self . rolloff_factor . store ( value, Ordering :: SeqCst ) ;
507
+ self . rolloff_factor . store ( value, Ordering :: Release ) ;
508
+ self . registration
509
+ . post_message ( ControlMessage :: RollOffFactor ( value) ) ;
486
510
}
487
511
488
512
pub fn cone_inner_angle ( & self ) -> f64 {
489
- self . cone_inner_angle . load ( Ordering :: SeqCst )
513
+ self . cone_inner_angle . load ( Ordering :: Acquire )
490
514
}
491
515
492
516
pub fn set_cone_inner_angle ( & self , value : f64 ) {
493
- self . cone_inner_angle . store ( value, Ordering :: SeqCst ) ;
517
+ self . cone_inner_angle . store ( value, Ordering :: Release ) ;
518
+ self . registration
519
+ . post_message ( ControlMessage :: ConeInnerAngle ( value) ) ;
494
520
}
495
521
496
522
pub fn cone_outer_angle ( & self ) -> f64 {
497
- self . cone_outer_angle . load ( Ordering :: SeqCst )
523
+ self . cone_outer_angle . load ( Ordering :: Acquire )
498
524
}
499
525
500
526
pub fn set_cone_outer_angle ( & self , value : f64 ) {
501
- self . cone_outer_angle . store ( value, Ordering :: SeqCst ) ;
527
+ self . cone_outer_angle . store ( value, Ordering :: Release ) ;
528
+ self . registration
529
+ . post_message ( ControlMessage :: ConeOuterAngle ( value) ) ;
502
530
}
503
531
504
532
pub fn cone_outer_gain ( & self ) -> f64 {
505
- self . cone_outer_gain . load ( Ordering :: SeqCst )
533
+ self . cone_outer_gain . load ( Ordering :: Acquire )
506
534
}
507
535
508
536
pub fn set_cone_outer_gain ( & self , value : f64 ) {
509
- self . cone_outer_gain . store ( value, Ordering :: SeqCst ) ;
537
+ self . cone_outer_gain . store ( value, Ordering :: Release ) ;
538
+ self . registration
539
+ . post_message ( ControlMessage :: ConeOuterGain ( value) ) ;
510
540
}
511
541
512
542
pub fn panning_model ( & self ) -> PanningModelType {
513
- self . panning_model . load ( Ordering :: SeqCst ) . into ( )
543
+ self . panning_model . load ( Ordering :: Acquire ) . into ( )
514
544
}
515
545
516
546
// can panic when loading HRIR-sphere
@@ -527,7 +557,7 @@ impl PannerNode {
527
557
} ;
528
558
529
559
let _ = self . sender . send ( hrtf_option) ; // can fail when render thread shut down
530
- self . panning_model . store ( value as u8 , Ordering :: SeqCst ) ;
560
+ self . panning_model . store ( value as u8 , Ordering :: Release ) ;
531
561
}
532
562
}
533
563
@@ -546,13 +576,13 @@ struct PannerRenderer {
546
576
orientation_x : AudioParamId ,
547
577
orientation_y : AudioParamId ,
548
578
orientation_z : AudioParamId ,
549
- distance_model : Arc < AtomicU8 > ,
550
- ref_distance : Arc < AtomicF64 > ,
551
- max_distance : Arc < AtomicF64 > ,
552
- rolloff_factor : Arc < AtomicF64 > ,
553
- cone_inner_angle : Arc < AtomicF64 > ,
554
- cone_outer_angle : Arc < AtomicF64 > ,
555
- cone_outer_gain : Arc < AtomicF64 > ,
579
+ distance_model : DistanceModelType ,
580
+ ref_distance : f64 ,
581
+ max_distance : f64 ,
582
+ rolloff_factor : f64 ,
583
+ cone_inner_angle : f64 ,
584
+ cone_outer_angle : f64 ,
585
+ cone_outer_gain : f64 ,
556
586
receiver : Receiver < Option < HrtfState > > ,
557
587
hrtf_state : Option < HrtfState > ,
558
588
tail_time_counter : usize ,
@@ -762,6 +792,24 @@ impl AudioProcessor for PannerRenderer {
762
792
// tail time only for HRTF panning
763
793
self . hrtf_state . is_some ( )
764
794
}
795
+
796
+ fn onmessage ( & mut self , msg : & mut dyn Any ) {
797
+ if let Some ( control) = msg. downcast_ref :: < ControlMessage > ( ) {
798
+ match control {
799
+ ControlMessage :: DistanceModel ( value) => self . distance_model = ( * value) . into ( ) ,
800
+ ControlMessage :: RefDistance ( value) => self . ref_distance = * value,
801
+ ControlMessage :: MaxDistance ( value) => self . max_distance = * value,
802
+ ControlMessage :: RollOffFactor ( value) => self . rolloff_factor = * value,
803
+ ControlMessage :: ConeInnerAngle ( value) => self . cone_inner_angle = * value,
804
+ ControlMessage :: ConeOuterAngle ( value) => self . cone_outer_angle = * value,
805
+ ControlMessage :: ConeOuterGain ( value) => self . cone_outer_gain = * value,
806
+ }
807
+
808
+ return ;
809
+ }
810
+
811
+ log:: warn!( "PannerRenderer: Dropping incoming message {msg:?}" ) ;
812
+ }
765
813
}
766
814
767
815
impl PannerRenderer {
@@ -771,12 +819,12 @@ impl PannerRenderer {
771
819
source_orientation : [ f32 ; 3 ] ,
772
820
listener_position : [ f32 ; 3 ] ,
773
821
) -> f32 {
774
- let abs_inner_angle = self . cone_inner_angle . load ( Ordering :: SeqCst ) . abs ( ) as f32 / 2. ;
775
- let abs_outer_angle = self . cone_outer_angle . load ( Ordering :: SeqCst ) . abs ( ) as f32 / 2. ;
822
+ let abs_inner_angle = self . cone_inner_angle . abs ( ) as f32 / 2. ;
823
+ let abs_outer_angle = self . cone_outer_angle . abs ( ) as f32 / 2. ;
776
824
if abs_inner_angle >= 180. && abs_outer_angle >= 180. {
777
825
1. // no cone specified
778
826
} else {
779
- let cone_outer_gain = self . cone_outer_gain . load ( Ordering :: SeqCst ) as f32 ;
827
+ let cone_outer_gain = self . cone_outer_gain as f32 ;
780
828
781
829
let abs_angle =
782
830
crate :: spatial:: angle ( source_position, source_orientation, listener_position) ;
@@ -794,14 +842,14 @@ impl PannerRenderer {
794
842
}
795
843
796
844
fn dist_gain ( & self , source_position : [ f32 ; 3 ] , listener_position : [ f32 ; 3 ] ) -> f32 {
797
- let distance_model = self . distance_model . load ( Ordering :: SeqCst ) . into ( ) ;
798
- let ref_distance = self . ref_distance . load ( Ordering :: SeqCst ) ;
799
- let rolloff_factor = self . rolloff_factor . load ( Ordering :: SeqCst ) ;
845
+ let distance_model = self . distance_model ;
846
+ let ref_distance = self . ref_distance ;
847
+ let rolloff_factor = self . rolloff_factor ;
800
848
let distance = crate :: spatial:: distance ( source_position, listener_position) as f64 ;
801
849
802
850
let dist_gain = match distance_model {
803
851
DistanceModelType :: Linear => {
804
- let max_distance = self . max_distance . load ( Ordering :: SeqCst ) ;
852
+ let max_distance = self . max_distance ;
805
853
let d2ref = ref_distance. min ( max_distance) ;
806
854
let d2max = ref_distance. max ( max_distance) ;
807
855
let d_clamped = distance. clamp ( d2ref, d2max) ;
0 commit comments