1use 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, 1, )?;
65 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, f32::MIN, 40. * f32::MAX.log10(), );
78 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, f32::MIN, f32::MAX, );
91 let nyquist = (*context.SampleRate()) / 2.;
92 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, 0., nyquist, );
105 let detune_max = 1200. * f32::MAX.log2();
106 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, -detune_max, detune_max, );
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 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 fn Gain(&self) -> DomRoot<AudioParam> {
170 DomRoot::from_ref(&self.gain)
171 }
172
173 fn Q(&self) -> DomRoot<AudioParam> {
175 DomRoot::from_ref(&self.q)
176 }
177
178 fn Detune(&self) -> DomRoot<AudioParam> {
180 DomRoot::from_ref(&self.detune)
181 }
182
183 fn Frequency(&self) -> DomRoot<AudioParam> {
185 DomRoot::from_ref(&self.frequency)
186 }
187
188 fn Type(&self) -> BiquadFilterType {
190 self.filter.get()
191 }
192
193 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}