Support overlapping FFT blocks

Now an arbitrary number of samples can be read and appended to the FFT
block. Old samples will be dropped such that only BLOCK_LEN samples
are in the memory buffer. Currently one half of the FFT block is
replaced per update.
This commit is contained in:
Thomas Kolb 2021-02-19 23:26:28 +01:00
parent ff018a98e1
commit 942f66f7df
3 changed files with 37 additions and 4 deletions

View file

@ -1,3 +1,6 @@
// definitions for the FFT // definitions for the FFT
pub const BLOCK_LEN: usize = 512; pub const BLOCK_LEN: usize = 512;
pub const SAMP_RATE: f32 = 48000.0; pub const SAMP_RATE: f32 = 48000.0;
// samples read from stdin per update
pub const SAMPLES_PER_UPDATE: usize = BLOCK_LEN/2;

View file

@ -1,6 +1,7 @@
// vim: noet // vim: noet
use std::process::exit; use std::process::exit;
use std::collections::VecDeque;
use byteorder::{NativeEndian, ReadBytesExt}; use byteorder::{NativeEndian, ReadBytesExt};
@ -34,17 +35,24 @@ fn main()
println!("Done! Starting main loop…"); println!("Done! Starting main loop…");
// array for samples directly read from stream // array for samples directly read from stream
let mut samples = [0i16; config::BLOCK_LEN]; let mut samples: VecDeque<i16> = VecDeque::with_capacity(config::BLOCK_LEN);
// main loop // main loop
loop { loop {
// read a block of samples and exit gracefully on EOF // read a block of samples and exit gracefully on EOF
for sample in samples.iter_mut() { for _i in 0 .. config::SAMPLES_PER_UPDATE {
// avoid increasing the size of the deque
if samples.len() == config::BLOCK_LEN {
samples.pop_front();
}
// read a sample from the input
let res = stdin.read_i16::<NativeEndian>(); let res = stdin.read_i16::<NativeEndian>();
// if everything is ok, append it to the samples deque
match res { match res {
Ok(s) => *sample = s, Ok(s) => samples.push_back(s),
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => { Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
println!("End of stream. Exiting."); println!("End of stream. Exiting.");
exit(0); exit(0);
@ -53,7 +61,13 @@ fn main()
} }
} }
sigproc.import_i16_mono(&samples).unwrap(); // only run calculations if the deque has been filled enough
if samples.len() < config::BLOCK_LEN {
continue;
}
sigproc.import_i16_mono_from_iter(samples.iter()).unwrap();
sigproc.update_fft().unwrap(); sigproc.update_fft().unwrap();
let energy_bass = sigproc.get_energy_in_band( 0.0, 400.0); let energy_bass = sigproc.get_energy_in_band( 0.0, 400.0);

View file

@ -1,5 +1,7 @@
// vim: noet // vim: noet
#[allow(dead_code)]
use fftw::array::AlignedVec; use fftw::array::AlignedVec;
use fftw::plan::*; use fftw::plan::*;
use fftw::types::*; use fftw::types::*;
@ -88,6 +90,20 @@ impl SignalProcessing
Ok(()) Ok(())
} }
pub fn import_i16_mono_from_iter<'a>(&mut self, mut iter: impl std::iter::Iterator<Item=&'a i16>) -> std::result::Result<(), &str>
{
for fft_samp in self.fft_input.iter_mut() {
match iter.next() {
Some(sample) => *fft_samp = *sample as f32,
None => return Err("Too few samples in input.")
}
}
self.apply_window();
Ok(())
}
pub fn update_fft(&mut self) -> fftw::error::Result<()> pub fn update_fft(&mut self) -> fftw::error::Result<()>
{ {
self.fft_plan.r2c(&mut self.fft_input, &mut self.fft_output)?; self.fft_plan.r2c(&mut self.fft_input, &mut self.fft_output)?;