mod convert;
mod mel;
mod transform;
pub use convert::{fft_frequencies, hz_to_mel, mel_frequencies, mel_to_hz};
pub use mel::{MelFilterParams, MelNorm, MelSpectrogramParams, mel_filter, mel_spectrogram};
pub use transform::{IstftParams, StftParams, istft, spectrogram, stft, stft_as_real_imag};
use crate::matrix::Matrix;
pub fn magphase(re: &Matrix, im: &Matrix, power: f64) -> (Matrix, Matrix) {
assert_eq!(re.shape(), im.shape());
let (rows, cols) = re.shape();
let mut mag = Matrix::zeros(rows, cols);
let mut phase = Matrix::zeros(rows, cols);
for r in 0..rows {
for c in 0..cols {
let re_val = re.get(r, c);
let im_val = im.get(r, c);
let m = (re_val * re_val + im_val * im_val).sqrt();
*mag.get_mut(r, c) = if power == 1.0 { m } else { m.powf(power) };
*phase.get_mut(r, c) = if m == 0.0 { 0.0 } else { im_val.atan2(re_val) };
}
}
(mag, phase)
}
#[cfg(test)]
mod tests {
use super::*;
use std::f64::consts::PI;
#[test]
fn test_magphase_known_values() {
let re = Matrix::from_vec(vec![3.0, 0.0, -1.0, 1.0], 2, 2);
let im = Matrix::from_vec(vec![4.0, 1.0, 0.0, -1.0], 2, 2);
let (mag, phase) = magphase(&re, &im, 1.0);
assert!((mag.get(0, 0) - 5.0).abs() < 1e-12);
assert!((mag.get(0, 1) - 1.0).abs() < 1e-12);
assert!((mag.get(1, 0) - 1.0).abs() < 1e-12);
assert!((mag.get(1, 1) - 2.0_f64.sqrt()).abs() < 1e-12);
assert!((phase.get(0, 0) - (4.0_f64).atan2(3.0)).abs() < 1e-12);
assert!((phase.get(0, 1) - PI / 2.0).abs() < 1e-12); assert!((phase.get(1, 0) - PI).abs() < 1e-12); }
#[test]
fn test_magphase_power() {
let re = Matrix::from_vec(vec![3.0, 0.0], 1, 2);
let im = Matrix::from_vec(vec![4.0, 0.0], 1, 2);
let (mag, _phase) = magphase(&re, &im, 2.0);
assert!((mag.get(0, 0) - 25.0).abs() < 1e-12); assert!((mag.get(0, 1) - 0.0).abs() < 1e-12);
}
#[test]
fn test_magphase_zero_phase() {
let re = Matrix::from_vec(vec![0.0], 1, 1);
let im = Matrix::from_vec(vec![0.0], 1, 1);
let (_mag, phase) = magphase(&re, &im, 1.0);
assert_eq!(phase.get(0, 0), 0.0);
}
#[test]
fn test_magphase_roundtrip() {
let re = Matrix::from_vec(vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0], 2, 3);
let im = Matrix::from_vec(vec![7.0, -8.0, 9.0, -10.0, 11.0, -12.0], 2, 3);
let (mag, phase) = magphase(&re, &im, 1.0);
let (rows, cols) = re.shape();
for r in 0..rows {
for c in 0..cols {
let m = mag.get(r, c);
let p = phase.get(r, c);
let re_recovered = m * p.cos();
let im_recovered = m * p.sin();
assert!(
(re_recovered - re.get(r, c)).abs() < 1e-10,
"re roundtrip failed at ({r},{c})"
);
assert!(
(im_recovered - im.get(r, c)).abs() < 1e-10,
"im roundtrip failed at ({r},{c})"
);
}
}
}
}