script/dom/audio/
iirfilternode.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::sync::Arc;
6
7use dom_struct::dom_struct;
8use itertools::Itertools;
9use js::gc::CustomAutoRooterGuard;
10use js::rust::HandleObject;
11use js::typedarray::Float32Array;
12use servo_media::audio::iir_filter_node::{IIRFilterNode as IIRFilter, IIRFilterNodeOptions};
13use servo_media::audio::node::AudioNodeInit;
14
15use crate::conversions::Convert;
16use crate::dom::audio::audionode::{AudioNode, AudioNodeOptionsHelper};
17use crate::dom::audio::baseaudiocontext::BaseAudioContext;
18use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
19    ChannelCountMode, ChannelInterpretation,
20};
21use crate::dom::bindings::codegen::Bindings::IIRFilterNodeBinding::{
22    IIRFilterNodeMethods, IIRFilterOptions,
23};
24use crate::dom::bindings::error::{Error, Fallible};
25use crate::dom::bindings::num::Finite;
26use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
27use crate::dom::bindings::root::DomRoot;
28use crate::dom::window::Window;
29use crate::script_runtime::CanGc;
30
31#[dom_struct]
32pub(crate) struct IIRFilterNode {
33    node: AudioNode,
34    feedforward: Vec<Finite<f64>>,
35    feedback: Vec<Finite<f64>>,
36}
37
38impl IIRFilterNode {
39    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
40    pub(crate) fn new_inherited(
41        _window: &Window,
42        context: &BaseAudioContext,
43        options: &IIRFilterOptions,
44    ) -> Fallible<IIRFilterNode> {
45        if !(1..=20).contains(&options.feedforward.len()) ||
46            !(1..=20).contains(&options.feedback.len())
47        {
48            return Err(Error::NotSupported);
49        }
50        if options.feedforward.iter().all(|v| **v == 0.0) || *options.feedback[0] == 0.0 {
51            return Err(Error::InvalidState);
52        }
53        let node_options =
54            options
55                .parent
56                .unwrap_or(2, ChannelCountMode::Max, ChannelInterpretation::Speakers);
57        let feedforward = (*options.feedforward).to_vec();
58        let feedback = (*options.feedback).to_vec();
59        let init_options = options.clone().convert();
60        let node = AudioNode::new_inherited(
61            AudioNodeInit::IIRFilterNode(init_options),
62            context,
63            node_options,
64            1, // inputs
65            1, // outputs
66        )?;
67        Ok(IIRFilterNode {
68            node,
69            feedforward,
70            feedback,
71        })
72    }
73
74    pub(crate) fn new(
75        window: &Window,
76        context: &BaseAudioContext,
77        options: &IIRFilterOptions,
78        can_gc: CanGc,
79    ) -> Fallible<DomRoot<IIRFilterNode>> {
80        Self::new_with_proto(window, None, context, options, can_gc)
81    }
82
83    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
84    fn new_with_proto(
85        window: &Window,
86        proto: Option<HandleObject>,
87        context: &BaseAudioContext,
88        options: &IIRFilterOptions,
89        can_gc: CanGc,
90    ) -> Fallible<DomRoot<IIRFilterNode>> {
91        let node = IIRFilterNode::new_inherited(window, context, options)?;
92        Ok(reflect_dom_object_with_proto(
93            Box::new(node),
94            window,
95            proto,
96            can_gc,
97        ))
98    }
99}
100
101impl IIRFilterNodeMethods<crate::DomTypeHolder> for IIRFilterNode {
102    /// <https://webaudio.github.io/web-audio-api/#dom-iirfilternode-iirfilternode>
103    fn Constructor(
104        window: &Window,
105        proto: Option<HandleObject>,
106        can_gc: CanGc,
107        context: &BaseAudioContext,
108        options: &IIRFilterOptions,
109    ) -> Fallible<DomRoot<IIRFilterNode>> {
110        IIRFilterNode::new_with_proto(window, proto, context, options, can_gc)
111    }
112
113    #[allow(unsafe_code)]
114    /// <https://webaudio.github.io/web-audio-api/#dom-iirfilternode-getfrequencyresponse>
115    fn GetFrequencyResponse(
116        &self,
117        frequency_hz: CustomAutoRooterGuard<Float32Array>,
118        mut mag_response: CustomAutoRooterGuard<Float32Array>,
119        mut phase_response: CustomAutoRooterGuard<Float32Array>,
120    ) -> Result<(), Error> {
121        let len = frequency_hz.len();
122        if len != mag_response.len() || len != phase_response.len() {
123            return Err(Error::InvalidAccess);
124        }
125        let feedforward: Vec<f64> = (self.feedforward.iter().map(|v| **v).collect_vec()).to_vec();
126        let feedback: Vec<f64> = (self.feedback.iter().map(|v| **v).collect_vec()).to_vec();
127        let frequency_hz_vec = frequency_hz.to_vec();
128        let mut mag_response_vec = mag_response.to_vec();
129        let mut phase_response_vec = phase_response.to_vec();
130        IIRFilter::get_frequency_response(
131            &feedforward,
132            &feedback,
133            &frequency_hz_vec,
134            &mut mag_response_vec,
135            &mut phase_response_vec,
136        );
137
138        mag_response.update(&mag_response_vec);
139        phase_response.update(&phase_response_vec);
140
141        Ok(())
142    }
143}
144
145impl Convert<IIRFilterNodeOptions> for IIRFilterOptions {
146    fn convert(self) -> IIRFilterNodeOptions {
147        let feedforward: Vec<f64> = (*self.feedforward.iter().map(|v| **v).collect_vec()).to_vec();
148        let feedback: Vec<f64> = (*self.feedback.iter().map(|v| **v).collect_vec()).to_vec();
149        IIRFilterNodeOptions {
150            feedforward: Arc::new(feedforward),
151            feedback: Arc::new(feedback),
152        }
153    }
154}