aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/baseaudiocontext.rs
diff options
context:
space:
mode:
authorFernando Jiménez Moreno <ferjmoreno@gmail.com>2018-07-09 13:32:26 +0200
committerFernando Jiménez Moreno <ferjmoreno@gmail.com>2018-07-30 14:21:44 +0200
commitb87fc17b4b14898835014a26053fb93f13a47ec9 (patch)
tree93314f89bc68c14b5ed580b6d2edfa08e6b4f099 /components/script/dom/baseaudiocontext.rs
parente0e1141e74ec8729b68e3362eab753137543f40a (diff)
downloadservo-b87fc17b4b14898835014a26053fb93f13a47ec9.tar.gz
servo-b87fc17b4b14898835014a26053fb93f13a47ec9.zip
decodeAudioData
Diffstat (limited to 'components/script/dom/baseaudiocontext.rs')
-rw-r--r--components/script/dom/baseaudiocontext.rs108
1 files changed, 100 insertions, 8 deletions
diff --git a/components/script/dom/baseaudiocontext.rs b/components/script/dom/baseaudiocontext.rs
index 59247516659..7f65eb04cbd 100644
--- a/components/script/dom/baseaudiocontext.rs
+++ b/components/script/dom/baseaudiocontext.rs
@@ -6,12 +6,15 @@ use dom::audiobuffer::AudioBuffer;
use dom::audiobuffersourcenode::AudioBufferSourceNode;
use dom::audiodestinationnode::AudioDestinationNode;
use dom::audionode::MAX_CHANNEL_COUNT;
+use dom::bindings::callback::ExceptionHandling;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::AudioBufferSourceNodeBinding::AudioBufferSourceOptions;
use dom::bindings::codegen::Bindings::AudioNodeBinding::AudioNodeOptions;
use dom::bindings::codegen::Bindings::AudioNodeBinding::{ChannelCountMode, ChannelInterpretation};
-use dom::bindings::codegen::Bindings::BaseAudioContextBinding::BaseAudioContextMethods;
use dom::bindings::codegen::Bindings::BaseAudioContextBinding::AudioContextState;
+use dom::bindings::codegen::Bindings::BaseAudioContextBinding::BaseAudioContextMethods;
+use dom::bindings::codegen::Bindings::BaseAudioContextBinding::DecodeErrorCallback;
+use dom::bindings::codegen::Bindings::BaseAudioContextBinding::DecodeSuccessCallback;
use dom::bindings::codegen::Bindings::GainNodeBinding::GainOptions;
use dom::bindings::codegen::Bindings::OscillatorNodeBinding::OscillatorOptions;
use dom::bindings::error::{Error, ErrorResult, Fallible};
@@ -20,6 +23,7 @@ use dom::bindings::num::Finite;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::DomObject;
use dom::bindings::root::DomRoot;
+use dom::domexception::{DOMErrorName, DOMException};
use dom::eventtarget::EventTarget;
use dom::gainnode::GainNode;
use dom::globalscope::GlobalScope;
@@ -28,20 +32,33 @@ use dom::promise::Promise;
use dom::window::Window;
use dom_struct::dom_struct;
use servo_media::ServoMedia;
+use js::rust::CustomAutoRooterGuard;
+use js::typedarray::ArrayBuffer;
use servo_media::audio::context::{AudioContext, ProcessingState};
use servo_media::audio::context::{OfflineAudioContextOptions, RealTimeAudioContextOptions};
+use servo_media::audio::decoder::AudioDecoderCallbacks;
use servo_media::audio::graph::NodeId;
use std::cell::Cell;
-use std::collections::VecDeque;
+use std::collections::{HashMap, VecDeque};
use std::mem;
use std::rc::Rc;
+use std::sync::{Arc, Mutex};
use task_source::TaskSource;
+use uuid::Uuid;
pub enum BaseAudioContextOptions {
AudioContext(RealTimeAudioContextOptions),
OfflineAudioContext(OfflineAudioContextOptions),
}
+#[derive(JSTraceable)]
+#[allow(unrooted_must_root)]
+struct DecodeResolver {
+ pub promise: Rc<Promise>,
+ pub success_callback: Option<Rc<DecodeSuccessCallback>>,
+ pub error_callback: Option<Rc<DecodeErrorCallback>>,
+}
+
#[dom_struct]
pub struct BaseAudioContext {
eventtarget: EventTarget,
@@ -55,6 +72,8 @@ pub struct BaseAudioContext {
/// https://webaudio.github.io/web-audio-api/#pendingresumepromises
#[ignore_malloc_size_of = "promises are hard"]
pending_resume_promises: DomRefCell<Vec<Rc<Promise>>>,
+ #[ignore_malloc_size_of = "promises are hard"]
+ decode_resolvers: DomRefCell<HashMap<String, DecodeResolver>>,
/// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-samplerate
sample_rate: f32,
/// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-state
@@ -85,6 +104,7 @@ impl BaseAudioContext {
destination: None,
in_flight_resume_promises_queue: Default::default(),
pending_resume_promises: Default::default(),
+ decode_resolvers: Default::default(),
sample_rate,
state: Cell::new(AudioContextState::Suspended),
};
@@ -290,13 +310,13 @@ impl BaseAudioContextMethods for BaseAudioContext {
length: u32,
sample_rate: Finite<f32>) -> Fallible<DomRoot<AudioBuffer>> {
if number_of_channels <= 0 ||
- number_of_channels > MAX_CHANNEL_COUNT ||
- length <= 0 ||
- *sample_rate <= 0. {
- return Err(Error::NotSupported);
- }
+ number_of_channels > MAX_CHANNEL_COUNT ||
+ length <= 0 ||
+ *sample_rate <= 0. {
+ return Err(Error::NotSupported);
+ }
let global = self.global();
- Ok(AudioBuffer::new(&global.as_window(), number_of_channels, length, *sample_rate))
+ Ok(AudioBuffer::new(&global.as_window(), number_of_channels, length, *sample_rate, None))
}
#[allow(unsafe_code)]
@@ -306,6 +326,78 @@ impl BaseAudioContextMethods for BaseAudioContext {
let options = unsafe { AudioBufferSourceOptions::empty(global.get_cx()) };
AudioBufferSourceNode::new(&global.as_window(), &self, &options)
}
+
+ #[allow(unrooted_must_root)]
+ fn DecodeAudioData(&self,
+ audio_data: CustomAutoRooterGuard<ArrayBuffer>,
+ decode_success_callback: Option<Rc<DecodeSuccessCallback>>,
+ decode_error_callback: Option<Rc<DecodeErrorCallback>>)
+ -> Rc<Promise> {
+ // Step 1.
+ let promise = Promise::new(&self.global());
+
+ if audio_data.len() > 0 {
+ // Step 2.
+ // XXX detach array buffer.
+ let uuid = Uuid::new_v4().simple().to_string();
+ let uuid_ = uuid.clone();
+ self.decode_resolvers.borrow_mut().insert(uuid.clone(), DecodeResolver {
+ promise: promise.clone(),
+ success_callback: decode_success_callback,
+ error_callback: decode_error_callback,
+ });
+ let audio_data = audio_data.to_vec();
+ let decoded_audio = Arc::new(Mutex::new(Vec::new()));
+ let decoded_audio_ = decoded_audio.clone();
+ let this = Trusted::new(self);
+ let this_ = this.clone();
+ let callbacks = AudioDecoderCallbacks::new()
+ .eos(move || {
+ let this = this_.root();
+ let decoded_audio = decoded_audio.lock().unwrap();
+ let buffer = AudioBuffer::new(
+ &this.global().as_window(),
+ 1, // XXX servo-media should provide this info
+ decoded_audio.len() as u32,
+ this.sample_rate,
+ Some(decoded_audio.as_slice()));
+ let mut resolvers = this.decode_resolvers.borrow_mut();
+ assert!(resolvers.contains_key(&uuid_));
+ let resolver = resolvers.remove(&uuid_).unwrap();
+ if let Some(callback) = resolver.success_callback {
+ let _ = callback.Call__(&buffer, ExceptionHandling::Report);
+ }
+ resolver.promise.resolve_native(&buffer);
+ })
+ .error(move || {
+ let this = this.root();
+ let mut resolvers = this.decode_resolvers.borrow_mut();
+ assert!(resolvers.contains_key(&uuid));
+ let resolver = resolvers.remove(&uuid).unwrap();
+ if let Some(callback) = resolver.error_callback {
+ let _ = callback.Call__(
+ &DOMException::new(&this.global(), DOMErrorName::DataCloneError),
+ ExceptionHandling::Report);
+ }
+ resolver.promise.reject_error(Error::Type("Audio decode error".to_owned()));
+ })
+ .progress(move |buffer| {
+ decoded_audio_
+ .lock()
+ .unwrap()
+ .extend_from_slice((*buffer).as_ref());
+ })
+ .build();
+ self.audio_context_impl.decode_audio_data(audio_data, callbacks);
+ } else {
+ // Step 3.
+ promise.reject_error(Error::DataClone);
+ return promise;
+ }
+
+ // Step 4.
+ promise
+ }
}
impl From<ProcessingState> for AudioContextState {