1use std::cmp::min;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::rust::{CustomAutoRooterGuard, HandleObject};
10use js::typedarray::{Float32, Float32Array, HeapFloat32Array};
11use script_bindings::cell::{DomRefCell, Ref};
12use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto_and_cx};
13use script_bindings::trace::RootedTraceableBox;
14use servo_media::audio::buffer_source_node::AudioBuffer as ServoMediaAudioBuffer;
15
16use crate::dom::audio::audionode::MAX_CHANNEL_COUNT;
17use crate::dom::bindings::buffer_source::HeapBufferSource;
18use crate::dom::bindings::codegen::Bindings::AudioBufferBinding::{
19 AudioBufferMethods, AudioBufferOptions,
20};
21use crate::dom::bindings::error::{Error, Fallible};
22use crate::dom::bindings::num::Finite;
23use crate::dom::bindings::root::DomRoot;
24use crate::dom::window::Window;
25use crate::realms::enter_auto_realm;
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, expect(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 cx: &mut JSContext,
84 global: &Window,
85 number_of_channels: u32,
86 length: u32,
87 sample_rate: f32,
88 initial_data: Option<&[Vec<f32>]>,
89 ) -> DomRoot<AudioBuffer> {
90 Self::new_with_proto(
91 cx,
92 global,
93 None,
94 number_of_channels,
95 length,
96 sample_rate,
97 initial_data,
98 )
99 }
100
101 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
102 fn new_with_proto(
103 cx: &mut JSContext,
104 global: &Window,
105 proto: Option<HandleObject>,
106 number_of_channels: u32,
107 length: u32,
108 sample_rate: f32,
109 initial_data: Option<&[Vec<f32>]>,
110 ) -> DomRoot<AudioBuffer> {
111 let buffer = AudioBuffer::new_inherited(number_of_channels, length, sample_rate);
112 let buffer = reflect_dom_object_with_proto_and_cx(Box::new(buffer), global, proto, cx);
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: &mut JSContext) -> bool {
135 let mut realm = enter_auto_realm(cx, self);
136 let cx = &mut realm.current_realm();
137 for (i, channel) in self.js_channels.borrow().iter().enumerate() {
138 if channel.is_initialized() {
139 continue;
141 }
142
143 if let Some(ref shared_channels) = *self.shared_channels.borrow() {
144 if channel.set_data(cx, &shared_channels.buffers[i]).is_err() {
149 return false;
150 }
151 }
152 }
153
154 *self.shared_channels.borrow_mut() = None;
155
156 true
157 }
158
159 fn acquire_contents(&self, cx: &mut JSContext) -> Option<ServoMediaAudioBuffer> {
161 let mut result = ServoMediaAudioBuffer::new(
162 self.number_of_channels as u8,
163 self.length as usize,
164 self.sample_rate,
165 );
166 for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
167 if !channel.is_initialized() {
169 return None;
170 }
171
172 result.buffers[i] = channel.acquire_data(cx).ok()?;
174 }
175
176 Some(result)
177 }
178
179 pub(crate) fn get_channels(
180 &self,
181 cx: &mut JSContext,
182 ) -> Ref<'_, Option<ServoMediaAudioBuffer>> {
183 if self.shared_channels.borrow().is_none() {
184 let channels = self.acquire_contents(cx);
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 cx: &mut JSContext,
197 window: &Window,
198 proto: Option<HandleObject>,
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 cx,
211 window,
212 proto,
213 options.numberOfChannels,
214 options.length,
215 *options.sampleRate,
216 None,
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: &mut JSContext,
244 channel: u32,
245 ) -> Fallible<RootedTraceableBox<HeapFloat32Array>> {
246 if channel >= self.number_of_channels {
247 return Err(Error::IndexSize(None));
248 }
249
250 if !self.restore_js_channel_data(cx) {
251 return Err(Error::JSFailed);
252 }
253
254 self.js_channels.borrow()[channel as usize]
255 .get_typed_array()
256 .map_err(|_| Error::JSFailed)
257 }
258
259 fn CopyFromChannel(
261 &self,
262 cx: &mut JSContext,
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(c"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 channel_number = channel_number as usize;
277 let offset = start_in_channel as usize;
278 let mut dest = vec![0.0_f32; bytes_to_copy];
279
280 let js_channel = &self.js_channels.borrow()[channel_number];
282 if js_channel.is_initialized() {
283 if js_channel
284 .copy_data_to(cx, &mut dest, offset, offset + bytes_to_copy)
285 .is_err()
286 {
287 return Err(Error::IndexSize(None));
288 }
289 } else if let Some(ref shared_channels) = *self.shared_channels.borrow() &&
290 let Some(shared_channel) = shared_channels.buffers.get(channel_number)
291 {
292 dest.extend_from_slice(&shared_channel.as_slice()[offset..offset + bytes_to_copy]);
293 }
294
295 destination.update(&dest);
296
297 Ok(())
298 }
299
300 fn CopyToChannel(
302 &self,
303 cx: &mut JSContext,
304 source: CustomAutoRooterGuard<Float32Array>,
305 channel_number: u32,
306 start_in_channel: u32,
307 ) -> Fallible<()> {
308 if source.is_shared() {
309 return Err(Error::Type(c"Cannot copy from shared buffer".to_owned()));
310 }
311
312 if channel_number >= self.number_of_channels || start_in_channel > (source.len() as u32) {
313 return Err(Error::IndexSize(None));
314 }
315
316 if !self.restore_js_channel_data(cx) {
317 return Err(Error::JSFailed);
318 }
319
320 let js_channel = &self.js_channels.borrow()[channel_number as usize];
321 if !js_channel.is_initialized() {
322 return Err(Error::IndexSize(None));
324 }
325
326 let bytes_to_copy = min(self.length - start_in_channel, source.len() as u32) as usize;
327 js_channel
328 .copy_data_from(cx, source, start_in_channel as usize, bytes_to_copy)
329 .map_err(|_| Error::IndexSize(None))
330 }
331}