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