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;
6use std::f32;
7
8use dom_struct::dom_struct;
9use js::rust::HandleObject;
10use servo_media::audio::biquad_filter_node::{
11    BiquadFilterNodeMessage, BiquadFilterNodeOptions, FilterType,
12};
13use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioNodeType};
14use servo_media::audio::param::ParamType;
15
16use crate::conversions::Convert;
17use crate::dom::audio::audionode::{AudioNode, AudioNodeOptionsHelper};
18use crate::dom::audio::audioparam::AudioParam;
19use crate::dom::audio::baseaudiocontext::BaseAudioContext;
20use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
21    ChannelCountMode, ChannelInterpretation,
22};
23use crate::dom::bindings::codegen::Bindings::AudioParamBinding::AutomationRate;
24use crate::dom::bindings::codegen::Bindings::BiquadFilterNodeBinding::{
25    BiquadFilterNodeMethods, BiquadFilterOptions, BiquadFilterType,
26};
27use crate::dom::bindings::error::Fallible;
28use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
29use crate::dom::bindings::root::{Dom, DomRoot};
30use crate::dom::window::Window;
31use crate::script_runtime::CanGc;
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, allow(crown::unrooted_must_root))]
45    pub(crate) fn new_inherited(
46        window: &Window,
47        context: &BaseAudioContext,
48        options: &BiquadFilterOptions,
49        can_gc: CanGc,
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            AudioNodeInit::BiquadFilterNode(options),
59            context,
60            node_options,
61            1, // inputs
62            1, // outputs
63        )?;
64        let gain = AudioParam::new(
65            window,
66            context,
67            node.node_id(),
68            AudioNodeType::BiquadFilterNode,
69            ParamType::Gain,
70            AutomationRate::A_rate,
71            options.gain, // default value
72            f32::MIN,     // min value
73            f32::MAX,     // max value
74            can_gc,
75        );
76        let q = AudioParam::new(
77            window,
78            context,
79            node.node_id(),
80            AudioNodeType::BiquadFilterNode,
81            ParamType::Q,
82            AutomationRate::A_rate,
83            options.q, // default value
84            f32::MIN,  // min value
85            f32::MAX,  // max value
86            can_gc,
87        );
88        let frequency = AudioParam::new(
89            window,
90            context,
91            node.node_id(),
92            AudioNodeType::BiquadFilterNode,
93            ParamType::Frequency,
94            AutomationRate::A_rate,
95            options.frequency, // default value
96            f32::MIN,          // min value
97            f32::MAX,          // max value
98            can_gc,
99        );
100        let detune = AudioParam::new(
101            window,
102            context,
103            node.node_id(),
104            AudioNodeType::BiquadFilterNode,
105            ParamType::Detune,
106            AutomationRate::A_rate,
107            options.detune, // default value
108            f32::MIN,       // min value
109            f32::MAX,       // max value
110            can_gc,
111        );
112        Ok(BiquadFilterNode {
113            node,
114            filter,
115            gain: Dom::from_ref(&gain),
116            q: Dom::from_ref(&q),
117            frequency: Dom::from_ref(&frequency),
118            detune: Dom::from_ref(&detune),
119        })
120    }
121
122    pub(crate) fn new(
123        window: &Window,
124        context: &BaseAudioContext,
125        options: &BiquadFilterOptions,
126        can_gc: CanGc,
127    ) -> Fallible<DomRoot<BiquadFilterNode>> {
128        Self::new_with_proto(window, None, context, options, can_gc)
129    }
130
131    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
132    fn new_with_proto(
133        window: &Window,
134        proto: Option<HandleObject>,
135        context: &BaseAudioContext,
136        options: &BiquadFilterOptions,
137        can_gc: CanGc,
138    ) -> Fallible<DomRoot<BiquadFilterNode>> {
139        let node = BiquadFilterNode::new_inherited(window, context, options, can_gc)?;
140        Ok(reflect_dom_object_with_proto(
141            Box::new(node),
142            window,
143            proto,
144            can_gc,
145        ))
146    }
147}
148
149impl BiquadFilterNodeMethods<crate::DomTypeHolder> for BiquadFilterNode {
150    // https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-biquadfilternode-context-options
151    fn Constructor(
152        window: &Window,
153        proto: Option<HandleObject>,
154        can_gc: CanGc,
155        context: &BaseAudioContext,
156        options: &BiquadFilterOptions,
157    ) -> Fallible<DomRoot<BiquadFilterNode>> {
158        BiquadFilterNode::new_with_proto(window, proto, context, options, can_gc)
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}