mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
OfflineAudioContext
This commit is contained in:
parent
e034159423
commit
6aaf5806b1
10 changed files with 424 additions and 36 deletions
85
Cargo.lock
generated
85
Cargo.lock
generated
|
@ -1309,6 +1309,35 @@ dependencies = [
|
|||
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-player"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-player-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-video 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-player-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-video-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-sys"
|
||||
version = "0.5.0"
|
||||
|
@ -1321,6 +1350,37 @@ dependencies = [
|
|||
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-video"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-base 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-video-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gstreamer-video-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gvr-sys"
|
||||
version = "0.7.0"
|
||||
|
@ -3076,38 +3136,48 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "servo-media"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/servo/media#9968654a5602adfcdc01d6a9ba9d54fd8b341eaf"
|
||||
source = "git+https://github.com/servo/media#dadf87f24830712b75e1d909bad7500f9dae952c"
|
||||
dependencies = [
|
||||
"servo-media-audio 0.1.0 (git+https://github.com/servo/media)",
|
||||
"servo-media-gstreamer 0.1.0 (git+https://github.com/servo/media)",
|
||||
"servo-media-player 0.1.0 (git+https://github.com/servo/media)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo-media-audio"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/servo/media#9968654a5602adfcdc01d6a9ba9d54fd8b341eaf"
|
||||
source = "git+https://github.com/servo/media#dadf87f24830712b75e1d909bad7500f9dae952c"
|
||||
dependencies = [
|
||||
"byte-slice-cast 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"petgraph 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo_media_derive 0.1.0 (git+https://github.com/servo/media)",
|
||||
"smallvec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo-media-gstreamer"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/servo/media#9968654a5602adfcdc01d6a9ba9d54fd8b341eaf"
|
||||
source = "git+https://github.com/servo/media#dadf87f24830712b75e1d909bad7500f9dae952c"
|
||||
dependencies = [
|
||||
"byte-slice-cast 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-app 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-audio 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gstreamer-player 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-media-audio 0.1.0 (git+https://github.com/servo/media)",
|
||||
"servo-media-player 0.1.0 (git+https://github.com/servo/media)",
|
||||
"zip 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo-media-player"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/servo/media#dadf87f24830712b75e1d909bad7500f9dae952c"
|
||||
|
||||
[[package]]
|
||||
name = "servo-skia"
|
||||
version = "0.30000017.0"
|
||||
|
@ -3203,7 +3273,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "servo_media_derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/servo/media#9968654a5602adfcdc01d6a9ba9d54fd8b341eaf"
|
||||
source = "git+https://github.com/servo/media#dadf87f24830712b75e1d909bad7500f9dae952c"
|
||||
dependencies = [
|
||||
"quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -4213,7 +4283,11 @@ dependencies = [
|
|||
"checksum gstreamer-audio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd60631f2dd055f0aae2831e86bd6c1d45e24528d4c478002cc07490dd84b56e"
|
||||
"checksum gstreamer-base 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05ec7a84b4160b61c72ea27ccf3f46eb9c8f996c5991746623e69e3e532e3cb5"
|
||||
"checksum gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "501a7add44f256aab6cb5b65ef121c449197cf55087d6a7586846c8d1e42e88b"
|
||||
"checksum gstreamer-player 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "63e81d78e9e7ee5d448ba50c52722005bbbf1bfe606767c4c407f45e8996f050"
|
||||
"checksum gstreamer-player-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b9476078cc76164446e88b2c4331e81e24a07f7b7c3a8b4bf8975a47998ebd4"
|
||||
"checksum gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b2f51e25a6f97dd4bfd640cba96f192f8759b8766afd66d6d9ea0f82ca14a37"
|
||||
"checksum gstreamer-video 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75300cf1ed8d8d65811349fc755fac22be05ea55df551ab29e43664d4a575c92"
|
||||
"checksum gstreamer-video-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ed798787e78a0f1c8be06bd3adcab03f962f049a820743aae9f690f56a0d538"
|
||||
"checksum gvr-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1334b94d8ce67319ddc44663daef53d8c1538629a11562530c981dbd9085b9a"
|
||||
"checksum half 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63d68db75012a85555434ee079e7e6337931f87a087ab2988becbadf64673a7f"
|
||||
"checksum harfbuzz-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bad27f59fc853bb88eca58088d469cd365ff0ccf362d24c4b1c0cbb1b21b3489"
|
||||
|
@ -4356,6 +4430,7 @@ dependencies = [
|
|||
"checksum servo-media 0.1.0 (git+https://github.com/servo/media)" = "<none>"
|
||||
"checksum servo-media-audio 0.1.0 (git+https://github.com/servo/media)" = "<none>"
|
||||
"checksum servo-media-gstreamer 0.1.0 (git+https://github.com/servo/media)" = "<none>"
|
||||
"checksum servo-media-player 0.1.0 (git+https://github.com/servo/media)" = "<none>"
|
||||
"checksum servo-skia 0.30000017.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fe33a2592a74f2096daf70bcbee39f2d183efcdc81f536a28af3a357baa6f5b0"
|
||||
"checksum servo-websocket 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bac1e2295e72f0525147d993c626761811acf0441dac1cee8707f12dc7f3363"
|
||||
"checksum servo_media_derive 0.1.0 (git+https://github.com/servo/media)" = "<none>"
|
||||
|
|
|
@ -12,6 +12,7 @@ checkbox
|
|||
click
|
||||
close
|
||||
color
|
||||
complete
|
||||
controllerchange
|
||||
cursive
|
||||
date
|
||||
|
|
|
@ -19,6 +19,11 @@ use std::cmp::min;
|
|||
use std::mem;
|
||||
use std::ptr::{self, NonNull};
|
||||
|
||||
// This range is defined by the spec.
|
||||
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createbuffer
|
||||
pub const MIN_SAMPLE_RATE: f32 = 8000.;
|
||||
pub const MAX_SAMPLE_RATE: f32 = 96000.;
|
||||
|
||||
type JSAudioChannel = Heap<*mut JSObject>;
|
||||
|
||||
#[dom_struct]
|
||||
|
@ -112,7 +117,9 @@ impl AudioBuffer {
|
|||
window: &Window,
|
||||
options: &AudioBufferOptions,
|
||||
) -> Fallible<DomRoot<AudioBuffer>> {
|
||||
if options.numberOfChannels > MAX_CHANNEL_COUNT {
|
||||
if options.numberOfChannels > MAX_CHANNEL_COUNT ||
|
||||
*options.sampleRate < MIN_SAMPLE_RATE ||
|
||||
*options.sampleRate > MAX_SAMPLE_RATE {
|
||||
return Err(Error::NotSupported);
|
||||
}
|
||||
Ok(AudioBuffer::new(
|
||||
|
|
|
@ -14,7 +14,6 @@ use dom::bindings::num::Finite;
|
|||
use dom::bindings::refcounted::{Trusted, TrustedPromise};
|
||||
use dom::bindings::reflector::{DomObject, reflect_dom_object};
|
||||
use dom::bindings::root::DomRoot;
|
||||
use dom::globalscope::GlobalScope;
|
||||
use dom::promise::Promise;
|
||||
use dom::window::Window;
|
||||
use dom_struct::dom_struct;
|
||||
|
@ -35,10 +34,9 @@ pub struct AudioContext {
|
|||
impl AudioContext {
|
||||
#[allow(unrooted_must_root)]
|
||||
// https://webaudio.github.io/web-audio-api/#AudioContext-constructors
|
||||
fn new_inherited(global: &GlobalScope, options: &AudioContextOptions) -> AudioContext {
|
||||
fn new_inherited(options: &AudioContextOptions) -> AudioContext {
|
||||
// Steps 1-3.
|
||||
let context = BaseAudioContext::new_inherited(
|
||||
global,
|
||||
BaseAudioContextOptions::AudioContext(options.into()),
|
||||
);
|
||||
|
||||
|
@ -61,9 +59,9 @@ impl AudioContext {
|
|||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn new(global: &GlobalScope, options: &AudioContextOptions) -> DomRoot<AudioContext> {
|
||||
let context = AudioContext::new_inherited(global, options);
|
||||
let context = reflect_dom_object(Box::new(context), global, AudioContextBinding::Wrap);
|
||||
pub fn new(window: &Window, options: &AudioContextOptions) -> DomRoot<AudioContext> {
|
||||
let context = AudioContext::new_inherited(options);
|
||||
let context = reflect_dom_object(Box::new(context), window, AudioContextBinding::Wrap);
|
||||
context.resume();
|
||||
context
|
||||
}
|
||||
|
@ -73,8 +71,7 @@ impl AudioContext {
|
|||
window: &Window,
|
||||
options: &AudioContextOptions,
|
||||
) -> Fallible<DomRoot<AudioContext>> {
|
||||
let global = window.upcast::<GlobalScope>();
|
||||
Ok(AudioContext::new(global, options))
|
||||
Ok(AudioContext::new(window, options))
|
||||
}
|
||||
|
||||
fn resume(&self) {
|
||||
|
|
|
@ -26,7 +26,6 @@ use dom::bindings::root::{DomRoot, MutNullableDom};
|
|||
use dom::domexception::{DOMErrorName, DOMException};
|
||||
use dom::eventtarget::EventTarget;
|
||||
use dom::gainnode::GainNode;
|
||||
use dom::globalscope::GlobalScope;
|
||||
use dom::oscillatornode::OscillatorNode;
|
||||
use dom::promise::Promise;
|
||||
use dom::window::Window;
|
||||
|
@ -34,7 +33,7 @@ use dom_struct::dom_struct;
|
|||
use js::rust::CustomAutoRooterGuard;
|
||||
use js::typedarray::ArrayBuffer;
|
||||
use servo_media::{Backend, ServoMedia};
|
||||
use servo_media::audio::context::{AudioContext, ProcessingState};
|
||||
use servo_media::audio::context::{AudioContext, AudioContextOptions, ProcessingState};
|
||||
use servo_media::audio::context::{OfflineAudioContextOptions, RealTimeAudioContextOptions};
|
||||
use servo_media::audio::decoder::AudioDecoderCallbacks;
|
||||
use servo_media::audio::graph::NodeId;
|
||||
|
@ -83,18 +82,17 @@ pub struct BaseAudioContext {
|
|||
/// throw when trying to do things on the context when the context has just
|
||||
/// been "closed()".
|
||||
state: Cell<AudioContextState>,
|
||||
channel_count: u32,
|
||||
}
|
||||
|
||||
impl BaseAudioContext {
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn new_inherited(_: &GlobalScope, options: BaseAudioContextOptions) -> BaseAudioContext {
|
||||
let options = match options {
|
||||
BaseAudioContextOptions::AudioContext(options) => options,
|
||||
BaseAudioContextOptions::OfflineAudioContext(_) => unimplemented!(),
|
||||
pub fn new_inherited(options: BaseAudioContextOptions) -> BaseAudioContext {
|
||||
let (sample_rate, channel_count) = match options {
|
||||
BaseAudioContextOptions::AudioContext(ref opt) => (opt.sample_rate, 2),
|
||||
BaseAudioContextOptions::OfflineAudioContext(ref opt) => (opt.sample_rate, opt.channels),
|
||||
};
|
||||
|
||||
let sample_rate = options.sample_rate;
|
||||
|
||||
let context = BaseAudioContext {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
audio_context_impl: Rc::new(
|
||||
|
@ -108,6 +106,7 @@ impl BaseAudioContext {
|
|||
decode_resolvers: Default::default(),
|
||||
sample_rate,
|
||||
state: Cell::new(AudioContextState::Suspended),
|
||||
channel_count: channel_count.into(),
|
||||
};
|
||||
|
||||
context
|
||||
|
@ -206,19 +205,19 @@ impl BaseAudioContext {
|
|||
self.take_pending_resume_promises(Ok(()));
|
||||
let _ = task_source.queue(
|
||||
task!(resume_success: move || {
|
||||
let this = this.root();
|
||||
this.fulfill_in_flight_resume_promises(|| {
|
||||
if this.state.get() != AudioContextState::Running {
|
||||
this.state.set(AudioContextState::Running);
|
||||
let window = DomRoot::downcast::<Window>(this.global()).unwrap();
|
||||
window.dom_manipulation_task_source().queue_simple_event(
|
||||
this.upcast(),
|
||||
atom!("statechange"),
|
||||
&window
|
||||
);
|
||||
}
|
||||
});
|
||||
}),
|
||||
let this = this.root();
|
||||
this.fulfill_in_flight_resume_promises(|| {
|
||||
if this.state.get() != AudioContextState::Running {
|
||||
this.state.set(AudioContextState::Running);
|
||||
let window = DomRoot::downcast::<Window>(this.global()).unwrap();
|
||||
window.dom_manipulation_task_source().queue_simple_event(
|
||||
this.upcast(),
|
||||
atom!("statechange"),
|
||||
&window
|
||||
);
|
||||
}
|
||||
});
|
||||
}),
|
||||
window.upcast(),
|
||||
);
|
||||
},
|
||||
|
@ -291,7 +290,7 @@ impl BaseAudioContextMethods for BaseAudioContext {
|
|||
let global = self.global();
|
||||
self.destination.or_init(|| {
|
||||
let mut options = AudioNodeOptions::empty();
|
||||
options.channelCount = Some(2);
|
||||
options.channelCount = Some(self.channel_count);
|
||||
options.channelCountMode = Some(ChannelCountMode::Explicit);
|
||||
options.channelInterpretation = Some(ChannelInterpretation::Speakers);
|
||||
AudioDestinationNode::new(&global, self, &options)
|
||||
|
@ -442,6 +441,17 @@ impl BaseAudioContextMethods for BaseAudioContext {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<BaseAudioContextOptions> for AudioContextOptions {
|
||||
fn from(options: BaseAudioContextOptions) -> Self {
|
||||
match options {
|
||||
BaseAudioContextOptions::AudioContext(options) =>
|
||||
AudioContextOptions::RealTimeAudioContext(options),
|
||||
BaseAudioContextOptions::OfflineAudioContext(options) =>
|
||||
AudioContextOptions::OfflineAudioContext(options),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProcessingState> for AudioContextState {
|
||||
fn from(state: ProcessingState) -> Self {
|
||||
match state {
|
||||
|
|
|
@ -401,6 +401,8 @@ pub mod navigatorinfo;
|
|||
pub mod node;
|
||||
pub mod nodeiterator;
|
||||
pub mod nodelist;
|
||||
pub mod offlineaudiocompletionevent;
|
||||
pub mod offlineaudiocontext;
|
||||
pub mod oscillatornode;
|
||||
pub mod pagetransitionevent;
|
||||
pub mod paintrenderingcontext2d;
|
||||
|
|
77
components/script/dom/offlineaudiocompletionevent.rs
Normal file
77
components/script/dom/offlineaudiocompletionevent.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::audiobuffer::AudioBuffer;
|
||||
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
|
||||
use dom::bindings::codegen::Bindings::OfflineAudioCompletionEventBinding;
|
||||
use dom::bindings::codegen::Bindings::OfflineAudioCompletionEventBinding::OfflineAudioCompletionEventInit;
|
||||
use dom::bindings::codegen::Bindings::OfflineAudioCompletionEventBinding::OfflineAudioCompletionEventMethods;
|
||||
use dom::bindings::error::Fallible;
|
||||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::reflector::reflect_dom_object;
|
||||
use dom::bindings::root::{Dom, DomRoot, RootedReference};
|
||||
use dom::bindings::str::DOMString;
|
||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use dom::window::Window;
|
||||
use dom_struct::dom_struct;
|
||||
use servo_atoms::Atom;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct OfflineAudioCompletionEvent {
|
||||
event: Event,
|
||||
rendered_buffer: Dom<AudioBuffer>,
|
||||
}
|
||||
|
||||
impl OfflineAudioCompletionEvent {
|
||||
pub fn new_inherited(rendered_buffer: &AudioBuffer) -> OfflineAudioCompletionEvent {
|
||||
OfflineAudioCompletionEvent {
|
||||
event: Event::new_inherited(),
|
||||
rendered_buffer: Dom::from_ref(rendered_buffer),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
window: &Window,
|
||||
type_: Atom,
|
||||
bubbles: EventBubbles,
|
||||
cancelable: EventCancelable,
|
||||
rendered_buffer: &AudioBuffer,
|
||||
) -> DomRoot<OfflineAudioCompletionEvent> {
|
||||
let event = Box::new(OfflineAudioCompletionEvent::new_inherited(rendered_buffer));
|
||||
let ev = reflect_dom_object(event, window, OfflineAudioCompletionEventBinding::Wrap);
|
||||
{
|
||||
let event = ev.upcast::<Event>();
|
||||
event.init_event(type_, bool::from(bubbles), bool::from(cancelable));
|
||||
}
|
||||
ev
|
||||
}
|
||||
|
||||
pub fn Constructor(
|
||||
window: &Window,
|
||||
type_: DOMString,
|
||||
init: &OfflineAudioCompletionEventInit,
|
||||
) -> Fallible<DomRoot<OfflineAudioCompletionEvent>> {
|
||||
let bubbles = EventBubbles::from(init.parent.bubbles);
|
||||
let cancelable = EventCancelable::from(init.parent.cancelable);
|
||||
Ok(OfflineAudioCompletionEvent::new(
|
||||
window,
|
||||
Atom::from(type_),
|
||||
bubbles,
|
||||
cancelable,
|
||||
init.renderedBuffer.r(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl OfflineAudioCompletionEventMethods for OfflineAudioCompletionEvent {
|
||||
// https://webaudio.github.io/web-audio-api/#dom-offlineaudiocompletionevent-renderedbuffer
|
||||
fn RenderedBuffer(&self) -> DomRoot<AudioBuffer> {
|
||||
DomRoot::from_ref(&*self.rendered_buffer)
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-event-istrusted
|
||||
fn IsTrusted(&self) -> bool {
|
||||
self.event.IsTrusted()
|
||||
}
|
||||
}
|
178
components/script/dom/offlineaudiocontext.rs
Normal file
178
components/script/dom/offlineaudiocontext.rs
Normal file
|
@ -0,0 +1,178 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::audiobuffer::{AudioBuffer, MAX_SAMPLE_RATE, MIN_SAMPLE_RATE};
|
||||
use dom::audionode::MAX_CHANNEL_COUNT;
|
||||
use dom::baseaudiocontext::{BaseAudioContext, BaseAudioContextOptions};
|
||||
use dom::bindings::cell::DomRefCell;
|
||||
use dom::bindings::codegen::Bindings::BaseAudioContextBinding::BaseAudioContextBinding::BaseAudioContextMethods;
|
||||
use dom::bindings::codegen::Bindings::OfflineAudioContextBinding;
|
||||
use dom::bindings::codegen::Bindings::OfflineAudioContextBinding::OfflineAudioContextMethods;
|
||||
use dom::bindings::codegen::Bindings::OfflineAudioContextBinding::OfflineAudioContextOptions;
|
||||
use dom::bindings::error::{Error, Fallible};
|
||||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::num::Finite;
|
||||
use dom::bindings::refcounted::Trusted;
|
||||
use dom::bindings::reflector::{DomObject, reflect_dom_object};
|
||||
use dom::bindings::root::DomRoot;
|
||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use dom::offlineaudiocompletionevent::OfflineAudioCompletionEvent;
|
||||
use dom::promise::Promise;
|
||||
use dom::window::Window;
|
||||
use dom_struct::dom_struct;
|
||||
use servo_media::audio::context::OfflineAudioContextOptions as ServoMediaOfflineAudioContextOptions;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::mpsc;
|
||||
use std::thread::Builder;
|
||||
use task_source::{TaskSource, TaskSourceName};
|
||||
|
||||
#[dom_struct]
|
||||
pub struct OfflineAudioContext {
|
||||
context: BaseAudioContext,
|
||||
channel_count: u32,
|
||||
length: u32,
|
||||
rendering_started: Cell<bool>,
|
||||
#[ignore_malloc_size_of = "promises are hard"]
|
||||
pending_rendering_promise: DomRefCell<Option<Rc<Promise>>>,
|
||||
}
|
||||
|
||||
impl OfflineAudioContext {
|
||||
#[allow(unrooted_must_root)]
|
||||
fn new_inherited(options: &OfflineAudioContextOptions) -> OfflineAudioContext {
|
||||
let context = BaseAudioContext::new_inherited(
|
||||
BaseAudioContextOptions::OfflineAudioContext(options.into()),
|
||||
);
|
||||
OfflineAudioContext {
|
||||
context,
|
||||
channel_count: options.numberOfChannels,
|
||||
length: options.length,
|
||||
rendering_started: Cell::new(false),
|
||||
pending_rendering_promise: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
fn new(window: &Window, options: &OfflineAudioContextOptions) -> DomRoot<OfflineAudioContext> {
|
||||
let context = OfflineAudioContext::new_inherited(options);
|
||||
reflect_dom_object(Box::new(context), window, OfflineAudioContextBinding::Wrap)
|
||||
}
|
||||
|
||||
pub fn Constructor(
|
||||
window: &Window,
|
||||
options: &OfflineAudioContextOptions,
|
||||
) -> Fallible<DomRoot<OfflineAudioContext>> {
|
||||
Ok(OfflineAudioContext::new(window, options))
|
||||
}
|
||||
|
||||
pub fn Constructor_(
|
||||
window: &Window,
|
||||
number_of_channels: u32,
|
||||
length: u32,
|
||||
sample_rate: Finite<f32>,
|
||||
) -> Fallible<DomRoot<OfflineAudioContext>> {
|
||||
if number_of_channels > MAX_CHANNEL_COUNT ||
|
||||
number_of_channels <= 0 ||
|
||||
length <= 0 ||
|
||||
*sample_rate < MIN_SAMPLE_RATE ||
|
||||
*sample_rate > MAX_SAMPLE_RATE
|
||||
{
|
||||
return Err(Error::NotSupported);
|
||||
}
|
||||
|
||||
let mut options = OfflineAudioContextOptions::empty();
|
||||
options.numberOfChannels = number_of_channels;
|
||||
options.length = length;
|
||||
options.sampleRate = sample_rate;
|
||||
Ok(OfflineAudioContext::new(window, &options))
|
||||
}
|
||||
}
|
||||
|
||||
impl OfflineAudioContextMethods for OfflineAudioContext {
|
||||
// https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-oncomplete
|
||||
event_handler!(complete, GetOncomplete, SetOncomplete);
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-length
|
||||
fn Length(&self) -> u32 {
|
||||
self.length
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-startrendering
|
||||
#[allow(unrooted_must_root)]
|
||||
fn StartRendering(&self) -> Rc<Promise> {
|
||||
let promise = Promise::new(&self.global());
|
||||
if self.rendering_started.get() {
|
||||
promise.reject_error(Error::InvalidState);
|
||||
return promise;
|
||||
}
|
||||
self.rendering_started.set(true);
|
||||
|
||||
*self.pending_rendering_promise.borrow_mut() = Some(promise.clone());
|
||||
|
||||
let processed_audio = Arc::new(Mutex::new(Vec::new()));
|
||||
let processed_audio_ = processed_audio.clone();
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let sender = Mutex::new(sender);
|
||||
self.context
|
||||
.audio_context_impl()
|
||||
.set_eos_callback(Box::new(move |buffer| {
|
||||
processed_audio_
|
||||
.lock()
|
||||
.unwrap()
|
||||
.extend_from_slice((*buffer).as_ref());
|
||||
let _ = sender.lock().unwrap().send(());
|
||||
}));
|
||||
|
||||
let this = Trusted::new(self);
|
||||
let global = self.global();
|
||||
let window = global.as_window();
|
||||
let task_source = window.dom_manipulation_task_source();
|
||||
let canceller = window.task_canceller(TaskSourceName::DOMManipulation);
|
||||
Builder::new()
|
||||
.name("OfflineAudioContextResolver".to_owned())
|
||||
.spawn(move || {
|
||||
let _ = receiver.recv();
|
||||
let _ = task_source.queue_with_canceller(
|
||||
task!(resolve: move || {
|
||||
let this = this.root();
|
||||
let processed_audio = processed_audio.lock().unwrap();
|
||||
let buffer = AudioBuffer::new(
|
||||
&this.global().as_window(),
|
||||
this.channel_count,
|
||||
this.length,
|
||||
*this.context.SampleRate(),
|
||||
Some(processed_audio.as_slice()));
|
||||
(*this.pending_rendering_promise.borrow_mut()).take().unwrap().resolve_native(&buffer);
|
||||
let global = &this.global();
|
||||
let window = global.as_window();
|
||||
let event = OfflineAudioCompletionEvent::new(&window,
|
||||
atom!("complete"),
|
||||
EventBubbles::DoesNotBubble,
|
||||
EventCancelable::NotCancelable,
|
||||
&buffer);
|
||||
event.upcast::<Event>().fire(this.upcast());
|
||||
}),
|
||||
&canceller,
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if self.context.audio_context_impl().resume().is_err() {
|
||||
promise.reject_error(Error::Type("Could not start offline rendering".to_owned()));
|
||||
}
|
||||
|
||||
promise
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a OfflineAudioContextOptions> for ServoMediaOfflineAudioContextOptions {
|
||||
fn from(options: &OfflineAudioContextOptions) -> Self {
|
||||
Self {
|
||||
channels: options.numberOfChannels as u8,
|
||||
length: options.length as usize,
|
||||
sample_rate: *options.sampleRate,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/*
|
||||
* For more information on this interface please see
|
||||
* https://webaudio.github.io/web-audio-api/#offlineaudiocompletionevent
|
||||
*/
|
||||
|
||||
dictionary OfflineAudioCompletionEventInit : EventInit {
|
||||
required AudioBuffer renderedBuffer;
|
||||
};
|
||||
|
||||
[Exposed=Window,
|
||||
Constructor(DOMString type, OfflineAudioCompletionEventInit eventInitDict)]
|
||||
interface OfflineAudioCompletionEvent : Event {
|
||||
readonly attribute AudioBuffer renderedBuffer;
|
||||
};
|
24
components/script/dom/webidls/OfflineAudioContext.webidl
Normal file
24
components/script/dom/webidls/OfflineAudioContext.webidl
Normal file
|
@ -0,0 +1,24 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/*
|
||||
* The origin of this IDL file is
|
||||
* https://webaudio.github.io/web-audio-api/#OfflineAudioContext
|
||||
*/
|
||||
|
||||
dictionary OfflineAudioContextOptions {
|
||||
unsigned long numberOfChannels = 1;
|
||||
unsigned long length = 0;
|
||||
float sampleRate = 48000.;
|
||||
};
|
||||
|
||||
[Exposed=Window,
|
||||
Constructor (optional OfflineAudioContextOptions contextOptions),
|
||||
Constructor (unsigned long numberOfChannels, unsigned long length, float sampleRate)]
|
||||
interface OfflineAudioContext : BaseAudioContext {
|
||||
readonly attribute unsigned long length;
|
||||
attribute EventHandler oncomplete;
|
||||
|
||||
Promise<AudioBuffer> startRendering();
|
||||
// Promise<void> suspend(double suspendTime);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue