mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Create AudioBuffer from BaseAudioContext
This commit is contained in:
parent
cb16c596b3
commit
25a74a75ea
5 changed files with 115 additions and 80 deletions
|
@ -11,19 +11,21 @@ use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
|
||||||
use dom::bindings::root::DomRoot;
|
use dom::bindings::root::DomRoot;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use js::conversions::ToJSValConvertible;
|
|
||||||
use js::jsapi::{Heap, JSContext, JSObject, JS_StealArrayBufferContents};
|
use js::jsapi::{Heap, JSContext, JSObject, JS_StealArrayBufferContents};
|
||||||
|
use js::rust::CustomAutoRooterGuard;
|
||||||
use js::typedarray::{CreateWith, Float32Array};
|
use js::typedarray::{CreateWith, Float32Array};
|
||||||
use std::ptr::{self, NonNull};
|
use std::ptr::{self, NonNull};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
type JSAudioChannel = Heap<*mut JSObject>;
|
type JSAudioChannel = Heap<*mut JSObject>;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct AudioBuffer {
|
pub struct AudioBuffer {
|
||||||
reflector_: Reflector,
|
reflector_: Reflector,
|
||||||
js_channels: Vec<JSAudioChannel>,
|
js_channels: DomRefCell<Vec<JSAudioChannel>>,
|
||||||
shared_channels: DomRefCell<Option<Vec<Vec<f32>>>>,
|
#[ignore_malloc_size_of = "Arc"]
|
||||||
|
shared_channels: Arc<Mutex<Vec<Vec<f32>>>>,
|
||||||
sample_rate: f32,
|
sample_rate: f32,
|
||||||
length: u32,
|
length: u32,
|
||||||
duration: f64,
|
duration: f64,
|
||||||
|
@ -33,22 +35,38 @@ pub struct AudioBuffer {
|
||||||
impl AudioBuffer {
|
impl AudioBuffer {
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub fn new_inherited(options: &AudioBufferOptions) -> AudioBuffer {
|
pub fn new_inherited(cx: *mut JSContext,
|
||||||
|
number_of_channels: u32,
|
||||||
|
length: u32,
|
||||||
|
sample_rate: f32) -> AudioBuffer {
|
||||||
|
let initial_data = vec![0.; length as usize];
|
||||||
|
let mut js_channels: Vec<JSAudioChannel> = Vec::with_capacity(number_of_channels as usize);
|
||||||
|
for _ in 0..number_of_channels {
|
||||||
|
rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>());
|
||||||
|
let _ = unsafe {
|
||||||
|
Float32Array::create(cx, CreateWith::Slice(initial_data.as_slice()), array.handle_mut())
|
||||||
|
};
|
||||||
|
let js_channel = Heap::default();
|
||||||
|
js_channel.set(array.get());
|
||||||
|
js_channels.push(js_channel);
|
||||||
|
}
|
||||||
AudioBuffer {
|
AudioBuffer {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
js_channels: Vec::with_capacity(options.numberOfChannels as usize),
|
js_channels: DomRefCell::new(js_channels),
|
||||||
shared_channels: DomRefCell::new(None),
|
shared_channels: Arc::new(Mutex::new(vec![vec![0.; length as usize]; number_of_channels as usize])),
|
||||||
sample_rate: *options.sampleRate,
|
sample_rate: sample_rate,
|
||||||
length: options.length,
|
length: length,
|
||||||
duration: options.length as f64 / *options.sampleRate as f64,
|
duration: length as f64 / sample_rate as f64,
|
||||||
number_of_channels: options.numberOfChannels,
|
number_of_channels: number_of_channels,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
pub fn new(global: &Window,
|
pub fn new(global: &Window,
|
||||||
options: &AudioBufferOptions) -> DomRoot<AudioBuffer> {
|
number_of_channels: u32,
|
||||||
let buffer = AudioBuffer::new_inherited(options);
|
length: u32,
|
||||||
|
sample_rate: f32) -> DomRoot<AudioBuffer> {
|
||||||
|
let buffer = AudioBuffer::new_inherited(global.get_cx(), number_of_channels, length, sample_rate);
|
||||||
reflect_dom_object(Box::new(buffer), global, AudioBufferBinding::Wrap)
|
reflect_dom_object(Box::new(buffer), global, AudioBufferBinding::Wrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,48 +75,39 @@ impl AudioBuffer {
|
||||||
if options.numberOfChannels > MAX_CHANNEL_COUNT {
|
if options.numberOfChannels > MAX_CHANNEL_COUNT {
|
||||||
return Err(Error::NotSupported);
|
return Err(Error::NotSupported);
|
||||||
}
|
}
|
||||||
Ok(AudioBuffer::new(window, options))
|
Ok(AudioBuffer::new(window, options.numberOfChannels, options.length, *options.sampleRate))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn restore_js_channel_data(&self, cx: *mut JSContext) -> bool {
|
fn restore_js_channel_data(&self, cx: *mut JSContext) -> bool {
|
||||||
for (i, channel) in self.js_channels.iter().enumerate() {
|
for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
|
||||||
if !channel.get().is_null() {
|
if !channel.get().is_null() {
|
||||||
// Already have data in JS array.
|
// Already have data in JS array.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
match *self.shared_channels.borrow_mut() {
|
// Move the channel data from shared_channels to js_channels.
|
||||||
Some(ref mut shared_channels) => {
|
|
||||||
// Step 4 of https://webaudio.github.io/web-audio-api/#acquire-the-content
|
|
||||||
// "Attach ArrayBuffers containing copies of the data of the AudioBuffer, to
|
|
||||||
// be returned by the next call to getChannelData()".
|
|
||||||
rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>());
|
rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>());
|
||||||
let shared_channel = shared_channels.remove(i);
|
let shared_channel = (*self.shared_channels.lock().unwrap()).remove(i);
|
||||||
if unsafe {
|
if unsafe {
|
||||||
Float32Array::create(cx, CreateWith::Slice(&shared_channel), array.handle_mut())
|
Float32Array::create(cx, CreateWith::Slice(&shared_channel), array.handle_mut())
|
||||||
}.is_err() {
|
}.is_err() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
channel.set(array.get());
|
channel.set(array.get());
|
||||||
},
|
|
||||||
None => return false,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
*self.shared_channels.borrow_mut() = None;
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://webaudio.github.io/web-audio-api/#acquire-the-content
|
/// https://webaudio.github.io/web-audio-api/#acquire-the-content
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub fn acquire_contents(&self) {
|
pub fn acquire_contents(&self) -> Option<Arc<Mutex<Vec<Vec<f32>>>>> {
|
||||||
let cx = self.global().get_cx();
|
let cx = self.global().get_cx();
|
||||||
for (i, channel) in self.js_channels.iter().enumerate() {
|
for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
if channel.get().is_null() {
|
if channel.get().is_null() {
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
|
@ -109,13 +118,16 @@ impl AudioBuffer {
|
||||||
).to_vec()
|
).to_vec()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 3.
|
channel.set(ptr::null_mut());
|
||||||
let mut shared_channels = self.shared_channels.borrow_mut();
|
|
||||||
if shared_channels.is_none() {
|
// Step 3 and part of 4 (which will complete turning shared_channels
|
||||||
*shared_channels = Some(Vec::with_capacity(self.number_of_channels as usize));
|
// data into js_channels ArrayBuffers in restore_js_channel_data).
|
||||||
}
|
(*self.shared_channels.lock().unwrap())[i] = channel_data;
|
||||||
(*shared_channels).as_mut().unwrap()[i] = channel_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.js_channels.borrow_mut().clear();
|
||||||
|
|
||||||
|
Some(self.shared_channels.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +163,20 @@ impl AudioBufferMethods for AudioBuffer {
|
||||||
return Err(Error::JSFailed);
|
return Err(Error::JSFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(NonNull::new_unchecked(self.js_channels[channel as usize].get()))
|
Ok(NonNull::new_unchecked(self.js_channels.borrow()[channel as usize].get()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CopyFromChannel(&self,
|
||||||
|
destination: CustomAutoRooterGuard<Float32Array>,
|
||||||
|
channel_number: u32,
|
||||||
|
start_in_channel: u32) -> Fallible<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CopyToChannel(&self,
|
||||||
|
source: CustomAutoRooterGuard<Float32Array>,
|
||||||
|
channel_number: u32,
|
||||||
|
start_in_channel: u32) -> Fallible<()> {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use dom::audiobuffer::AudioBuffer;
|
||||||
use dom::audiodestinationnode::AudioDestinationNode;
|
use dom::audiodestinationnode::AudioDestinationNode;
|
||||||
use dom::bindings::cell::DomRefCell;
|
use dom::bindings::cell::DomRefCell;
|
||||||
use dom::bindings::codegen::Bindings::AudioNodeBinding::AudioNodeOptions;
|
use dom::bindings::codegen::Bindings::AudioNodeBinding::AudioNodeOptions;
|
||||||
|
@ -277,6 +278,14 @@ impl BaseAudioContextMethods for BaseAudioContext {
|
||||||
let options = unsafe { GainOptions::empty(window.get_cx()) };
|
let options = unsafe { GainOptions::empty(window.get_cx()) };
|
||||||
GainNode::new(&window, &self, &options)
|
GainNode::new(&window, &self, &options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn CreateBuffer(&self,
|
||||||
|
number_of_channels: u32,
|
||||||
|
length: u32,
|
||||||
|
sample_rate: Finite<f32>) -> DomRoot<AudioBuffer> {
|
||||||
|
let global = self.global();
|
||||||
|
AudioBuffer::new(&global.as_window(), number_of_channels, length, *sample_rate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ProcessingState> for AudioContextState {
|
impl From<ProcessingState> for AudioContextState {
|
||||||
|
|
|
@ -590,9 +590,9 @@ unsafe impl<U> JSTraceable for TypedSize2D<f32, U> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl JSTraceable for Mutex<Option<SharedRt>> {
|
unsafe impl<T: JSTraceable> JSTraceable for Mutex<T> {
|
||||||
unsafe fn trace(&self, _trc: *mut JSTracer) {
|
unsafe fn trace(&self, trc: *mut JSTracer) {
|
||||||
// Do nothing.
|
self.lock().unwrap().trace(trc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,10 @@ interface AudioBuffer {
|
||||||
readonly attribute double duration;
|
readonly attribute double duration;
|
||||||
readonly attribute unsigned long numberOfChannels;
|
readonly attribute unsigned long numberOfChannels;
|
||||||
[Throws] Float32Array getChannelData(unsigned long channel);
|
[Throws] Float32Array getChannelData(unsigned long channel);
|
||||||
//[Throws] void copyFromChannel(Float32Array destination,
|
[Throws] void copyFromChannel(Float32Array destination,
|
||||||
// unsigned long channelNumber,
|
unsigned long channelNumber,
|
||||||
// optional unsigned long startInChannel = 0);
|
optional unsigned long startInChannel = 0);
|
||||||
//[Throws] void copyToChannel(Float32Array source,
|
[Throws] void copyToChannel(Float32Array source,
|
||||||
// unsigned long channelNumber,
|
unsigned long channelNumber,
|
||||||
// optional unsigned long startInChannel = 0);
|
optional unsigned long startInChannel = 0);
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,9 +24,9 @@ interface BaseAudioContext : EventTarget {
|
||||||
readonly attribute AudioContextState state;
|
readonly attribute AudioContextState state;
|
||||||
Promise<void> resume();
|
Promise<void> resume();
|
||||||
attribute EventHandler onstatechange;
|
attribute EventHandler onstatechange;
|
||||||
// AudioBuffer createBuffer(unsigned long numberOfChannels,
|
AudioBuffer createBuffer(unsigned long numberOfChannels,
|
||||||
// unsigned long length,
|
unsigned long length,
|
||||||
// float sampleRate);
|
float sampleRate);
|
||||||
// Promise<AudioBuffer> decodeAudioData(ArrayBuffer audioData,
|
// Promise<AudioBuffer> decodeAudioData(ArrayBuffer audioData,
|
||||||
// optional DecodeSuccessCallback successCallback,
|
// optional DecodeSuccessCallback successCallback,
|
||||||
// optional DecodeErrorCallback errorCallback);
|
// optional DecodeErrorCallback errorCallback);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue