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    ) -> Fallible<OscillatorNode> {
50        let node_options =
51            options
52                .parent
53                .unwrap_or(2, ChannelCountMode::Max, ChannelInterpretation::Speakers);
54        let source_node = AudioScheduledSourceNode::new_inherited(
55            AudioNodeInit::OscillatorNode(options.convert()),
56            context,
57            node_options,
58            0, /* inputs */
59            1, /* outputs */
60        )?;
61        let node_id = source_node.node().node_id();
62        let frequency = AudioParam::new(
63            window,
64            context,
65            node_id,
66            AudioNodeType::OscillatorNode,
67            ParamType::Frequency,
68            AutomationRate::A_rate,
69            440.,
70            f32::MIN,
71            f32::MAX,
72            CanGc::note(),
73        );
74        let detune = AudioParam::new(
75            window,
76            context,
77            node_id,
78            AudioNodeType::OscillatorNode,
79            ParamType::Detune,
80            AutomationRate::A_rate,
81            0.,
82            -440. / 2.,
83            440. / 2.,
84            CanGc::note(),
85        );
86        Ok(OscillatorNode {
87            source_node,
88            oscillator_type: Cell::new(options.type_),
89            frequency: Dom::from_ref(&frequency),
90            detune: Dom::from_ref(&detune),
91        })
92    }
93
94    pub(crate) fn new(
95        window: &Window,
96        context: &BaseAudioContext,
97        options: &OscillatorOptions,
98        can_gc: CanGc,
99    ) -> Fallible<DomRoot<OscillatorNode>> {
100        Self::new_with_proto(window, None, context, options, can_gc)
101    }
102
103    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
104    fn new_with_proto(
105        window: &Window,
106        proto: Option<HandleObject>,
107        context: &BaseAudioContext,
108        options: &OscillatorOptions,
109        can_gc: CanGc,
110    ) -> Fallible<DomRoot<OscillatorNode>> {
111        let node = OscillatorNode::new_inherited(window, context, options)?;
112        Ok(reflect_dom_object_with_proto(
113            Box::new(node),
114            window,
115            proto,
116            can_gc,
117        ))
118    }
119}
120
121impl OscillatorNodeMethods<crate::DomTypeHolder> for OscillatorNode {
122    // https://webaudio.github.io/web-audio-api/#dom-oscillatornode-oscillatornode
123    fn Constructor(
124        window: &Window,
125        proto: Option<HandleObject>,
126        can_gc: CanGc,
127        context: &BaseAudioContext,
128        options: &OscillatorOptions,
129    ) -> Fallible<DomRoot<OscillatorNode>> {
130        OscillatorNode::new_with_proto(window, proto, context, options, can_gc)
131    }
132
133    // https://webaudio.github.io/web-audio-api/#dom-oscillatornode-frequency
134    fn Frequency(&self) -> DomRoot<AudioParam> {
135        DomRoot::from_ref(&self.frequency)
136    }
137
138    // https://webaudio.github.io/web-audio-api/#dom-oscillatornode-detune
139    fn Detune(&self) -> DomRoot<AudioParam> {
140        DomRoot::from_ref(&self.detune)
141    }
142
143    // https://webaudio.github.io/web-audio-api/#dom-oscillatornode-type
144    fn Type(&self) -> OscillatorType {
145        self.oscillator_type.get()
146    }
147
148    // https://webaudio.github.io/web-audio-api/#dom-oscillatornode-type
149    fn SetType(&self, type_: OscillatorType) -> ErrorResult {
150        if type_ == OscillatorType::Custom {
151            return Err(Error::InvalidState);
152        }
153        self.oscillator_type.set(type_);
154        self.source_node
155            .node()
156            .message(AudioNodeMessage::OscillatorNode(
157                OscillatorNodeMessage::SetOscillatorType(type_.convert()),
158            ));
159        Ok(())
160    }
161}
162
163impl Convert<ServoMediaOscillatorOptions> for &OscillatorOptions {
164    fn convert(self) -> ServoMediaOscillatorOptions {
165        ServoMediaOscillatorOptions {
166            oscillator_type: self.type_.convert(),
167            freq: *self.frequency,
168            detune: *self.detune,
169            periodic_wave_options: None, // XXX
170        }
171    }
172}
173
174impl Convert<ServoMediaOscillatorType> for OscillatorType {
175    fn convert(self) -> ServoMediaOscillatorType {
176        match self {
177            OscillatorType::Sine => ServoMediaOscillatorType::Sine,
178            OscillatorType::Square => ServoMediaOscillatorType::Square,
179            OscillatorType::Sawtooth => ServoMediaOscillatorType::Sawtooth,
180            OscillatorType::Triangle => ServoMediaOscillatorType::Triangle,
181            OscillatorType::Custom => ServoMediaOscillatorType::Custom,
182        }
183    }
184}