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::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, 1, ),
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 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 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, f32::MIN, 40. * f32::MAX.log10(), );
106 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, f32::MIN, f32::MAX, );
119 let nyquist = (*context.SampleRate()) / 2.;
120 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, 0., nyquist, );
133 let detune_max = 1200. * f32::MAX.log2();
134 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, -detune_max, detune_max, );
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 fn Gain(&self) -> DomRoot<AudioParam> {
163 DomRoot::from_ref(&self.gain)
164 }
165
166 fn Q(&self) -> DomRoot<AudioParam> {
168 DomRoot::from_ref(&self.q)
169 }
170
171 fn Detune(&self) -> DomRoot<AudioParam> {
173 DomRoot::from_ref(&self.detune)
174 }
175
176 fn Frequency(&self) -> DomRoot<AudioParam> {
178 DomRoot::from_ref(&self.frequency)
179 }
180
181 fn Type(&self) -> BiquadFilterType {
183 self.filter.get()
184 }
185
186 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}