Skip to main content

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