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::graph::NodeId;
14use servo_media::audio::oscillator_node::{
15 OscillatorNodeMessage, OscillatorNodeOptions as ServoMediaOscillatorOptions,
16 OscillatorType as ServoMediaOscillatorType,
17};
18use servo_media::audio::param::ParamType;
19use servo_media::audio::periodic_wave::PeriodicWave as ServoMediaPeriodicWave;
20
21use crate::conversions::Convert;
22use crate::dom::audio::audionode::{AudioNode, AudioNodeOptionsHelper};
23use crate::dom::audio::audioparam::AudioParam;
24use crate::dom::audio::audioscheduledsourcenode::AudioScheduledSourceNode;
25use crate::dom::audio::baseaudiocontext::BaseAudioContext;
26use crate::dom::audionode::UnwrappedAudioNodeOptions;
27use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
28 ChannelCountMode, ChannelInterpretation,
29};
30use crate::dom::bindings::codegen::Bindings::AudioParamBinding::AutomationRate;
31use crate::dom::bindings::codegen::Bindings::OscillatorNodeBinding::{
32 OscillatorNodeMethods, OscillatorOptions, OscillatorType,
33};
34use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
35use crate::dom::bindings::root::{Dom, DomRoot};
36use crate::dom::types::PeriodicWave;
37use crate::dom::window::Window;
38
39#[dom_struct]
40pub(crate) struct OscillatorNode {
41 source_node: AudioScheduledSourceNode,
42 detune: Dom<AudioParam>,
43 frequency: Dom<AudioParam>,
44 oscillator_type: Cell<OscillatorType>,
45}
46
47impl OscillatorNode {
48 fn new_inherited(
49 node_id: Option<NodeId>,
50 context: &BaseAudioContext,
51 node_options: UnwrappedAudioNodeOptions,
52 oscillator_type: OscillatorType,
53 frequency: &AudioParam,
54 detune: &AudioParam,
55 ) -> Self {
56 OscillatorNode {
57 source_node: AudioScheduledSourceNode::new_inherited(
58 node_id,
59 context,
60 node_options,
61 0, 1, ),
64 oscillator_type: Cell::new(oscillator_type),
65 frequency: Dom::from_ref(frequency),
66 detune: Dom::from_ref(detune),
67 }
68 }
69}
70
71impl OscillatorNodeMethods<crate::DomTypeHolder> for OscillatorNode {
72 fn Constructor(
74 cx: &mut JSContext,
75 window: &Window,
76 proto: Option<HandleObject>,
77 context: &BaseAudioContext,
78 options: &OscillatorOptions,
79 ) -> Fallible<DomRoot<OscillatorNode>> {
80 if matches!(options.type_, OscillatorType::Custom) && options.periodicWave.is_none() {
81 return Err(Error::InvalidState(Some(String::from(
82 "Can not set oscillator type to custom without providing a periodic wave",
83 ))));
84 }
85
86 let oscillator_type = if options.periodicWave.is_some() {
87 OscillatorType::Custom
88 } else {
89 options.type_
90 };
91
92 let node_options =
93 options
94 .parent
95 .unwrap_or(2, ChannelCountMode::Max, ChannelInterpretation::Speakers);
96 if AudioNode::channel_count_check(node_options.count) {
97 return Err(Error::NotSupported(Some(String::from(
98 "Channel count not supported",
99 ))));
100 }
101
102 let maybe_periodic_wave = options
103 .periodicWave
104 .as_ref()
105 .map(|x| (*x.as_traced()).convert());
106
107 let options = ServoMediaOscillatorOptions {
108 oscillator_type: convert_oscillator_options(oscillator_type, maybe_periodic_wave)?,
109 freq: *options.frequency,
110 detune: *options.detune,
111 };
112 let audio_node_init = AudioNodeInit::OscillatorNode(options);
113 let node_id =
114 AudioNode::generate_audio_node_id(cx, &node_options, audio_node_init, context);
115
116 let nyquist = (*context.SampleRate()) / 2.;
117 let frequency = AudioParam::new(
119 cx,
120 window,
121 context,
122 node_id,
123 AudioNodeType::OscillatorNode,
124 ParamType::Frequency,
125 AutomationRate::A_rate,
126 440.,
127 -nyquist,
128 nyquist,
129 );
130 let detune_max = 1200. * f32::MAX.log2();
132 let detune = AudioParam::new(
133 cx,
134 window,
135 context,
136 node_id,
137 AudioNodeType::OscillatorNode,
138 ParamType::Detune,
139 AutomationRate::A_rate,
140 0.,
141 -detune_max,
142 detune_max,
143 );
144 Ok(reflect_dom_object_with_proto(
145 cx,
146 Box::new(OscillatorNode::new_inherited(
147 node_id,
148 context,
149 node_options,
150 oscillator_type,
151 &frequency,
152 &detune,
153 )),
154 window,
155 proto,
156 ))
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}