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