Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement AnalyserNode #21712

Merged
merged 8 commits into from Sep 19, 2018

AnalyserNode in script

  • Loading branch information
Manishearth committed Sep 18, 2018
commit 18b9ad9e5c978a2e218673d992d8c36a90780e1a
@@ -0,0 +1,218 @@
/* 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::audionode::AudioNode;
use dom::baseaudiocontext::BaseAudioContext;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::AnalyserNodeBinding::{self, AnalyserNodeMethods, AnalyserOptions};
use dom::bindings::codegen::Bindings::AudioNodeBinding::{ChannelCountMode, ChannelInterpretation};
use dom::bindings::error::{Error, Fallible};
use dom::bindings::num::Finite;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::reflect_dom_object;
use dom::bindings::root::DomRoot;
use dom::window::Window;
use dom_struct::dom_struct;
use ipc_channel::ipc::{self, IpcReceiver};
use ipc_channel::router::ROUTER;
use js::rust::CustomAutoRooterGuard;
use js::typedarray::{Float32Array, Uint8Array};
use servo_media::audio::analyser_node::AnalysisEngine;
use servo_media::audio::block::Block;
use servo_media::audio::node::AudioNodeInit;
use task_source::{TaskSource, TaskSourceName};

#[dom_struct]
pub struct AnalyserNode {
node: AudioNode,
#[ignore_malloc_size_of = "Defined in servo-media"]
engine: DomRefCell<AnalysisEngine>,
}

impl AnalyserNode {
#[allow(unrooted_must_root)]
pub fn new_inherited(
_: &Window,
context: &BaseAudioContext,
options: &AnalyserOptions,
) -> Fallible<(AnalyserNode, IpcReceiver<Block>)> {
let node_options = options.parent
.unwrap_or(1, ChannelCountMode::Max,
ChannelInterpretation::Speakers);

if options.fftSize > 32768 || options.fftSize < 32 ||
(options.fftSize & (options.fftSize - 1) != 0) {
return Err(Error::IndexSize)
}

if *options.maxDecibels <= *options.minDecibels {
return Err(Error::IndexSize)
}

if *options.smoothingTimeConstant < 0. || *options.smoothingTimeConstant > 1. {
return Err(Error::IndexSize);
}

let (send, rcv) = ipc::channel().unwrap();
let callback = move |block| {
send.send(block).unwrap();
};

let node = AudioNode::new_inherited(
AudioNodeInit::AnalyserNode(Box::new(callback)),
context,
node_options,
1, // inputs
1, // outputs
)?;


let engine = AnalysisEngine::new(options.fftSize as usize,
*options.smoothingTimeConstant,
*options.minDecibels, *options.maxDecibels);
Ok((AnalyserNode {
node,
engine: DomRefCell::new(engine)
}, rcv))
}

#[allow(unrooted_must_root)]
pub fn new(
window: &Window,
context: &BaseAudioContext,
options: &AnalyserOptions,
) -> Fallible<DomRoot<AnalyserNode>> {
let (node, recv) = AnalyserNode::new_inherited(window, context, options)?;
let object = reflect_dom_object(Box::new(node), window, AnalyserNodeBinding::Wrap);
let source = window.dom_manipulation_task_source();
let canceller = window.task_canceller(TaskSourceName::DOMManipulation);
let this = Trusted::new(&*object);

ROUTER.add_route(recv.to_opaque(), Box::new(move |block| {
let this = this.clone();
let _ = source.queue_with_canceller(task!(append_analysis_block: move || {
let this = this.root();
this.push_block(block.to().unwrap())
}), &canceller);
}));
Ok(object)
}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-analysernode
pub fn Constructor(
window: &Window,
context: &BaseAudioContext,
options: &AnalyserOptions,
) -> Fallible<DomRoot<AnalyserNode>> {
AnalyserNode::new(window, context, options)
}

pub fn push_block(&self, block: Block) {
self.engine.borrow_mut().push(block)
}
}

impl AnalyserNodeMethods for AnalyserNode {
#[allow(unsafe_code)]
/// https://webaudio.github.io/web-audio-api/#dom-analysernode-getfloatfrequencydata
fn GetFloatFrequencyData(&self, mut array: CustomAutoRooterGuard<Float32Array>) {
// Invariant to maintain: No JS code that may touch the array should
// run whilst we're writing to it
let dest = unsafe { array.as_mut_slice() };
self.engine.borrow_mut().fill_frequency_data(dest);
}

#[allow(unsafe_code)]
/// https://webaudio.github.io/web-audio-api/#dom-analysernode-getbytefrequencydata
fn GetByteFrequencyData(&self, mut array: CustomAutoRooterGuard<Uint8Array>) {
// Invariant to maintain: No JS code that may touch the array should
// run whilst we're writing to it
let dest = unsafe { array.as_mut_slice() };
self.engine.borrow_mut().fill_byte_frequency_data(dest);

}

#[allow(unsafe_code)]
/// https://webaudio.github.io/web-audio-api/#dom-analysernode-getfloattimedomaindata
fn GetFloatTimeDomainData(&self, mut array: CustomAutoRooterGuard<Float32Array>) {
// Invariant to maintain: No JS code that may touch the array should
// run whilst we're writing to it
let dest = unsafe { array.as_mut_slice() };
self.engine.borrow().fill_time_domain_data(dest);

}

#[allow(unsafe_code)]
/// https://webaudio.github.io/web-audio-api/#dom-analysernode-getbytetimedomaindata
fn GetByteTimeDomainData(&self, mut array: CustomAutoRooterGuard<Uint8Array>) {
// Invariant to maintain: No JS code that may touch the array should
// run whilst we're writing to it
let dest = unsafe { array.as_mut_slice() };
self.engine.borrow().fill_byte_time_domain_data(dest);

}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-fftsize
fn SetFftSize(&self, value: u32) -> Fallible<()> {
if value > 32768 || value < 32 ||
(value & (value - 1) != 0) {
return Err(Error::IndexSize)
}
self.engine.borrow_mut().set_fft_size(value as usize);
Ok(())
}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-fftsize
fn FftSize(&self) -> u32 {
self.engine.borrow().get_fft_size() as u32
}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-frequencybincount
fn FrequencyBinCount(&self) -> u32 {
self.FftSize() / 2
}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-mindecibels
fn MinDecibels(&self) -> Finite<f64> {
Finite::wrap(self.engine.borrow().get_min_decibels())
}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-mindecibels
fn SetMinDecibels(&self, value: Finite<f64>) -> Fallible<()> {
if *value >= self.engine.borrow().get_max_decibels() {
return Err(Error::IndexSize)
}
self.engine.borrow_mut().set_min_decibels(*value);
Ok(())
}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-maxdecibels
fn MaxDecibels(&self) -> Finite<f64> {
Finite::wrap(self.engine.borrow().get_max_decibels())
}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-maxdecibels
fn SetMaxDecibels(&self, value: Finite<f64>) -> Fallible<()> {
if *value <= self.engine.borrow().get_min_decibels() {
return Err(Error::IndexSize)
}
self.engine.borrow_mut().set_max_decibels(*value);
Ok(())
}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-smoothingtimeconstant
fn SmoothingTimeConstant(&self) -> Finite<f64> {
Finite::wrap(self.engine.borrow().get_smoothing_constant())
}

/// https://webaudio.github.io/web-audio-api/#dom-analysernode-smoothingtimeconstant
fn SetSmoothingTimeConstant(&self, value: Finite<f64>) -> Fallible<()> {
if *value < 0. || *value > 1. {
return Err(Error::IndexSize)
}
self.engine.borrow_mut().set_smoothing_constant(*value);
Ok(())
}
}

@@ -90,6 +90,7 @@ use servo_arc::Arc as ServoArc;
use servo_atoms::Atom;
use servo_channel::{Receiver, Sender};
use servo_media::Backend;
use servo_media::audio::analyser_node::AnalysisEngine;
use servo_media::audio::buffer_source_node::AudioBuffer;
use servo_media::audio::context::AudioContext;
use servo_media::audio::graph::NodeId;
@@ -435,7 +436,7 @@ unsafe_no_jsmanaged_fields!(SourceSet);
unsafe_no_jsmanaged_fields!(AudioBuffer);
unsafe_no_jsmanaged_fields!(AudioContext<Backend>);
unsafe_no_jsmanaged_fields!(NodeId);
unsafe_no_jsmanaged_fields!(DistanceModel, PanningModel, ParamType);
unsafe_no_jsmanaged_fields!(AnalysisEngine, DistanceModel, PanningModel, ParamType);

unsafe impl<'a> JSTraceable for &'a str {
#[inline]
@@ -215,6 +215,7 @@ pub mod types {
pub mod abstractworker;
pub mod abstractworkerglobalscope;
pub mod activation;
pub mod analysernode;
pub mod attr;
pub mod audiobuffer;
pub mod audiobuffersourcenode;
@@ -0,0 +1,28 @@
/* 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/#analysernode
*/

dictionary AnalyserOptions : AudioNodeOptions {
unsigned long fftSize = 2048;
double maxDecibels = -30;
double minDecibels = -100;
double smoothingTimeConstant = 0.8;
};

[Exposed=Window,
Constructor (BaseAudioContext context, optional AnalyserOptions options)]
interface AnalyserNode : AudioNode {
void getFloatFrequencyData (Float32Array array);
void getByteFrequencyData (Uint8Array array);
void getFloatTimeDomainData (Float32Array array);
void getByteTimeDomainData (Uint8Array array);
[SetterThrows] attribute unsigned long fftSize;
readonly attribute unsigned long frequencyBinCount;
[SetterThrows] attribute double minDecibels;
[SetterThrows] attribute double maxDecibels;
[SetterThrows] attribute double smoothingTimeConstant;
};
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.