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