lookas 1.10.0

Perception aligned terminal-based audio spectrum visualizer
Documentation
use lookas::dsp::{hann, prepare_fft_input_inplace};
use realfft::num_complex::Complex;
use realfft::{RealFftPlanner, RealToComplex};
use std::sync::Arc;

pub struct FftState {
    pub window: Vec<f32>,
    pub half: usize,
    pub fft: Arc<dyn RealToComplex<f32>>,
    pub buf: Vec<f32>,
    pub fft_out: Vec<Complex<f32>>,
    pub spec_pow: Vec<f32>,
}

impl FftState {
    pub fn new(fft_size: usize) -> Self {
        let half = fft_size / 2;
        let mut planner = RealFftPlanner::<f32>::new();
        let fft = planner.plan_fft_forward(fft_size);
        let buf = fft.make_input_vec();
        let fft_out = fft.make_output_vec();

        Self {
            window: hann(fft_size),
            half,
            fft,
            buf,
            fft_out,
            spec_pow: vec![0.0f32; half],
        }
    }

    pub fn resize(&mut self, fft_size: usize) {
        *self = Self::new(fft_size);
    }

    pub fn compute(&mut self, samples: &[f32], fft_size: usize) {
        prepare_fft_input_inplace(
            samples,
            &self.window,
            &mut self.buf,
        );

        if let Err(e) =
            self.fft.process(&mut self.buf, &mut self.fft_out)
        {
            eprintln!("[lookas] FFT processing error: {e}");
            return;
        }

        #[allow(clippy::cast_precision_loss)]
        let norm_inv = 1.0 / ((fft_size as f32) * (fft_size as f32));
        #[allow(clippy::indexing_slicing)]
        for i in 0..self.half {
            let re = self.fft_out[i].re;
            let im = self.fft_out[i].im;
            self.spec_pow[i] = re.mul_add(re, im * im) * norm_inv;
        }
    }
}