use rustfft::{FftPlanner, num_complex::Complex};
use super::fft_tools::overlap_add;
use crate::{WindowType, generate_window};
#[derive(Debug, Clone)]
pub struct SpectrumError {
pub error_msg: String
}
pub fn rfft(audio: &[f64], fft_size: usize) -> Vec<Complex<f64>> {
let mut planner = FftPlanner::new();
let fft = planner.plan_fft_forward(fft_size);
let mut spectrum: Vec<Complex<f64>> = Vec::with_capacity(audio.len());
for i in 0..audio.len() {
spectrum.push(Complex{re: audio[i], im: 0.0});
}
fft.process(&mut spectrum);
spectrum[..fft_size / 2 + 1].to_vec()
}
pub fn irfft(spectrum: &[Complex<f64>], fft_size: usize) -> Result<Vec<f64>, SpectrumError> {
let mut planner = FftPlanner::new();
let ifft = planner.plan_fft_inverse(fft_size);
if spectrum.len() != fft_size / 2 + 1 {
return Err(SpectrumError{error_msg: String::from(format!("The spectrum is the wrong size. It should be {}, but it is actually {}.", fft_size / 2 + 1, spectrum.len()))})
}
let mut spectrum_input: Vec<Complex<f64>> = Vec::with_capacity(fft_size);
for i in 0..spectrum.len() {
spectrum_input.push(spectrum[i]);
}
for i in 0..fft_size / 2 - 1 {
spectrum_input.push(spectrum[fft_size / 2 - 1 - i].conj());
}
ifft.process(&mut spectrum_input);
let mut audio: Vec<f64> = Vec::with_capacity(fft_size);
for i in 0..spectrum_input.len() {
audio.push(spectrum_input[i].re);
}
Ok(audio)
}
pub fn rstft(audio: &[f64], fft_size: usize, hop_size: usize, window_type: WindowType) -> Vec<Vec<Complex<f64>>> {
let mut planner: FftPlanner<f64> = FftPlanner::new();
let fft = planner.plan_fft_forward(fft_size);
let mut spectrogram: Vec<Vec<Complex<f64>>> = Vec::new();
let window = generate_window(window_type, fft_size);
let mut hop_idx = 0;
let mut finished = false;
while !finished {
let start_idx = hop_idx * hop_size;
let end_idx = if hop_idx * hop_size + fft_size < audio.len() {
hop_idx * hop_size + fft_size
} else {
audio.len()
};
let num_zeros = if end_idx == audio.len() {
finished = true;
start_idx + fft_size - end_idx
} else {
0
};
let mut fft_data = if num_zeros > 0 {
let window = generate_window(window_type, end_idx - start_idx);
let mut input = {
let audio_chunk_len = end_idx - start_idx;
let mut audio_chunk: Vec<Complex<f64>> = Vec::with_capacity(audio_chunk_len);
for i in 0..audio_chunk_len {
audio_chunk.push(Complex{re: audio[start_idx..end_idx][i] * window[i], im: 0.0});
}
audio_chunk
};
input.extend(vec![Complex{re: 0.0, im: 0.0}; num_zeros]);
input
} else {
let audio_chunk_len = end_idx - start_idx;
let mut audio_chunk: Vec<Complex<f64>> = Vec::with_capacity(audio_chunk_len);
for i in 0..audio_chunk_len {
audio_chunk.push(Complex{re: audio[start_idx..end_idx][i] * window[i], im: 0.0});
}
audio_chunk
};
fft.process(&mut fft_data);
spectrogram.push(fft_data[..fft_size / 2 + 1].to_vec());
hop_idx += 1;
}
spectrogram
}
pub fn irstft(spectrogram: &[Vec<Complex<f64>>], fft_size: usize, hop_size: usize, window_type: WindowType) -> Result<Vec<f64>, SpectrumError> {
let mut planner: FftPlanner<f64> = FftPlanner::new();
let fft = planner.plan_fft_inverse(fft_size);
let num_stft_frames = spectrogram.len();
let num_output_frames = fft_size + (hop_size * (num_stft_frames - 1));
let mut audio_chunks: Vec<Vec<f64>> = vec![Vec::with_capacity(fft_size); num_stft_frames];
let mut window_norm: Vec<f64> = vec![0.0; num_output_frames];
let window_samples = generate_window(window_type, fft_size);
for i in 0..num_stft_frames {
if spectrogram[i].len() != fft_size / 2 + 1 {
return Err(SpectrumError{error_msg: String::from(format!("The spectrum at frame {} has an incorrect length. It should be {}, but it is actually {}.", i, fft_size / 2 + 1, spectrogram[i].len()))})
}
let mut spectrum_input: Vec<Complex<f64>> = Vec::with_capacity(fft_size);
for j in 0..spectrogram[i].len() {
spectrum_input.push(spectrogram[i][j]);
}
for j in 0..fft_size / 2 - 1 {
spectrum_input.push(spectrogram[i][fft_size / 2 - 1 - j].conj());
}
fft.process(&mut spectrum_input);
for j in 0..spectrum_input.len() {
audio_chunks[i].push(spectrum_input[j].re * window_samples[j]);
}
let start_idx = i * hop_size;
let end_idx = start_idx + fft_size;
for j in start_idx..end_idx {
window_norm[j] += window_samples[j - start_idx].powf(2.0);
}
}
let mut audio = overlap_add(&audio_chunks, fft_size, hop_size);
for i in 0..audio.len() {
audio[i] /= window_norm[i];
}
let mut maxval = 0.0;
for i in 0..audio.len() {
maxval = f64::max(audio[i], maxval);
}
let max_dbfs = -6.0;
let max_level_scaler = f64::powf(10.0, max_dbfs / 20.0) / maxval;
for i in 0..audio.len() {
audio[i] *= max_level_scaler;
}
Ok(audio)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::spectrum;
const DIR: &str = "D:/Recording/tests";
const AUDIO: &str = "D:/Recording/tests/viola.wav";
#[test]
fn test_stft() {
let fft_size: usize = 4096;
let hop_size: usize = fft_size / 2;
let window_type = crate::WindowType::Hamming;
let path = String::from(AUDIO);
let mut audio = match crate::read(&path) {
Ok(x) => x,
Err(_) => panic!("could not read audio")
};
let spectrogram: Vec<Vec<Complex<f64>>> = spectrum::rstft(&mut audio.samples[0], fft_size, hop_size, window_type);
let (mags, phases) = spectrum::complex_to_polar_rstft(&spectrogram);
let output_spectrogram = spectrum::polar_to_complex_rstft(&mags, &phases).unwrap();
let output_audio = spectrum::irstft(&output_spectrogram, fft_size, hop_size, window_type).unwrap();
let output_audiofile = crate::AudioFile::new_mono(crate::AudioFormat::S24, 44100, output_audio);
let path: String = String::from(format!("{}/out3.wav", DIR));
match crate::write(&path, &output_audiofile) {
Ok(_) => (),
Err(_) => panic!("could not write audio")
}
}
}