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