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::ConvertWithCx;
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_options = AudioNodeInit::AudioBufferSourceNode(options.convert(cx));
56 let source_node = AudioScheduledSourceNode::new_inherited(
57 cx,
58 source_options,
59 context,
60 node_options,
61 0, 1, )?;
64 let node_id = source_node.node().node_id();
65 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(
78 cx,
79 window,
80 context,
81 node_id,
82 AudioNodeType::AudioBufferSourceNode,
83 ParamType::Detune,
84 AutomationRate::K_rate,
85 *options.detune,
86 f32::MIN,
87 f32::MAX,
88 );
89 let node = AudioBufferSourceNode {
90 source_node,
91 buffer: Default::default(),
92 buffer_set: Cell::new(false),
93 playback_rate: Dom::from_ref(&playback_rate),
94 detune: Dom::from_ref(&detune),
95 loop_enabled: Cell::new(options.loop_),
96 loop_start: Cell::new(*options.loopStart),
97 loop_end: Cell::new(*options.loopEnd),
98 };
99 if let Some(Some(ref buffer)) = options.buffer {
100 node.SetBuffer(cx, Some(buffer))?;
101 }
102 Ok(node)
103 }
104
105 pub(crate) fn new(
106 cx: &mut JSContext,
107 window: &Window,
108 context: &BaseAudioContext,
109 options: &AudioBufferSourceOptions,
110 ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
111 Self::new_with_proto(cx, window, None, context, options)
112 }
113
114 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
115 fn new_with_proto(
116 cx: &mut JSContext,
117 window: &Window,
118 proto: Option<HandleObject>,
119 context: &BaseAudioContext,
120 options: &AudioBufferSourceOptions,
121 ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
122 let node = AudioBufferSourceNode::new_inherited(cx, window, context, options)?;
123 Ok(reflect_dom_object_with_proto_and_cx(
124 Box::new(node),
125 window,
126 proto,
127 cx,
128 ))
129 }
130}
131
132impl AudioBufferSourceNodeMethods<crate::DomTypeHolder> for AudioBufferSourceNode {
133 fn Constructor(
135 cx: &mut JSContext,
136 window: &Window,
137 proto: Option<HandleObject>,
138 context: &BaseAudioContext,
139 options: &AudioBufferSourceOptions,
140 ) -> Fallible<DomRoot<AudioBufferSourceNode>> {
141 AudioBufferSourceNode::new_with_proto(cx, window, proto, context, options)
142 }
143
144 fn GetBuffer(&self) -> Fallible<Option<DomRoot<AudioBuffer>>> {
146 Ok(self.buffer.get())
147 }
148
149 fn SetBuffer(&self, cx: &mut JSContext, new_buffer: Option<&AudioBuffer>) -> Fallible<()> {
151 if new_buffer.is_some() {
152 if self.buffer_set.get() {
153 return Err(Error::InvalidState(None));
155 }
156 self.buffer_set.set(true);
158 }
159
160 self.buffer.set(new_buffer);
162
163 if self.source_node.has_start() &&
165 let Some(buffer) = self.buffer.get()
166 {
167 let buffer = buffer.get_channels(cx);
168 if buffer.is_some() {
169 self.source_node
170 .node()
171 .message(AudioNodeMessage::AudioBufferSourceNode(
172 AudioBufferSourceNodeMessage::SetBuffer((*buffer).clone()),
173 ));
174 }
175 }
176
177 Ok(())
178 }
179
180 fn PlaybackRate(&self) -> DomRoot<AudioParam> {
182 DomRoot::from_ref(&self.playback_rate)
183 }
184
185 fn Detune(&self) -> DomRoot<AudioParam> {
187 DomRoot::from_ref(&self.detune)
188 }
189
190 fn Loop(&self) -> bool {
192 self.loop_enabled.get()
193 }
194
195 fn SetLoop(&self, should_loop: bool) {
197 self.loop_enabled.set(should_loop);
198 let msg = AudioNodeMessage::AudioBufferSourceNode(
199 AudioBufferSourceNodeMessage::SetLoopEnabled(should_loop),
200 );
201 self.source_node.node().message(msg);
202 }
203
204 fn LoopStart(&self) -> Finite<f64> {
206 Finite::wrap(self.loop_start.get())
207 }
208
209 fn SetLoopStart(&self, loop_start: Finite<f64>) {
211 self.loop_start.set(*loop_start);
212 let msg = AudioNodeMessage::AudioBufferSourceNode(
213 AudioBufferSourceNodeMessage::SetLoopStart(*loop_start),
214 );
215 self.source_node.node().message(msg);
216 }
217
218 fn LoopEnd(&self) -> Finite<f64> {
220 Finite::wrap(self.loop_end.get())
221 }
222
223 fn SetLoopEnd(&self, loop_end: Finite<f64>) {
225 self.loop_end.set(*loop_end);
226 let msg = AudioNodeMessage::AudioBufferSourceNode(
227 AudioBufferSourceNodeMessage::SetLoopEnd(*loop_end),
228 );
229 self.source_node.node().message(msg);
230 }
231
232 fn Start(
234 &self,
235 cx: &mut JSContext,
236 when: Finite<f64>,
237 offset: Option<Finite<f64>>,
238 duration: Option<Finite<f64>>,
239 ) -> Fallible<()> {
240 if let Some(offset) = offset &&
241 *offset < 0.
242 {
243 return Err(Error::Range(
244 c"'offset' must be a positive value".to_owned(),
245 ));
246 }
247
248 if let Some(duration) = duration &&
249 *duration < 0.
250 {
251 return Err(Error::Range(
252 c"'duration' must be a positive value".to_owned(),
253 ));
254 }
255
256 if let Some(buffer) = self.buffer.get() {
257 let buffer = buffer.get_channels(cx);
258 if buffer.is_some() {
259 self.source_node
260 .node()
261 .message(AudioNodeMessage::AudioBufferSourceNode(
262 AudioBufferSourceNodeMessage::SetBuffer((*buffer).clone()),
263 ));
264 }
265 }
266
267 self.source_node
268 .node()
269 .message(AudioNodeMessage::AudioBufferSourceNode(
270 AudioBufferSourceNodeMessage::SetStartParams(
271 *when,
272 offset.map(|f| *f),
273 duration.map(|f| *f),
274 ),
275 ));
276
277 self.source_node
278 .upcast::<AudioScheduledSourceNode>()
279 .Start(when)
280 }
281}
282
283impl ConvertWithCx<AudioBufferSourceNodeOptions> for AudioBufferSourceOptions {
284 fn convert(&self, cx: &mut JSContext) -> AudioBufferSourceNodeOptions {
285 AudioBufferSourceNodeOptions {
286 buffer: self
287 .buffer
288 .as_ref()
289 .and_then(|b| (*b.as_ref()?.get_channels(cx)).clone()),
290 detune: *self.detune,
291 loop_enabled: self.loop_,
292 loop_end: Some(*self.loopEnd),
293 loop_start: Some(*self.loopStart),
294 playback_rate: *self.playbackRate,
295 }
296 }
297}