use std::str::FromStr;
extern crate num;
use num::complex::Complex;
use num::traits::{Float, Signed, FromPrimitive, Zero};
extern crate apodize;
use apodize::CanRepresentPi;
extern crate strider;
use strider::{SliceRing, SliceRingImpl};
extern crate rustfft;
use rustfft::FFT;
#[inline]
pub fn log10_positive<T: Float + Signed + Zero>(value: T) -> T {
let log = value.log10();
if log.is_negative() {
T::zero()
} else {
log
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum WindowType {
Hanning,
Hamming,
Blackman,
Nuttall,
None,
}
impl FromStr for WindowType {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let lower = s.to_lowercase();
match &lower[..] {
"hanning" => Ok(WindowType::Hanning),
"hann" => Ok(WindowType::Hanning),
"hamming" => Ok(WindowType::Hamming),
"blackman" => Ok(WindowType::Blackman),
"nuttall" => Ok(WindowType::Nuttall),
"none" => Ok(WindowType::None),
_ => Err("no match"),
}
}
}
impl std::fmt::Display for WindowType {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "{:?}", self)
}
}
static WINDOW_TYPES: [WindowType; 5] = [WindowType::Hanning,
WindowType::Hamming,
WindowType::Blackman,
WindowType::Nuttall,
WindowType::None];
impl WindowType {
pub fn values() -> [WindowType; 5] {
WINDOW_TYPES
}
}
pub struct STFT<T> {
pub window_size: usize,
pub step_size: usize,
pub fft: FFT<T>,
pub window: Option<Vec<T>>,
pub sample_ring: SliceRingImpl<T>,
pub real_input: Vec<T>,
pub complex_input: Vec<Complex<T>>,
pub complex_output: Vec<Complex<T>>,
}
impl<T: Float + Signed + Zero + FromPrimitive + CanRepresentPi> STFT<T> {
pub fn window_type_to_window_vec(window_type: WindowType,
window_size: usize)
-> Option<Vec<T>> {
match window_type {
WindowType::Hanning => Some(apodize::hanning_iter(window_size).collect::<Vec<T>>()),
WindowType::Hamming => Some(apodize::hamming_iter(window_size).collect::<Vec<T>>()),
WindowType::Blackman => Some(apodize::blackman_iter(window_size).collect::<Vec<T>>()),
WindowType::Nuttall => Some(apodize::nuttall_iter(window_size).collect::<Vec<T>>()),
WindowType::None => None,
}
}
pub fn new(window_type: WindowType, window_size: usize, step_size: usize) -> STFT<T> {
let window = STFT::window_type_to_window_vec(window_type, window_size);
STFT::<T>::new_with_window_vec(window, window_size, step_size)
}
pub fn new_with_window_vec(window: Option<Vec<T>>,
window_size: usize,
step_size: usize)
-> STFT<T> {
assert!(step_size <= window_size);
let inverse = false;
STFT {
window_size: window_size,
step_size: step_size,
fft: FFT::new(window_size, inverse),
sample_ring: SliceRingImpl::new(),
window: window,
real_input: std::iter::repeat(T::zero())
.take(window_size)
.collect(),
complex_input: std::iter::repeat(Complex::<T>::zero())
.take(window_size)
.collect(),
complex_output: std::iter::repeat(Complex::<T>::zero())
.take(window_size)
.collect(),
}
}
#[inline]
pub fn output_size(&self) -> usize {
self.window_size / 2
}
#[inline]
pub fn len(&self) -> usize {
self.sample_ring.len()
}
pub fn append_samples(&mut self, input: &[T]) {
self.sample_ring.push_many_back(input);
}
#[inline]
pub fn contains_enough_to_compute(&self) -> bool {
self.window_size <= self.sample_ring.len()
}
fn compute_into_complex_output(&mut self) {
assert!(self.contains_enough_to_compute());
self.sample_ring.read_many_front(&mut self.real_input[..]);
if let Some(ref window) = self.window {
for (dst, src) in self.real_input.iter_mut().zip(window.iter()) {
*dst = *dst * *src;
}
}
for (dst, src) in self.complex_input.iter_mut().zip(self.real_input.iter()) {
dst.re = src.clone();
}
self.fft.process(&self.complex_input, &mut self.complex_output);
}
pub fn compute_complex_column(&mut self, output: &mut [Complex<T>]) {
assert_eq!(self.output_size(), output.len());
self.compute_into_complex_output();
for (dst, src) in output.iter_mut().zip(self.complex_output.iter()) {
*dst = src.clone();
}
}
pub fn compute_magnitude_column(&mut self, output: &mut [T]) {
assert_eq!(self.output_size(), output.len());
self.compute_into_complex_output();
for (dst, src) in output.iter_mut().zip(self.complex_output.iter()) {
*dst = src.norm();
}
}
pub fn compute_column(&mut self, output: &mut [T]) {
assert_eq!(self.output_size(), output.len());
self.compute_into_complex_output();
for (dst, src) in output.iter_mut().zip(self.complex_output.iter()) {
*dst = log10_positive(src.norm());
}
}
pub fn move_to_next_column(&mut self) {
self.sample_ring.drop_many_front(self.step_size);
}
}