@@ -2,7 +2,6 @@ use std::any::Any;
2
2
use std:: f32:: consts:: PI ;
3
3
use std:: sync:: atomic:: { AtomicU8 , Ordering } ;
4
4
5
- use crossbeam_channel:: { Receiver , Sender } ;
6
5
use float_eq:: float_eq;
7
6
use hrtf:: { HrirSphere , HrtfContext , HrtfProcessor , Vec3 } ;
8
7
@@ -118,13 +117,13 @@ impl Default for PannerOptions {
118
117
#[ derive( Debug , Copy , Clone ) ]
119
118
enum ControlMessage {
120
119
DistanceModel ( u8 ) ,
120
+ PanningModel ( u8 ) ,
121
121
RefDistance ( f64 ) ,
122
122
MaxDistance ( f64 ) ,
123
123
RollOffFactor ( f64 ) ,
124
124
ConeInnerAngle ( f64 ) ,
125
125
ConeOuterAngle ( f64 ) ,
126
126
ConeOuterGain ( f64 ) ,
127
- // Panning // @todo
128
127
}
129
128
130
129
/// Assert that the channel count is valid for the PannerNode
@@ -291,8 +290,6 @@ pub struct PannerNode {
291
290
max_distance : AtomicF64 ,
292
291
rolloff_factor : AtomicF64 ,
293
292
panning_model : AtomicU8 ,
294
- /// HRTF message bus to the renderer
295
- sender : Sender < Option < HrtfState > > ,
296
293
}
297
294
298
295
impl AudioNode for PannerNode {
@@ -385,9 +382,11 @@ impl PannerNode {
385
382
param_oy. set_value_at_time ( orientation_y, 0. ) ;
386
383
param_oz. set_value_at_time ( orientation_z, 0. ) ;
387
384
388
- // Channel to send a HRTF processor to the renderer. A capacity of 1 suffices, it will
389
- // simply block the control thread when used concurrently
390
- let ( sender, receiver) = crossbeam_channel:: bounded ( 1 ) ;
385
+ // @note - we just pass the hrtf engine to every created panner node
386
+ // this might not be the best solution in terms of memory footprint
387
+ let resource = include_bytes ! ( "../../resources/IRC_1003_C.bin" ) ;
388
+ let sample_rate = context. sample_rate ( ) as u32 ;
389
+ let hrir_sphere = HrirSphere :: new ( & resource[ ..] , sample_rate) . unwrap ( ) ;
391
390
392
391
let render = PannerRenderer {
393
392
position_x : render_px,
@@ -403,8 +402,10 @@ impl PannerNode {
403
402
cone_inner_angle,
404
403
cone_outer_angle,
405
404
cone_outer_gain,
406
- hrtf_state : None ,
407
- receiver,
405
+ panning_model,
406
+ // @note - The `Some`is just here as a dirty workaround of borrow
407
+ // stuff in PannerRenderer::process
408
+ hrtf_state : Some ( HrtfState :: new ( hrir_sphere) ) ,
408
409
tail_time_counter : 0 ,
409
410
} ;
410
411
@@ -424,12 +425,9 @@ impl PannerNode {
424
425
cone_inner_angle : AtomicF64 :: new ( cone_inner_angle) ,
425
426
cone_outer_angle : AtomicF64 :: new ( cone_outer_angle) ,
426
427
cone_outer_gain : AtomicF64 :: new ( cone_outer_gain) ,
427
- sender,
428
- panning_model : AtomicU8 :: new ( 0 ) ,
428
+ panning_model : AtomicU8 :: new ( panning_model as u8 ) ,
429
429
} ;
430
430
431
- node. set_panning_model ( panning_model) ;
432
-
433
431
// instruct to BaseContext to add the AudioListener if it has not already
434
432
context. base ( ) . ensure_audio_listener_present ( ) ;
435
433
@@ -543,21 +541,11 @@ impl PannerNode {
543
541
self . panning_model . load ( Ordering :: Acquire ) . into ( )
544
542
}
545
543
546
- // can panic when loading HRIR-sphere
547
- #[ allow( clippy:: missing_panics_doc) ]
548
544
pub fn set_panning_model ( & self , value : PanningModelType ) {
549
- let hrtf_option = match value {
550
- PanningModelType :: EqualPower => None ,
551
- PanningModelType :: HRTF => {
552
- let resource = include_bytes ! ( "../../resources/IRC_1003_C.bin" ) ;
553
- let sample_rate = self . context ( ) . sample_rate ( ) as u32 ;
554
- let hrir_sphere = HrirSphere :: new ( & resource[ ..] , sample_rate) . unwrap ( ) ;
555
- Some ( HrtfState :: new ( hrir_sphere) )
556
- }
557
- } ;
558
-
559
- let _ = self . sender . send ( hrtf_option) ; // can fail when render thread shut down
560
- self . panning_model . store ( value as u8 , Ordering :: Release ) ;
545
+ let value = value as u8 ;
546
+ self . panning_model . store ( value, Ordering :: Release ) ;
547
+ self . registration
548
+ . post_message ( ControlMessage :: PanningModel ( value) ) ;
561
549
}
562
550
}
563
551
@@ -577,13 +565,16 @@ struct PannerRenderer {
577
565
orientation_y : AudioParamId ,
578
566
orientation_z : AudioParamId ,
579
567
distance_model : DistanceModelType ,
568
+ panning_model : PanningModelType ,
580
569
ref_distance : f64 ,
581
570
max_distance : f64 ,
582
571
rolloff_factor : f64 ,
583
572
cone_inner_angle : f64 ,
584
573
cone_outer_angle : f64 ,
585
574
cone_outer_gain : f64 ,
586
- receiver : Receiver < Option < HrtfState > > ,
575
+ // hrtf engine to be used if panning model is set to "hrtf"
576
+ // @note - we keep the Some as a workaround of borrow reasons in process,
577
+ // this is quite dirty should be improved
587
578
hrtf_state : Option < HrtfState > ,
588
579
tail_time_counter : usize ,
589
580
}
@@ -606,30 +597,30 @@ impl AudioProcessor for PannerRenderer {
606
597
// only handle mono for now (todo issue #44)
607
598
output. mix ( 1 , ChannelInterpretation :: Speakers ) ;
608
599
600
+ // for borrow reasons, take the hrtf_state out of self
601
+ let mut hrtf_state = self . hrtf_state . take ( ) ;
602
+
609
603
// early exit for silence
610
604
if input. is_silent ( ) {
611
605
// HRTF panner has tail time equal to the max length of the impulse response buffers
612
606
// (12 ms)
613
- let tail_time = match & self . hrtf_state {
614
- None => false ,
615
- Some ( hrtf_state) => hrtf_state. tail_time_samples ( ) > self . tail_time_counter ,
607
+ let tail_time = match self . panning_model {
608
+ PanningModelType :: EqualPower => false ,
609
+ PanningModelType :: HRTF => {
610
+ hrtf_state. as_ref ( ) . unwrap ( ) . tail_time_samples ( ) > self . tail_time_counter
611
+ }
616
612
} ;
613
+
617
614
if !tail_time {
618
615
return false ;
619
616
}
617
+
620
618
self . tail_time_counter += RENDER_QUANTUM_SIZE ;
621
619
}
622
620
623
621
// convert mono to identical stereo
624
622
output. mix ( 2 , ChannelInterpretation :: Speakers ) ;
625
623
626
- // handle changes in panning_model_type mandated from control thread
627
- if let Ok ( hrtf_state) = self . receiver . try_recv ( ) {
628
- self . hrtf_state = hrtf_state;
629
- }
630
- // for borrow reasons, take the hrtf_state out of self
631
- let mut hrtf_state = self . hrtf_state . take ( ) ;
632
-
633
624
// source parameters (Panner)
634
625
let source_position_x = params. get ( & self . position_x ) ;
635
626
let source_position_y = params. get ( & self . position_y ) ;
@@ -695,14 +686,17 @@ impl AudioProcessor for PannerRenderer {
695
686
}
696
687
} ) ;
697
688
698
- if let Some ( hrtf_state) = & mut hrtf_state {
689
+ if self . panning_model == PanningModelType :: HRTF {
690
+ let Some ( hrtf_state) = & mut hrtf_state else { unreachable ! ( ) } ;
691
+
699
692
// HRTF panning - always k-rate so take a single value from the a-rate iter
700
693
let SpatialParams {
701
694
dist_gain,
702
695
cone_gain,
703
696
azimuth,
704
697
elevation,
705
698
} = a_rate_params. next ( ) . unwrap ( ) ;
699
+
706
700
let new_distance_gain = cone_gain * dist_gain;
707
701
708
702
// convert az/el to cartesian coordinates to determine unit direction
@@ -797,6 +791,7 @@ impl AudioProcessor for PannerRenderer {
797
791
if let Some ( control) = msg. downcast_ref :: < ControlMessage > ( ) {
798
792
match control {
799
793
ControlMessage :: DistanceModel ( value) => self . distance_model = ( * value) . into ( ) ,
794
+ ControlMessage :: PanningModel ( value) => self . panning_model = ( * value) . into ( ) ,
800
795
ControlMessage :: RefDistance ( value) => self . ref_distance = * value,
801
796
ControlMessage :: MaxDistance ( value) => self . max_distance = * value,
802
797
ControlMessage :: RollOffFactor ( value) => self . rolloff_factor = * value,
0 commit comments