web_audio_api/node/
analyser.rs1use crate::analysis::{
2 Analyser, AnalyserRingBuffer, DEFAULT_FFT_SIZE, DEFAULT_MAX_DECIBELS, DEFAULT_MIN_DECIBELS,
3 DEFAULT_SMOOTHING_TIME_CONSTANT,
4};
5use crate::context::{AudioContextRegistration, BaseAudioContext};
6use crate::render::{
7 AudioParamValues, AudioProcessor, AudioRenderQuantum, AudioWorkletGlobalScope,
8};
9
10use super::{AudioNode, AudioNodeOptions, ChannelConfig, ChannelInterpretation};
11
12#[derive(Clone, Debug)]
20pub struct AnalyserOptions {
21 pub fft_size: usize,
22 pub max_decibels: f64,
23 pub min_decibels: f64,
24 pub smoothing_time_constant: f64,
25 pub audio_node_options: AudioNodeOptions,
26}
27
28impl Default for AnalyserOptions {
29 fn default() -> Self {
30 Self {
31 fft_size: DEFAULT_FFT_SIZE,
32 max_decibels: DEFAULT_MAX_DECIBELS,
33 min_decibels: DEFAULT_MIN_DECIBELS,
34 smoothing_time_constant: DEFAULT_SMOOTHING_TIME_CONSTANT,
35 audio_node_options: AudioNodeOptions::default(),
36 }
37 }
38}
39
40#[derive(Debug)]
83pub struct AnalyserNode {
84 registration: AudioContextRegistration,
85 channel_config: ChannelConfig,
86 analyser: Analyser,
87}
88
89impl AudioNode for AnalyserNode {
90 fn registration(&self) -> &AudioContextRegistration {
91 &self.registration
92 }
93
94 fn channel_config(&self) -> &ChannelConfig {
95 &self.channel_config
96 }
97
98 fn number_of_inputs(&self) -> usize {
99 1
100 }
101
102 fn number_of_outputs(&self) -> usize {
103 1
104 }
105}
106
107impl AnalyserNode {
108 pub fn new<C: BaseAudioContext>(context: &C, options: AnalyserOptions) -> Self {
109 context.base().register(move |registration| {
110 let fft_size = options.fft_size;
111 let smoothing_time_constant = options.smoothing_time_constant;
112 let min_decibels = options.min_decibels;
113 let max_decibels = options.max_decibels;
114
115 let mut analyser = Analyser::new();
116 analyser.set_fft_size(fft_size);
117 analyser.set_smoothing_time_constant(smoothing_time_constant);
118 analyser.set_decibels(min_decibels, max_decibels);
119
120 let render = AnalyserRenderer {
121 ring_buffer: analyser.get_ring_buffer_clone(),
122 };
123
124 let node = AnalyserNode {
125 registration,
126 channel_config: options.audio_node_options.into(),
127 analyser,
128 };
129
130 (node, Box::new(render))
131 })
132 }
133
134 pub fn fft_size(&self) -> usize {
140 self.analyser.fft_size()
141 }
142
143 pub fn set_fft_size(&mut self, fft_size: usize) {
149 self.analyser.set_fft_size(fft_size);
150 }
151
152 pub fn smoothing_time_constant(&self) -> f64 {
160 self.analyser.smoothing_time_constant()
161 }
162
163 pub fn set_smoothing_time_constant(&mut self, value: f64) {
169 self.analyser.set_smoothing_time_constant(value);
170 }
171
172 pub fn min_decibels(&self) -> f64 {
179 self.analyser.min_decibels()
180 }
181
182 pub fn set_min_decibels(&mut self, value: f64) {
189 self.analyser.set_decibels(value, self.max_decibels());
190 }
191
192 pub fn max_decibels(&self) -> f64 {
199 self.analyser.max_decibels()
200 }
201
202 pub fn set_max_decibels(&mut self, value: f64) {
209 self.analyser.set_decibels(self.min_decibels(), value);
210 }
211
212 pub fn frequency_bin_count(&self) -> usize {
218 self.analyser.frequency_bin_count()
219 }
220
221 pub fn get_float_time_domain_data(&mut self, buffer: &mut [f32]) {
227 self.analyser.get_float_time_domain_data(buffer);
228 }
229
230 pub fn get_byte_time_domain_data(&mut self, buffer: &mut [u8]) {
236 self.analyser.get_byte_time_domain_data(buffer);
237 }
238
239 pub fn get_float_frequency_data(&mut self, buffer: &mut [f32]) {
245 let current_time = self.registration.context().current_time();
246 self.analyser.get_float_frequency_data(buffer, current_time);
247 }
248
249 pub fn get_byte_frequency_data(&mut self, buffer: &mut [u8]) {
256 let current_time = self.registration.context().current_time();
257 self.analyser.get_byte_frequency_data(buffer, current_time);
258 }
259}
260
261struct AnalyserRenderer {
262 ring_buffer: AnalyserRingBuffer,
263}
264
265impl AudioProcessor for AnalyserRenderer {
266 fn process(
267 &mut self,
268 inputs: &[AudioRenderQuantum],
269 outputs: &mut [AudioRenderQuantum],
270 _params: AudioParamValues<'_>,
271 _scope: &AudioWorkletGlobalScope,
272 ) -> bool {
273 let input = &inputs[0];
275 let output = &mut outputs[0];
276
277 *output = input.clone();
279
280 let mut mono = input.clone();
282 mono.mix(1, ChannelInterpretation::Speakers);
283
284 let data = mono.channel_data(0).as_ref();
286 self.ring_buffer.write(data);
287
288 false
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use crate::context::{
297 AudioContext, AudioContextOptions, BaseAudioContext, OfflineAudioContext,
298 };
299 use crate::node::{AudioNode, AudioScheduledSourceNode};
300 use float_eq::assert_float_eq;
301
302 #[test]
303 fn test_analyser_after_closed() {
304 let options = AudioContextOptions {
305 sink_id: "none".into(),
306 ..AudioContextOptions::default()
307 };
308 let context = AudioContext::new(options);
309
310 let mut src = context.create_constant_source();
311 src.start();
312
313 let mut analyser = context.create_analyser();
314 src.connect(&analyser);
315
316 std::thread::sleep(std::time::Duration::from_millis(20));
318
319 let mut buffer = vec![0.; 128];
320 analyser.get_float_time_domain_data(&mut buffer);
321 assert_float_eq!(&buffer[..], &[1.; 128][..], abs_all <= 0.); context.close_sync();
325 std::thread::sleep(std::time::Duration::from_millis(50));
326
327 let mut buffer = vec![0.; 128];
328 analyser.get_float_time_domain_data(&mut buffer); assert_float_eq!(&buffer[..], &[1.; 128][..], abs_all <= 0.);
332 }
333
334 #[test]
335 fn test_construct_decibels() {
336 let context = OfflineAudioContext::new(1, 128, 44_100.);
337 let options = AnalyserOptions {
338 min_decibels: -10.,
339 max_decibels: 20.,
340 ..AnalyserOptions::default()
341 };
342 let _ = AnalyserNode::new(&context, options);
343 }
344}