Skip to content

Commit 5bc2acc

Browse files
committed
Fix loading of HRTF-sphere for panner node in onmessage handler
1 parent f704a1b commit 5bc2acc

File tree

1 file changed

+30
-35
lines changed

1 file changed

+30
-35
lines changed

src/node/panner.rs

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,10 @@ impl Default for PannerOptions {
114114
}
115115
}
116116

117-
#[derive(Debug, Copy, Clone)]
118117
enum ControlMessage {
119118
DistanceModel(u8),
120-
PanningModel(u8),
119+
// Box this payload - one large variant can penalize the memory layout of this enum
120+
PanningModel(Box<Option<HrtfState>>),
121121
RefDistance(f64),
122122
MaxDistance(f64),
123123
RollOffFactor(f64),
@@ -340,7 +340,7 @@ impl PannerNode {
340340
/// Can panic when loading HRIR-sphere
341341
#[allow(clippy::missing_panics_doc)]
342342
pub fn new<C: BaseAudioContext>(context: &C, options: PannerOptions) -> Self {
343-
let node = context.register(move |registration| {
343+
let node = context.register(|registration| {
344344
use crate::spatial::PARAM_OPTS;
345345

346346
let PannerOptions {
@@ -382,12 +382,6 @@ impl PannerNode {
382382
param_oy.set_value_at_time(orientation_y, 0.);
383383
param_oz.set_value_at_time(orientation_z, 0.);
384384

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();
390-
391385
let render = PannerRenderer {
392386
position_x: render_px,
393387
position_y: render_py,
@@ -402,10 +396,7 @@ impl PannerNode {
402396
cone_inner_angle,
403397
cone_outer_angle,
404398
cone_outer_gain,
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)),
399+
hrtf_state: None,
409400
tail_time_counter: 0,
410401
};
411402

@@ -439,6 +430,9 @@ impl PannerNode {
439430
.base()
440431
.connect_listener_to_panner(node.registration().id());
441432

433+
// load the HRTF sphere if requested
434+
node.set_panning_model(options.panning_model);
435+
442436
node
443437
}
444438

@@ -553,11 +547,21 @@ impl PannerNode {
553547
self.panning_model.load(Ordering::Acquire).into()
554548
}
555549

550+
#[allow(clippy::missing_panics_doc)] // loading the provided HRTF will not panic
556551
pub fn set_panning_model(&self, value: PanningModelType) {
557-
let value = value as u8;
558-
self.panning_model.store(value, Ordering::Release);
552+
let hrtf_option = match value {
553+
PanningModelType::EqualPower => None,
554+
PanningModelType::HRTF => {
555+
let resource = include_bytes!("../../resources/IRC_1003_C.bin");
556+
let sample_rate = self.context().sample_rate() as u32;
557+
let hrir_sphere = HrirSphere::new(&resource[..], sample_rate).unwrap();
558+
Some(HrtfState::new(hrir_sphere))
559+
}
560+
};
561+
562+
self.panning_model.store(value as u8, Ordering::Release);
559563
self.registration
560-
.post_message(ControlMessage::PanningModel(value));
564+
.post_message(ControlMessage::PanningModel(Box::new(hrtf_option)));
561565
}
562566
}
563567

@@ -577,17 +581,13 @@ struct PannerRenderer {
577581
orientation_y: AudioParamId,
578582
orientation_z: AudioParamId,
579583
distance_model: DistanceModelType,
580-
panning_model: PanningModelType,
581584
ref_distance: f64,
582585
max_distance: f64,
583586
rolloff_factor: f64,
584587
cone_inner_angle: f64,
585588
cone_outer_angle: f64,
586589
cone_outer_gain: f64,
587-
// hrtf engine to be used if panning model is set to "hrtf"
588-
// @note - we keep the Some as a workaround of borrow reasons in process,
589-
// this is quite dirty should be improved
590-
hrtf_state: Option<HrtfState>,
590+
hrtf_state: Option<HrtfState>, // use EqualPower panning model if `None`
591591
tail_time_counter: usize,
592592
}
593593

@@ -609,20 +609,14 @@ impl AudioProcessor for PannerRenderer {
609609
// only handle mono for now (todo issue #44)
610610
output.mix(1, ChannelInterpretation::Speakers);
611611

612-
// for borrow reasons, take the hrtf_state out of self
613-
let mut hrtf_state = self.hrtf_state.take();
614-
615612
// early exit for silence
616613
if input.is_silent() {
617614
// HRTF panner has tail time equal to the max length of the impulse response buffers
618615
// (12 ms)
619-
let tail_time = match self.panning_model {
620-
PanningModelType::EqualPower => false,
621-
PanningModelType::HRTF => {
622-
hrtf_state.as_ref().unwrap().tail_time_samples() > self.tail_time_counter
623-
}
616+
let tail_time = match &self.hrtf_state {
617+
None => false,
618+
Some(hrtf_state) => hrtf_state.tail_time_samples() > self.tail_time_counter,
624619
};
625-
626620
if !tail_time {
627621
return false;
628622
}
@@ -633,6 +627,9 @@ impl AudioProcessor for PannerRenderer {
633627
// convert mono to identical stereo
634628
output.mix(2, ChannelInterpretation::Speakers);
635629

630+
// for borrow reasons, take the hrtf_state out of self
631+
let mut hrtf_state = self.hrtf_state.take();
632+
636633
// source parameters (Panner)
637634
let source_position_x = params.get(&self.position_x);
638635
let source_position_y = params.get(&self.position_y);
@@ -698,9 +695,7 @@ impl AudioProcessor for PannerRenderer {
698695
}
699696
});
700697

701-
if self.panning_model == PanningModelType::HRTF {
702-
let Some(hrtf_state) = &mut hrtf_state else { unreachable!() };
703-
698+
if let Some(hrtf_state) = &mut hrtf_state {
704699
// HRTF panning - always k-rate so take a single value from the a-rate iter
705700
let SpatialParams {
706701
dist_gain,
@@ -800,16 +795,16 @@ impl AudioProcessor for PannerRenderer {
800795
}
801796

802797
fn onmessage(&mut self, msg: &mut dyn Any) {
803-
if let Some(control) = msg.downcast_ref::<ControlMessage>() {
798+
if let Some(control) = msg.downcast_mut::<ControlMessage>() {
804799
match control {
805800
ControlMessage::DistanceModel(value) => self.distance_model = (*value).into(),
806-
ControlMessage::PanningModel(value) => self.panning_model = (*value).into(),
807801
ControlMessage::RefDistance(value) => self.ref_distance = *value,
808802
ControlMessage::MaxDistance(value) => self.max_distance = *value,
809803
ControlMessage::RollOffFactor(value) => self.rolloff_factor = *value,
810804
ControlMessage::ConeInnerAngle(value) => self.cone_inner_angle = *value,
811805
ControlMessage::ConeOuterAngle(value) => self.cone_outer_angle = *value,
812806
ControlMessage::ConeOuterGain(value) => self.cone_outer_gain = *value,
807+
ControlMessage::PanningModel(value) => self.hrtf_state = value.take(),
813808
}
814809

815810
return;

0 commit comments

Comments
 (0)