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::node::{
9    AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage, OnEndedCallback,
10};
11
12use crate::dom::audio::audionode::{AudioNode, UnwrappedAudioNodeOptions};
13use crate::dom::audio::baseaudiocontext::BaseAudioContext;
14use crate::dom::bindings::codegen::Bindings::AudioScheduledSourceNodeBinding::AudioScheduledSourceNodeMethods;
15use crate::dom::bindings::error::{Error, Fallible};
16use crate::dom::bindings::inheritance::Castable;
17use crate::dom::bindings::num::Finite;
18use crate::dom::bindings::refcounted::Trusted;
19use crate::dom::bindings::reflector::DomGlobal;
20
21#[dom_struct]
22pub(crate) struct AudioScheduledSourceNode {
23    node: AudioNode,
24    has_start: Cell<bool>,
25    has_stop: Cell<bool>,
26}
27
28impl AudioScheduledSourceNode {
29    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
30    pub(crate) fn new_inherited(
31        node_type: AudioNodeInit,
32        context: &BaseAudioContext,
33        options: UnwrappedAudioNodeOptions,
34        number_of_inputs: u32,
35        number_of_outputs: u32,
36    ) -> Fallible<AudioScheduledSourceNode> {
37        Ok(AudioScheduledSourceNode {
38            node: AudioNode::new_inherited(
39                node_type,
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("'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);
71        }
72
73        let this = Trusted::new(self);
74        let task_source = self
75            .global()
76            .task_manager()
77            .dom_manipulation_task_source()
78            .to_sendable();
79        let callback = OnEndedCallback::new(move || {
80            task_source.queue(task!(ended: move || {
81                let this = this.root();
82                this.global().task_manager().dom_manipulation_task_source().queue_simple_event(
83                    this.upcast(),
84                    atom!("ended"),
85                    );
86            }));
87        });
88
89        self.node()
90            .message(AudioNodeMessage::AudioScheduledSourceNode(
91                AudioScheduledSourceNodeMessage::RegisterOnEndedCallback(callback),
92            ));
93
94        self.has_start.set(true);
95        self.node
96            .message(AudioNodeMessage::AudioScheduledSourceNode(
97                AudioScheduledSourceNodeMessage::Start(*when),
98            ));
99        Ok(())
100    }
101
102    // https://webaudio.github.io/web-audio-api/#dom-audioscheduledsourcenode-stop
103    fn Stop(&self, when: Finite<f64>) -> Fallible<()> {
104        if *when < 0. {
105            return Err(Error::Range("'when' must be a positive value".to_owned()));
106        }
107
108        if !self.has_start.get() {
109            return Err(Error::InvalidState);
110        }
111        self.has_stop.set(true);
112        self.node
113            .message(AudioNodeMessage::AudioScheduledSourceNode(
114                AudioScheduledSourceNodeMessage::Stop(*when),
115            ));
116        Ok(())
117    }
118}