mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
webaudio: Implement IIRFilterNode (#33001)
* Basic IIRFIlterNode bindings Signed-off-by: Daniel Adams <msub2official@gmail.com> * Add constructor to BaseAudioContext Signed-off-by: Daniel Adams <msub2official@gmail.com> * Update IDL and use statements Signed-off-by: Daniel Adams <msub2official@gmail.com> * Update non-crashing test expectations Signed-off-by: Daniel Adams <msub2official@gmail.com> * Tidy Signed-off-by: Daniel Adams <msub2official@gmail.com> * Add missing spec link Signed-off-by: Daniel Adams <msub2official@gmail.com> * Optimize error checks Signed-off-by: Daniel Adams <msub2official@gmail.com> * Pass context channel count to servo-media Signed-off-by: Daniel Adams <msub2official@gmail.com> * Update test expectations Signed-off-by: Daniel Adams <msub2official@gmail.com> * Update legacy expectations Signed-off-by: Daniel Adams <msub2official@gmail.com> * Add IIRFilterNode in interfaces.html Signed-off-by: Daniel Adams <msub2official@gmail.com> * Update MANIFEST Signed-off-by: Daniel Adams <msub2official@gmail.com> --------- Signed-off-by: Daniel Adams <msub2official@gmail.com>
This commit is contained in:
parent
1af3ad8a74
commit
5520a9eb50
19 changed files with 206 additions and 175 deletions
|
@ -58,6 +58,7 @@ impl AudioNode {
|
|||
count: options.count as u8,
|
||||
mode: options.mode.into(),
|
||||
interpretation: options.interpretation.into(),
|
||||
context_channel_count: context.channel_count() as u8,
|
||||
};
|
||||
let node_id = context
|
||||
.audio_context_impl()
|
||||
|
|
|
@ -42,6 +42,7 @@ use crate::dom::bindings::codegen::Bindings::ChannelMergerNodeBinding::ChannelMe
|
|||
use crate::dom::bindings::codegen::Bindings::ChannelSplitterNodeBinding::ChannelSplitterOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::ConstantSourceNodeBinding::ConstantSourceOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::GainNodeBinding::GainOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::IIRFilterNodeBinding::IIRFilterOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::OscillatorNodeBinding::OscillatorOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::PannerNodeBinding::PannerOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::StereoPannerNodeBinding::StereoPannerOptions;
|
||||
|
@ -58,6 +59,7 @@ use crate::dom::constantsourcenode::ConstantSourceNode;
|
|||
use crate::dom::domexception::{DOMErrorName, DOMException};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::gainnode::GainNode;
|
||||
use crate::dom::iirfilternode::IIRFilterNode;
|
||||
use crate::dom::oscillatornode::OscillatorNode;
|
||||
use crate::dom::pannernode::PannerNode;
|
||||
use crate::dom::promise::Promise;
|
||||
|
@ -264,6 +266,10 @@ impl BaseAudioContext {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn channel_count(&self) -> u32 {
|
||||
self.channel_count
|
||||
}
|
||||
}
|
||||
|
||||
impl BaseAudioContextMethods for BaseAudioContext {
|
||||
|
@ -550,6 +556,20 @@ impl BaseAudioContextMethods for BaseAudioContext {
|
|||
// Step 4.
|
||||
promise
|
||||
}
|
||||
|
||||
/// <https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createiirfilter>
|
||||
fn CreateIIRFilter(
|
||||
&self,
|
||||
feedforward: Vec<Finite<f64>>,
|
||||
feedback: Vec<Finite<f64>>,
|
||||
) -> Fallible<DomRoot<IIRFilterNode>> {
|
||||
let opts = IIRFilterOptions {
|
||||
parent: AudioNodeOptions::empty(),
|
||||
feedback,
|
||||
feedforward,
|
||||
};
|
||||
IIRFilterNode::new(self.global().as_window(), self, &opts)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BaseAudioContextOptions> for AudioContextOptions {
|
||||
|
|
145
components/script/dom/iirfilternode.rs
Normal file
145
components/script/dom/iirfilternode.rs
Normal file
|
@ -0,0 +1,145 @@
|
|||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use itertools::Itertools;
|
||||
use js::gc::CustomAutoRooterGuard;
|
||||
use js::rust::HandleObject;
|
||||
use js::typedarray::Float32Array;
|
||||
use servo_media::audio::iir_filter_node::{IIRFilterNode as IIRFilter, IIRFilterNodeOptions};
|
||||
use servo_media::audio::node::AudioNodeInit;
|
||||
|
||||
use crate::dom::audionode::AudioNode;
|
||||
use crate::dom::baseaudiocontext::BaseAudioContext;
|
||||
use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
|
||||
ChannelCountMode, ChannelInterpretation,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::IIRFilterNodeBinding::{
|
||||
IIRFilterNodeMethods, IIRFilterOptions,
|
||||
};
|
||||
use crate::dom::bindings::error::{Error, Fallible};
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::window::Window;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct IIRFilterNode {
|
||||
node: AudioNode,
|
||||
feedforward: Vec<Finite<f64>>,
|
||||
feedback: Vec<Finite<f64>>,
|
||||
}
|
||||
|
||||
impl IIRFilterNode {
|
||||
#[allow(crown::unrooted_must_root)]
|
||||
pub fn new_inherited(
|
||||
window: &Window,
|
||||
context: &BaseAudioContext,
|
||||
options: &IIRFilterOptions,
|
||||
) -> Fallible<IIRFilterNode> {
|
||||
if !(1..=20).contains(&options.feedforward.len()) ||
|
||||
!(1..=20).contains(&options.feedback.len())
|
||||
{
|
||||
return Err(Error::NotSupported);
|
||||
}
|
||||
if options.feedforward.iter().all(|v| **v == 0.0) || *options.feedback[0] == 0.0 {
|
||||
return Err(Error::InvalidState);
|
||||
}
|
||||
let node_options =
|
||||
options
|
||||
.parent
|
||||
.unwrap_or(2, ChannelCountMode::Max, ChannelInterpretation::Speakers);
|
||||
let init_options = options.into();
|
||||
let node = AudioNode::new_inherited(
|
||||
AudioNodeInit::IIRFilterNode(init_options),
|
||||
context,
|
||||
node_options,
|
||||
1, // inputs
|
||||
1, // outputs
|
||||
)?;
|
||||
Ok(IIRFilterNode {
|
||||
node,
|
||||
feedforward: (*options.feedforward).to_vec(),
|
||||
feedback: (*options.feedback).to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
window: &Window,
|
||||
context: &BaseAudioContext,
|
||||
options: &IIRFilterOptions,
|
||||
) -> Fallible<DomRoot<IIRFilterNode>> {
|
||||
Self::new_with_proto(window, None, context, options)
|
||||
}
|
||||
|
||||
#[allow(crown::unrooted_must_root)]
|
||||
fn new_with_proto(
|
||||
window: &Window,
|
||||
proto: Option<HandleObject>,
|
||||
context: &BaseAudioContext,
|
||||
options: &IIRFilterOptions,
|
||||
) -> Fallible<DomRoot<IIRFilterNode>> {
|
||||
let node = IIRFilterNode::new_inherited(window, context, options)?;
|
||||
Ok(reflect_dom_object_with_proto(Box::new(node), window, proto))
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Constructor(
|
||||
window: &Window,
|
||||
proto: Option<HandleObject>,
|
||||
context: &BaseAudioContext,
|
||||
options: &IIRFilterOptions,
|
||||
) -> Fallible<DomRoot<IIRFilterNode>> {
|
||||
IIRFilterNode::new_with_proto(window, proto, context, options)
|
||||
}
|
||||
}
|
||||
|
||||
impl IIRFilterNodeMethods for IIRFilterNode {
|
||||
#[allow(unsafe_code)]
|
||||
/// <https://webaudio.github.io/web-audio-api/#dom-iirfilternode-getfrequencyresponse>
|
||||
fn GetFrequencyResponse(
|
||||
&self,
|
||||
frequency_hz: CustomAutoRooterGuard<Float32Array>,
|
||||
mut mag_response: CustomAutoRooterGuard<Float32Array>,
|
||||
mut phase_response: CustomAutoRooterGuard<Float32Array>,
|
||||
) -> Result<(), Error> {
|
||||
let len = frequency_hz.len();
|
||||
if len != mag_response.len() || len != phase_response.len() {
|
||||
return Err(Error::InvalidAccess);
|
||||
}
|
||||
let feedforward: Vec<f64> = (self.feedforward.iter().map(|v| **v).collect_vec()).to_vec();
|
||||
let feedback: Vec<f64> = (self.feedback.iter().map(|v| **v).collect_vec()).to_vec();
|
||||
let frequency_hz_vec = frequency_hz.to_vec();
|
||||
let mut mag_response_vec = mag_response.to_vec();
|
||||
let mut phase_response_vec = phase_response.to_vec();
|
||||
IIRFilter::get_frequency_response(
|
||||
&feedforward,
|
||||
&feedback,
|
||||
&frequency_hz_vec,
|
||||
&mut mag_response_vec,
|
||||
&mut phase_response_vec,
|
||||
);
|
||||
|
||||
unsafe {
|
||||
mag_response.update(&mag_response_vec);
|
||||
phase_response.update(&phase_response_vec);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a IIRFilterOptions> for IIRFilterNodeOptions {
|
||||
fn from(options: &'a IIRFilterOptions) -> Self {
|
||||
let feedforward: Vec<f64> =
|
||||
(*options.feedforward.iter().map(|v| **v).collect_vec()).to_vec();
|
||||
let feedback: Vec<f64> = (*options.feedback.iter().map(|v| **v).collect_vec()).to_vec();
|
||||
Self {
|
||||
feedforward: Arc::new(feedforward),
|
||||
feedback: Arc::new(feedback),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -440,6 +440,7 @@ pub mod htmlulistelement;
|
|||
pub mod htmlunknownelement;
|
||||
pub mod htmlvideoelement;
|
||||
pub mod identityhub;
|
||||
pub mod iirfilternode;
|
||||
pub mod imagebitmap;
|
||||
pub mod imagedata;
|
||||
pub mod inputevent;
|
||||
|
|
|
@ -39,8 +39,8 @@ interface BaseAudioContext : EventTarget {
|
|||
[Throws] GainNode createGain();
|
||||
// DelayNode createDelay(optional double maxDelayTime = 1);
|
||||
[Throws] BiquadFilterNode createBiquadFilter();
|
||||
// IIRFilterNode createIIRFilter(sequence<double> feedforward,
|
||||
// sequence<double> feedback);
|
||||
[Throws] IIRFilterNode createIIRFilter(sequence<double> feedforward,
|
||||
sequence<double> feedback);
|
||||
// WaveShaperNode createWaveShaper();
|
||||
[Throws] PannerNode createPanner();
|
||||
[Throws] StereoPannerNode createStereoPanner();
|
||||
|
|
22
components/script/dom/webidls/IIRFilterNode.webidl
Normal file
22
components/script/dom/webidls/IIRFilterNode.webidl
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
/*
|
||||
* The origin of this IDL file is
|
||||
* https://webaudio.github.io/web-audio-api/#IIRFilterNode
|
||||
*/
|
||||
|
||||
[Exposed=Window]
|
||||
interface IIRFilterNode : AudioNode {
|
||||
[Throws] constructor (BaseAudioContext context, IIRFilterOptions options);
|
||||
[Throws] undefined getFrequencyResponse (
|
||||
Float32Array frequencyHz,
|
||||
Float32Array magResponse,
|
||||
Float32Array phaseResponse
|
||||
);
|
||||
};
|
||||
|
||||
dictionary IIRFilterOptions : AudioNodeOptions {
|
||||
required sequence<double> feedforward;
|
||||
required sequence<double> feedback;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue