1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::rust::HandleObject;
10use script_bindings::reflector::reflect_dom_object_with_proto;
11use servo_media::audio::audio_node::{AudioNodeInit, AudioNodeMessage, AudioNodeType};
12use servo_media::audio::oscillator_node::{
13 OscillatorNodeMessage, OscillatorNodeOptions as ServoMediaOscillatorOptions,
14 OscillatorType as ServoMediaOscillatorType,
15};
16use servo_media::audio::param::ParamType;
17use servo_media::audio::periodic_wave::PeriodicWave as ServoMediaPeriodicWave;
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::types::PeriodicWave;
34use crate::dom::window::Window;
35
36#[dom_struct]
37pub(crate) struct OscillatorNode {
38 source_node: AudioScheduledSourceNode,
39 detune: Dom<AudioParam>,
40 frequency: Dom<AudioParam>,
41 oscillator_type: Cell<OscillatorType>,
42}
43
44impl OscillatorNode {
45 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
46 pub(crate) fn new_inherited(
47 cx: &mut JSContext,
48 window: &Window,
49 context: &BaseAudioContext,
50 options: &OscillatorOptions,
51 ) -> Fallible<OscillatorNode> {
52 if matches!(options.type_, OscillatorType::Custom) && options.periodicWave.is_none() {
53 return Err(Error::InvalidState(Some(String::from(
54 "Can not set oscillator type to custom without providing a periodic wave",
55 ))));
56 }
57
58 let oscillator_type = if options.periodicWave.is_some() {
59 OscillatorType::Custom
60 } else {
61 options.type_
62 };
63
64 let node_options =
65 options
66 .parent
67 .unwrap_or(2, ChannelCountMode::Max, ChannelInterpretation::Speakers);
68
69 let maybe_periodic_wave = options
70 .periodicWave
71 .as_ref()
72 .map(|x| (*x.as_traced()).convert());
73
74 let options = ServoMediaOscillatorOptions {
75 oscillator_type: convert_oscillator_options(oscillator_type, maybe_periodic_wave)?,
76 freq: *options.frequency,
77 detune: *options.detune,
78 };
79 let source_node = AudioScheduledSourceNode::new_inherited(
80 cx,
81 AudioNodeInit::OscillatorNode(options),
82 context,
83 node_options,
84 0, 1, )?;
87 let node_id = source_node.node().node_id();
88 let frequency = AudioParam::new(
89 cx,
90 window,
91 context,
92 node_id,
93 AudioNodeType::OscillatorNode,
94 ParamType::Frequency,
95 AutomationRate::A_rate,
96 440.,
97 f32::MIN,
98 f32::MAX,
99 );
100 let detune = AudioParam::new(
101 cx,
102 window,
103 context,
104 node_id,
105 AudioNodeType::OscillatorNode,
106 ParamType::Detune,
107 AutomationRate::A_rate,
108 0.,
109 -440. / 2.,
110 440. / 2.,
111 );
112 Ok(OscillatorNode {
113 source_node,
114 oscillator_type: Cell::new(oscillator_type),
115 frequency: Dom::from_ref(&frequency),
116 detune: Dom::from_ref(&detune),
117 })
118 }
119
120 pub(crate) fn new(
121 cx: &mut JSContext,
122 window: &Window,
123 context: &BaseAudioContext,
124 options: &OscillatorOptions,
125 ) -> Fallible<DomRoot<OscillatorNode>> {
126 Self::new_with_proto(cx, window, None, context, options)
127 }
128
129 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
130 fn new_with_proto(
131 cx: &mut JSContext,
132 window: &Window,
133 proto: Option<HandleObject>,
134 context: &BaseAudioContext,
135 options: &OscillatorOptions,
136 ) -> Fallible<DomRoot<OscillatorNode>> {
137 let node = OscillatorNode::new_inherited(cx, window, context, options)?;
138 Ok(reflect_dom_object_with_proto(
139 cx,
140 Box::new(node),
141 window,
142 proto,
143 ))
144 }
145}
146
147impl OscillatorNodeMethods<crate::DomTypeHolder> for OscillatorNode {
148 fn Constructor(
150 cx: &mut JSContext,
151 window: &Window,
152 proto: Option<HandleObject>,
153 context: &BaseAudioContext,
154 options: &OscillatorOptions,
155 ) -> Fallible<DomRoot<OscillatorNode>> {
156 OscillatorNode::new_with_proto(cx, window, proto, context, options)
157 }
158
159 fn SetPeriodicWave(&self, periodic_wave: &PeriodicWave) -> ErrorResult {
161 self.oscillator_type.set(OscillatorType::Custom);
162 self.source_node
163 .node()
164 .message(AudioNodeMessage::OscillatorNode(
165 OscillatorNodeMessage::SetPeriodicWave(periodic_wave.convert()),
166 ));
167 Ok(())
168 }
169
170 fn Frequency(&self) -> DomRoot<AudioParam> {
172 DomRoot::from_ref(&self.frequency)
173 }
174
175 fn Detune(&self) -> DomRoot<AudioParam> {
177 DomRoot::from_ref(&self.detune)
178 }
179
180 fn Type(&self) -> OscillatorType {
182 self.oscillator_type.get()
183 }
184
185 fn SetType(&self, type_: OscillatorType) -> ErrorResult {
187 if type_ == OscillatorType::Custom {
188 return Err(Error::InvalidState(None));
189 }
190 self.oscillator_type.set(type_);
191 self.source_node
192 .node()
193 .message(AudioNodeMessage::OscillatorNode(
194 OscillatorNodeMessage::SetOscillatorType(convert_oscillator_options(type_, None)?),
195 ));
196 Ok(())
197 }
198}
199
200fn convert_oscillator_options(
202 oscillator_type: OscillatorType,
203 maybe_periodic_wave: Option<ServoMediaPeriodicWave>,
204) -> Fallible<ServoMediaOscillatorType> {
205 match oscillator_type {
206 OscillatorType::Sine => Ok(ServoMediaOscillatorType::Sine),
207 OscillatorType::Square => Ok(ServoMediaOscillatorType::Square),
208 OscillatorType::Sawtooth => Ok(ServoMediaOscillatorType::Sawtooth),
209 OscillatorType::Triangle => Ok(ServoMediaOscillatorType::Triangle),
210 OscillatorType::Custom => {
211 let Some(periodic_wave) = maybe_periodic_wave else {
212 return Err(Error::InvalidState(Some(String::from(
213 "Can not have oscillator type custom without a periodic wave",
214 ))));
215 };
216 Ok(ServoMediaOscillatorType::Custom(periodic_wave))
217 },
218 }
219}