Skip to main content

script/dom/audio/
audioscheduledsourcenode.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use servo_media::audio::audio_node::{
9    AudioNodeMessage, AudioScheduledSourceNodeMessage, OnEndedCallback,
10};
11use servo_media::audio::graph::NodeId;
12
13use crate::dom::audio::audionode::{AudioNode, UnwrappedAudioNodeOptions};
14use crate::dom::audio::baseaudiocontext::BaseAudioContext;
15use crate::dom::bindings::codegen::Bindings::AudioScheduledSourceNodeBinding::AudioScheduledSourceNodeMethods;
16use crate::dom::bindings::error::{Error, Fallible};
17use crate::dom::bindings::inheritance::Castable;
18use crate::dom::bindings::num::Finite;
19use crate::dom::bindings::refcounted::Trusted;
20use crate::dom::bindings::reflector::DomGlobal;
21
22#[dom_struct]
23pub(crate) struct AudioScheduledSourceNode {
24    node: AudioNode,
25    has_start: Cell<bool>,
26    has_stop: Cell<bool>,
27}
28
29impl AudioScheduledSourceNode {
30    pub(crate) fn new_inherited(
31        node_id: Option<NodeId>,
32        context: &BaseAudioContext,
33        options: UnwrappedAudioNodeOptions,
34        number_of_inputs: u32,
35        number_of_outputs: u32,
36    ) -> AudioScheduledSourceNode {
37        AudioScheduledSourceNode {
38            node: AudioNode::new_inherited(
39                node_id,
40                context,
41                options,
42                number_of_inputs,
43                number_of_outputs,
44            ),
45            has_start: Cell::new(false),
46            has_stop: Cell::new(false),
47        }
48    }
49
50    pub(crate) fn node(&self) -> &AudioNode {
51        &self.node
52    }
53
54    pub(crate) fn has_start(&self) -> bool {
55        self.has_start.get()
56    }
57}
58
59impl AudioScheduledSourceNodeMethods<crate::DomTypeHolder> for AudioScheduledSourceNode {
60    // https://webaudio.github.io/web-audio-api/#dom-audioscheduledsourcenode-onended
61    event_handler!(ended, GetOnended, SetOnended);
62
63    /// <https://webaudio.github.io/web-audio-api/#dom-audioscheduledsourcenode-start>
64    fn Start(&self, when: Finite<f64>) -> Fallible<()> {
65        if *when < 0. {
66            return Err(Error::Range(c"'when' must be a positive value".to_owned()));
67        }
68
69        if self.has_start.get() || self.has_stop.get() {
70            return Err(Error::InvalidState(Some(
71                "Node has already been started".into(),
72            )));
73        }
74
75        let this = Trusted::new(self);
76        let task_source = self
77            .global()
78            .task_manager()
79            .dom_manipulation_task_source()
80            .to_sendable();
81        let callback = OnEndedCallback::new(move || {
82            task_source.queue(task!(ended: move || {
83                let this = this.root();
84                this.global().task_manager().dom_manipulation_task_source().queue_simple_event(
85                    this.upcast(),
86                    atom!("ended"),
87                    );
88            }));
89        });
90
91        self.node()
92            .message(AudioNodeMessage::AudioScheduledSourceNode(
93                AudioScheduledSourceNodeMessage::RegisterOnEndedCallback(callback),
94            ));
95
96        self.has_start.set(true);
97        self.node
98            .message(AudioNodeMessage::AudioScheduledSourceNode(
99                AudioScheduledSourceNodeMessage::Start(*when),
100            ));
101        Ok(())
102    }
103
104    /// <https://webaudio.github.io/web-audio-api/#dom-audioscheduledsourcenode-stop>
105    fn Stop(&self, when: Finite<f64>) -> Fallible<()> {
106        if *when < 0. {
107            return Err(Error::Range(c"'when' must be a positive value".to_owned()));
108        }
109
110        if !self.has_start.get() {
111            return Err(Error::InvalidState(Some(
112                "Cannot stop a node that has not been started".into(),
113            )));
114        }
115        self.has_stop.set(true);
116        self.node
117            .message(AudioNodeMessage::AudioScheduledSourceNode(
118                AudioScheduledSourceNodeMessage::Stop(*when),
119            ));
120        Ok(())
121    }
122}