use crate::alloc::ChannelData;
use crate::BUFFER_SIZE;
use realfft::{num_complex::Complex, RealFftPlanner};
use std::f32::consts::PI;
const MAX_QUANTA: usize = 256;
const MAX_SAMPLES: usize = MAX_QUANTA * BUFFER_SIZE as usize;
pub fn generate_blackman(size: usize) -> impl Iterator<Item = f32> {
let alpha = 0.16;
let a0 = (1. - alpha) / 2.;
let a1 = 1. / 2.;
let a2 = alpha / 2.;
(0..size).map(move |i| {
a0 - a1 * (2. * PI * i as f32 / size as f32).cos()
+ a2 * (4. * PI * i as f32 / size as f32).cos()
})
}
struct TimeAnalyser {
buffer: Vec<ChannelData>,
index: u8,
previous_cycle_index: u8,
}
impl TimeAnalyser {
fn new() -> Self {
Self {
buffer: Vec::with_capacity(MAX_QUANTA),
index: 0,
previous_cycle_index: 0,
}
}
fn add_data(&mut self, data: ChannelData) {
if self.buffer.len() < 256 {
self.buffer.push(data);
} else {
self.buffer[self.index as usize] = data;
}
self.index = self.index.wrapping_add(1);
}
fn check_complete_cycle(&mut self, fft_size: usize) -> bool {
let processed = self.index.wrapping_sub(self.previous_cycle_index);
let processed_samples = processed as usize * BUFFER_SIZE as usize;
if processed_samples % fft_size == 0 {
self.previous_cycle_index = self.index;
return true;
}
false
}
fn get_float_time(&self, buffer: &mut [f32], fft_size: usize) {
debug_assert!(!self.buffer.is_empty());
let silence = self.buffer[0].silence();
let data_chunks = self.buffer[self.index as usize..]
.iter()
.chain(self.buffer[..self.index as usize].iter())
.rev()
.chain(std::iter::repeat(&silence));
let true_size = fft_size.min(buffer.len());
let buf_chunks = buffer[0..true_size].chunks_mut(BUFFER_SIZE as usize).rev();
buf_chunks
.zip(data_chunks)
.for_each(|(b, d)| b.copy_from_slice(&d[..b.len()]));
}
}
pub(crate) struct Analyser {
time: TimeAnalyser,
fft_planner: RealFftPlanner<f32>,
fft_input: Vec<f32>,
fft_scratch: Vec<Complex<f32>>,
fft_output: Vec<Complex<f32>>,
current_fft_size: usize,
previous_block: Vec<f32>,
blackman: Vec<f32>,
}
impl Analyser {
pub fn new(initial_fft_size: usize) -> Self {
let mut fft_planner = RealFftPlanner::<f32>::new();
let max_fft = fft_planner.plan_fft_forward(MAX_SAMPLES);
let fft_input = max_fft.make_input_vec();
let fft_scratch = max_fft.make_scratch_vec();
let fft_output = max_fft.make_output_vec();
let previous_block = vec![0.; fft_output.len()];
let mut blackman = Vec::with_capacity(fft_input.len());
generate_blackman(initial_fft_size).for_each(|v| blackman.push(v));
Self {
time: TimeAnalyser::new(),
fft_planner,
fft_input,
fft_scratch,
fft_output,
current_fft_size: initial_fft_size,
previous_block,
blackman,
}
}
pub fn current_fft_size(&self) -> usize {
self.current_fft_size
}
pub fn add_data(&mut self, data: ChannelData) {
self.time.add_data(data);
}
pub fn get_float_time(&self, buffer: &mut [f32], fft_size: usize) {
self.time.get_float_time(buffer, fft_size);
}
pub fn check_complete_cycle(&mut self, fft_size: usize) -> bool {
self.time.check_complete_cycle(fft_size)
}
pub fn get_float_frequency(&mut self, buffer: &mut [f32]) {
let previous_block = &mut self.previous_block[..self.current_fft_size / 2 + 1];
let norm = 20. * (self.current_fft_size as f32).sqrt().log10();
buffer
.iter_mut()
.zip(previous_block.iter())
.for_each(|(b, o)| *b = 20. * o.log10() - norm);
}
pub fn calculate_float_frequency(&mut self, fft_size: usize, smoothing_time_constant: f32) {
if self.current_fft_size != fft_size {
self.previous_block[0..fft_size / 2 + 1]
.iter_mut()
.for_each(|v| *v = 0.);
self.blackman.clear();
generate_blackman(fft_size).for_each(|v| self.blackman.push(v));
self.current_fft_size = fft_size;
}
let r2c = self.fft_planner.plan_fft_forward(fft_size);
let input = &mut self.fft_input[..fft_size];
let output = &mut self.fft_output[..fft_size / 2 + 1];
let scratch = &mut self.fft_scratch[..r2c.get_scratch_len()];
let previous_block = &mut self.previous_block[..fft_size / 2 + 1];
self.time.get_float_time(input, fft_size);
input
.iter_mut()
.zip(self.blackman.iter())
.for_each(|(i, b)| *i *= *b);
r2c.process_with_scratch(input, output, scratch).unwrap();
previous_block
.iter_mut()
.zip(output.iter())
.for_each(|(p, c)| {
*p = smoothing_time_constant * *p + (1. - smoothing_time_constant) * c.norm()
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::alloc::Alloc;
const LEN: usize = BUFFER_SIZE as usize;
#[test]
fn assert_index_size() {
assert_eq!(u8::MAX as usize + 1, MAX_QUANTA);
}
#[test]
fn test_time_domain() {
let alloc = Alloc::with_capacity(256);
let mut analyser = TimeAnalyser::new();
let mut buffer = vec![-1.; LEN * 5];
analyser.add_data(alloc.silence());
analyser.get_float_time(&mut buffer[..], LEN * 5);
assert_eq!(&buffer[..], &[0.; LEN * 5]);
for i in 0..258 {
let mut signal = alloc.silence();
signal.copy_from_slice(&[i as f32; LEN]);
analyser.add_data(signal);
}
analyser.get_float_time(&mut buffer[..], LEN * 4);
assert_eq!(&buffer[0..LEN], &[254.; LEN]);
assert_eq!(&buffer[LEN..LEN * 2], &[255.; LEN]);
assert_eq!(&buffer[LEN * 2..LEN * 3], &[256.; LEN]);
assert_eq!(&buffer[LEN * 3..LEN * 4], &[257.; LEN]);
assert_eq!(&buffer[LEN * 4..LEN * 5], &[0.; LEN]);
buffer.resize(32, 0.);
analyser.get_float_time(&mut buffer[..], LEN);
assert_eq!(&buffer[..], &[257.; 32]);
}
#[test]
fn test_complete_cycle() {
let alloc = Alloc::with_capacity(256);
let mut analyser = TimeAnalyser::new();
analyser.add_data(alloc.silence());
assert!(analyser.check_complete_cycle(32));
analyser.add_data(alloc.silence());
assert!(analyser.check_complete_cycle(LEN));
analyser.add_data(alloc.silence());
assert!(!analyser.check_complete_cycle(LEN * 2));
analyser.add_data(alloc.silence());
assert!(analyser.check_complete_cycle(LEN * 2));
analyser.add_data(alloc.silence());
assert!(!analyser.check_complete_cycle(LEN * 2));
}
#[test]
fn test_freq_domain() {
let alloc = Alloc::with_capacity(256);
let fft_size: usize = LEN * 4;
let mut analyser = Analyser::new(fft_size);
let mut buffer = vec![-1.; fft_size];
analyser.add_data(alloc.silence());
analyser.calculate_float_frequency(fft_size, 0.8);
analyser.get_float_frequency(&mut buffer[..]);
assert_eq!(&buffer[0..LEN * 2 + 1], &[f32::NEG_INFINITY; LEN * 2 + 1]);
assert_eq!(&buffer[LEN * 2 + 1..], &[-1.; LEN * 2 - 1]);
for i in 0..258 {
let mut signal = alloc.silence();
signal.copy_from_slice(&[i as f32; LEN]);
analyser.add_data(signal);
}
analyser.calculate_float_frequency(fft_size, 0.8);
analyser.get_float_frequency(&mut buffer[..]);
assert!(&buffer[0..LEN * 2 + 1] != &[f32::NEG_INFINITY; LEN * 2 + 1]);
}
#[test]
fn test_blackman() {
let values: Vec<f32> = generate_blackman(2048).collect();
let min = values
.iter()
.fold(1000., |min, &val| if val < min { val } else { min });
let max = values
.iter()
.fold(0., |max, &val| if val > max { val } else { max });
assert!(min < 0.01 && min > 0.);
assert!(max > 0.99 && max <= 1.);
let min_pos = values.iter().position(|&v| v == min).unwrap();
let max_pos = values.iter().position(|&v| v == max).unwrap();
assert_eq!(min_pos, 0);
assert_eq!(max_pos, 1024);
}
}