1use std::cmp::min;
6
7use dom_struct::dom_struct;
8use js::rust::{CustomAutoRooterGuard, HandleObject};
9use js::typedarray::{Float32, Float32Array, HeapFloat32Array};
10use script_bindings::trace::RootedTraceableBox;
11use servo_media::audio::buffer_source_node::AudioBuffer as ServoMediaAudioBuffer;
12
13use crate::dom::audio::audionode::MAX_CHANNEL_COUNT;
14use crate::dom::bindings::buffer_source::HeapBufferSource;
15use crate::dom::bindings::cell::{DomRefCell, Ref};
16use crate::dom::bindings::codegen::Bindings::AudioBufferBinding::{
17 AudioBufferMethods, AudioBufferOptions,
18};
19use crate::dom::bindings::error::{Error, Fallible};
20use crate::dom::bindings::num::Finite;
21use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
22use crate::dom::bindings::root::DomRoot;
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::window::Window;
25use crate::realms::enter_realm;
26use crate::script_runtime::{CanGc, JSContext};
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 #[ignore_malloc_size_of = "servo_media"]
50 #[no_trace]
51 shared_channels: DomRefCell<Option<ServoMediaAudioBuffer>>,
52 sample_rate: f32,
54 length: u32,
56 duration: f64,
58 number_of_channels: u32,
60}
61
62impl AudioBuffer {
63 pub(crate) fn new_inherited(
64 number_of_channels: u32,
65 length: u32,
66 sample_rate: f32,
67 ) -> AudioBuffer {
68 let vec = (0..number_of_channels)
69 .map(|_| HeapBufferSource::default())
70 .collect();
71 AudioBuffer {
72 reflector_: Reflector::new(),
73 js_channels: DomRefCell::new(vec),
74 shared_channels: DomRefCell::new(None),
75 sample_rate,
76 length,
77 duration: length as f64 / sample_rate as f64,
78 number_of_channels,
79 }
80 }
81
82 pub(crate) fn new(
83 global: &Window,
84 number_of_channels: u32,
85 length: u32,
86 sample_rate: f32,
87 initial_data: Option<&[Vec<f32>]>,
88 can_gc: CanGc,
89 ) -> DomRoot<AudioBuffer> {
90 Self::new_with_proto(
91 global,
92 None,
93 number_of_channels,
94 length,
95 sample_rate,
96 initial_data,
97 can_gc,
98 )
99 }
100
101 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
102 fn new_with_proto(
103 global: &Window,
104 proto: Option<HandleObject>,
105 number_of_channels: u32,
106 length: u32,
107 sample_rate: f32,
108 initial_data: Option<&[Vec<f32>]>,
109 can_gc: CanGc,
110 ) -> DomRoot<AudioBuffer> {
111 let buffer = AudioBuffer::new_inherited(number_of_channels, length, sample_rate);
112 let buffer = reflect_dom_object_with_proto(Box::new(buffer), global, proto, can_gc);
113 buffer.set_initial_data(initial_data);
114 buffer
115 }
116
117 fn set_initial_data(&self, initial_data: Option<&[Vec<f32>]>) {
120 let mut channels = ServoMediaAudioBuffer::new(
121 self.number_of_channels as u8,
122 self.length as usize,
123 self.sample_rate,
124 );
125 for channel in 0..self.number_of_channels {
126 channels.buffers[channel as usize] = match initial_data {
127 Some(data) => data[channel as usize].clone(),
128 None => vec![0.; self.length as usize],
129 };
130 }
131 *self.shared_channels.borrow_mut() = Some(channels);
132 }
133
134 fn restore_js_channel_data(&self, cx: JSContext, can_gc: CanGc) -> bool {
135 let _ac = enter_realm(self);
136 for (i, channel) in self.js_channels.borrow().iter().enumerate() {
137 if channel.is_initialized() {
138 continue;
140 }
141
142 if let Some(ref shared_channels) = *self.shared_channels.borrow() {
143 if channel
148 .set_data(cx, &shared_channels.buffers[i], can_gc)
149 .is_err()
150 {
151 return false;
152 }
153 }
154 }
155
156 *self.shared_channels.borrow_mut() = None;
157
158 true
159 }
160
161 fn acquire_contents(&self) -> Option<ServoMediaAudioBuffer> {
163 let mut result = ServoMediaAudioBuffer::new(
164 self.number_of_channels as u8,
165 self.length as usize,
166 self.sample_rate,
167 );
168 let cx = GlobalScope::get_cx();
169 for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
170 if !channel.is_initialized() {
172 return None;
173 }
174
175 result.buffers[i] = channel.acquire_data(cx).ok()?;
177 }
178
179 Some(result)
180 }
181
182 pub(crate) fn get_channels(&self) -> Ref<'_, Option<ServoMediaAudioBuffer>> {
183 if self.shared_channels.borrow().is_none() {
184 let channels = self.acquire_contents();
185 if channels.is_some() {
186 *self.shared_channels.borrow_mut() = channels;
187 }
188 }
189 self.shared_channels.borrow()
190 }
191}
192
193impl AudioBufferMethods<crate::DomTypeHolder> for AudioBuffer {
194 fn Constructor(
196 window: &Window,
197 proto: Option<HandleObject>,
198 can_gc: CanGc,
199 options: &AudioBufferOptions,
200 ) -> Fallible<DomRoot<AudioBuffer>> {
201 if options.length == 0 ||
202 options.numberOfChannels == 0 ||
203 options.numberOfChannels > MAX_CHANNEL_COUNT ||
204 *options.sampleRate < MIN_SAMPLE_RATE ||
205 *options.sampleRate > MAX_SAMPLE_RATE
206 {
207 return Err(Error::NotSupported(None));
208 }
209 Ok(AudioBuffer::new_with_proto(
210 window,
211 proto,
212 options.numberOfChannels,
213 options.length,
214 *options.sampleRate,
215 None,
216 can_gc,
217 ))
218 }
219
220 fn SampleRate(&self) -> Finite<f32> {
222 Finite::wrap(self.sample_rate)
223 }
224
225 fn Length(&self) -> u32 {
227 self.length
228 }
229
230 fn Duration(&self) -> Finite<f64> {
232 Finite::wrap(self.duration)
233 }
234
235 fn NumberOfChannels(&self) -> u32 {
237 self.number_of_channels
238 }
239
240 fn GetChannelData(
242 &self,
243 cx: JSContext,
244 channel: u32,
245 can_gc: CanGc,
246 ) -> Fallible<RootedTraceableBox<HeapFloat32Array>> {
247 if channel >= self.number_of_channels {
248 return Err(Error::IndexSize(None));
249 }
250
251 if !self.restore_js_channel_data(cx, can_gc) {
252 return Err(Error::JSFailed);
253 }
254
255 self.js_channels.borrow()[channel as usize]
256 .get_typed_array()
257 .map_err(|_| Error::JSFailed)
258 }
259
260 fn CopyFromChannel(
262 &self,
263 mut destination: CustomAutoRooterGuard<Float32Array>,
264 channel_number: u32,
265 start_in_channel: u32,
266 ) -> Fallible<()> {
267 if destination.is_shared() {
268 return Err(Error::Type("Cannot copy to shared buffer".to_owned()));
269 }
270
271 if channel_number >= self.number_of_channels || start_in_channel >= self.length {
272 return Err(Error::IndexSize(None));
273 }
274
275 let bytes_to_copy = min(self.length - start_in_channel, destination.len() as u32) as usize;
276 let cx = GlobalScope::get_cx();
277 let channel_number = channel_number as usize;
278 let offset = start_in_channel as usize;
279 let mut dest = vec![0.0_f32; bytes_to_copy];
280
281 let js_channel = &self.js_channels.borrow()[channel_number];
283 if js_channel.is_initialized() {
284 if js_channel
285 .copy_data_to(cx, &mut dest, offset, offset + bytes_to_copy)
286 .is_err()
287 {
288 return Err(Error::IndexSize(None));
289 }
290 } else if let Some(ref shared_channels) = *self.shared_channels.borrow() {
291 if let Some(shared_channel) = shared_channels.buffers.get(channel_number) {
292 dest.extend_from_slice(&shared_channel.as_slice()[offset..offset + bytes_to_copy]);
293 }
294 }
295
296 destination.update(&dest);
297
298 Ok(())
299 }
300
301 fn CopyToChannel(
303 &self,
304 source: CustomAutoRooterGuard<Float32Array>,
305 channel_number: u32,
306 start_in_channel: u32,
307 can_gc: CanGc,
308 ) -> Fallible<()> {
309 if source.is_shared() {
310 return Err(Error::Type("Cannot copy from shared buffer".to_owned()));
311 }
312
313 if channel_number >= self.number_of_channels || start_in_channel > (source.len() as u32) {
314 return Err(Error::IndexSize(None));
315 }
316
317 let cx = GlobalScope::get_cx();
318 if !self.restore_js_channel_data(cx, can_gc) {
319 return Err(Error::JSFailed);
320 }
321
322 let js_channel = &self.js_channels.borrow()[channel_number as usize];
323 if !js_channel.is_initialized() {
324 return Err(Error::IndexSize(None));
326 }
327
328 let bytes_to_copy = min(self.length - start_in_channel, source.len() as u32) as usize;
329 js_channel
330 .copy_data_from(cx, source, start_in_channel as usize, bytes_to_copy)
331 .map_err(|_| Error::IndexSize(None))
332 }
333}