Skip to content

Commit 2544ef1

Browse files
authored
Merge pull request #365 from orottier/feature/no-interior-mutability-with-atomics
Propagate changes to node's ChannelConfig via the main message bus
2 parents 7cb0ebd + 12ac573 commit 2544ef1

File tree

11 files changed

+137
-51
lines changed

11 files changed

+137
-51
lines changed

src/context/concrete_base.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ impl BaseAudioContext for ConcreteBaseAudioContext {
128128
node: render,
129129
inputs: node.number_of_inputs(),
130130
outputs: node.number_of_outputs(),
131-
channel_config: node.channel_config().clone(),
131+
channel_config: node.channel_config().inner(),
132132
};
133133

134134
// if this is the AudioListener or its params, do not add it to the graph just yet

src/message.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::any::Any;
44

55
use crate::context::AudioNodeId;
6-
use crate::node::ChannelConfig;
6+
use crate::node::{ChannelConfigInner, ChannelCountMode, ChannelInterpretation};
77
use crate::render::graph::Graph;
88
use crate::render::AudioProcessor;
99

@@ -16,7 +16,7 @@ pub(crate) enum ControlMessage {
1616
node: Box<dyn AudioProcessor>,
1717
inputs: usize,
1818
outputs: usize,
19-
channel_config: ChannelConfig,
19+
channel_config: ChannelConfigInner,
2020
},
2121

2222
/// Connect a node to another in the audio graph
@@ -64,6 +64,21 @@ pub(crate) enum ControlMessage {
6464

6565
/// Request a diagnostic report of the audio graph
6666
RunDiagnostics { buffer: Vec<u8> },
67+
68+
/// Update the channel count of a node
69+
SetChannelCount { id: AudioNodeId, count: usize },
70+
71+
/// Update the channel count mode of a node
72+
SetChannelCountMode {
73+
id: AudioNodeId,
74+
mode: ChannelCountMode,
75+
},
76+
77+
/// Update the channel interpretation of a node
78+
SetChannelInterpretation {
79+
id: AudioNodeId,
80+
interpretation: ChannelInterpretation,
81+
},
6782
}
6883

6984
/// Helper object to emit single notification

src/node/channel_merger.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,13 @@ impl AudioNode for ChannelMergerNode {
8080

8181
fn set_channel_count(&self, count: usize) {
8282
assert_valid_channel_count(count);
83-
self.channel_config.set_count(count);
83+
self.channel_config.set_count(count, self.registration());
8484
}
8585

8686
fn set_channel_count_mode(&self, mode: ChannelCountMode) {
8787
assert_valid_channel_count_mode(mode);
88-
self.channel_config.set_count_mode(mode);
88+
self.channel_config
89+
.set_count_mode(mode, self.registration());
8990
}
9091

9192
fn number_of_inputs(&self) -> usize {

src/node/channel_splitter.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,14 @@ impl AudioNode for ChannelSplitterNode {
8787

8888
fn set_channel_count_mode(&self, mode: ChannelCountMode) {
8989
assert_valid_channel_count_mode(mode);
90-
self.channel_config.set_count_mode(mode);
90+
self.channel_config
91+
.set_count_mode(mode, self.registration());
9192
}
9293

9394
fn set_channel_interpretation(&self, interpretation: ChannelInterpretation) {
9495
assert_valid_channel_interpretation(interpretation);
95-
self.channel_config.set_interpretation(interpretation);
96+
self.channel_config
97+
.set_interpretation(interpretation, self.registration());
9698
}
9799

98100
fn number_of_inputs(&self) -> usize {

src/node/destination.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl AudioNode for AudioDestinationNode {
6363
self.max_channel_count()
6464
);
6565

66-
self.channel_config.set_count(v);
66+
self.channel_config.set_count(v, self.registration());
6767
}
6868

6969
fn set_channel_count_mode(&self, v: ChannelCountMode) {
@@ -75,7 +75,7 @@ impl AudioNode for AudioDestinationNode {
7575
"InvalidStateError - AudioDestinationNode has channel count mode constraints",
7676
);
7777

78-
self.channel_config.set_count_mode(v);
78+
self.channel_config.set_count_mode(v, self.registration());
7979
}
8080
}
8181

src/node/mod.rs

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
//! The AudioNode interface and concrete types
22
use std::f32::consts::PI;
3-
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
4-
use std::sync::{Arc, OnceLock};
3+
use std::sync::{Arc, Mutex, OnceLock};
54

65
use crate::context::{AudioContextRegistration, ConcreteBaseAudioContext};
76
use crate::events::{ErrorEvent, EventHandler, EventPayload, EventType};
7+
use crate::message::ControlMessage;
88
use crate::render::{AudioParamValues, AudioProcessor, AudioRenderQuantum, RenderScope};
99
use crate::AudioBufferIter;
1010
use crate::Event;
@@ -153,14 +153,14 @@ impl Default for ChannelConfigOptions {
153153
/// let _: ChannelConfig = opts.into();
154154
#[derive(Clone, Debug)]
155155
pub struct ChannelConfig {
156-
inner: Arc<ChannelConfigInner>,
156+
inner: Arc<Mutex<ChannelConfigInner>>,
157157
}
158158

159-
#[derive(Debug)]
160-
struct ChannelConfigInner {
161-
count: AtomicUsize,
162-
count_mode: AtomicU32,
163-
interpretation: AtomicU32,
159+
#[derive(Debug, Clone)]
160+
pub(crate) struct ChannelConfigInner {
161+
pub(crate) count: usize,
162+
pub(crate) count_mode: ChannelCountMode,
163+
pub(crate) interpretation: ChannelInterpretation,
164164
}
165165

166166
impl Default for ChannelConfig {
@@ -180,32 +180,71 @@ impl ChannelConfig {
180180
/// Represents an enumerated value describing the way channels must be matched between the
181181
/// node's inputs and outputs.
182182
pub(crate) fn count_mode(&self) -> ChannelCountMode {
183-
self.inner.count_mode.load(Ordering::Acquire).into()
183+
self.inner.lock().unwrap().count_mode
184184
}
185185

186-
fn set_count_mode(&self, v: ChannelCountMode) {
187-
self.inner.count_mode.store(v as u32, Ordering::Release)
186+
fn set_count_mode(&self, v: ChannelCountMode, registration: &AudioContextRegistration) {
187+
let mut guard = self.inner.lock().unwrap();
188+
guard.count_mode = v;
189+
190+
let message = ControlMessage::SetChannelCountMode {
191+
id: registration.id(),
192+
mode: v,
193+
};
194+
registration.context().send_control_msg(message);
195+
196+
drop(guard); // drop guard after sending message to prevent out of order arrivals on
197+
// concurrent access
188198
}
189199

190200
/// Represents an enumerated value describing the meaning of the channels. This interpretation
191201
/// will define how audio up-mixing and down-mixing will happen.
192202
pub(crate) fn interpretation(&self) -> ChannelInterpretation {
193-
self.inner.interpretation.load(Ordering::Acquire).into()
203+
self.inner.lock().unwrap().interpretation
194204
}
195205

196-
fn set_interpretation(&self, v: ChannelInterpretation) {
197-
self.inner.interpretation.store(v as u32, Ordering::Release)
206+
fn set_interpretation(
207+
&self,
208+
v: ChannelInterpretation,
209+
registration: &AudioContextRegistration,
210+
) {
211+
let mut guard = self.inner.lock().unwrap();
212+
guard.interpretation = v;
213+
214+
let message = ControlMessage::SetChannelInterpretation {
215+
id: registration.id(),
216+
interpretation: v,
217+
};
218+
registration.context().send_control_msg(message);
219+
220+
drop(guard); // drop guard after sending message to prevent out of order arrivals on
221+
// concurrent access
198222
}
199223

200224
/// Represents an integer used to determine how many channels are used when up-mixing and
201225
/// down-mixing connections to any inputs to the node.
202226
pub(crate) fn count(&self) -> usize {
203-
self.inner.count.load(Ordering::Acquire)
227+
self.inner.lock().unwrap().count
204228
}
205229

206-
fn set_count(&self, v: usize) {
230+
fn set_count(&self, v: usize, registration: &AudioContextRegistration) {
207231
crate::assert_valid_number_of_channels(v);
208-
self.inner.count.store(v, Ordering::Release)
232+
233+
let mut guard = self.inner.lock().unwrap();
234+
guard.count = v;
235+
236+
let message = ControlMessage::SetChannelCount {
237+
id: registration.id(),
238+
count: v,
239+
};
240+
registration.context().send_control_msg(message);
241+
242+
drop(guard); // drop guard after sending message to prevent out of order arrivals on
243+
// concurrent access
244+
}
245+
246+
pub(crate) fn inner(&self) -> ChannelConfigInner {
247+
self.inner.lock().unwrap().clone()
209248
}
210249
}
211250

@@ -214,12 +253,12 @@ impl From<ChannelConfigOptions> for ChannelConfig {
214253
crate::assert_valid_number_of_channels(opts.count);
215254

216255
let inner = ChannelConfigInner {
217-
count: AtomicUsize::from(opts.count),
218-
count_mode: AtomicU32::from(opts.count_mode as u32),
219-
interpretation: AtomicU32::from(opts.interpretation as u32),
256+
count: opts.count,
257+
count_mode: opts.count_mode,
258+
interpretation: opts.interpretation,
220259
};
221260
Self {
222-
inner: Arc::new(inner),
261+
inner: Arc::new(Mutex::new(inner)),
223262
}
224263
}
225264
}
@@ -325,7 +364,7 @@ pub trait AudioNode {
325364

326365
/// Update the `channel_count_mode` attribute
327366
fn set_channel_count_mode(&self, v: ChannelCountMode) {
328-
self.channel_config().set_count_mode(v)
367+
self.channel_config().set_count_mode(v, self.registration())
329368
}
330369

331370
/// Represents an enumerated value describing the meaning of the channels. This interpretation
@@ -336,7 +375,8 @@ pub trait AudioNode {
336375

337376
/// Update the `channel_interpretation` attribute
338377
fn set_channel_interpretation(&self, v: ChannelInterpretation) {
339-
self.channel_config().set_interpretation(v)
378+
self.channel_config()
379+
.set_interpretation(v, self.registration())
340380
}
341381
/// Represents an integer used to determine how many channels are used when up-mixing and
342382
/// down-mixing connections to any inputs to the node.
@@ -346,7 +386,7 @@ pub trait AudioNode {
346386

347387
/// Update the `channel_count` attribute
348388
fn set_channel_count(&self, v: usize) {
349-
self.channel_config().set_count(v)
389+
self.channel_config().set_count(v, self.registration())
350390
}
351391

352392
/// Register callback to run when an unhandled exception occurs in the audio processor.

src/node/panner.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,12 +361,13 @@ impl AudioNode for PannerNode {
361361
// see: https://webaudio.github.io/web-audio-api/#panner-channel-limitations
362362
fn set_channel_count(&self, count: usize) {
363363
assert_valid_channel_count(count);
364-
self.channel_config.set_count(count);
364+
self.channel_config.set_count(count, self.registration());
365365
}
366366

367367
fn set_channel_count_mode(&self, mode: ChannelCountMode) {
368368
assert_valid_channel_count_mode(mode);
369-
self.channel_config.set_count_mode(mode);
369+
self.channel_config
370+
.set_count_mode(mode, self.registration());
370371
}
371372
}
372373

src/node/stereo_panner.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,13 @@ impl AudioNode for StereoPannerNode {
144144

145145
fn set_channel_count_mode(&self, mode: ChannelCountMode) {
146146
assert_valid_channel_count_mode(mode);
147-
self.channel_config.set_count_mode(mode);
147+
self.channel_config
148+
.set_count_mode(mode, self.registration());
148149
}
149150

150151
fn set_channel_count(&self, count: usize) {
151152
assert_valid_channel_count(count);
152-
self.channel_config.set_count(count);
153+
self.channel_config.set_count(count, self.registration());
153154
}
154155
}
155156

src/render/graph.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::context::AudioNodeId;
77
use smallvec::{smallvec, SmallVec};
88

99
use super::{Alloc, AudioParamValues, AudioProcessor, AudioRenderQuantum, NodeCollection};
10-
use crate::node::ChannelConfig;
10+
use crate::node::{ChannelConfigInner, ChannelCountMode, ChannelInterpretation};
1111
use crate::render::RenderScope;
1212

1313
/// Connection between two audio nodes
@@ -46,7 +46,7 @@ pub struct Node {
4646
/// Reusable output buffers, consumed by subsequent Nodes in this graph
4747
outputs: Vec<AudioRenderQuantum>,
4848
/// Channel configuration: determines up/down-mixing of inputs
49-
channel_config: ChannelConfig,
49+
channel_config: ChannelConfigInner,
5050
/// Outgoing edges: tuple of outcoming node reference, our output index and their input index
5151
outgoing_edges: SmallVec<[OutgoingEdge; 2]>,
5252
/// Indicates if the control thread has dropped this Node
@@ -163,7 +163,7 @@ impl Graph {
163163
processor: Box<dyn AudioProcessor>,
164164
number_of_inputs: usize,
165165
number_of_outputs: usize,
166-
channel_config: ChannelConfig,
166+
channel_config: ChannelConfigInner,
167167
) {
168168
// todo: pre-allocate the buffers on the control thread
169169

@@ -240,6 +240,16 @@ impl Graph {
240240
self.nodes[index].get_mut().cycle_breaker = true;
241241
}
242242

243+
pub fn set_channel_count(&mut self, index: AudioNodeId, v: usize) {
244+
self.nodes[index].get_mut().channel_config.count = v;
245+
}
246+
pub fn set_channel_count_mode(&mut self, index: AudioNodeId, v: ChannelCountMode) {
247+
self.nodes[index].get_mut().channel_config.count_mode = v;
248+
}
249+
pub fn set_channel_interpretation(&mut self, index: AudioNodeId, v: ChannelInterpretation) {
250+
self.nodes[index].get_mut().channel_config.interpretation = v;
251+
}
252+
243253
pub fn route_message(&mut self, index: AudioNodeId, msg: &mut dyn Any) {
244254
self.nodes[index].get_mut().processor.onmessage(msg);
245255
}
@@ -545,13 +555,12 @@ mod tests {
545555
}
546556
}
547557

548-
fn config() -> ChannelConfig {
549-
crate::node::ChannelConfigOptions {
558+
fn config() -> ChannelConfigInner {
559+
ChannelConfigInner {
550560
count: 2,
551561
count_mode: crate::node::ChannelCountMode::Explicit,
552562
interpretation: crate::node::ChannelInterpretation::Speakers,
553563
}
554-
.into()
555564
}
556565

557566
fn add_node(graph: &mut Graph, id: u64, node: Box<dyn AudioProcessor>) {

src/render/quantum.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use arrayvec::ArrayVec;
33
use std::cell::RefCell;
44
use std::rc::Rc;
55

6-
use crate::node::{ChannelConfig, ChannelCountMode, ChannelInterpretation};
6+
use crate::node::{ChannelConfigInner, ChannelCountMode, ChannelInterpretation};
77

88
use crate::assert_valid_number_of_channels;
99
use crate::{MAX_CHANNELS, RENDER_QUANTUM_SIZE};
@@ -620,16 +620,16 @@ impl AudioRenderQuantum {
620620
/// Sum two `AudioRenderQuantum`s
621621
///
622622
/// Both buffers will be mixed up front according to the supplied `channel_config`
623-
pub(crate) fn add(&mut self, other: &Self, channel_config: &ChannelConfig) {
623+
pub(crate) fn add(&mut self, other: &Self, channel_config: &ChannelConfigInner) {
624624
// gather initial channel counts
625625
let channels_self = self.number_of_channels();
626626
let channels_other = other.number_of_channels();
627627
let max_channels = channels_self.max(channels_other);
628628

629629
// up/down-mix the to the desired channel count for the receiving node
630-
let interpretation = channel_config.interpretation();
631-
let mode = channel_config.count_mode();
632-
let count = channel_config.count();
630+
let interpretation = channel_config.interpretation;
631+
let mode = channel_config.count_mode;
632+
let count = channel_config.count;
633633

634634
let new_channels = match mode {
635635
ChannelCountMode::Max => max_channels,
@@ -1542,12 +1542,11 @@ mod tests {
15421542
signal2.copy_from_slice(&[2.; RENDER_QUANTUM_SIZE]);
15431543
let buffer2 = AudioRenderQuantum::from(signal2);
15441544

1545-
let channel_config = crate::node::ChannelConfigOptions {
1545+
let channel_config = ChannelConfigInner {
15461546
count: 2,
15471547
count_mode: ChannelCountMode::Explicit,
15481548
interpretation: ChannelInterpretation::Discrete,
1549-
}
1550-
.into();
1549+
};
15511550

15521551
buffer.add(&buffer2, &channel_config);
15531552

0 commit comments

Comments
 (0)