mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01: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)",
|
"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]]
|
[[package]]
|
||||||
name = "gstreamer-sys"
|
name = "gstreamer-sys"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -1321,6 +1350,37 @@ dependencies = [
|
||||||
"pkg-config 0.3.12 (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-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]]
|
[[package]]
|
||||||
name = "gvr-sys"
|
name = "gvr-sys"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
@ -3076,38 +3136,48 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media"
|
name = "servo-media"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/media#9968654a5602adfcdc01d6a9ba9d54fd8b341eaf"
|
source = "git+https://github.com/servo/media#dadf87f24830712b75e1d909bad7500f9dae952c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"servo-media-audio 0.1.0 (git+https://github.com/servo/media)",
|
"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-gstreamer 0.1.0 (git+https://github.com/servo/media)",
|
||||||
|
"servo-media-player 0.1.0 (git+https://github.com/servo/media)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servo-media-audio"
|
name = "servo-media-audio"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/media#9968654a5602adfcdc01d6a9ba9d54fd8b341eaf"
|
source = "git+https://github.com/servo/media#dadf87f24830712b75e1d909bad7500f9dae952c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byte-slice-cast 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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]]
|
[[package]]
|
||||||
name = "servo-media-gstreamer"
|
name = "servo-media-gstreamer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/media#9968654a5602adfcdc01d6a9ba9d54fd8b341eaf"
|
source = "git+https://github.com/servo/media#dadf87f24830712b75e1d909bad7500f9dae952c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byte-slice-cast 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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 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-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-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)",
|
"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-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)",
|
"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]]
|
[[package]]
|
||||||
name = "servo-skia"
|
name = "servo-skia"
|
||||||
version = "0.30000017.0"
|
version = "0.30000017.0"
|
||||||
|
@ -3203,7 +3273,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servo_media_derive"
|
name = "servo_media_derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/media#9968654a5602adfcdc01d6a9ba9d54fd8b341eaf"
|
source = "git+https://github.com/servo/media#dadf87f24830712b75e1d909bad7500f9dae952c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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-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 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-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-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 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 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"
|
"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 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-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-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-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-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>"
|
"checksum servo_media_derive 0.1.0 (git+https://github.com/servo/media)" = "<none>"
|
||||||
|
|
|
@ -12,6 +12,7 @@ checkbox
|
||||||
click
|
click
|
||||||
close
|
close
|
||||||
color
|
color
|
||||||
|
complete
|
||||||
controllerchange
|
controllerchange
|
||||||
cursive
|
cursive
|
||||||
date
|
date
|
||||||
|
|
|
@ -19,6 +19,11 @@ use std::cmp::min;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr::{self, NonNull};
|
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>;
|
type JSAudioChannel = Heap<*mut JSObject>;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
|
@ -112,7 +117,9 @@ impl AudioBuffer {
|
||||||
window: &Window,
|
window: &Window,
|
||||||
options: &AudioBufferOptions,
|
options: &AudioBufferOptions,
|
||||||
) -> Fallible<DomRoot<AudioBuffer>> {
|
) -> 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);
|
return Err(Error::NotSupported);
|
||||||
}
|
}
|
||||||
Ok(AudioBuffer::new(
|
Ok(AudioBuffer::new(
|
||||||
|
|
|
@ -14,7 +14,6 @@ use dom::bindings::num::Finite;
|
||||||
use dom::bindings::refcounted::{Trusted, TrustedPromise};
|
use dom::bindings::refcounted::{Trusted, TrustedPromise};
|
||||||
use dom::bindings::reflector::{DomObject, reflect_dom_object};
|
use dom::bindings::reflector::{DomObject, reflect_dom_object};
|
||||||
use dom::bindings::root::DomRoot;
|
use dom::bindings::root::DomRoot;
|
||||||
use dom::globalscope::GlobalScope;
|
|
||||||
use dom::promise::Promise;
|
use dom::promise::Promise;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
@ -35,10 +34,9 @@ pub struct AudioContext {
|
||||||
impl AudioContext {
|
impl AudioContext {
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
// https://webaudio.github.io/web-audio-api/#AudioContext-constructors
|
// 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.
|
// Steps 1-3.
|
||||||
let context = BaseAudioContext::new_inherited(
|
let context = BaseAudioContext::new_inherited(
|
||||||
global,
|
|
||||||
BaseAudioContextOptions::AudioContext(options.into()),
|
BaseAudioContextOptions::AudioContext(options.into()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -61,9 +59,9 @@ impl AudioContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
pub fn new(global: &GlobalScope, options: &AudioContextOptions) -> DomRoot<AudioContext> {
|
pub fn new(window: &Window, options: &AudioContextOptions) -> DomRoot<AudioContext> {
|
||||||
let context = AudioContext::new_inherited(global, options);
|
let context = AudioContext::new_inherited(options);
|
||||||
let context = reflect_dom_object(Box::new(context), global, AudioContextBinding::Wrap);
|
let context = reflect_dom_object(Box::new(context), window, AudioContextBinding::Wrap);
|
||||||
context.resume();
|
context.resume();
|
||||||
context
|
context
|
||||||
}
|
}
|
||||||
|
@ -73,8 +71,7 @@ impl AudioContext {
|
||||||
window: &Window,
|
window: &Window,
|
||||||
options: &AudioContextOptions,
|
options: &AudioContextOptions,
|
||||||
) -> Fallible<DomRoot<AudioContext>> {
|
) -> Fallible<DomRoot<AudioContext>> {
|
||||||
let global = window.upcast::<GlobalScope>();
|
Ok(AudioContext::new(window, options))
|
||||||
Ok(AudioContext::new(global, options))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resume(&self) {
|
fn resume(&self) {
|
||||||
|
|
|
@ -26,7 +26,6 @@ use dom::bindings::root::{DomRoot, MutNullableDom};
|
||||||
use dom::domexception::{DOMErrorName, DOMException};
|
use dom::domexception::{DOMErrorName, DOMException};
|
||||||
use dom::eventtarget::EventTarget;
|
use dom::eventtarget::EventTarget;
|
||||||
use dom::gainnode::GainNode;
|
use dom::gainnode::GainNode;
|
||||||
use dom::globalscope::GlobalScope;
|
|
||||||
use dom::oscillatornode::OscillatorNode;
|
use dom::oscillatornode::OscillatorNode;
|
||||||
use dom::promise::Promise;
|
use dom::promise::Promise;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
|
@ -34,7 +33,7 @@ use dom_struct::dom_struct;
|
||||||
use js::rust::CustomAutoRooterGuard;
|
use js::rust::CustomAutoRooterGuard;
|
||||||
use js::typedarray::ArrayBuffer;
|
use js::typedarray::ArrayBuffer;
|
||||||
use servo_media::{Backend, ServoMedia};
|
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::context::{OfflineAudioContextOptions, RealTimeAudioContextOptions};
|
||||||
use servo_media::audio::decoder::AudioDecoderCallbacks;
|
use servo_media::audio::decoder::AudioDecoderCallbacks;
|
||||||
use servo_media::audio::graph::NodeId;
|
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
|
/// throw when trying to do things on the context when the context has just
|
||||||
/// been "closed()".
|
/// been "closed()".
|
||||||
state: Cell<AudioContextState>,
|
state: Cell<AudioContextState>,
|
||||||
|
channel_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseAudioContext {
|
impl BaseAudioContext {
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
pub fn new_inherited(_: &GlobalScope, options: BaseAudioContextOptions) -> BaseAudioContext {
|
pub fn new_inherited(options: BaseAudioContextOptions) -> BaseAudioContext {
|
||||||
let options = match options {
|
let (sample_rate, channel_count) = match options {
|
||||||
BaseAudioContextOptions::AudioContext(options) => options,
|
BaseAudioContextOptions::AudioContext(ref opt) => (opt.sample_rate, 2),
|
||||||
BaseAudioContextOptions::OfflineAudioContext(_) => unimplemented!(),
|
BaseAudioContextOptions::OfflineAudioContext(ref opt) => (opt.sample_rate, opt.channels),
|
||||||
};
|
};
|
||||||
|
|
||||||
let sample_rate = options.sample_rate;
|
|
||||||
|
|
||||||
let context = BaseAudioContext {
|
let context = BaseAudioContext {
|
||||||
eventtarget: EventTarget::new_inherited(),
|
eventtarget: EventTarget::new_inherited(),
|
||||||
audio_context_impl: Rc::new(
|
audio_context_impl: Rc::new(
|
||||||
|
@ -108,6 +106,7 @@ impl BaseAudioContext {
|
||||||
decode_resolvers: Default::default(),
|
decode_resolvers: Default::default(),
|
||||||
sample_rate,
|
sample_rate,
|
||||||
state: Cell::new(AudioContextState::Suspended),
|
state: Cell::new(AudioContextState::Suspended),
|
||||||
|
channel_count: channel_count.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
context
|
context
|
||||||
|
@ -206,19 +205,19 @@ impl BaseAudioContext {
|
||||||
self.take_pending_resume_promises(Ok(()));
|
self.take_pending_resume_promises(Ok(()));
|
||||||
let _ = task_source.queue(
|
let _ = task_source.queue(
|
||||||
task!(resume_success: move || {
|
task!(resume_success: move || {
|
||||||
let this = this.root();
|
let this = this.root();
|
||||||
this.fulfill_in_flight_resume_promises(|| {
|
this.fulfill_in_flight_resume_promises(|| {
|
||||||
if this.state.get() != AudioContextState::Running {
|
if this.state.get() != AudioContextState::Running {
|
||||||
this.state.set(AudioContextState::Running);
|
this.state.set(AudioContextState::Running);
|
||||||
let window = DomRoot::downcast::<Window>(this.global()).unwrap();
|
let window = DomRoot::downcast::<Window>(this.global()).unwrap();
|
||||||
window.dom_manipulation_task_source().queue_simple_event(
|
window.dom_manipulation_task_source().queue_simple_event(
|
||||||
this.upcast(),
|
this.upcast(),
|
||||||
atom!("statechange"),
|
atom!("statechange"),
|
||||||
&window
|
&window
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
window.upcast(),
|
window.upcast(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -291,7 +290,7 @@ impl BaseAudioContextMethods for BaseAudioContext {
|
||||||
let global = self.global();
|
let global = self.global();
|
||||||
self.destination.or_init(|| {
|
self.destination.or_init(|| {
|
||||||
let mut options = AudioNodeOptions::empty();
|
let mut options = AudioNodeOptions::empty();
|
||||||
options.channelCount = Some(2);
|
options.channelCount = Some(self.channel_count);
|
||||||
options.channelCountMode = Some(ChannelCountMode::Explicit);
|
options.channelCountMode = Some(ChannelCountMode::Explicit);
|
||||||
options.channelInterpretation = Some(ChannelInterpretation::Speakers);
|
options.channelInterpretation = Some(ChannelInterpretation::Speakers);
|
||||||
AudioDestinationNode::new(&global, self, &options)
|
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 {
|
impl From<ProcessingState> for AudioContextState {
|
||||||
fn from(state: ProcessingState) -> Self {
|
fn from(state: ProcessingState) -> Self {
|
||||||
match state {
|
match state {
|
||||||
|
|
|
@ -401,6 +401,8 @@ pub mod navigatorinfo;
|
||||||
pub mod node;
|
pub mod node;
|
||||||
pub mod nodeiterator;
|
pub mod nodeiterator;
|
||||||
pub mod nodelist;
|
pub mod nodelist;
|
||||||
|
pub mod offlineaudiocompletionevent;
|
||||||
|
pub mod offlineaudiocontext;
|
||||||
pub mod oscillatornode;
|
pub mod oscillatornode;
|
||||||
pub mod pagetransitionevent;
|
pub mod pagetransitionevent;
|
||||||
pub mod paintrenderingcontext2d;
|
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