1use std::cmp::min;
6use std::sync::Arc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::rust::{CustomAutoRooterGuard, HandleObject};
11use js::typedarray::{Float32, Float32Array, HeapFloat32Array};
12use script_bindings::cell::DomRefCell;
13use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
14use script_bindings::trace::RootedTraceableBox;
15use servo_media::audio::buffer_source_node::AudioBuffer as ServoMediaAudioBuffer;
16
17use crate::dom::audio::audionode::MAX_CHANNEL_COUNT;
18use crate::dom::bindings::buffer_source::HeapBufferSource;
19use crate::dom::bindings::codegen::Bindings::AudioBufferBinding::{
20 AudioBufferMethods, AudioBufferOptions,
21};
22use crate::dom::bindings::error::{Error, Fallible};
23use crate::dom::bindings::num::Finite;
24use crate::dom::bindings::root::DomRoot;
25use crate::dom::window::Window;
26use crate::realms::enter_auto_realm;
27
28pub(crate) const MIN_SAMPLE_RATE: f32 = 8000.;
31pub(crate) const MAX_SAMPLE_RATE: f32 = 192000.;
32
33#[dom_struct]
42pub(crate) struct AudioBuffer {
43 reflector_: Reflector,
44 #[ignore_malloc_size_of = "mozjs"]
46 js_channels: DomRefCell<Vec<HeapBufferSource<Float32>>>,
47 #[no_trace]
50 #[conditional_malloc_size_of]
51 shared_channels: DomRefCell<Option<Arc<ServoMediaAudioBuffer>>>,
52 sample_rate: f32,
54 length: u32,
56 duration: f64,
58 number_of_channels: u32,
60}
61
62impl AudioBuffer {
63 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
64 pub(crate) fn new_inherited(
65 number_of_channels: u32,
66 length: u32,
67 sample_rate: f32,
68 ) -> AudioBuffer {
69 let vec = (0..number_of_channels)
70 .map(|_| HeapBufferSource::default())
71 .collect();
72 AudioBuffer {
73 reflector_: Reflector::new(),
74 js_channels: DomRefCell::new(vec),
75 shared_channels: DomRefCell::new(None),
76 sample_rate,
77 length,
78 duration: length as f64 / sample_rate as f64,
79 number_of_channels,
80 }
81 }
82
83 pub(crate) fn new(
84 cx: &mut JSContext,
85 global: &Window,
86 number_of_channels: u32,
87 length: u32,
88 sample_rate: f32,
89 initial_data: Option<&[Vec<f32>]>,
90 ) -> DomRoot<AudioBuffer> {
91 Self::new_with_proto(
92 cx,
93 global,
94 None,
95 number_of_channels,
96 length,
97 sample_rate,
98 initial_data,
99 )
100 }
101
102 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
104 pub(crate) fn new_with_channel_data(
105 cx: &mut JSContext,
106 global: &Window,
107 length: u32,
108 sample_rate: f32,
109 channels: Vec<Vec<f32>>,
110 ) -> DomRoot<AudioBuffer> {
111 let buffer = AudioBuffer::new_inherited(channels.len() as u32, length, sample_rate);
112 let buffer = reflect_dom_object_with_proto(cx, Box::new(buffer), global, None);
113 *buffer.shared_channels.safe_borrow_mut(cx.no_gc()) = Some(Arc::new(
114 ServoMediaAudioBuffer::from_buffers(channels, sample_rate),
115 ));
116 buffer
117 }
118
119 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
120 fn new_with_proto(
121 cx: &mut JSContext,
122 global: &Window,
123 proto: Option<HandleObject>,
124 number_of_channels: u32,
125 length: u32,
126 sample_rate: f32,
127 initial_data: Option<&[Vec<f32>]>,
128 ) -> DomRoot<AudioBuffer> {
129 let buffer = AudioBuffer::new_inherited(number_of_channels, length, sample_rate);
130 let buffer = reflect_dom_object_with_proto(cx, Box::new(buffer), global, proto);
131 buffer.set_initial_data(initial_data);
132 buffer
133 }
134
135 fn set_initial_data(&self, initial_data: Option<&[Vec<f32>]>) {
138 let mut channels = ServoMediaAudioBuffer::new(
139 self.number_of_channels as u8,
140 self.length as usize,
141 self.sample_rate,
142 );
143 for channel in 0..self.number_of_channels {
144 channels.buffers[channel as usize] = match initial_data {
145 Some(data) => data[channel as usize].clone(),
146 None => vec![0.; self.length as usize],
147 };
148 }
149 *self.shared_channels.borrow_mut() = Some(Arc::new(channels));
150 }
151
152 fn restore_js_channel_data(&self, cx: &mut JSContext) -> bool {
153 let mut realm = enter_auto_realm(cx, self);
154 let cx = &mut realm.current_realm();
155 for (i, channel) in self.js_channels.borrow().iter().enumerate() {
156 if channel.is_initialized() {
157 continue;
159 }
160
161 if let Some(ref shared_channels) = *self.shared_channels.borrow() {
162 if channel.set_data(cx, &shared_channels.buffers[i]).is_err() {
167 return false;
168 }
169 }
170 }
171
172 *self.shared_channels.safe_borrow_mut(cx.no_gc()) = None;
173
174 true
175 }
176
177 fn acquire_contents(&self, cx: &mut JSContext) -> Option<ServoMediaAudioBuffer> {
179 let mut result = ServoMediaAudioBuffer::new(
180 self.number_of_channels as u8,
181 self.length as usize,
182 self.sample_rate,
183 );
184 for (i, channel) in self.js_channels.borrow().iter().enumerate() {
185 if !channel.is_initialized() {
187 return None;
188 }
189
190 if channel.is_detached_buffer(cx) {
194 return Some(ServoMediaAudioBuffer::new(
195 self.number_of_channels as u8,
196 0,
197 self.sample_rate,
198 ));
199 }
200 result.buffers[i] = channel.acquire_data(cx).ok()?;
202 }
203
204 Some(result)
205 }
206
207 pub(crate) fn get_channels(&self, cx: &mut JSContext) -> Option<Arc<ServoMediaAudioBuffer>> {
208 if self.shared_channels.borrow().is_none() {
209 let channels = self.acquire_contents(cx).map(Arc::new);
210 if channels.is_some() {
211 *self.shared_channels.safe_borrow_mut(cx.no_gc()) = channels;
212 }
213 }
214 self.shared_channels.borrow().clone()
215 }
216}
217
218impl AudioBufferMethods<crate::DomTypeHolder> for AudioBuffer {
219 fn Constructor(
221 cx: &mut JSContext,
222 window: &Window,
223 proto: Option<HandleObject>,
224 options: &AudioBufferOptions,
225 ) -> Fallible<DomRoot<AudioBuffer>> {
226 if options.length == 0 ||
227 options.numberOfChannels == 0 ||
228 options.numberOfChannels > MAX_CHANNEL_COUNT ||
229 *options.sampleRate < MIN_SAMPLE_RATE ||
230 *options.sampleRate > MAX_SAMPLE_RATE
231 {
232 return Err(Error::NotSupported(Some(
233 "Invalid number of channels, length or sample rate".into(),
234 )));
235 }
236 Ok(AudioBuffer::new_with_proto(
237 cx,
238 window,
239 proto,
240 options.numberOfChannels,
241 options.length,
242 *options.sampleRate,
243 None,
244 ))
245 }
246
247 fn SampleRate(&self) -> Finite<f32> {
249 Finite::wrap(self.sample_rate)
250 }
251
252 fn Length(&self) -> u32 {
254 self.length
255 }
256
257 fn Duration(&self) -> Finite<f64> {
259 Finite::wrap(self.duration)
260 }
261
262 fn NumberOfChannels(&self) -> u32 {
264 self.number_of_channels
265 }
266
267 fn GetChannelData(
269 &self,
270 cx: &mut JSContext,
271 channel: u32,
272 ) -> Fallible<RootedTraceableBox<HeapFloat32Array>> {
273 if channel >= self.number_of_channels {
274 return Err(Error::IndexSize(Some(
275 "Channel index is out of range".into(),
276 )));
277 }
278
279 if !self.restore_js_channel_data(cx) {
280 return Err(Error::JSFailed);
281 }
282
283 self.js_channels.borrow()[channel as usize]
284 .get_typed_array()
285 .map_err(|_| Error::JSFailed)
286 }
287
288 fn CopyFromChannel(
290 &self,
291 cx: &mut JSContext,
292 mut destination: CustomAutoRooterGuard<Float32Array>,
293 channel_number: u32,
294 start_in_channel: u32,
295 ) -> Fallible<()> {
296 if destination.is_shared() {
297 return Err(Error::Type(c"Cannot copy to shared buffer".to_owned()));
298 }
299
300 if channel_number >= self.number_of_channels {
303 return Err(Error::IndexSize(Some(
304 "Channel number is out of range".into(),
305 )));
306 }
307
308 let count = min(
312 self.length.saturating_sub(start_in_channel),
313 destination.len() as u32,
314 ) as usize;
315 if count == 0 {
316 return Ok(());
317 }
318
319 let channel_number = channel_number as usize;
320 let offset = start_in_channel as usize;
321 let mut dest = vec![0.0_f32; count];
322
323 let js_channel = &self.js_channels.borrow()[channel_number];
325 if js_channel.is_initialized() {
326 if js_channel
327 .copy_data_to(cx, &mut dest, offset, offset + count)
328 .is_err()
329 {
330 return Err(Error::IndexSize(Some("Failed to copy channel data".into())));
331 }
332 } else if let Some(ref shared_channels) = *self.shared_channels.borrow() &&
333 let Some(shared_channel) = shared_channels.buffers.get(channel_number)
334 {
335 dest.copy_from_slice(&shared_channel.as_slice()[offset..offset + count]);
336 }
337
338 destination.update(&dest);
339
340 Ok(())
341 }
342
343 fn CopyToChannel(
345 &self,
346 cx: &mut JSContext,
347 source: CustomAutoRooterGuard<Float32Array>,
348 channel_number: u32,
349 start_in_channel: u32,
350 ) -> Fallible<()> {
351 if source.is_shared() {
352 return Err(Error::Type(c"Cannot copy from shared buffer".to_owned()));
353 }
354
355 if channel_number >= self.number_of_channels {
358 return Err(Error::IndexSize(Some(
359 "Channel number is out of range".into(),
360 )));
361 }
362
363 let count = min(
367 self.length.saturating_sub(start_in_channel),
368 source.len() as u32,
369 ) as usize;
370 if count == 0 {
371 return Ok(());
372 }
373
374 if !self.restore_js_channel_data(cx) {
375 return Err(Error::JSFailed);
376 }
377
378 let js_channel = &self.js_channels.borrow()[channel_number as usize];
379 if !js_channel.is_initialized() {
380 return Err(Error::IndexSize(Some(
382 "Channel data buffer is detached".into(),
383 )));
384 }
385
386 js_channel
387 .copy_data_from(cx, source, start_in_channel as usize, count)
388 .map_err(|_| Error::IndexSize(Some("Failed to copy channel data".into())))
389 }
390}