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