1use std::cell::Cell;
6use std::f32;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::rust::HandleObject;
11use script_bindings::reflector::reflect_dom_object_with_proto_and_cx;
12use servo_media::audio::biquad_filter_node::{
13 BiquadFilterNodeMessage, BiquadFilterNodeOptions, FilterType,
14};
15use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioNodeType};
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(
66 cx,
67 window,
68 context,
69 node.node_id(),
70 AudioNodeType::BiquadFilterNode,
71 ParamType::Gain,
72 AutomationRate::A_rate,
73 options.gain, f32::MIN, f32::MAX, );
77 let q = AudioParam::new(
78 cx,
79 window,
80 context,
81 node.node_id(),
82 AudioNodeType::BiquadFilterNode,
83 ParamType::Q,
84 AutomationRate::A_rate,
85 options.q, f32::MIN, f32::MAX, );
89 let frequency = AudioParam::new(
90 cx,
91 window,
92 context,
93 node.node_id(),
94 AudioNodeType::BiquadFilterNode,
95 ParamType::Frequency,
96 AutomationRate::A_rate,
97 options.frequency, f32::MIN, f32::MAX, );
101 let detune = AudioParam::new(
102 cx,
103 window,
104 context,
105 node.node_id(),
106 AudioNodeType::BiquadFilterNode,
107 ParamType::Detune,
108 AutomationRate::A_rate,
109 options.detune, f32::MIN, f32::MAX, );
113 Ok(BiquadFilterNode {
114 node,
115 filter,
116 gain: Dom::from_ref(&gain),
117 q: Dom::from_ref(&q),
118 frequency: Dom::from_ref(&frequency),
119 detune: Dom::from_ref(&detune),
120 })
121 }
122
123 pub(crate) fn new(
124 cx: &mut JSContext,
125 window: &Window,
126 context: &BaseAudioContext,
127 options: &BiquadFilterOptions,
128 ) -> Fallible<DomRoot<BiquadFilterNode>> {
129 Self::new_with_proto(cx, window, None, context, options)
130 }
131
132 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
133 fn new_with_proto(
134 cx: &mut JSContext,
135 window: &Window,
136 proto: Option<HandleObject>,
137 context: &BaseAudioContext,
138 options: &BiquadFilterOptions,
139 ) -> Fallible<DomRoot<BiquadFilterNode>> {
140 let node = BiquadFilterNode::new_inherited(cx, window, context, options)?;
141 Ok(reflect_dom_object_with_proto_and_cx(
142 Box::new(node),
143 window,
144 proto,
145 cx,
146 ))
147 }
148}
149
150impl BiquadFilterNodeMethods<crate::DomTypeHolder> for BiquadFilterNode {
151 fn Constructor(
153 cx: &mut JSContext,
154 window: &Window,
155 proto: Option<HandleObject>,
156 context: &BaseAudioContext,
157 options: &BiquadFilterOptions,
158 ) -> Fallible<DomRoot<BiquadFilterNode>> {
159 BiquadFilterNode::new_with_proto(cx, window, proto, context, options)
160 }
161
162 fn Gain(&self) -> DomRoot<AudioParam> {
164 DomRoot::from_ref(&self.gain)
165 }
166
167 fn Q(&self) -> DomRoot<AudioParam> {
169 DomRoot::from_ref(&self.q)
170 }
171
172 fn Detune(&self) -> DomRoot<AudioParam> {
174 DomRoot::from_ref(&self.detune)
175 }
176
177 fn Frequency(&self) -> DomRoot<AudioParam> {
179 DomRoot::from_ref(&self.frequency)
180 }
181
182 fn Type(&self) -> BiquadFilterType {
184 self.filter.get()
185 }
186
187 fn SetType(&self, filter: BiquadFilterType) {
189 self.filter.set(filter);
190 self.node.message(AudioNodeMessage::BiquadFilterNode(
191 BiquadFilterNodeMessage::SetFilterType(filter.convert()),
192 ));
193 }
194}
195
196impl Convert<BiquadFilterNodeOptions> for &BiquadFilterOptions {
197 fn convert(self) -> BiquadFilterNodeOptions {
198 BiquadFilterNodeOptions {
199 gain: *self.gain,
200 q: *self.Q,
201 frequency: *self.frequency,
202 detune: *self.detune,
203 filter: self.type_.convert(),
204 }
205 }
206}
207
208impl Convert<FilterType> for BiquadFilterType {
209 fn convert(self) -> FilterType {
210 match self {
211 BiquadFilterType::Lowpass => FilterType::LowPass,
212 BiquadFilterType::Highpass => FilterType::HighPass,
213 BiquadFilterType::Bandpass => FilterType::BandPass,
214 BiquadFilterType::Lowshelf => FilterType::LowShelf,
215 BiquadFilterType::Highshelf => FilterType::HighShelf,
216 BiquadFilterType::Peaking => FilterType::Peaking,
217 BiquadFilterType::Allpass => FilterType::AllPass,
218 BiquadFilterType::Notch => FilterType::Notch,
219 }
220 }
221}