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}