web_audio_api/node/
audio_node.rs

1use std::sync::{Arc, Mutex};
2
3use crate::context::{AudioContextRegistration, ConcreteBaseAudioContext};
4use crate::events::{ErrorEvent, EventHandler, EventPayload, EventType};
5use crate::message::ControlMessage;
6
7/// How channels must be matched between the node's inputs and outputs.
8#[derive(Copy, Clone, PartialEq, Eq, Debug)]
9pub 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
20impl 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)]
35pub enum ChannelInterpretation {
36    Speakers,
37    Discrete,
38}
39
40impl 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)]
54pub 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
63impl 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)]
91pub struct ChannelConfig {
92    inner: Arc<Mutex<ChannelConfigInner>>,
93}
94
95#[derive(Debug, Clone)]
96pub(crate) struct ChannelConfigInner {
97    pub(crate) count: usize,
98    pub(crate) count_mode: ChannelCountMode,
99    pub(crate) interpretation: ChannelInterpretation,
100}
101
102impl Default for ChannelConfig {
103    fn default() -> Self {
104        AudioNodeOptions::default().into()
105    }
106}
107
108impl 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.
125impl 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
201impl 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.
224pub 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_from_output_to_input(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_from_output_to_input<'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 outgoing connections from the AudioNode.
292    fn disconnect(&self) {
293        self.context()
294            .disconnect(self.registration().id(), None, None, None);
295    }
296
297    /// Disconnects all outputs of the AudioNode that go to a specific destination AudioNode.
298    ///
299    /// # Panics
300    ///
301    /// This function will panic when
302    /// - the AudioContext of the source and destination does not match
303    /// - the source node was not connected to the destination node
304    fn disconnect_dest(&self, dest: &dyn AudioNode) {
305        assert!(
306            self.context() == dest.context(),
307            "InvalidAccessError - Attempting to disconnect nodes from different contexts"
308        );
309
310        self.context().disconnect(
311            self.registration().id(),
312            None,
313            Some(dest.registration().id()),
314            None,
315        );
316    }
317
318    /// Disconnects all outgoing connections at the given output port from the AudioNode.
319    ///
320    /// # Panics
321    ///
322    /// This function will panic when
323    /// - if the output port is out of bounds for this node
324    fn disconnect_output(&self, output: usize) {
325        assert!(
326            self.number_of_outputs() > output,
327            "IndexSizeError - output port {} is out of bounds",
328            output
329        );
330
331        self.context()
332            .disconnect(self.registration().id(), Some(output), None, None);
333    }
334
335    /// Disconnects a specific output of the AudioNode to a specific destination AudioNode
336    ///
337    /// # Panics
338    ///
339    /// This function will panic when
340    /// - the AudioContext of the source and destination does not match
341    /// - if the output port is out of bounds for the source node
342    /// - the source node was not connected to the destination node
343    fn disconnect_dest_from_output(&self, dest: &dyn AudioNode, output: usize) {
344        assert!(
345            self.context() == dest.context(),
346            "InvalidAccessError - Attempting to disconnect nodes from different contexts"
347        );
348
349        assert!(
350            self.number_of_outputs() > output,
351            "IndexSizeError - output port {} is out of bounds",
352            output
353        );
354
355        self.context().disconnect(
356            self.registration().id(),
357            Some(output),
358            Some(dest.registration().id()),
359            None,
360        );
361    }
362
363    /// Disconnects a specific output of the AudioNode to a specific input of some destination
364    /// AudioNode
365    ///
366    /// # Panics
367    ///
368    /// This function will panic when
369    /// - the AudioContext of the source and destination does not match
370    /// - if the input port is out of bounds for the destination node
371    /// - if the output port is out of bounds for the source node
372    /// - the source node was not connected to the destination node
373    fn disconnect_dest_from_output_to_input(
374        &self,
375        dest: &dyn AudioNode,
376        output: usize,
377        input: usize,
378    ) {
379        assert!(
380            self.context() == dest.context(),
381            "InvalidAccessError - Attempting to disconnect nodes from different contexts"
382        );
383
384        assert!(
385            self.number_of_outputs() > output,
386            "IndexSizeError - output port {} is out of bounds",
387            output
388        );
389
390        assert!(
391            dest.number_of_inputs() > input,
392            "IndexSizeError - input port {} is out of bounds",
393            input
394        );
395
396        self.context().disconnect(
397            self.registration().id(),
398            Some(output),
399            Some(dest.registration().id()),
400            Some(input),
401        );
402    }
403
404    /// The number of inputs feeding into the AudioNode. For source nodes, this will be 0.
405    fn number_of_inputs(&self) -> usize;
406
407    /// The number of outputs coming out of the AudioNode.
408    fn number_of_outputs(&self) -> usize;
409
410    /// Represents an enumerated value describing the way channels must be matched between the
411    /// node's inputs and outputs.
412    fn channel_count_mode(&self) -> ChannelCountMode {
413        self.channel_config().count_mode()
414    }
415
416    /// Update the `channel_count_mode` attribute
417    fn set_channel_count_mode(&self, v: ChannelCountMode) {
418        self.channel_config().set_count_mode(v, self.registration())
419    }
420
421    /// Represents an enumerated value describing the meaning of the channels. This interpretation
422    /// will define how audio up-mixing and down-mixing will happen.
423    fn channel_interpretation(&self) -> ChannelInterpretation {
424        self.channel_config().interpretation()
425    }
426
427    /// Update the `channel_interpretation` attribute
428    fn set_channel_interpretation(&self, v: ChannelInterpretation) {
429        self.channel_config()
430            .set_interpretation(v, self.registration())
431    }
432    /// Represents an integer used to determine how many channels are used when up-mixing and
433    /// down-mixing connections to any inputs to the node.
434    fn channel_count(&self) -> usize {
435        self.channel_config().count()
436    }
437
438    /// Update the `channel_count` attribute
439    fn set_channel_count(&self, v: usize) {
440        self.channel_config().set_count(v, self.registration())
441    }
442
443    /// Register callback to run when an unhandled exception occurs in the audio processor.
444    ///
445    /// Note that once a unhandled exception is thrown, the processor will output silence throughout its lifetime.
446    ///
447    /// Only a single event handler is active at any time. Calling this method multiple times will
448    /// override the previous event handler.
449    fn set_onprocessorerror(&self, callback: Box<dyn FnOnce(ErrorEvent) + Send + 'static>) {
450        let callback = move |v| match v {
451            EventPayload::ProcessorError(v) => callback(v),
452            _ => unreachable!(),
453        };
454
455        self.context().set_event_handler(
456            EventType::ProcessorError(self.registration().id()),
457            EventHandler::Once(Box::new(callback)),
458        );
459    }
460
461    /// Unset the callback to run when an unhandled exception occurs in the audio processor.
462    fn clear_onprocessorerror(&self) {
463        self.context()
464            .clear_event_handler(EventType::ProcessorError(self.registration().id()));
465    }
466}