Skip to content

Commit d432715

Browse files
b-maorottier
authored andcommitted
refactor: wip message passing
1 parent 3235d24 commit d432715

File tree

1 file changed

+140
-92
lines changed

1 file changed

+140
-92
lines changed

src/node/panner.rs

Lines changed: 140 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
use std::any::Any;
12
use std::f32::consts::PI;
23
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};
48

59
use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
610
use crate::param::{AudioParam, AudioParamDescriptor};
@@ -11,10 +15,6 @@ use super::{
1115
AudioNode, ChannelConfig, ChannelConfigOptions, ChannelCountMode, ChannelInterpretation,
1216
};
1317

14-
use crossbeam_channel::{Receiver, Sender};
15-
use float_eq::float_eq;
16-
use hrtf::{HrirSphere, HrtfContext, HrtfProcessor, Vec3};
17-
1818
/// Spatialization algorithm used to position the audio in 3D space
1919
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
2020
pub enum PanningModelType {
@@ -115,6 +115,18 @@ impl Default for PannerOptions {
115115
}
116116
}
117117

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+
118130
/// Assert that the channel count is valid for the PannerNode
119131
/// see <https://webaudio.github.io/web-audio-api/#audionode-channelcount-constraints>
120132
///
@@ -271,13 +283,13 @@ pub struct PannerNode {
271283
orientation_x: AudioParam,
272284
orientation_y: AudioParam,
273285
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,
281293
panning_model: AtomicU8,
282294
/// HRTF message bus to the renderer
283295
sender: Sender<Option<HrtfState>>,
@@ -333,37 +345,45 @@ impl PannerNode {
333345
pub fn new<C: BaseAudioContext>(context: &C, options: PannerOptions) -> Self {
334346
let node = context.register(move |registration| {
335347
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+
336367
// 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.);
343374

344375
// orientation params
345376
let orientation_x_opts = AudioParamDescriptor {
346377
default_value: 1.0,
347378
..PARAM_OPTS
348379
};
349-
let (orientation_x, render_ox) =
380+
let (param_ox, render_ox) =
350381
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.);
367387

368388
// Channel to send a HRTF processor to the renderer. A capacity of 1 suffices, it will
369389
// simply block the control thread when used concurrently
@@ -376,44 +396,39 @@ impl PannerNode {
376396
orientation_x: render_ox,
377397
orientation_y: render_oy,
378398
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,
405399
distance_model,
406400
ref_distance,
407401
max_distance,
408402
rolloff_factor,
409403
cone_inner_angle,
410404
cone_outer_angle,
411405
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),
412427
sender,
413428
panning_model: AtomicU8::new(0),
414429
};
415430

416-
node.set_panning_model(options.panning_model);
431+
node.set_panning_model(panning_model);
417432

418433
// instruct to BaseContext to add the AudioListener if it has not already
419434
context.base().ensure_audio_listener_present();
@@ -454,63 +469,78 @@ impl PannerNode {
454469
}
455470

456471
pub fn distance_model(&self) -> DistanceModelType {
457-
self.distance_model.load(Ordering::SeqCst).into()
472+
self.distance_model.load(Ordering::Acquire).into()
458473
}
459474

460475
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));
462480
}
463481

464482
pub fn ref_distance(&self) -> f64 {
465-
self.ref_distance.load(Ordering::SeqCst)
483+
self.ref_distance.load(Ordering::Acquire)
466484
}
467485

468486
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));
470490
}
471491

472492
pub fn max_distance(&self) -> f64 {
473-
self.max_distance.load(Ordering::SeqCst)
493+
self.max_distance.load(Ordering::Acquire)
474494
}
475495

476496
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));
478500
}
479501

480502
pub fn rolloff_factor(&self) -> f64 {
481-
self.rolloff_factor.load(Ordering::SeqCst)
503+
self.rolloff_factor.load(Ordering::Acquire)
482504
}
483505

484506
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));
486510
}
487511

488512
pub fn cone_inner_angle(&self) -> f64 {
489-
self.cone_inner_angle.load(Ordering::SeqCst)
513+
self.cone_inner_angle.load(Ordering::Acquire)
490514
}
491515

492516
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));
494520
}
495521

496522
pub fn cone_outer_angle(&self) -> f64 {
497-
self.cone_outer_angle.load(Ordering::SeqCst)
523+
self.cone_outer_angle.load(Ordering::Acquire)
498524
}
499525

500526
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));
502530
}
503531

504532
pub fn cone_outer_gain(&self) -> f64 {
505-
self.cone_outer_gain.load(Ordering::SeqCst)
533+
self.cone_outer_gain.load(Ordering::Acquire)
506534
}
507535

508536
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));
510540
}
511541

512542
pub fn panning_model(&self) -> PanningModelType {
513-
self.panning_model.load(Ordering::SeqCst).into()
543+
self.panning_model.load(Ordering::Acquire).into()
514544
}
515545

516546
// can panic when loading HRIR-sphere
@@ -527,7 +557,7 @@ impl PannerNode {
527557
};
528558

529559
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);
531561
}
532562
}
533563

@@ -546,13 +576,13 @@ struct PannerRenderer {
546576
orientation_x: AudioParamId,
547577
orientation_y: AudioParamId,
548578
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,
556586
receiver: Receiver<Option<HrtfState>>,
557587
hrtf_state: Option<HrtfState>,
558588
tail_time_counter: usize,
@@ -762,6 +792,24 @@ impl AudioProcessor for PannerRenderer {
762792
// tail time only for HRTF panning
763793
self.hrtf_state.is_some()
764794
}
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+
}
765813
}
766814

767815
impl PannerRenderer {
@@ -771,12 +819,12 @@ impl PannerRenderer {
771819
source_orientation: [f32; 3],
772820
listener_position: [f32; 3],
773821
) -> 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.;
776824
if abs_inner_angle >= 180. && abs_outer_angle >= 180. {
777825
1. // no cone specified
778826
} 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;
780828

781829
let abs_angle =
782830
crate::spatial::angle(source_position, source_orientation, listener_position);
@@ -794,14 +842,14 @@ impl PannerRenderer {
794842
}
795843

796844
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;
800848
let distance = crate::spatial::distance(source_position, listener_position) as f64;
801849

802850
let dist_gain = match distance_model {
803851
DistanceModelType::Linear => {
804-
let max_distance = self.max_distance.load(Ordering::SeqCst);
852+
let max_distance = self.max_distance;
805853
let d2ref = ref_distance.min(max_distance);
806854
let d2max = ref_distance.max(max_distance);
807855
let d_clamped = distance.clamp(d2ref, d2max);

0 commit comments

Comments
 (0)