Skip to main content

script/dom/audio/
biquadfilternode.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::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::rust::HandleObject;
10use script_bindings::codegen::GenericBindings::BaseAudioContextBinding::BaseAudioContextMethods;
11use script_bindings::reflector::reflect_dom_object_with_proto;
12use servo_media::audio::audio_node::{AudioNodeInit, AudioNodeMessage, AudioNodeType};
13use servo_media::audio::biquad_filter_node::{
14    BiquadFilterNodeMessage, BiquadFilterNodeOptions, FilterType,
15};
16use servo_media::audio::graph::NodeId;
17use servo_media::audio::param::ParamType;
18
19use crate::conversions::Convert;
20use crate::dom::audio::audionode::{AudioNode, AudioNodeOptionsHelper};
21use crate::dom::audio::audioparam::AudioParam;
22use crate::dom::audio::baseaudiocontext::BaseAudioContext;
23use crate::dom::audionode::UnwrappedAudioNodeOptions;
24use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
25    ChannelCountMode, ChannelInterpretation,
26};
27use crate::dom::bindings::codegen::Bindings::AudioParamBinding::AutomationRate;
28use crate::dom::bindings::codegen::Bindings::BiquadFilterNodeBinding::{
29    BiquadFilterNodeMethods, BiquadFilterOptions, BiquadFilterType,
30};
31use crate::dom::bindings::error::{Error, Fallible};
32use crate::dom::bindings::root::{Dom, DomRoot};
33use crate::dom::window::Window;
34
35#[dom_struct]
36pub(crate) struct BiquadFilterNode {
37    node: AudioNode,
38    gain: Dom<AudioParam>,
39    frequency: Dom<AudioParam>,
40    q: Dom<AudioParam>,
41    detune: Dom<AudioParam>,
42    filter: Cell<BiquadFilterType>,
43}
44
45impl BiquadFilterNode {
46    fn new_inherited(
47        node_id: Option<NodeId>,
48        context: &BaseAudioContext,
49        node_options: UnwrappedAudioNodeOptions,
50        filter: BiquadFilterType,
51        audio_params: [&AudioParam; 4],
52    ) -> Self {
53        BiquadFilterNode {
54            node: AudioNode::new_inherited(
55                node_id,
56                context,
57                node_options,
58                1, // inputs
59                1, // outputs
60            ),
61            filter: Cell::new(filter),
62            gain: Dom::from_ref(audio_params[0]),
63            q: Dom::from_ref(audio_params[1]),
64            frequency: Dom::from_ref(audio_params[2]),
65            detune: Dom::from_ref(audio_params[3]),
66        }
67    }
68}
69
70impl BiquadFilterNodeMethods<crate::DomTypeHolder> for BiquadFilterNode {
71    /// <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-biquadfilternode-context-options>
72    fn Constructor(
73        cx: &mut JSContext,
74        window: &Window,
75        proto: Option<HandleObject>,
76        context: &BaseAudioContext,
77        options: &BiquadFilterOptions,
78    ) -> Fallible<DomRoot<BiquadFilterNode>> {
79        let node_options =
80            options
81                .parent
82                .unwrap_or(2, ChannelCountMode::Max, ChannelInterpretation::Speakers);
83        if AudioNode::channel_count_check(node_options.count) {
84            return Err(Error::NotSupported(Some(String::from(
85                "Channel count not supported",
86            ))));
87        }
88        let filter = options.type_;
89        let options = options.convert();
90        let audio_node_init = AudioNodeInit::BiquadFilterNode(options);
91        let node_id =
92            AudioNode::generate_audio_node_id(cx, &node_options, audio_node_init, context);
93        // <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-gain>
94        let gain = AudioParam::new(
95            cx,
96            window,
97            context,
98            node_id,
99            AudioNodeType::BiquadFilterNode,
100            ParamType::Gain,
101            AutomationRate::A_rate,
102            options.gain,           // default value
103            f32::MIN,               // min value
104            40. * f32::MAX.log10(), // max value
105        );
106        // <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-q>
107        let q = AudioParam::new(
108            cx,
109            window,
110            context,
111            node_id,
112            AudioNodeType::BiquadFilterNode,
113            ParamType::Q,
114            AutomationRate::A_rate,
115            options.q, // default value
116            f32::MIN,  // min value
117            f32::MAX,  // max value
118        );
119        let nyquist = (*context.SampleRate()) / 2.;
120        // <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-frequency>
121        let frequency = AudioParam::new(
122            cx,
123            window,
124            context,
125            node_id,
126            AudioNodeType::BiquadFilterNode,
127            ParamType::Frequency,
128            AutomationRate::A_rate,
129            options.frequency, // default value
130            0.,                // min value
131            nyquist,           // max value
132        );
133        let detune_max = 1200. * f32::MAX.log2();
134        // <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-detune>
135        let detune = AudioParam::new(
136            cx,
137            window,
138            context,
139            node_id,
140            AudioNodeType::BiquadFilterNode,
141            ParamType::Detune,
142            AutomationRate::A_rate,
143            options.detune, // default value
144            -detune_max,    // min value
145            detune_max,     // max value
146        );
147        Ok(reflect_dom_object_with_proto(
148            cx,
149            Box::new(BiquadFilterNode::new_inherited(
150                node_id,
151                context,
152                node_options,
153                filter,
154                [&gain, &q, &frequency, &detune],
155            )),
156            window,
157            proto,
158        ))
159    }
160
161    /// <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-gain>
162    fn Gain(&self) -> DomRoot<AudioParam> {
163        DomRoot::from_ref(&self.gain)
164    }
165
166    /// <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-q>
167    fn Q(&self) -> DomRoot<AudioParam> {
168        DomRoot::from_ref(&self.q)
169    }
170
171    /// <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-detune>
172    fn Detune(&self) -> DomRoot<AudioParam> {
173        DomRoot::from_ref(&self.detune)
174    }
175
176    /// <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-frequency>
177    fn Frequency(&self) -> DomRoot<AudioParam> {
178        DomRoot::from_ref(&self.frequency)
179    }
180
181    /// <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-type>
182    fn Type(&self) -> BiquadFilterType {
183        self.filter.get()
184    }
185
186    /// <https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-type>
187    fn SetType(&self, filter: BiquadFilterType) {
188        self.filter.set(filter);
189        self.node.message(AudioNodeMessage::BiquadFilterNode(
190            BiquadFilterNodeMessage::SetFilterType(filter.convert()),
191        ));
192    }
193}
194
195impl Convert<BiquadFilterNodeOptions> for &BiquadFilterOptions {
196    fn convert(self) -> BiquadFilterNodeOptions {
197        BiquadFilterNodeOptions {
198            gain: *self.gain,
199            q: *self.Q,
200            frequency: *self.frequency,
201            detune: *self.detune,
202            filter: self.type_.convert(),
203        }
204    }
205}
206
207impl Convert<FilterType> for BiquadFilterType {
208    fn convert(self) -> FilterType {
209        match self {
210            BiquadFilterType::Lowpass => FilterType::LowPass,
211            BiquadFilterType::Highpass => FilterType::HighPass,
212            BiquadFilterType::Bandpass => FilterType::BandPass,
213            BiquadFilterType::Lowshelf => FilterType::LowShelf,
214            BiquadFilterType::Highshelf => FilterType::HighShelf,
215            BiquadFilterType::Peaking => FilterType::Peaking,
216            BiquadFilterType::Allpass => FilterType::AllPass,
217            BiquadFilterType::Notch => FilterType::Notch,
218        }
219    }
220}