servo_media_audio/
analyser_node.rs1use crate::block::{Block, Chunk, FRAMES_PER_BLOCK_USIZE};
2use crate::node::AudioNodeEngine;
3use crate::node::BlockInfo;
4use crate::node::{AudioNodeType, ChannelInfo, ChannelInterpretation};
5use std::cmp;
6use std::f32::consts::PI;
7
8#[derive(AudioNodeCommon)]
9pub(crate) struct AnalyserNode {
10 channel_info: ChannelInfo,
11 callback: Box<dyn FnMut(Block) + Send>,
12}
13
14impl AnalyserNode {
15 pub fn new(callback: Box<dyn FnMut(Block) + Send>, channel_info: ChannelInfo) -> Self {
16 Self {
17 callback,
18 channel_info,
19 }
20 }
21}
22
23impl AudioNodeEngine for AnalyserNode {
24 fn node_type(&self) -> AudioNodeType {
25 AudioNodeType::AnalyserNode
26 }
27
28 fn process(&mut self, inputs: Chunk, _: &BlockInfo) -> Chunk {
29 debug_assert!(inputs.len() == 1);
30
31 let mut push = inputs.blocks[0].clone();
32 push.mix(1, ChannelInterpretation::Speakers);
33
34 (self.callback)(push);
35
36 inputs
38 }
39}
40
41pub const MAX_FFT_SIZE: usize = 32768;
43pub const MAX_BLOCK_COUNT: usize = MAX_FFT_SIZE / FRAMES_PER_BLOCK_USIZE;
44
45pub struct AnalysisEngine {
49 fft_size: usize,
51 smoothing_constant: f64,
52 min_decibels: f64,
53 max_decibels: f64,
54 data: Box<[f32; MAX_FFT_SIZE]>,
57 current_block: usize,
59 fft_computed: bool,
61 blackman_windows: Vec<f32>,
63 smoothed_fft_data: Vec<f32>,
65 computed_fft_data: Vec<f32>,
67 windowed: Vec<f32>,
70}
71
72impl AnalysisEngine {
73 pub fn new(
74 fft_size: usize,
75 smoothing_constant: f64,
76 min_decibels: f64,
77 max_decibels: f64,
78 ) -> Self {
79 debug_assert!(fft_size >= 32 && fft_size <= 32768);
80 debug_assert!(fft_size & fft_size - 1 == 0);
82 debug_assert!(smoothing_constant <= 1. && smoothing_constant >= 0.);
83 debug_assert!(max_decibels > min_decibels);
84 Self {
85 fft_size,
86 smoothing_constant,
87 min_decibels,
88 max_decibels,
89 data: Box::new([0.; MAX_FFT_SIZE]),
90 current_block: MAX_BLOCK_COUNT - 1,
91 fft_computed: false,
92 blackman_windows: Vec::with_capacity(fft_size),
93 computed_fft_data: Vec::with_capacity(fft_size / 2),
94 smoothed_fft_data: Vec::with_capacity(fft_size / 2),
95 windowed: Vec::with_capacity(fft_size),
96 }
97 }
98
99 pub fn set_fft_size(&mut self, fft_size: usize) {
100 debug_assert!(fft_size >= 32 && fft_size <= 32768);
101 debug_assert!(fft_size & fft_size - 1 == 0);
103 self.fft_size = fft_size;
104 self.fft_computed = false;
105 }
106
107 pub fn get_fft_size(&self) -> usize {
108 self.fft_size
109 }
110
111 pub fn set_smoothing_constant(&mut self, smoothing_constant: f64) {
112 debug_assert!(smoothing_constant <= 1. && smoothing_constant >= 0.);
113 self.smoothing_constant = smoothing_constant;
114 self.fft_computed = false;
115 }
116
117 pub fn get_smoothing_constant(&self) -> f64 {
118 self.smoothing_constant
119 }
120
121 pub fn set_min_decibels(&mut self, min_decibels: f64) {
122 debug_assert!(min_decibels < self.max_decibels);
123 self.min_decibels = min_decibels;
124 }
125
126 pub fn get_min_decibels(&self) -> f64 {
127 self.min_decibels
128 }
129
130 pub fn set_max_decibels(&mut self, max_decibels: f64) {
131 debug_assert!(self.min_decibels < max_decibels);
132 self.max_decibels = max_decibels;
133 }
134
135 pub fn get_max_decibels(&self) -> f64 {
136 self.max_decibels
137 }
138
139 fn advance(&mut self) {
140 self.current_block += 1;
141 if self.current_block >= MAX_BLOCK_COUNT {
142 self.current_block = 0;
143 }
144 }
145
146 fn curent_block_mut(&mut self) -> &mut [f32] {
148 let index = FRAMES_PER_BLOCK_USIZE * self.current_block;
149 &mut self.data[index..(index + FRAMES_PER_BLOCK_USIZE)]
150 }
151
152 fn convert_index(&self, index: usize) -> usize {
155 let offset = self.fft_size - index;
156 let last_element = (1 + self.current_block) * FRAMES_PER_BLOCK_USIZE - 1;
157 if offset > last_element {
158 MAX_FFT_SIZE - offset + last_element
159 } else {
160 last_element - offset
161 }
162 }
163
164 fn advance_index(&self, index: &mut usize) {
166 *index += 1;
167 if *index >= MAX_FFT_SIZE {
168 *index = 0;
169 }
170 }
171
172 pub fn push(&mut self, mut block: Block) {
173 debug_assert!(block.chan_count() == 1);
174 self.advance();
175 if !block.is_silence() {
176 self.curent_block_mut().copy_from_slice(block.data_mut());
177 }
178 self.fft_computed = false;
179 }
180
181 fn compute_blackman_windows(&mut self) {
183 if self.blackman_windows.len() == self.fft_size {
184 return;
185 }
186 const ALPHA: f32 = 0.16;
187 const ALPHA_0: f32 = (1. - ALPHA) / 2.;
188 const ALPHA_1: f32 = 1. / 2.;
189 const ALPHA_2: f32 = ALPHA / 2.;
190 self.blackman_windows.resize(self.fft_size, 0.);
191 let coeff = PI * 2. / self.fft_size as f32;
192 for n in 0..self.fft_size {
193 self.blackman_windows[n] = ALPHA_0 - ALPHA_1 * (coeff * n as f32).cos()
194 + ALPHA_2 * (2. * coeff * n as f32).cos();
195 }
196 }
197
198 fn apply_blackman_window(&mut self) {
199 self.compute_blackman_windows();
200 self.windowed.resize(self.fft_size, 0.);
201
202 let mut data_idx = self.convert_index(0);
203 for n in 0..self.fft_size {
204 self.windowed[n] = self.blackman_windows[n] * self.data[data_idx];
205 self.advance_index(&mut data_idx);
206 }
207 }
208
209 fn compute_fft(&mut self) {
210 if self.fft_computed {
211 return;
212 }
213 self.fft_computed = true;
214 self.apply_blackman_window();
215 self.computed_fft_data.resize(self.fft_size / 2, 0.);
216 self.smoothed_fft_data.resize(self.fft_size / 2, 0.);
217
218 for k in 0..(self.fft_size / 2) {
219 let mut sum_real = 0.;
220 let mut sum_imaginary = 0.;
221 let factor = -2. * PI * k as f32 / self.fft_size as f32;
222 for n in 0..(self.fft_size) {
223 sum_real += self.windowed[n] * (factor * n as f32).cos();
224 sum_imaginary += self.windowed[n] * (factor * n as f32).sin();
225 }
226 let sum_real = sum_real / self.fft_size as f32;
227 let sum_imaginary = sum_imaginary / self.fft_size as f32;
228 let magnitude = (sum_real * sum_real + sum_imaginary * sum_imaginary).sqrt();
229 self.smoothed_fft_data[k] = (self.smoothing_constant * self.smoothed_fft_data[k] as f64
230 + (1. - self.smoothing_constant) * magnitude as f64)
231 as f32;
232 self.computed_fft_data[k] = 20. * self.smoothed_fft_data[k].log(10.);
233 }
234 }
235
236 pub fn fill_time_domain_data(&self, dest: &mut [f32]) {
237 let mut data_idx = self.convert_index(0);
238 let end = cmp::min(self.fft_size, dest.len());
239 for n in 0..end {
240 dest[n] = self.data[data_idx];
241 self.advance_index(&mut data_idx);
242 }
243 }
244
245 pub fn fill_byte_time_domain_data(&self, dest: &mut [u8]) {
246 let mut data_idx = self.convert_index(0);
247 let end = cmp::min(self.fft_size, dest.len());
248 for n in 0..end {
249 let result = 128. * (1. + self.data[data_idx]);
250 dest[n] = clamp_255(result);
251 self.advance_index(&mut data_idx)
252 }
253 }
254
255 pub fn fill_frequency_data(&mut self, dest: &mut [f32]) {
256 self.compute_fft();
257 let len = cmp::min(dest.len(), self.computed_fft_data.len());
258 dest[0..len].copy_from_slice(&mut self.computed_fft_data[0..len]);
259 }
260
261 pub fn fill_byte_frequency_data(&mut self, dest: &mut [u8]) {
262 self.compute_fft();
263 let len = cmp::min(dest.len(), self.computed_fft_data.len());
264 let ratio = 255. / (self.max_decibels - self.min_decibels);
265 for freq in 0..len {
266 let result = ratio * (self.computed_fft_data[freq] as f64 - self.min_decibels);
267 dest[freq] = clamp_255(result as f32);
268 }
269 }
270}
271
272fn clamp_255(val: f32) -> u8 {
273 if val > 255. {
274 255
275 } else if val < 0. {
276 0
277 } else {
278 val as u8
279 }
280}