Create AudioBuffer from BaseAudioContext

This commit is contained in:
Fernando Jiménez Moreno 2018-07-04 15:39:40 +02:00
parent cb16c596b3
commit 25a74a75ea
5 changed files with 115 additions and 80 deletions

View file

@ -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) => { rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>());
// Step 4 of https://webaudio.github.io/web-audio-api/#acquire-the-content let shared_channel = (*self.shared_channels.lock().unwrap()).remove(i);
// "Attach ArrayBuffers containing copies of the data of the AudioBuffer, to if unsafe {
// be returned by the next call to getChannelData()". Float32Array::create(cx, CreateWith::Slice(&shared_channel), array.handle_mut())
rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>()); }.is_err() {
let shared_channel = shared_channels.remove(i); return false;
if unsafe {
Float32Array::create(cx, CreateWith::Slice(&shared_channel), array.handle_mut())
}.is_err() {
return false;
}
channel.set(array.get());
},
None => return false,
} }
channel.set(array.get());
} }
*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.
@ -106,16 +115,19 @@ impl AudioBuffer {
slice::from_raw_parts( slice::from_raw_parts(
JS_StealArrayBufferContents(cx, channel.handle()) as *mut f32, JS_StealArrayBufferContents(cx, channel.handle()) as *mut f32,
self.length as usize self.length as usize
).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(())
} }
} }

View file

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

View file

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

View file

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

View file

@ -20,36 +20,36 @@ interface BaseAudioContext : EventTarget {
readonly attribute AudioDestinationNode destination; readonly attribute AudioDestinationNode destination;
readonly attribute float sampleRate; readonly attribute float sampleRate;
readonly attribute double currentTime; readonly attribute double currentTime;
// readonly attribute AudioListener listener; // readonly attribute AudioListener listener;
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);
// AudioBufferSourceNode createBufferSource(); // AudioBufferSourceNode createBufferSource();
// ConstantSourceNode createConstantSource(); // ConstantSourceNode createConstantSource();
// ScriptProcessorNode createScriptProcessor(optional unsigned long bufferSize = 0, // ScriptProcessorNode createScriptProcessor(optional unsigned long bufferSize = 0,
// optional unsigned long numberOfInputChannels = 2, // optional unsigned long numberOfInputChannels = 2,
// optional unsigned long numberOfOutputChannels = 2); // optional unsigned long numberOfOutputChannels = 2);
// AnalyserNode createAnalyser(); // AnalyserNode createAnalyser();
GainNode createGain(); GainNode createGain();
// DelayNode createDelay(optional double maxDelayTime = 1); // DelayNode createDelay(optional double maxDelayTime = 1);
// BiquadFilterNode createBiquadFilter(); // BiquadFilterNode createBiquadFilter();
// IIRFilterNode createIIRFilter(sequence<double> feedforward, // IIRFilterNode createIIRFilter(sequence<double> feedforward,
// sequence<double> feedback); // sequence<double> feedback);
// WaveShaperNode createWaveShaper(); // WaveShaperNode createWaveShaper();
// PannerNode createPanner(); // PannerNode createPanner();
// StereoPannerNode createStereoPanner(); // StereoPannerNode createStereoPanner();
// ConvolverNode createConvolver(); // ConvolverNode createConvolver();
// ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6); // ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6);
// ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs = 6); // ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs = 6);
// DynamicsCompressorNode createDynamicsCompressor(); // DynamicsCompressorNode createDynamicsCompressor();
OscillatorNode createOscillator(); OscillatorNode createOscillator();
// PeriodicWave createPeriodicWave(sequence<float> real, // PeriodicWave createPeriodicWave(sequence<float> real,
// sequence<float> imag, // sequence<float> imag,
// optional PeriodicWaveConstraints constraints); // optional PeriodicWaveConstraints constraints);
}; };