use core::f64;
use num::Complex;
use rustfft::FftPlanner;
use crate::spectrum;
use crate::spectrum::SpectrumError;
use crate::util::*;
use crate::analysis::computation::*;
pub fn alpha_ratio(magnitude_spectrum: &[f64], rfft_freqs: &[f64]) -> f64 {
let mut idx_50: usize = 0;
let mut idx_1k: usize = 0;
let mut idx_2k: usize = 0;
for i in 0..rfft_freqs.len() {
if rfft_freqs[i] < 50.0 {
idx_50 += 1;
}
if rfft_freqs[i] < 1000.0 {
idx_1k += 1;
}
if rfft_freqs[i] > 2000.0 {
break;
}
idx_2k += 1;
}
let lower_sum: f64 = magnitude_spectrum[idx_50..idx_1k].iter().sum();
let upper_sum: f64 = magnitude_spectrum[idx_1k..idx_2k].iter().sum();
lower_sum / upper_sum
}
pub fn autocorrelation(audio: &[f64], fft_size: usize) -> Result<Vec<f64>, SpectrumError> {
if audio.len() != fft_size {
return Err(SpectrumError{ error_msg: String::from("The audio length does not match the FFT size.")});
}
let padded_fft_size = fft_size * 2;
let mut auto: Vec<f64> = vec![0.0; fft_size];
let mut planner = FftPlanner::new();
let fft = planner.plan_fft_forward(padded_fft_size);
let ifft = planner.plan_fft_inverse(padded_fft_size);
let mut spectrum: Vec<Complex<f64>> = Vec::with_capacity(padded_fft_size);
for _ in 0..fft_size / 2 {
spectrum.push(Complex{re: 0.0, im: 0.0})
}
for i in 0..audio.len() {
spectrum.push(Complex{re: audio[i], im: 0.0});
}
for _ in 0..fft_size / 2 {
spectrum.push(Complex{re: 0.0, im: 0.0})
}
fft.process(&mut spectrum);
for i in 0..padded_fft_size {
spectrum[i] = spectrum[i] * spectrum[i].conj();
}
ifft.process(&mut spectrum);
for i in 0..fft_size {
auto[i] = spectrum[i + fft_size].re;
}
Ok(auto)
}
pub fn autocorrelation_power_spectrum(audio: &[f64], fft_size: usize) -> Result<Vec<f64>, SpectrumError> {
if audio.len() != fft_size {
return Err(SpectrumError{error_msg: String::from(format!("The audio vector length ({}) was not the same as the FFT size ({}).", audio.len(), fft_size))});
}
let complex_spectrum = spectrum::rfft(audio, fft_size);
let (mut magnitude_spectrum, phase_spectrum) = spectrum::complex_to_polar_rfft(&complex_spectrum);
for i in 0..magnitude_spectrum.len() {
magnitude_spectrum[i] = magnitude_spectrum[i] * magnitude_spectrum[i];
}
let complex_power_spectrum = match spectrum::polar_to_complex_rfft(&magnitude_spectrum, &phase_spectrum) {
Ok(x) => x,
Err(err) => return Err(err)
};
match spectrum::irfft(&complex_power_spectrum, fft_size) {
Ok(x) => Ok(x),
Err(err) => Err(err)
}
}
pub fn hammarberg_index(magnitude_spectrum: &[f64], rfft_freqs: &[f64]) -> f64 {
let mut idx_2k: usize = 0;
for i in 0..rfft_freqs.len() {
if rfft_freqs[i] > 2000.0 {
break;
}
idx_2k += 1;
}
let lower_max = match maxargmax(&magnitude_spectrum[..idx_2k]) {
Some((val, _)) => val,
None => 0.0
};
let upper_max = match maxargmax(&magnitude_spectrum[idx_2k..]) {
Some((val, _)) => val,
None => 0.0
};
lower_max / upper_max
}
pub fn harmonicity(magnitude_spectrum: &[f64], normalize_by_total_energy: bool) -> f64 {
let mut argmaxmin: Vec<usize> = Vec::new();
let mut maxmin: Vec<f64> = Vec::new();
for i in 2..magnitude_spectrum.len() - 2 {
if magnitude_spectrum[i-2] < magnitude_spectrum[i] &&
magnitude_spectrum[i-1] < magnitude_spectrum[i] &&
magnitude_spectrum[i+1] < magnitude_spectrum[i] &&
magnitude_spectrum[i+2] < magnitude_spectrum[i] {
argmaxmin.push(i);
maxmin.push(magnitude_spectrum[i]);
}
else if magnitude_spectrum[i-2] > magnitude_spectrum[i] &&
magnitude_spectrum[i-1] > magnitude_spectrum[i] &&
magnitude_spectrum[i+1] > magnitude_spectrum[i] &&
magnitude_spectrum[i+2] > magnitude_spectrum[i] {
argmaxmin.push(i);
maxmin.push(magnitude_spectrum[i]);
}
}
let mut distance_sum: f64 = 0.0;
for i in 1..maxmin.len() {
distance_sum += f64::abs(maxmin[i] - maxmin[i-1]);
}
if normalize_by_total_energy {
distance_sum / magnitude_spectrum.iter().sum::<f64>()
} else {
distance_sum / magnitude_spectrum.len() as f64
}
}
#[inline]
pub fn make_log_spectrum(spectrum: &[f64], coef: f64, floor: f64, ceil: Option<f64>) -> Vec<f64> {
let mut log_spec: Vec<f64> = Vec::with_capacity(spectrum.len());
for i in 0..spectrum.len() {
let logval = coef * spectrum[i].log10();
if logval < floor {
log_spec.push(floor);
} else {
log_spec.push(logval);
}
}
if let Some(ceil_val) = ceil {
let maxval = match crate::util::max(&log_spec) {
Some(x) => x,
None => 0.0
};
let adj_offset = ceil_val - maxval;
for i in 0..log_spec.len() {
log_spec[i] += adj_offset;
if log_spec[i] < floor {
log_spec[i] = floor;
}
}
}
log_spec
}
#[inline]
pub fn make_log_spectrogram(spectrogram: &[Vec<f64>], coef: f64, floor: f64, ceil: Option<f64>) -> Vec<Vec<f64>> {
let mut log_spectrogram: Vec<Vec<f64>> = Vec::with_capacity(spectrogram.len());
for i in 0..spectrogram.len() {
log_spectrogram.push(make_log_spectrum(&spectrogram[i], coef, floor, ceil));
}
log_spectrogram
}
#[inline]
pub fn make_power_spectrum(magnitude_spectrum: &[f64]) -> Vec<f64> {
let mut power_spec: Vec<f64> = vec![0.0; magnitude_spectrum.len()];
for i in 0..magnitude_spectrum.len() {
power_spec[i] = magnitude_spectrum[i].powf(2.0);
}
power_spec
}
#[inline]
pub fn make_power_spectrogram(magnitude_spectrogram: &[Vec<f64>]) -> Vec<Vec<f64>> {
let mut power_spectrogram: Vec<Vec<f64>> = Vec::with_capacity(magnitude_spectrogram.len());
for i in 0..magnitude_spectrogram.len() {
let mut power_spec: Vec<f64> = vec![0.0; magnitude_spectrogram[i].len()];
for j in 0..magnitude_spectrogram[i].len() {
power_spec[j] = magnitude_spectrogram[i][j].powf(2.0);
}
power_spectrogram.push(power_spec);
}
power_spectrogram
}
#[inline]
pub fn make_spectrum_pmf(power_spectrum: &[f64], power_spectrum_sum: f64) -> Vec<f64> {
let mut pmf_vector: Vec<f64> = vec![0.0; power_spectrum.len()];
for i in 0..power_spectrum.len() {
pmf_vector[i] = power_spectrum[i] / power_spectrum_sum;
}
pmf_vector
}
#[inline]
pub fn normalize_spectrogram(spectrogram: &mut Vec<Vec<f64>>, scaling_coef: Option<f64>) -> f64 {
let maxval: f64 = match scaling_coef {
Some(val) => val,
None => {
let mut max = f64::NEG_INFINITY;
for i in 0..spectrogram.len() {
for j in 0..spectrogram[i].len() {
if spectrogram[i][j] > max {
max = spectrogram[i][j];
}
}
}
max
}
};
for i in 0..spectrogram.len() {
for j in 0..spectrogram[i].len() {
spectrogram[i][j] /= maxval;
}
}
maxval
}
#[inline]
pub fn normalize_spectrum(spectrum: &mut Vec<f64>, scaling_coef: Option<f64>) -> f64 {
let maxval: f64 = match scaling_coef {
Some(val) => val,
None => {
let mut max = f64::NEG_INFINITY;
for i in 0..spectrum.len() {
if spectrum[i] > max {
max = spectrum[i];
}
}
max
}
};
for i in 0..spectrum.len() {
spectrum[i] /= maxval;
}
maxval
}
pub fn spectral_centroid(magnitude_spectrum: &[f64], rfft_freqs: &[f64]) -> f64 {
let magnitude_spectrum_sum: f64 = magnitude_spectrum.iter().sum();
compute_spectral_centroid(magnitude_spectrum, rfft_freqs, magnitude_spectrum_sum)
}
pub fn spectral_difference(magnitude_spectrum1: &[f64], magnitude_spectrum2: &[f64], positive_only: bool) -> Result<f64, SpectrumError> {
if magnitude_spectrum1.len() != magnitude_spectrum2.len() {
return Err(SpectrumError{error_msg: String::from(format!("The provided spectra have different lengths: magnitude_spectrum1 has length {} but magnitude_spectrum2 has length {}.", magnitude_spectrum1.len(), magnitude_spectrum2.len()))});
}
let mut difference: f64 = 0.0;
for i in 0..magnitude_spectrum1.len() {
let local_difference = magnitude_spectrum2[i] - magnitude_spectrum1[i];
if !positive_only || local_difference > 0.0 {
difference += local_difference * local_difference;
}
}
Ok(f64::sqrt(difference))
}
pub fn spectral_flux(magnitude_spectrum1: &[f64], magnitude_spectrum2: &[f64], normalization_type: Option<Norm>) -> Result<f64, SpectrumError> {
if magnitude_spectrum1.len() != magnitude_spectrum2.len() {
return Err(SpectrumError{error_msg: String::from(format!("The provided spectra have different lengths: magnitude_spectrum1 has length {} but magnitude_spectrum2 has length {}.", magnitude_spectrum1.len(), magnitude_spectrum2.len()))});
}
let mut flux = 0.0;
let mut norm1 = 1.0;
let mut norm2 = 1.0;
match normalization_type {
Some(x) => {
norm1 = lnorm(magnitude_spectrum1, &x);
norm2 = lnorm(magnitude_spectrum2, &x);
},
None => ()
};
for i in 0..magnitude_spectrum1.len() {
let local_difference = magnitude_spectrum2[i] / norm2 - magnitude_spectrum1[i] / norm1;
flux += local_difference * local_difference;
}
Ok(flux)
}
pub fn spectral_entropy(magnitude_spectrum: &[f64]) -> f64 {
let power_spectrum = make_power_spectrum(magnitude_spectrum);
let spectrum_pmf = make_spectrum_pmf(&power_spectrum, power_spectrum.iter().sum());
compute_spectral_entropy(&spectrum_pmf)
}
pub fn spectral_flatness(magnitude_spectrum: &[f64]) -> f64 {
let magnitude_spectrum_sum: f64 = magnitude_spectrum.iter().sum();
compute_spectral_flatness(magnitude_spectrum, magnitude_spectrum_sum)
}
pub fn spectral_kurtosis(magnitude_spectrum: &[f64], rfft_freqs: &[f64]) -> f64 {
let power_spectrum = make_power_spectrum(magnitude_spectrum);
let spectrum_pmf = make_spectrum_pmf(&power_spectrum, power_spectrum.iter().sum());
let spectral_centroid = compute_spectral_centroid(magnitude_spectrum, rfft_freqs, magnitude_spectrum.iter().sum());
let spectral_variance = compute_spectral_variance(&spectrum_pmf, rfft_freqs, spectral_centroid);
compute_spectral_kurtosis(&spectrum_pmf, rfft_freqs, spectral_centroid, spectral_variance)
}
pub fn spectral_roll_off_point(magnitude_spectrum: &[f64], rfft_freqs: &[f64], n: f64) -> f64 {
let power_spectrum = make_power_spectrum(magnitude_spectrum);
let power_spectrum_sum: f64 = power_spectrum.iter().sum();
compute_spectral_roll_off_point(&power_spectrum, rfft_freqs, power_spectrum_sum, n)
}
pub fn spectral_skewness(magnitude_spectrum: &[f64], rfft_freqs: &[f64]) -> f64 {
let power_spectrum = make_power_spectrum(magnitude_spectrum);
let spectrum_pmf = make_spectrum_pmf(&power_spectrum, power_spectrum.iter().sum());
let spectral_centroid = compute_spectral_centroid(magnitude_spectrum, rfft_freqs, magnitude_spectrum.iter().sum());
let spectral_variance = compute_spectral_variance(&spectrum_pmf, rfft_freqs, spectral_centroid);
compute_spectral_skewness(&spectrum_pmf, rfft_freqs, spectral_centroid, spectral_variance)
}
pub fn spectral_slope(magnitude_spectrum: &[f64]) -> f64 {
let power_spectrum = make_power_spectrum(magnitude_spectrum);
let power_spectrum_sum: f64 = power_spectrum.iter().sum();
compute_spectral_slope(&power_spectrum, power_spectrum_sum)
}
pub fn spectral_slope_region(magnitude_spectrum: &[f64], rfft_freqs: &[f64], f_lower: f64, f_upper: f64, sample_rate: u32) -> f64 {
let power_spectrum = make_power_spectrum(magnitude_spectrum);
compute_spectral_slope_region(&power_spectrum, rfft_freqs, f_lower, f_upper, sample_rate)
}
pub fn spectral_variance(magnitude_spectrum: &[f64], rfft_freqs: &[f64]) -> f64 {
let power_spectrum = make_power_spectrum(magnitude_spectrum);
let spectrum_pmf = make_spectrum_pmf(&power_spectrum, power_spectrum.iter().sum());
let spectral_centroid = compute_spectral_centroid(magnitude_spectrum, rfft_freqs, magnitude_spectrum.iter().sum());
compute_spectral_variance(&spectrum_pmf, rfft_freqs, spectral_centroid)
}
#[cfg(test)]
mod test {
use super::*;
use std::io;
use std::io::Write;
const AUDIO: &str = "myfile.wav";
const FFT_SIZE: usize = 2048;
#[test]
fn test_log_spec() {
const EPSILON: f64 = 1e-6;
let in_vec: Vec<f64> = vec![6.76895304e-06, 1.36028451e-06, 2.77367273e-06, 1.89267587e-05,
4.07858842e-04, 3.19873457e-04, 5.04253261e-06, 8.52845397e-06,
4.99160938e-06, 5.40207921e-06, 3.29421796e-04, 7.69145971e-05,
6.61126227e-06, 2.21605018e-04, 4.42958155e-05, 1.83195179e-04,
9.42967421e-05, 1.23944028e-04, 1.34227360e-04, 1.39157067e-04];
let expected_output = vec![
-97.7998838, -104.7688013, -101.67454666, -93.33433635, -80.0,
-81.05531678, -99.07861167, -96.79639572, -99.1226929, -98.77948934,
-80.92757551, -87.24501112, -97.90225495, -82.64930292, -89.6414718,
-83.47595841, -86.36013193, -85.17284276, -84.82668833, -84.67004614];
let output = make_log_spectrum(&in_vec, 10.0, -10e8, Some(-80.0));
for i in 0..output.len() {
assert!(f64::abs(expected_output[i] - output[i]) < EPSILON);
}
}
#[test]
fn test_autocorrelation() {
let audio = crate::read(AUDIO).unwrap();
let auto = autocorrelation(&audio.samples[0][44100..44100+FFT_SIZE], FFT_SIZE).unwrap();
for val in auto.iter() {
print!("{} ", val);
io::stdout().flush().unwrap();
}
let mut max = auto[0];
let mut max_idx: usize = 0;
for i in 0..auto.len() {
if auto[i] > max {
max = auto[i];
max_idx = i;
}
}
println!("\nMax: {}, max_idx: {}", max, max_idx);
}
}