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