1use 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, 1, )?;
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 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 fn Frequency(&self) -> DomRoot<AudioParam> {
135 DomRoot::from_ref(&self.frequency)
136 }
137
138 fn Detune(&self) -> DomRoot<AudioParam> {
140 DomRoot::from_ref(&self.detune)
141 }
142
143 fn Type(&self) -> OscillatorType {
145 self.oscillator_type.get()
146 }
147
148 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, }
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}