1use std::cell::Cell;
6use std::collections::hash_map::Entry;
7use std::collections::{HashMap, VecDeque};
8use std::sync::{Arc, Mutex};
9
10use dom_struct::dom_struct;
11use js::context::JSContext;
12use js::realm::CurrentRealm;
13use js::rust::CustomAutoRooterGuard;
14use js::typedarray::{ArrayBuffer, ArrayBufferU8};
15use script_bindings::cell::DomRefCell;
16use script_bindings::cformat;
17use script_bindings::codegen::GenericBindings::AnalyserNodeBinding::AnalyserNodeMethods;
18use script_bindings::codegen::GenericBindings::AudioBufferSourceNodeBinding::AudioBufferSourceNodeMethods;
19use script_bindings::codegen::GenericBindings::BiquadFilterNodeBinding::BiquadFilterNodeMethods;
20use script_bindings::codegen::GenericBindings::ChannelMergerNodeBinding::ChannelMergerNodeMethods;
21use script_bindings::codegen::GenericBindings::ChannelSplitterNodeBinding::ChannelSplitterNodeMethods;
22use script_bindings::codegen::GenericBindings::ConstantSourceNodeBinding::ConstantSourceNodeMethods;
23use script_bindings::codegen::GenericBindings::ConvolverNodeBinding::{
24 ConvolverNodeMethods, ConvolverOptions,
25};
26use script_bindings::codegen::GenericBindings::DelayNodeBinding::{DelayNodeMethods, DelayOptions};
27use script_bindings::codegen::GenericBindings::GainNodeBinding::GainNodeMethods;
28use script_bindings::codegen::GenericBindings::IIRFilterNodeBinding::IIRFilterNodeMethods;
29use script_bindings::codegen::GenericBindings::OscillatorNodeBinding::OscillatorNodeMethods;
30use script_bindings::codegen::GenericBindings::PannerNodeBinding::PannerNodeMethods;
31use script_bindings::codegen::GenericBindings::PeriodicWaveBinding::PeriodicWaveMethods;
32use script_bindings::codegen::GenericBindings::StereoPannerNodeBinding::StereoPannerNodeMethods;
33use servo_base::id::PipelineId;
34use servo_media::audio::context::{
35 AudioContext, AudioContextOptions, OfflineAudioContextOptions, ProcessingState,
36 RealTimeAudioContextOptions,
37};
38use servo_media::audio::decoder::AudioDecoderCallbacksBuilder;
39use servo_media::audio::graph::NodeId;
40use servo_media::{ClientContextId, ServoMedia};
41use uuid::Uuid;
42
43use crate::conversions::Convert;
44use crate::dom::audio::analysernode::AnalyserNode;
45use crate::dom::audio::audiobuffer::AudioBuffer;
46use crate::dom::audio::audiobuffersourcenode::AudioBufferSourceNode;
47use crate::dom::audio::audiodestinationnode::AudioDestinationNode;
48use crate::dom::audio::audiolistener::AudioListener;
49use crate::dom::audio::audionode::MAX_CHANNEL_COUNT;
50use crate::dom::audio::biquadfilternode::BiquadFilterNode;
51use crate::dom::audio::channelmergernode::ChannelMergerNode;
52use crate::dom::audio::channelsplitternode::ChannelSplitterNode;
53use crate::dom::audio::constantsourcenode::ConstantSourceNode;
54use crate::dom::audio::convolvernode::ConvolverNode;
55use crate::dom::audio::delaynode::DelayNode;
56use crate::dom::audio::gainnode::GainNode;
57use crate::dom::audio::iirfilternode::IIRFilterNode;
58use crate::dom::audio::oscillatornode::OscillatorNode;
59use crate::dom::audio::pannernode::PannerNode;
60use crate::dom::audio::periodicwave::PeriodicWave;
61use crate::dom::audio::stereopannernode::StereoPannerNode;
62use crate::dom::bindings::buffer_source::HeapBufferSource;
63use crate::dom::bindings::callback::{ExceptionHandling, RootedCallback, TracedCallback};
64use crate::dom::bindings::codegen::Bindings::AnalyserNodeBinding::AnalyserOptions;
65use crate::dom::bindings::codegen::Bindings::AudioBufferSourceNodeBinding::AudioBufferSourceOptions;
66use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
67 AudioNodeOptions, ChannelCountMode, ChannelInterpretation,
68};
69use crate::dom::bindings::codegen::Bindings::BaseAudioContextBinding::{
70 AudioContextState, BaseAudioContextMethods, DecodeErrorCallback, DecodeSuccessCallback,
71};
72use crate::dom::bindings::codegen::Bindings::BiquadFilterNodeBinding::BiquadFilterOptions;
73use crate::dom::bindings::codegen::Bindings::ChannelMergerNodeBinding::ChannelMergerOptions;
74use crate::dom::bindings::codegen::Bindings::ChannelSplitterNodeBinding::ChannelSplitterOptions;
75use crate::dom::bindings::codegen::Bindings::ConstantSourceNodeBinding::ConstantSourceOptions;
76use crate::dom::bindings::codegen::Bindings::GainNodeBinding::GainOptions;
77use crate::dom::bindings::codegen::Bindings::IIRFilterNodeBinding::IIRFilterOptions;
78use crate::dom::bindings::codegen::Bindings::OscillatorNodeBinding::OscillatorOptions;
79use crate::dom::bindings::codegen::Bindings::PannerNodeBinding::PannerOptions;
80use crate::dom::bindings::codegen::Bindings::PeriodicWaveBinding::{
81 PeriodicWaveConstraints, PeriodicWaveOptions,
82};
83use crate::dom::bindings::codegen::Bindings::StereoPannerNodeBinding::StereoPannerOptions;
84use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
85use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
86use crate::dom::bindings::inheritance::Castable;
87use crate::dom::bindings::num::Finite;
88use crate::dom::bindings::refcounted::Trusted;
89use crate::dom::bindings::reflector::DomGlobal;
90use crate::dom::bindings::root::{DomRoot, MutNullableDom};
91use crate::dom::domexception::{DOMErrorName, DOMException};
92use crate::dom::eventtarget::EventTarget;
93use crate::dom::promise::{Promise, RootedPromise, TracedPromise};
94
95pub(crate) enum BaseAudioContextOptions {
96 AudioContext(RealTimeAudioContextOptions),
97 OfflineAudioContext(OfflineAudioContextOptions),
98}
99
100#[derive(JSTraceable, MallocSizeOf)]
101#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
102struct DecodeResolver {
103 pub(crate) promise: TracedPromise,
104 pub(crate) success_callback: Option<TracedCallback<DecodeSuccessCallback>>,
105 pub(crate) error_callback: Option<TracedCallback<DecodeErrorCallback>>,
106}
107
108impl js::gc::Rootable for DecodeResolver {}
109
110type BoxedSliceOfPromises = Box<[TracedPromise]>;
111
112#[dom_struct]
113pub(crate) struct BaseAudioContext {
114 eventtarget: EventTarget,
115 #[no_trace]
116 #[conditional_malloc_size_of]
117 audio_context_impl: Arc<Mutex<AudioContext>>,
118 destination: MutNullableDom<AudioDestinationNode>,
120 listener: MutNullableDom<AudioListener>,
121 in_flight_resume_promises_queue: DomRefCell<VecDeque<(BoxedSliceOfPromises, ErrorResult)>>,
123 pending_resume_promises: DomRefCell<Vec<TracedPromise>>,
125 decode_resolvers: DomRefCell<HashMap<String, DecodeResolver>>,
126 sample_rate: f32,
128 state: Cell<AudioContextState>,
134 channel_count: u32,
135}
136
137impl BaseAudioContext {
138 pub(crate) fn new_inherited(
139 options: BaseAudioContextOptions,
140 pipeline_id: PipelineId,
141 ) -> Fallible<BaseAudioContext> {
142 let (sample_rate, channel_count) = match options {
143 BaseAudioContextOptions::AudioContext(ref opt) => (opt.sample_rate, 2),
144 BaseAudioContextOptions::OfflineAudioContext(ref opt) => {
145 (opt.sample_rate, opt.channels)
146 },
147 };
148
149 let client_context_id =
150 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
151 let audio_context_impl = ServoMedia::get()
152 .create_audio_context(&client_context_id, options.convert())
153 .map_err(|_| Error::NotSupported(Some("Failed to create audio context".into())))?;
154
155 Ok(BaseAudioContext {
156 eventtarget: EventTarget::new_inherited(),
157 audio_context_impl,
158 destination: Default::default(),
159 listener: Default::default(),
160 in_flight_resume_promises_queue: Default::default(),
161 pending_resume_promises: Default::default(),
162 decode_resolvers: Default::default(),
163 sample_rate,
164 state: Cell::new(AudioContextState::Suspended),
165 channel_count: channel_count.into(),
166 })
167 }
168
169 pub(crate) fn is_offline(&self) -> bool {
171 false
172 }
173
174 pub(crate) fn audio_context_impl(&self) -> Arc<Mutex<AudioContext>> {
175 self.audio_context_impl.clone()
176 }
177
178 pub(crate) fn destination_node(&self) -> NodeId {
179 self.audio_context_impl.lock().unwrap().dest_node()
180 }
181
182 pub(crate) fn listener(&self) -> NodeId {
183 self.audio_context_impl.lock().unwrap().listener()
184 }
185
186 pub(crate) fn is_allowed_to_start(&self) -> bool {
188 self.state.get() == AudioContextState::Suspended
189 }
190
191 fn push_pending_resume_promise(&self, promise: &RootedPromise) {
192 self.pending_resume_promises
193 .borrow_mut()
194 .push(promise.to_traced());
195 }
196
197 fn take_pending_resume_promises(&self, result: ErrorResult) {
208 self.in_flight_resume_promises_queue
209 .borrow_mut()
210 .push_back((
211 std::mem::take(&mut *self.pending_resume_promises.borrow_mut()).into(),
212 result,
213 ));
214 }
215
216 fn fulfill_in_flight_resume_promises<F>(&self, cx: &mut JSContext, f: F)
225 where
226 F: FnOnce(),
227 {
228 let (promises, result) = self
229 .in_flight_resume_promises_queue
230 .borrow_mut()
231 .pop_front()
232 .map(|(promises, result)| {
233 let promises: Vec<RootedPromise> =
234 promises.iter().map(|promise| promise.root(cx)).collect();
235 (promises, result)
236 })
237 .expect("there should be at least one list of in flight resume promises");
238 f();
239 for promise in &promises {
240 match result {
241 Ok(ref value) => promise.resolve_native(cx, value),
242 Err(ref error) => promise.reject_error(cx, error.clone()),
243 }
244 }
245 }
246
247 pub(crate) fn control_thread_state(&self) -> ProcessingState {
249 self.audio_context_impl.lock().unwrap().state()
250 }
251
252 pub(crate) fn set_state_attribute(&self, state: AudioContextState) {
254 self.state.set(state);
255 }
256
257 pub(crate) fn resume(&self) {
258 let this = Trusted::new(self);
259 match self.audio_context_impl.lock().unwrap().resume() {
262 Some(()) => {
263 self.take_pending_resume_promises(Ok(()));
264 self.global().task_manager().dom_manipulation_task_source().queue(
265 task!(resume_success: move |cx| {
266 let this = this.root();
267 this.fulfill_in_flight_resume_promises(cx, || {
268 if this.state.get() != AudioContextState::Running {
269 this.state.set(AudioContextState::Running);
270 this.global().task_manager().dom_manipulation_task_source().queue_simple_event(
271 this.upcast(),
272 atom!("statechange"),
273 );
274 }
275 });
276 })
277 );
278 },
279 None => {
280 self.take_pending_resume_promises(Err(Error::Type(
281 c"Something went wrong".to_owned(),
282 )));
283 self.global()
284 .task_manager()
285 .dom_manipulation_task_source()
286 .queue(task!(resume_error: move |cx| {
287 this.root().fulfill_in_flight_resume_promises(cx, || {})
288 }));
289 },
290 }
291 }
292
293 pub(crate) fn channel_count(&self) -> u32 {
294 self.channel_count
295 }
296}
297
298impl BaseAudioContextMethods<crate::DomTypeHolder> for BaseAudioContext {
299 fn SampleRate(&self) -> Finite<f32> {
301 Finite::wrap(self.sample_rate)
302 }
303
304 fn CurrentTime(&self) -> Finite<f64> {
306 let current_time = self.audio_context_impl.lock().unwrap().current_time();
307 Finite::wrap(current_time)
308 }
309
310 fn State(&self) -> AudioContextState {
312 self.state.get()
313 }
314
315 fn Resume(&self, cx: &mut CurrentRealm) -> RootedPromise {
317 let promise = Promise::new_in_realm(cx);
319
320 if self.audio_context_impl.lock().unwrap().state() == ProcessingState::Closed {
322 promise.reject_error(
323 cx,
324 Error::InvalidState(Some("Cannot resume a closed AudioContext".into())),
325 );
326 return promise;
327 }
328
329 if self.state.get() == AudioContextState::Running {
331 promise.resolve_native(cx, &());
332 return promise;
333 }
334
335 self.push_pending_resume_promise(&promise);
336
337 if !self.is_allowed_to_start() {
339 return promise;
340 }
341
342 self.resume();
344
345 promise
347 }
348
349 fn Destination(&self, cx: &mut JSContext) -> DomRoot<AudioDestinationNode> {
351 let global = self.global();
352 self.destination.or_init(|| {
353 let mut options = AudioNodeOptions::empty();
354 options.channelCount = Some(self.channel_count);
355 options.channelCountMode = Some(ChannelCountMode::Explicit);
356 options.channelInterpretation = Some(ChannelInterpretation::Speakers);
357 AudioDestinationNode::new(cx, &global, self, &options)
358 })
359 }
360
361 fn Listener(&self, cx: &mut JSContext) -> DomRoot<AudioListener> {
363 let global = self.global();
364 let window = global.as_window();
365 self.listener
366 .or_init(|| AudioListener::new(cx, window, self))
367 }
368
369 event_handler!(statechange, GetOnstatechange, SetOnstatechange);
371
372 fn CreateConvolver(&self, cx: &mut JSContext) -> Fallible<DomRoot<ConvolverNode>> {
374 ConvolverNode::Constructor(
375 cx,
376 self.global().as_window(),
377 None,
378 self,
379 &ConvolverOptions::empty(),
380 )
381 }
382
383 fn CreateOscillator(&self, cx: &mut JSContext) -> Fallible<DomRoot<OscillatorNode>> {
385 OscillatorNode::Constructor(
386 cx,
387 self.global().as_window(),
388 None,
389 self,
390 &OscillatorOptions::empty(),
391 )
392 }
393
394 fn CreateGain(&self, cx: &mut JSContext) -> Fallible<DomRoot<GainNode>> {
396 GainNode::Constructor(
397 cx,
398 self.global().as_window(),
399 None,
400 self,
401 &GainOptions::empty(),
402 )
403 }
404
405 fn CreateDelay(
407 &self,
408 cx: &mut JSContext,
409 max_delay_time: Finite<f64>,
410 ) -> Fallible<DomRoot<DelayNode>> {
411 if *max_delay_time <= 0. || *max_delay_time >= 180. {
415 return Err(Error::NotSupported(Some(String::from(
416 "maxDelayTime is not within 0 - 3 minutes",
417 ))));
418 }
419 let mut options = DelayOptions::empty();
420 options.maxDelayTime = max_delay_time;
421 DelayNode::Constructor(cx, self.global().as_window(), None, self, &options)
422 }
423
424 fn CreatePanner(&self, cx: &mut JSContext) -> Fallible<DomRoot<PannerNode>> {
426 PannerNode::Constructor(
427 cx,
428 self.global().as_window(),
429 None,
430 self,
431 &PannerOptions::empty(),
432 )
433 }
434
435 fn CreatePeriodicWave(
437 &self,
438 cx: &mut JSContext,
439 real: Vec<Finite<f32>>,
440 imag: Vec<Finite<f32>>,
441 constraints: &PeriodicWaveConstraints,
442 ) -> Fallible<DomRoot<PeriodicWave>> {
443 let mut options = PeriodicWaveOptions::empty();
445 let mut constraints_copy = PeriodicWaveConstraints::empty();
446 constraints_copy.disableNormalization = constraints.disableNormalization;
449 options.real = Some(real);
452 options.imag = Some(imag);
453 options.parent = constraints_copy;
454 PeriodicWave::Constructor(cx, self.global().as_window(), None, self, &options)
455 }
456
457 fn CreateAnalyser(&self, cx: &mut JSContext) -> Fallible<DomRoot<AnalyserNode>> {
459 AnalyserNode::Constructor(
460 cx,
461 self.global().as_window(),
462 None,
463 self,
464 &AnalyserOptions::empty(),
465 )
466 }
467
468 fn CreateBiquadFilter(&self, cx: &mut JSContext) -> Fallible<DomRoot<BiquadFilterNode>> {
470 BiquadFilterNode::Constructor(
471 cx,
472 self.global().as_window(),
473 None,
474 self,
475 &BiquadFilterOptions::empty(),
476 )
477 }
478
479 fn CreateStereoPanner(&self, cx: &mut JSContext) -> Fallible<DomRoot<StereoPannerNode>> {
481 StereoPannerNode::Constructor(
482 cx,
483 self.global().as_window(),
484 None,
485 self,
486 &StereoPannerOptions::empty(),
487 )
488 }
489
490 fn CreateConstantSource(&self, cx: &mut JSContext) -> Fallible<DomRoot<ConstantSourceNode>> {
492 ConstantSourceNode::Constructor(
493 cx,
494 self.global().as_window(),
495 None,
496 self,
497 &ConstantSourceOptions::empty(),
498 )
499 }
500
501 fn CreateChannelMerger(
503 &self,
504 cx: &mut JSContext,
505 count: u32,
506 ) -> Fallible<DomRoot<ChannelMergerNode>> {
507 let mut opts = ChannelMergerOptions::empty();
508 opts.numberOfInputs = count;
509 ChannelMergerNode::Constructor(cx, self.global().as_window(), None, self, &opts)
510 }
511
512 fn CreateChannelSplitter(
514 &self,
515 cx: &mut JSContext,
516 count: u32,
517 ) -> Fallible<DomRoot<ChannelSplitterNode>> {
518 let mut opts = ChannelSplitterOptions::empty();
519 opts.numberOfOutputs = count;
520 ChannelSplitterNode::Constructor(cx, self.global().as_window(), None, self, &opts)
521 }
522
523 fn CreateBuffer(
525 &self,
526 cx: &mut JSContext,
527 number_of_channels: u32,
528 length: u32,
529 sample_rate: Finite<f32>,
530 ) -> Fallible<DomRoot<AudioBuffer>> {
531 if number_of_channels == 0 ||
532 number_of_channels > MAX_CHANNEL_COUNT ||
533 length == 0 ||
534 *sample_rate <= 0.
535 {
536 return Err(Error::NotSupported(Some(
537 "Invalid number of channels, length or sample rate".into(),
538 )));
539 }
540 Ok(AudioBuffer::new(
541 cx,
542 self.global().as_window(),
543 number_of_channels,
544 length,
545 *sample_rate,
546 None,
547 ))
548 }
549
550 fn CreateBufferSource(&self, cx: &mut JSContext) -> Fallible<DomRoot<AudioBufferSourceNode>> {
552 AudioBufferSourceNode::Constructor(
553 cx,
554 self.global().as_window(),
555 None,
556 self,
557 &AudioBufferSourceOptions::empty(),
558 )
559 }
560
561 fn DecodeAudioData(
563 &self,
564 cx: &mut CurrentRealm,
565 audio_data: CustomAutoRooterGuard<ArrayBuffer>,
566 decode_success_callback: Option<Option<RootedCallback<DecodeSuccessCallback>>>,
567 decode_error_callback: Option<Option<RootedCallback<DecodeErrorCallback>>>,
568 ) -> RootedPromise {
569 if !self.global().as_window().Document().is_fully_active() {
572 let promise = Promise::new_in_realm(cx);
573 promise.reject_error(
574 cx,
575 Error::InvalidState(Some("Audio context's document is not fully active.".into())),
576 );
577 return promise;
578 }
579
580 let promise = Promise::new_in_realm(cx);
582
583 let decode_success_callback = decode_success_callback.flatten();
585 let decode_error_callback = decode_error_callback.flatten();
586
587 let audio_data = HeapBufferSource::<ArrayBufferU8>::from_view(cx, audio_data);
588
589 let audio_data = if !audio_data.is_detached_buffer(cx) {
594 audio_data
595 .get_typed_array()
596 .ok()
597 .and_then(|buffer| buffer.to_vec())
598 .filter(|_| audio_data.detach_buffer(cx))
599 } else {
600 None
601 };
602
603 if let Some(audio_data) = audio_data {
604 let uuid = Uuid::new_v4().simple().to_string();
605 let uuid_ = uuid.clone();
606 self.decode_resolvers.safe_borrow_mut(cx.no_gc()).insert(
607 uuid.clone(),
608 DecodeResolver {
609 promise: promise.to_traced(),
610 success_callback: decode_success_callback.map(|callback| callback.to_traced()),
611 error_callback: decode_error_callback.map(|callback| callback.to_traced()),
612 },
613 );
614 let decoded_audio = Arc::new(Mutex::new(Vec::new()));
615 let decoded_audio_ = decoded_audio.clone();
616 let decoded_audio__ = decoded_audio.clone();
617 let channels = Arc::new(Mutex::new(HashMap::new()));
622 let this = Trusted::new(self);
623 let this_ = this.clone();
624 let task_source = self
625 .global()
626 .task_manager()
627 .dom_manipulation_task_source()
628 .to_sendable();
629 let task_source_clone = task_source.clone();
630 let callbacks = AudioDecoderCallbacksBuilder::default()
631 .ready(move |channel_count| {
632 decoded_audio
633 .lock()
634 .unwrap()
635 .resize(channel_count as usize, Vec::new());
636 })
637 .progress(move |buffer, channel_pos_mask| {
638 let mut decoded_audio = decoded_audio_.lock().unwrap();
639 let mut channels = channels.lock().unwrap();
640 let channel = match channels.entry(channel_pos_mask) {
641 Entry::Occupied(entry) => *entry.get(),
642 Entry::Vacant(entry) => {
643 let x = (channel_pos_mask as f32).log2() as usize;
644 *entry.insert(x)
645 },
646 };
647 decoded_audio[channel].extend_from_slice((*buffer).as_ref());
648 })
649 .eos(move || {
650 let mut decoded_audio = std::mem::take(&mut *decoded_audio__.lock().unwrap());
651 for channel in &mut decoded_audio {
652 channel.shrink_to_fit();
653 }
654 task_source.queue(task!(audio_decode_eos: move |cx| {
655 let this = this.root();
656 let length = if !decoded_audio.is_empty() {
657 decoded_audio[0].len()
658 } else {
659 0
660 };
661 let buffer = AudioBuffer::new_with_channel_data(
662 cx,
663 this.global().as_window(),
664 length as u32,
665 this.sample_rate,
666 decoded_audio,
667 );
668 rooted!(&in(cx) let resolver = this
669 .decode_resolvers
670 .safe_borrow_mut(cx.no_gc())
671 .remove(&uuid_)
672 .expect("resolver should exist"));
673 let promise = resolver.promise.root(cx);
674
675 if let Some(callback) = &resolver.success_callback {
676 let _ = callback.Call__(cx, &buffer, ExceptionHandling::Report);
677 }
678 promise.resolve_native(cx, &buffer);
679 }));
680 })
681 .error(move |error| {
682 task_source_clone.queue(task!(audio_decode_eos: move |cx| {
683 let this = this_.root();
684 rooted!(&in(cx) let resolver = this
685 .decode_resolvers
686 .safe_borrow_mut(cx.no_gc())
687 .remove(&uuid)
688 .expect("resolver should exist"));
689 let promise = resolver.promise.root(cx);
690
691 if let Some(callback) = &resolver.error_callback {
692 let exception = DOMException::new(
693 cx,
694 &this.global(),
695 DOMErrorName::DataCloneError,
696 );
697 let _ =
698 callback.Call__(cx, &exception, ExceptionHandling::Report);
699 }
700 let error = cformat!("Audio decode error {:?}", error);
701 promise.reject_error(cx, Error::Type(error));
702 }));
703 })
704 .build();
705 self.audio_context_impl
706 .lock()
707 .unwrap()
708 .decode_audio_data(audio_data, callbacks);
709 } else {
710 let exception = DOMException::new(cx, &self.global(), DOMErrorName::DataCloneError);
716 promise.reject_native(cx, &exception);
717
718 if let Some(callback) = decode_error_callback {
719 let uuid = Uuid::new_v4().simple().to_string();
723 let uuid_ = uuid.clone();
724 let this = Trusted::new(self);
725 let exception = Trusted::new(&*exception);
726 let task = task!(decode_audio_data_detached_buffer: move |cx| {
727 let this = this.root();
728 let exception = exception.root();
729 rooted!(&in(cx) let resolver = this
730 .decode_resolvers
731 .safe_borrow_mut(cx.no_gc())
732 .remove(&uuid)
733 .expect("resolver should exist"));
734
735 if let Some(callback) = &resolver.error_callback {
736 let _ = callback.Call__(
737 cx,
738 &exception,
739 ExceptionHandling::Report
740 );
741 }
742 });
743 self.decode_resolvers.safe_borrow_mut(cx.no_gc()).insert(
744 uuid_,
745 DecodeResolver {
746 promise: promise.to_traced(),
747 success_callback: None,
748 error_callback: Some(callback.to_traced()),
749 },
750 );
751 self.global()
752 .task_manager()
753 .media_element_task_source()
754 .queue(task);
755 }
756 }
757
758 promise
760 }
761
762 fn CreateIIRFilter(
764 &self,
765 cx: &mut JSContext,
766 feedforward: Vec<Finite<f64>>,
767 feedback: Vec<Finite<f64>>,
768 ) -> Fallible<DomRoot<IIRFilterNode>> {
769 let opts = IIRFilterOptions {
770 parent: AudioNodeOptions::empty(),
771 feedback,
772 feedforward,
773 };
774 IIRFilterNode::Constructor(cx, self.global().as_window(), None, self, &opts)
775 }
776}
777
778impl Convert<AudioContextOptions> for BaseAudioContextOptions {
779 fn convert(self) -> AudioContextOptions {
780 match self {
781 BaseAudioContextOptions::AudioContext(options) => {
782 AudioContextOptions::RealTimeAudioContext(options)
783 },
784 BaseAudioContextOptions::OfflineAudioContext(options) => {
785 AudioContextOptions::OfflineAudioContext(options)
786 },
787 }
788 }
789}
790
791impl Convert<AudioContextState> for ProcessingState {
792 fn convert(self) -> AudioContextState {
793 match self {
794 ProcessingState::Suspended => AudioContextState::Suspended,
795 ProcessingState::Running => AudioContextState::Running,
796 ProcessingState::Closed => AudioContextState::Closed,
797 }
798 }
799}