#![no_std]
extern crate alloc;
#[cfg(test)]
#[macro_use]
extern crate std;
use alloc::vec::Vec;
use rustfft::algorithm::Radix4;
use rustfft::num_complex::Complex32;
use rustfft::{Fft, FftDirection};
pub use crate::frequency::{Frequency, FrequencyValue};
pub use crate::limit::FrequencyLimit;
pub use crate::spectrum::{FrequencySpectrum, SpectrumTotalScaleFunctionFactory};
use core::convert::identity;
mod frequency;
mod limit;
mod spectrum;
#[cfg(test)]
mod tests;
pub mod windows;
pub fn samples_fft_to_spectrum(
samples: &[f32],
sampling_rate: u32,
frequency_limit: FrequencyLimit,
per_element_scaling_fn: Option<&dyn Fn(f32) -> f32>,
total_scaling_fn: Option<SpectrumTotalScaleFunctionFactory>,
) -> FrequencySpectrum {
let mut buffer = samples_to_complex(samples);
let fft_len = samples.len();
let fft = Radix4::new(fft_len, FftDirection::Forward);
fft.process(&mut buffer);
fft_result_to_frequency_to_magnitude_map(
buffer,
sampling_rate,
frequency_limit,
per_element_scaling_fn,
total_scaling_fn,
)
}
#[inline(always)]
fn samples_to_complex(samples: &[f32]) -> Vec<Complex32> {
samples
.iter()
.map(|x| Complex32::new(x.clone(), 0.0))
.collect::<Vec<Complex32>>()
}
#[inline(always)]
fn fft_result_to_frequency_to_magnitude_map(
fft_result: Vec<Complex32>,
sampling_rate: u32,
frequency_limit: FrequencyLimit,
per_element_scaling_fn: Option<&dyn Fn(f32) -> f32>,
total_scaling_fn: Option<SpectrumTotalScaleFunctionFactory>,
) -> FrequencySpectrum {
let maybe_min = frequency_limit.maybe_min();
let maybe_max = frequency_limit.maybe_max();
let samples_len = fft_result.len();
let frequency_vec = fft_result
.into_iter()
.take(samples_len / 2)
.enumerate()
.map(|(fft_index, complex)| {
(
fft_index_to_corresponding_frequency(fft_index, samples_len as u32, sampling_rate),
complex,
)
})
.filter(|(fr, _complex)| {
if let Some(min_fr) = maybe_min {
*fr >= min_fr
} else {
true
}
})
.filter(|(fr, _complex)| {
if let Some(max_fr) = maybe_max {
*fr <= max_fr
} else {
true
}
})
.map(|(fr, complex)| (fr, complex.norm()))
.map(|(fr, val)| (fr, per_element_scaling_fn.unwrap_or(&identity)(val)))
.map(|(fr, val)| (Frequency::from(fr), FrequencyValue::from(val)))
.collect::<Vec<(Frequency, FrequencyValue)>>();
let fs = FrequencySpectrum::new(frequency_vec);
if let Some(total_scaling_fn) = total_scaling_fn {
fs.apply_total_scaling_fn(total_scaling_fn)
}
fs
}
#[inline(always)]
fn fft_index_to_corresponding_frequency(
fft_index: usize,
samples_len: u32,
sampling_rate: u32,
) -> f32 {
fft_index as f32 / samples_len as f32 * sampling_rate as f32
}