1use std::cmp::min;
6
7use dom_struct::dom_struct;
8use js::rust::{CustomAutoRooterGuard, HandleObject};
9use js::typedarray::{Float32, Float32Array};
10use servo_media::audio::buffer_source_node::AudioBuffer as ServoMediaAudioBuffer;
11
12use crate::dom::audio::audionode::MAX_CHANNEL_COUNT;
13use crate::dom::bindings::buffer_source::HeapBufferSource;
14use crate::dom::bindings::cell::{DomRefCell, Ref};
15use crate::dom::bindings::codegen::Bindings::AudioBufferBinding::{
16 AudioBufferMethods, AudioBufferOptions,
17};
18use crate::dom::bindings::error::{Error, Fallible};
19use crate::dom::bindings::num::Finite;
20use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::window::Window;
24use crate::realms::enter_realm;
25use crate::script_runtime::{CanGc, JSContext};
26
27pub(crate) const MIN_SAMPLE_RATE: f32 = 8000.;
30pub(crate) const MAX_SAMPLE_RATE: f32 = 192000.;
31
32#[dom_struct]
41pub(crate) struct AudioBuffer {
42 reflector_: Reflector,
43 #[ignore_malloc_size_of = "mozjs"]
45 js_channels: DomRefCell<Vec<HeapBufferSource<Float32>>>,
46 #[ignore_malloc_size_of = "servo_media"]
49 #[no_trace]
50 shared_channels: DomRefCell<Option<ServoMediaAudioBuffer>>,
51 sample_rate: f32,
53 length: u32,
55 duration: f64,
57 number_of_channels: u32,
59}
60
61impl AudioBuffer {
62 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
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, allow(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_mut().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);
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(&self, cx: JSContext, channel: u32, can_gc: CanGc) -> Fallible<Float32Array> {
242 if channel >= self.number_of_channels {
243 return Err(Error::IndexSize);
244 }
245
246 if !self.restore_js_channel_data(cx, can_gc) {
247 return Err(Error::JSFailed);
248 }
249
250 self.js_channels.borrow()[channel as usize]
251 .get_typed_array()
252 .map_err(|_| Error::JSFailed)
253 }
254
255 #[allow(unsafe_code)]
257 fn CopyFromChannel(
258 &self,
259 mut destination: CustomAutoRooterGuard<Float32Array>,
260 channel_number: u32,
261 start_in_channel: u32,
262 ) -> Fallible<()> {
263 if destination.is_shared() {
264 return Err(Error::Type("Cannot copy to shared buffer".to_owned()));
265 }
266
267 if channel_number >= self.number_of_channels || start_in_channel >= self.length {
268 return Err(Error::IndexSize);
269 }
270
271 let bytes_to_copy = min(self.length - start_in_channel, destination.len() as u32) as usize;
272 let cx = GlobalScope::get_cx();
273 let channel_number = channel_number as usize;
274 let offset = start_in_channel as usize;
275 let mut dest = vec![0.0_f32; bytes_to_copy];
276
277 let js_channel = &self.js_channels.borrow()[channel_number];
279 if js_channel.is_initialized() {
280 if js_channel
281 .copy_data_to(cx, &mut dest, offset, offset + bytes_to_copy)
282 .is_err()
283 {
284 return Err(Error::IndexSize);
285 }
286 } else if let Some(ref shared_channels) = *self.shared_channels.borrow() {
287 if let Some(shared_channel) = shared_channels.buffers.get(channel_number) {
288 dest.extend_from_slice(&shared_channel.as_slice()[offset..offset + bytes_to_copy]);
289 }
290 }
291
292 destination.update(&dest);
293
294 Ok(())
295 }
296
297 fn CopyToChannel(
299 &self,
300 source: CustomAutoRooterGuard<Float32Array>,
301 channel_number: u32,
302 start_in_channel: u32,
303 can_gc: CanGc,
304 ) -> Fallible<()> {
305 if source.is_shared() {
306 return Err(Error::Type("Cannot copy from shared buffer".to_owned()));
307 }
308
309 if channel_number >= self.number_of_channels || start_in_channel > (source.len() as u32) {
310 return Err(Error::IndexSize);
311 }
312
313 let cx = GlobalScope::get_cx();
314 if !self.restore_js_channel_data(cx, can_gc) {
315 return Err(Error::JSFailed);
316 }
317
318 let js_channel = &self.js_channels.borrow()[channel_number as usize];
319 if !js_channel.is_initialized() {
320 return Err(Error::IndexSize);
322 }
323
324 let bytes_to_copy = min(self.length - start_in_channel, source.len() as u32) as usize;
325 js_channel
326 .copy_data_from(cx, source, start_in_channel as usize, bytes_to_copy)
327 .map_err(|_| Error::IndexSize)
328 }
329}