1use std::cell::Cell;
6use std::f32;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::rust::HandleObject;
11use script_bindings::reflector::reflect_dom_object_with_proto_and_cx;
12use servo_media::audio::buffer_source_node::{
13 AudioBufferSourceNodeMessage, AudioBufferSourceNodeOptions,
14};
15use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioNodeType};
16use servo_media::audio::param::ParamType;
17
18use crate::conversions::Convert;
19use crate::dom::audio::audiobuffer::AudioBuffer;
20use crate::dom::audio::audioparam::AudioParam;
21use crate::dom::audio::audioscheduledsourcenode::AudioScheduledSourceNode;
22use crate::dom::audio::baseaudiocontext::BaseAudioContext;
23use crate::dom::bindings::codegen::Bindings::AudioBufferSourceNodeBinding::{
24 AudioBufferSourceNodeMethods, AudioBufferSourceOptions,
25};
26use crate::dom::bindings::codegen::Bindings::AudioParamBinding::AutomationRate;
27use crate::dom::bindings::codegen::Bindings::AudioScheduledSourceNodeBinding::AudioScheduledSourceNodeMethods;
28use crate::dom::bindings::error::{Error, Fallible};
29use crate::dom::bindings::inheritance::Castable;
30use crate::dom::bindings::num::Finite;
31use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
32use crate::dom::window::Window;
33
34#[dom_struct]
35pub(crate) struct AudioBufferSourceNode {
36 source_node: AudioScheduledSourceNode,
37 buffer: MutNullableDom<AudioBuffer>,
38 buffer_set: Cell<bool>,
39 playback_rate: Dom<AudioParam>,
40 detune: Dom<AudioParam>,
41 loop_enabled: Cell<bool>,
42 loop_start: Cell<f64>,
43 loop_end: Cell<f64>,
44}
45
46impl AudioBufferSourceNode {
47 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
48 fn new_inherited(
49 cx: &mut JSContext,
50 window: &Window,
51 context: &BaseAudioContext,
52 options: &AudioBufferSourceOptions,
53 ) -> Fallible<AudioBufferSourceNode> {
54 let node_options = Default::default();
55 let source_node = AudioScheduledSourceNode::new_inherited(
56 cx,
57 AudioNodeInit::AudioBufferSourceNode(options.convert()),
58 context,
59 node_options,
60 0, 1, )?;
63 let node_id = source_node.node().node_id();
64 let playback_rate = AudioParam::new(
65 cx,
66 window,
67 context,
68 node_id,
69 AudioNodeType::AudioBufferSourceNode,
70 ParamType::PlaybackRate,
71 AutomationRate::K_rate,
72 *options.playbackRate,
73 f32::MIN,
74 f32::MAX,
75 );
76 let detune = AudioParam::new(
77 cx,
78 window,
79 context,
80 node_id,
81 AudioNodeType::AudioBufferSourceNode,
82 ParamType::Detune,
83 AutomationRate::K_rate,
84 *options.detune,
85 f32::MIN,
86 f32::MAX,
87 );
88 let node = AudioBufferSourceNode {
89 source_node,
90 buffer: Default::default(),
91 buffer_set: Cell::new(false),
92 playback_rate: Dom::from_ref(&playback_rate),
93 detune: Dom::from_ref(&detune),
94 loop_enabled: Cell::new(options.loop_),
95 loop_start: Cell::new(*options.loopStart),
96 loop_end: Cell::new(*options.loopEnd),
97 };
98 if let Some(Some(ref buffer)) = options.buffer {
99 node.SetBuffer(Some(buffer))?;
100 }
101 Ok(node)
102 }
103
104 pub(crate) fn new(
105 cx: &mut JSContext,
106 window: &Window,
107 context: &BaseAudioContext,
108 options: &AudioBufferSourceOptions,
109 ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
110 Self::new_with_proto(cx, window, None, context, options)
111 }
112
113 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
114 fn new_with_proto(
115 cx: &mut JSContext,
116 window: &Window,
117 proto: Option<HandleObject>,
118 context: &BaseAudioContext,
119 options: &AudioBufferSourceOptions,
120 ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
121 let node = AudioBufferSourceNode::new_inherited(cx, window, context, options)?;
122 Ok(reflect_dom_object_with_proto_and_cx(
123 Box::new(node),
124 window,
125 proto,
126 cx,
127 ))
128 }
129}
130
131impl AudioBufferSourceNodeMethods<crate::DomTypeHolder> for AudioBufferSourceNode {
132 fn Constructor(
134 cx: &mut JSContext,
135 window: &Window,
136 proto: Option<HandleObject>,
137 context: &BaseAudioContext,
138 options: &AudioBufferSourceOptions,
139 ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
140 AudioBufferSourceNode::new_with_proto(cx, window, proto, context, options)
141 }
142
143 fn GetBuffer(&self) -> Fallible<Option<DomRoot<AudioBuffer>>> {
145 Ok(self.buffer.get())
146 }
147
148 fn SetBuffer(&self, new_buffer: Option<&AudioBuffer>) -> Fallible<()> {
150 if new_buffer.is_some() {
151 if self.buffer_set.get() {
152 return Err(Error::InvalidState(None));
154 }
155 self.buffer_set.set(true);
157 }
158
159 self.buffer.set(new_buffer);
161
162 if self.source_node.has_start() &&
164 let Some(buffer) = self.buffer.get()
165 {
166 let buffer = buffer.get_channels();
167 if buffer.is_some() {
168 self.source_node
169 .node()
170 .message(AudioNodeMessage::AudioBufferSourceNode(
171 AudioBufferSourceNodeMessage::SetBuffer((*buffer).clone()),
172 ));
173 }
174 }
175
176 Ok(())
177 }
178
179 fn PlaybackRate(&self) -> DomRoot<AudioParam> {
181 DomRoot::from_ref(&self.playback_rate)
182 }
183
184 fn Detune(&self) -> DomRoot<AudioParam> {
186 DomRoot::from_ref(&self.detune)
187 }
188
189 fn Loop(&self) -> bool {
191 self.loop_enabled.get()
192 }
193
194 fn SetLoop(&self, should_loop: bool) {
196 self.loop_enabled.set(should_loop);
197 let msg = AudioNodeMessage::AudioBufferSourceNode(
198 AudioBufferSourceNodeMessage::SetLoopEnabled(should_loop),
199 );
200 self.source_node.node().message(msg);
201 }
202
203 fn LoopStart(&self) -> Finite<f64> {
205 Finite::wrap(self.loop_start.get())
206 }
207
208 fn SetLoopStart(&self, loop_start: Finite<f64>) {
210 self.loop_start.set(*loop_start);
211 let msg = AudioNodeMessage::AudioBufferSourceNode(
212 AudioBufferSourceNodeMessage::SetLoopStart(*loop_start),
213 );
214 self.source_node.node().message(msg);
215 }
216
217 fn LoopEnd(&self) -> Finite<f64> {
219 Finite::wrap(self.loop_end.get())
220 }
221
222 fn SetLoopEnd(&self, loop_end: Finite<f64>) {
224 self.loop_end.set(*loop_end);
225 let msg = AudioNodeMessage::AudioBufferSourceNode(
226 AudioBufferSourceNodeMessage::SetLoopEnd(*loop_end),
227 );
228 self.source_node.node().message(msg);
229 }
230
231 fn Start(
233 &self,
234 when: Finite<f64>,
235 offset: Option<Finite<f64>>,
236 duration: Option<Finite<f64>>,
237 ) -> Fallible<()> {
238 if let Some(offset) = offset &&
239 *offset < 0.
240 {
241 return Err(Error::Range(
242 c"'offset' must be a positive value".to_owned(),
243 ));
244 }
245
246 if let Some(duration) = duration &&
247 *duration < 0.
248 {
249 return Err(Error::Range(
250 c"'duration' must be a positive value".to_owned(),
251 ));
252 }
253
254 if let Some(buffer) = self.buffer.get() {
255 let buffer = buffer.get_channels();
256 if buffer.is_some() {
257 self.source_node
258 .node()
259 .message(AudioNodeMessage::AudioBufferSourceNode(
260 AudioBufferSourceNodeMessage::SetBuffer((*buffer).clone()),
261 ));
262 }
263 }
264
265 self.source_node
266 .node()
267 .message(AudioNodeMessage::AudioBufferSourceNode(
268 AudioBufferSourceNodeMessage::SetStartParams(
269 *when,
270 offset.map(|f| *f),
271 duration.map(|f| *f),
272 ),
273 ));
274
275 self.source_node
276 .upcast::<AudioScheduledSourceNode>()
277 .Start(when)
278 }
279}
280
281impl Convert<AudioBufferSourceNodeOptions> for &AudioBufferSourceOptions {
282 fn convert(self) -> AudioBufferSourceNodeOptions {
283 AudioBufferSourceNodeOptions {
284 buffer: self
285 .buffer
286 .as_ref()
287 .and_then(|b| (*b.as_ref()?.get_channels()).clone()),
288 detune: *self.detune,
289 loop_enabled: self.loop_,
290 loop_end: Some(*self.loopEnd),
291 loop_start: Some(*self.loopStart),
292 playback_rate: *self.playbackRate,
293 }
294 }
295}