script/dom/audio/
oscillatornode.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;
6use std::f32;
7
8use dom_struct::dom_struct;
9use js::rust::HandleObject;
10use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioNodeType};
11use servo_media::audio::oscillator_node::{
12    OscillatorNodeMessage, OscillatorNodeOptions as ServoMediaOscillatorOptions,
13    OscillatorType as ServoMediaOscillatorType,
14};
15use servo_media::audio::param::ParamType;
16
17use crate::conversions::Convert;
18use crate::dom::audio::audionode::AudioNodeOptionsHelper;
19use crate::dom::audio::audioparam::AudioParam;
20use crate::dom::audio::audioscheduledsourcenode::AudioScheduledSourceNode;
21use crate::dom::audio::baseaudiocontext::BaseAudioContext;
22use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
23    ChannelCountMode, ChannelInterpretation,
24};
25use crate::dom::bindings::codegen::Bindings::AudioParamBinding::AutomationRate;
26use crate::dom::bindings::codegen::Bindings::OscillatorNodeBinding::{
27    OscillatorNodeMethods, OscillatorOptions, OscillatorType,
28};
29use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
30use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
31use crate::dom::bindings::root::{Dom, DomRoot};
32use crate::dom::window::Window;
33use crate::script_runtime::CanGc;
34
35#[dom_struct]
36pub(crate) struct OscillatorNode {
37    source_node: AudioScheduledSourceNode,
38    detune: Dom<AudioParam>,
39    frequency: Dom<AudioParam>,
40    oscillator_type: Cell<OscillatorType>,
41}
42
43impl OscillatorNode {
44    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
45    pub(crate) fn new_inherited(
46        window: &Window,
47        context: &BaseAudioContext,
48        options: &OscillatorOptions,
49        can_gc: CanGc,
50    ) -> Fallible<OscillatorNode> {
51        let node_options =
52            options
53                .parent
54                .unwrap_or(2, ChannelCountMode::Max, ChannelInterpretation::Speakers);
55        let source_node = AudioScheduledSourceNode::new_inherited(
56            AudioNodeInit::OscillatorNode(options.convert()),
57            context,
58            node_options,
59            0, /* inputs */
60            1, /* outputs */
61        )?;
62        let node_id = source_node.node().node_id();
63        let frequency = AudioParam::new(
64            window,
65            context,
66            node_id,
67            AudioNodeType::OscillatorNode,
68            ParamType::Frequency,
69            AutomationRate::A_rate,
70            440.,
71            f32::MIN,
72            f32::MAX,
73            can_gc,
74        );
75        let detune = AudioParam::new(
76            window,
77            context,
78            node_id,
79            AudioNodeType::OscillatorNode,
80            ParamType::Detune,
81            AutomationRate::A_rate,
82            0.,
83            -440. / 2.,
84            440. / 2.,
85            can_gc,
86        );
87        Ok(OscillatorNode {
88            source_node,
89            oscillator_type: Cell::new(options.type_),
90            frequency: Dom::from_ref(&frequency),
91            detune: Dom::from_ref(&detune),
92        })
93    }
94
95    pub(crate) fn new(
96        window: &Window,
97        context: &BaseAudioContext,
98        options: &OscillatorOptions,
99        can_gc: CanGc,
100    ) -> Fallible<DomRoot<OscillatorNode>> {
101        Self::new_with_proto(window, None, context, options, can_gc)
102    }
103
104    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
105    fn new_with_proto(
106        window: &Window,
107        proto: Option<HandleObject>,
108        context: &BaseAudioContext,
109        options: &OscillatorOptions,
110        can_gc: CanGc,
111    ) -> Fallible<DomRoot<OscillatorNode>> {
112        let node = OscillatorNode::new_inherited(window, context, options, can_gc)?;
113        Ok(reflect_dom_object_with_proto(
114            Box::new(node),
115            window,
116            proto,
117            can_gc,
118        ))
119    }
120}
121
122impl OscillatorNodeMethods<crate::DomTypeHolder> for OscillatorNode {
123    /// <https://webaudio.github.io/web-audio-api/#dom-oscillatornode-oscillatornode>
124    fn Constructor(
125        window: &Window,
126        proto: Option<HandleObject>,
127        can_gc: CanGc,
128        context: &BaseAudioContext,
129        options: &OscillatorOptions,
130    ) -> Fallible<DomRoot<OscillatorNode>> {
131        OscillatorNode::new_with_proto(window, proto, context, options, can_gc)
132    }
133
134    /// <https://webaudio.github.io/web-audio-api/#dom-oscillatornode-frequency>
135    fn Frequency(&self) -> DomRoot<AudioParam> {
136        DomRoot::from_ref(&self.frequency)
137    }
138
139    /// <https://webaudio.github.io/web-audio-api/#dom-oscillatornode-detune>
140    fn Detune(&self) -> DomRoot<AudioParam> {
141        DomRoot::from_ref(&self.detune)
142    }
143
144    /// <https://webaudio.github.io/web-audio-api/#dom-oscillatornode-type>
145    fn Type(&self) -> OscillatorType {
146        self.oscillator_type.get()
147    }
148
149    /// <https://webaudio.github.io/web-audio-api/#dom-oscillatornode-type>
150    fn SetType(&self, type_: OscillatorType) -> ErrorResult {
151        if type_ == OscillatorType::Custom {
152            return Err(Error::InvalidState(None));
153        }
154        self.oscillator_type.set(type_);
155        self.source_node
156            .node()
157            .message(AudioNodeMessage::OscillatorNode(
158                OscillatorNodeMessage::SetOscillatorType(type_.convert()),
159            ));
160        Ok(())
161    }
162}
163
164impl Convert<ServoMediaOscillatorOptions> for &OscillatorOptions {
165    fn convert(self) -> ServoMediaOscillatorOptions {
166        ServoMediaOscillatorOptions {
167            oscillator_type: self.type_.convert(),
168            freq: *self.frequency,
169            detune: *self.detune,
170            periodic_wave_options: None, // XXX
171        }
172    }
173}
174
175impl Convert<ServoMediaOscillatorType> for OscillatorType {
176    fn convert(self) -> ServoMediaOscillatorType {
177        match self {
178            OscillatorType::Sine => ServoMediaOscillatorType::Sine,
179            OscillatorType::Square => ServoMediaOscillatorType::Square,
180            OscillatorType::Sawtooth => ServoMediaOscillatorType::Sawtooth,
181            OscillatorType::Triangle => ServoMediaOscillatorType::Triangle,
182            OscillatorType::Custom => ServoMediaOscillatorType::Custom,
183        }
184    }
185}