Skip to content

Commit aa89aa2

Browse files
committed
Move AudioNode trait to src/node/audio_node.rs
1 parent 18c0681 commit aa89aa2

File tree

2 files changed

+374
-366
lines changed

2 files changed

+374
-366
lines changed

src/node/audio_node.rs

Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
use std::sync::{Arc, Mutex};
2+
3+
use crate::context::{AudioContextRegistration, ConcreteBaseAudioContext};
4+
use crate::events::{ErrorEvent, EventHandler, EventPayload, EventType};
5+
use crate::message::ControlMessage;
6+
7+
/// How channels must be matched between the node's inputs and outputs.
8+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
9+
pub enum ChannelCountMode {
10+
/// `computedNumberOfChannels` is the maximum of the number of channels of all connections to an
11+
/// input. In this mode channelCount is ignored.
12+
Max,
13+
/// `computedNumberOfChannels` is determined as for "max" and then clamped to a maximum value of
14+
/// the given channelCount.
15+
ClampedMax,
16+
/// `computedNumberOfChannels` is the exact value as specified by the channelCount.
17+
Explicit,
18+
}
19+
20+
impl From<u32> for ChannelCountMode {
21+
fn from(i: u32) -> Self {
22+
use ChannelCountMode::*;
23+
24+
match i {
25+
0 => Max,
26+
1 => ClampedMax,
27+
2 => Explicit,
28+
_ => unreachable!(),
29+
}
30+
}
31+
}
32+
33+
/// The meaning of the channels, defining how audio up-mixing and down-mixing will happen.
34+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
35+
pub enum ChannelInterpretation {
36+
Speakers,
37+
Discrete,
38+
}
39+
40+
impl From<u32> for ChannelInterpretation {
41+
fn from(i: u32) -> Self {
42+
use ChannelInterpretation::*;
43+
44+
match i {
45+
0 => Speakers,
46+
1 => Discrete,
47+
_ => unreachable!(),
48+
}
49+
}
50+
}
51+
52+
/// Options that can be used in constructing all AudioNodes.
53+
#[derive(Clone, Debug)]
54+
pub struct AudioNodeOptions {
55+
/// Desired number of channels for the [`AudioNode::channel_count`] attribute.
56+
pub channel_count: usize,
57+
/// Desired mode for the [`AudioNode::channel_count_mode`] attribute.
58+
pub channel_count_mode: ChannelCountMode,
59+
/// Desired mode for the [`AudioNode::channel_interpretation`] attribute.
60+
pub channel_interpretation: ChannelInterpretation,
61+
}
62+
63+
impl Default for AudioNodeOptions {
64+
fn default() -> Self {
65+
Self {
66+
channel_count: 2,
67+
channel_count_mode: ChannelCountMode::Max,
68+
channel_interpretation: ChannelInterpretation::Speakers,
69+
}
70+
}
71+
}
72+
73+
/// Config for up/down-mixing of input channels for audio nodes
74+
///
75+
/// Only when implementing the [`AudioNode`] trait manually, this struct is of any concern. The
76+
/// methods `set_channel_count`, `set_channel_count_mode` and `set_channel_interpretation` from the
77+
/// audio node interface will use this struct to sync the required info to the render thread.
78+
///
79+
/// The only way to construct an instance is with [`AudioNodeOptions`]
80+
///
81+
/// ```
82+
/// use web_audio_api::node::{AudioNodeOptions, ChannelConfig, ChannelInterpretation, ChannelCountMode};
83+
///
84+
/// let opts = AudioNodeOptions {
85+
/// channel_count: 1,
86+
/// channel_count_mode: ChannelCountMode::Explicit,
87+
/// channel_interpretation: ChannelInterpretation::Discrete,
88+
/// };
89+
/// let _: ChannelConfig = opts.into();
90+
#[derive(Clone)]
91+
pub struct ChannelConfig {
92+
inner: Arc<Mutex<ChannelConfigInner>>,
93+
}
94+
95+
#[derive(Debug, Clone)]
96+
pub(crate) struct ChannelConfigInner {
97+
pub(crate) count: usize,
98+
pub(crate) count_mode: ChannelCountMode,
99+
pub(crate) interpretation: ChannelInterpretation,
100+
}
101+
102+
impl Default for ChannelConfig {
103+
fn default() -> Self {
104+
AudioNodeOptions::default().into()
105+
}
106+
}
107+
108+
impl std::fmt::Debug for ChannelConfig {
109+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110+
f.debug_struct("ChannelConfig")
111+
.field("count", &self.count())
112+
.field("count_mode", &self.count_mode())
113+
.field("interpretation", &self.interpretation())
114+
.finish()
115+
}
116+
}
117+
118+
// All methods on this struct are marked `pub(crate)` because we don't want outside users to be
119+
// able to change the values directly. These methods are only accessible via the AudioNode
120+
// interface, so AudioNode's that have channel count/mode constraints should be able to assert
121+
// those.
122+
//
123+
// Uses the canonical ordering for handover of values, i.e. `Acquire` on load and `Release` on
124+
// store.
125+
impl ChannelConfig {
126+
/// Represents an enumerated value describing the way channels must be matched between the
127+
/// node's inputs and outputs.
128+
pub(crate) fn count_mode(&self) -> ChannelCountMode {
129+
self.inner.lock().unwrap().count_mode
130+
}
131+
132+
pub(super) fn set_count_mode(
133+
&self,
134+
v: ChannelCountMode,
135+
registration: &AudioContextRegistration,
136+
) {
137+
let mut guard = self.inner.lock().unwrap();
138+
guard.count_mode = v;
139+
140+
let message = ControlMessage::SetChannelCountMode {
141+
id: registration.id(),
142+
mode: v,
143+
};
144+
registration.context().send_control_msg(message);
145+
146+
drop(guard); // drop guard after sending message to prevent out of order arrivals on
147+
// concurrent access
148+
}
149+
150+
/// Represents an enumerated value describing the meaning of the channels. This interpretation
151+
/// will define how audio up-mixing and down-mixing will happen.
152+
pub(crate) fn interpretation(&self) -> ChannelInterpretation {
153+
self.inner.lock().unwrap().interpretation
154+
}
155+
156+
pub(super) fn set_interpretation(
157+
&self,
158+
v: ChannelInterpretation,
159+
registration: &AudioContextRegistration,
160+
) {
161+
let mut guard = self.inner.lock().unwrap();
162+
guard.interpretation = v;
163+
164+
let message = ControlMessage::SetChannelInterpretation {
165+
id: registration.id(),
166+
interpretation: v,
167+
};
168+
registration.context().send_control_msg(message);
169+
170+
drop(guard); // drop guard after sending message to prevent out of order arrivals on
171+
// concurrent access
172+
}
173+
174+
/// Represents an integer used to determine how many channels are used when up-mixing and
175+
/// down-mixing connections to any inputs to the node.
176+
pub(crate) fn count(&self) -> usize {
177+
self.inner.lock().unwrap().count
178+
}
179+
180+
pub(super) fn set_count(&self, v: usize, registration: &AudioContextRegistration) {
181+
crate::assert_valid_number_of_channels(v);
182+
183+
let mut guard = self.inner.lock().unwrap();
184+
guard.count = v;
185+
186+
let message = ControlMessage::SetChannelCount {
187+
id: registration.id(),
188+
count: v,
189+
};
190+
registration.context().send_control_msg(message);
191+
192+
drop(guard); // drop guard after sending message to prevent out of order arrivals on
193+
// concurrent access
194+
}
195+
196+
pub(crate) fn inner(&self) -> ChannelConfigInner {
197+
self.inner.lock().unwrap().clone()
198+
}
199+
}
200+
201+
impl From<AudioNodeOptions> for ChannelConfig {
202+
fn from(opts: AudioNodeOptions) -> Self {
203+
crate::assert_valid_number_of_channels(opts.channel_count);
204+
205+
let inner = ChannelConfigInner {
206+
count: opts.channel_count,
207+
count_mode: opts.channel_count_mode,
208+
interpretation: opts.channel_interpretation,
209+
};
210+
Self {
211+
inner: Arc::new(Mutex::new(inner)),
212+
}
213+
}
214+
}
215+
216+
/// This interface represents audio sources, the audio destination, and intermediate processing
217+
/// modules.
218+
///
219+
/// These modules can be connected together to form processing graphs for rendering audio
220+
/// to the audio hardware. Each node can have inputs and/or outputs.
221+
///
222+
/// Note that the AudioNode is typically constructed together with an `AudioWorkletProcessor`
223+
/// (the object that lives the render thread). See the [`crate::worklet`] mod.
224+
pub trait AudioNode {
225+
/// Handle of the associated [`BaseAudioContext`](crate::context::BaseAudioContext).
226+
///
227+
/// Only when implementing the AudioNode trait manually, this struct is of any concern.
228+
fn registration(&self) -> &AudioContextRegistration;
229+
230+
/// Config for up/down-mixing of input channels for this node.
231+
///
232+
/// Only when implementing the [`AudioNode`] trait manually, this struct is of any concern.
233+
fn channel_config(&self) -> &ChannelConfig;
234+
235+
/// The [`BaseAudioContext`](crate::context::BaseAudioContext) concrete type which owns this
236+
/// AudioNode.
237+
fn context(&self) -> &ConcreteBaseAudioContext {
238+
self.registration().context()
239+
}
240+
241+
/// Connect the output of this AudioNode to the input of another node.
242+
///
243+
/// # Panics
244+
///
245+
/// This function will panic when
246+
/// - the AudioContext of the source and destination does not match
247+
fn connect<'a>(&self, dest: &'a dyn AudioNode) -> &'a dyn AudioNode {
248+
self.connect_at(dest, 0, 0)
249+
}
250+
251+
/// Connect a specific output of this AudioNode to a specific input of another node.
252+
///
253+
/// # Panics
254+
///
255+
/// This function will panic when
256+
/// - the AudioContext of the source and destination does not match
257+
/// - if the input port is out of bounds for the destination node
258+
/// - if the output port is out of bounds for the source node
259+
fn connect_at<'a>(
260+
&self,
261+
dest: &'a dyn AudioNode,
262+
output: usize,
263+
input: usize,
264+
) -> &'a dyn AudioNode {
265+
assert!(
266+
self.context() == dest.context(),
267+
"InvalidAccessError - Attempting to connect nodes from different contexts",
268+
);
269+
270+
assert!(
271+
self.number_of_outputs() > output,
272+
"IndexSizeError - output port {} is out of bounds",
273+
output
274+
);
275+
276+
assert!(
277+
dest.number_of_inputs() > input,
278+
"IndexSizeError - input port {} is out of bounds",
279+
input
280+
);
281+
282+
self.context().connect(
283+
self.registration().id(),
284+
dest.registration().id(),
285+
output,
286+
input,
287+
);
288+
dest
289+
}
290+
291+
/// Disconnects all outputs of the AudioNode that go to a specific destination AudioNode.
292+
fn disconnect_from<'a>(&self, dest: &'a dyn AudioNode) -> &'a dyn AudioNode {
293+
assert!(
294+
self.context() == dest.context(),
295+
"InvalidAccessError - Attempting to disconnect nodes from different contexts"
296+
);
297+
298+
self.context()
299+
.disconnect_from(self.registration().id(), dest.registration().id());
300+
301+
dest
302+
}
303+
304+
/// Disconnects all outgoing connections from the AudioNode.
305+
fn disconnect(&self) {
306+
self.context().disconnect(self.registration().id());
307+
}
308+
309+
/// The number of inputs feeding into the AudioNode. For source nodes, this will be 0.
310+
fn number_of_inputs(&self) -> usize;
311+
312+
/// The number of outputs coming out of the AudioNode.
313+
fn number_of_outputs(&self) -> usize;
314+
315+
/// Represents an enumerated value describing the way channels must be matched between the
316+
/// node's inputs and outputs.
317+
fn channel_count_mode(&self) -> ChannelCountMode {
318+
self.channel_config().count_mode()
319+
}
320+
321+
/// Update the `channel_count_mode` attribute
322+
fn set_channel_count_mode(&self, v: ChannelCountMode) {
323+
self.channel_config().set_count_mode(v, self.registration())
324+
}
325+
326+
/// Represents an enumerated value describing the meaning of the channels. This interpretation
327+
/// will define how audio up-mixing and down-mixing will happen.
328+
fn channel_interpretation(&self) -> ChannelInterpretation {
329+
self.channel_config().interpretation()
330+
}
331+
332+
/// Update the `channel_interpretation` attribute
333+
fn set_channel_interpretation(&self, v: ChannelInterpretation) {
334+
self.channel_config()
335+
.set_interpretation(v, self.registration())
336+
}
337+
/// Represents an integer used to determine how many channels are used when up-mixing and
338+
/// down-mixing connections to any inputs to the node.
339+
fn channel_count(&self) -> usize {
340+
self.channel_config().count()
341+
}
342+
343+
/// Update the `channel_count` attribute
344+
fn set_channel_count(&self, v: usize) {
345+
self.channel_config().set_count(v, self.registration())
346+
}
347+
348+
/// Register callback to run when an unhandled exception occurs in the audio processor.
349+
///
350+
/// Note that once a unhandled exception is thrown, the processor will output silence throughout its lifetime.
351+
///
352+
/// Only a single event handler is active at any time. Calling this method multiple times will
353+
/// override the previous event handler.
354+
fn set_onprocessorerror(&self, callback: Box<dyn FnOnce(ErrorEvent) + Send + 'static>) {
355+
let callback = move |v| match v {
356+
EventPayload::ProcessorError(v) => callback(v),
357+
_ => unreachable!(),
358+
};
359+
360+
self.context().set_event_handler(
361+
EventType::ProcessorError(self.registration().id()),
362+
EventHandler::Once(Box::new(callback)),
363+
);
364+
}
365+
366+
/// Unset the callback to run when an unhandled exception occurs in the audio processor.
367+
fn clear_onprocessorerror(&self) {
368+
self.context()
369+
.clear_event_handler(EventType::ProcessorError(self.registration().id()));
370+
}
371+
}

0 commit comments

Comments
 (0)