Skip to main content

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