script/dom/audio/
analysernode.rs1use std::sync::{Arc, OnceLock};
6
7use dom_struct::dom_struct;
8use js::context::{JSContext, NoGC};
9use js::rust::{CustomAutoRooterGuard, HandleObject};
10use js::typedarray::{Float32Array, Uint8Array};
11use script_bindings::cell::DomRefCell;
12use script_bindings::reflector::reflect_dom_object_with_proto;
13use servo_base::generic_channel::GenericCallback;
14use servo_media::audio::analyser_node::AnalysisEngine;
15use servo_media::audio::audio_node::AudioNodeInit;
16use servo_media::audio::block::Block;
17use servo_media::audio::graph::NodeId;
18
19use crate::dom::audio::audionode::{AudioNode, AudioNodeOptionsHelper};
20use crate::dom::audio::baseaudiocontext::BaseAudioContext;
21use crate::dom::audionode::UnwrappedAudioNodeOptions;
22use crate::dom::bindings::codegen::Bindings::AnalyserNodeBinding::{
23 AnalyserNodeMethods, AnalyserOptions,
24};
25use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
26 ChannelCountMode, ChannelInterpretation,
27};
28use crate::dom::bindings::error::{Error, Fallible};
29use crate::dom::bindings::num::Finite;
30use crate::dom::bindings::refcounted::Trusted;
31use crate::dom::bindings::root::DomRoot;
32use crate::dom::window::Window;
33
34#[dom_struct]
35pub(crate) struct AnalyserNode {
36 node: AudioNode,
37 #[no_trace]
38 engine: DomRefCell<AnalysisEngine>,
39}
40
41impl AnalyserNode {
42 fn new_inherited(
43 node_id: Option<NodeId>,
44 context: &BaseAudioContext,
45 node_options: UnwrappedAudioNodeOptions,
46 engine: AnalysisEngine,
47 ) -> AnalyserNode {
48 AnalyserNode {
49 node: AudioNode::new_inherited(
50 node_id,
51 context,
52 node_options,
53 1, 1, ),
56 engine: DomRefCell::new(engine),
57 }
58 }
59
60 pub(crate) fn push_block(&self, block: Block) {
61 self.engine.borrow_mut().push(block)
62 }
63}
64
65impl AnalyserNodeMethods<crate::DomTypeHolder> for AnalyserNode {
66 fn Constructor(
68 cx: &mut JSContext,
69 window: &Window,
70 proto: Option<HandleObject>,
71 context: &BaseAudioContext,
72 options: &AnalyserOptions,
73 ) -> Fallible<DomRoot<AnalyserNode>> {
74 let node_options =
75 options
76 .parent
77 .unwrap_or(2, ChannelCountMode::Max, ChannelInterpretation::Speakers);
78 if AudioNode::channel_count_check(node_options.count) {
79 return Err(Error::NotSupported(Some(String::from(
80 "Channel count not supported",
81 ))));
82 }
83
84 if options.fftSize > 32768 ||
85 options.fftSize < 32 ||
86 (options.fftSize & (options.fftSize - 1) != 0)
87 {
88 return Err(Error::IndexSize(Some(
89 "fftSize must be a power of two between 32 and 32768".into(),
90 )));
91 }
92
93 if *options.maxDecibels <= *options.minDecibels {
94 return Err(Error::IndexSize(Some(
95 "maxDecibels must be greater than minDecibels".into(),
96 )));
97 }
98
99 if *options.smoothingTimeConstant < 0. || *options.smoothingTimeConstant > 1. {
100 return Err(Error::IndexSize(Some(
101 "smoothingTimeConstant must be between 0 and 1".into(),
102 )));
103 }
104 let callback_oncelock = Arc::new(OnceLock::new());
105 let audio_node_init = AudioNodeInit::AnalyserNode(callback_oncelock.clone());
106 let node_id =
107 AudioNode::generate_audio_node_id(cx, &node_options, audio_node_init, context);
108
109 let engine = AnalysisEngine::new(
110 options.fftSize as usize,
111 *options.smoothingTimeConstant,
112 *options.minDecibels,
113 *options.maxDecibels,
114 );
115 let node = reflect_dom_object_with_proto(
116 cx,
117 Box::new(AnalyserNode::new_inherited(
118 node_id,
119 context,
120 node_options,
121 engine,
122 )),
123 window,
124 proto,
125 );
126 let task_source = window
127 .as_global_scope()
128 .task_manager()
129 .dom_manipulation_task_source()
130 .to_sendable();
131 let this = Trusted::new(&*node);
132
133 let callback = GenericCallback::new(move |block| {
134 let this = this.clone();
135 task_source.queue(task!(append_analysis_block: move || {
136 let this = this.root();
137 this.push_block(block.unwrap())
138 }));
139 })
140 .unwrap();
141 if callback_oncelock.set(callback).is_err() {
142 log::error!("AnalyzerNode callback is already set. Not setting it again");
143 }
144 Ok(node)
145 }
146
147 fn GetFloatFrequencyData(&self, no_gc: &NoGC, mut array: CustomAutoRooterGuard<Float32Array>) {
149 let dest = array.as_mut_slice_safe(no_gc);
152 self.engine
153 .borrow_mut()
154 .fill_frequency_data(dest.unwrap_or(&mut []));
155 }
156
157 fn GetByteFrequencyData(&self, no_gc: &NoGC, mut array: CustomAutoRooterGuard<Uint8Array>) {
159 let dest = array.as_mut_slice_safe(no_gc);
162 self.engine
163 .borrow_mut()
164 .fill_byte_frequency_data(dest.unwrap_or(&mut []));
165 }
166
167 fn GetFloatTimeDomainData(&self, no_gc: &NoGC, mut array: CustomAutoRooterGuard<Float32Array>) {
169 let dest = array.as_mut_slice_safe(no_gc);
172 self.engine
173 .borrow()
174 .fill_time_domain_data(dest.unwrap_or(&mut []));
175 }
176
177 fn GetByteTimeDomainData(&self, no_gc: &NoGC, mut array: CustomAutoRooterGuard<Uint8Array>) {
179 let dest = array.as_mut_slice_safe(no_gc);
182 self.engine
183 .borrow()
184 .fill_byte_time_domain_data(dest.unwrap_or(&mut []));
185 }
186
187 fn SetFftSize(&self, value: u32) -> Fallible<()> {
189 if !(32..=32768).contains(&value) || (value & (value - 1) != 0) {
190 return Err(Error::IndexSize(Some(
191 "fftSize must be a power of two between 32 and 32768".into(),
192 )));
193 }
194 self.engine.borrow_mut().set_fft_size(value as usize);
195 Ok(())
196 }
197
198 fn FftSize(&self) -> u32 {
200 self.engine.borrow().get_fft_size() as u32
201 }
202
203 fn FrequencyBinCount(&self) -> u32 {
205 self.FftSize() / 2
206 }
207
208 fn MinDecibels(&self) -> Finite<f64> {
210 Finite::wrap(self.engine.borrow().get_min_decibels())
211 }
212
213 fn SetMinDecibels(&self, value: Finite<f64>) -> Fallible<()> {
215 if *value >= self.engine.borrow().get_max_decibels() {
216 return Err(Error::IndexSize(Some(
217 "minDecibels must be less than maxDecibels".into(),
218 )));
219 }
220 self.engine.borrow_mut().set_min_decibels(*value);
221 Ok(())
222 }
223
224 fn MaxDecibels(&self) -> Finite<f64> {
226 Finite::wrap(self.engine.borrow().get_max_decibels())
227 }
228
229 fn SetMaxDecibels(&self, value: Finite<f64>) -> Fallible<()> {
231 if *value <= self.engine.borrow().get_min_decibels() {
232 return Err(Error::IndexSize(Some(
233 "maxDecibels must be greater than minDecibels".into(),
234 )));
235 }
236 self.engine.borrow_mut().set_max_decibels(*value);
237 Ok(())
238 }
239
240 fn SmoothingTimeConstant(&self) -> Finite<f64> {
242 Finite::wrap(self.engine.borrow().get_smoothing_constant())
243 }
244
245 fn SetSmoothingTimeConstant(&self, value: Finite<f64>) -> Fallible<()> {
247 if *value < 0. || *value > 1. {
248 return Err(Error::IndexSize(Some(
249 "smoothingTimeConstant must be between 0 and 1".into(),
250 )));
251 }
252 self.engine.borrow_mut().set_smoothing_constant(*value);
253 Ok(())
254 }
255}