OfflineAudioContext

This commit is contained in:
Fernando Jiménez Moreno 2018-07-27 16:44:36 +02:00
parent e034159423
commit 6aaf5806b1
10 changed files with 424 additions and 36 deletions

85
Cargo.lock generated
View file

@ -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>"

View file

@ -12,6 +12,7 @@ checkbox
click
close
color
complete
controllerchange
cursive
date

View file

@ -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(

View file

@ -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) {

View file

@ -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 {

View file

@ -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;

View 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()
}
}

View 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,
}
}
}

View file

@ -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;
};

View 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);
};