mod common;
#[test]
fn test_stft_magnitude_matches_librosa() {
let ref_data = common::load_audio_reference("stft.json");
let n_fft = ref_data["n_fft"].as_u64().unwrap() as usize;
let hop_length = ref_data["hop_length"].as_u64().unwrap() as usize;
let expected_n_bins = ref_data["n_bins"].as_u64().unwrap() as usize;
let expected_n_frames = ref_data["n_frames"].as_u64().unwrap() as usize;
let wav = common::wav_path("20_Pink Noise mono 500Hz_2kHz -20dBFS 48k.wav");
let (samples, _sr) = common::read_wav_mono(&wav);
let params = rosa::stft::StftParams::new(n_fft);
assert_eq!(params.hop_length, hop_length);
let (mag, phase) = rosa::stft::stft(&samples, ¶ms);
assert_eq!(
mag.rows(),
expected_n_bins,
"n_bins mismatch: got {}, expected {}",
mag.rows(),
expected_n_bins
);
assert_eq!(
mag.cols(),
expected_n_frames,
"n_frames mismatch: got {}, expected {}",
mag.cols(),
expected_n_frames
);
assert_eq!(mag.shape(), phase.shape(), "mag and phase shape mismatch");
let frames = ref_data["frames"].as_array().unwrap();
assert!(!frames.is_empty(), "No reference frames found");
for frame_ref in frames {
let idx = frame_ref["index"].as_u64().unwrap() as usize;
let expected_mag: Vec<f64> =
serde_json::from_value(frame_ref["magnitude"].clone()).unwrap();
let expected_phase: Vec<f64> = serde_json::from_value(frame_ref["phase"].clone()).unwrap();
assert_eq!(expected_mag.len(), expected_n_bins);
assert_eq!(expected_phase.len(), expected_n_bins);
let actual_mag: Vec<f64> = (0..expected_n_bins).map(|bin| mag[(bin, idx)]).collect();
common::assert_approx_eq(
&actual_mag,
&expected_mag,
1e-6,
&format!("STFT magnitude frame {idx}"),
);
let actual_phase: Vec<f64> = (0..expected_n_bins).map(|bin| phase[(bin, idx)]).collect();
common::assert_approx_eq(
&actual_phase,
&expected_phase,
1e-6,
&format!("STFT phase frame {idx}"),
);
}
}
#[test]
fn test_spectrogram_power_matches_librosa() {
let ref_data = common::load_audio_reference("stft.json");
let n_fft = ref_data["n_fft"].as_u64().unwrap() as usize;
let expected_n_bins = ref_data["n_bins"].as_u64().unwrap() as usize;
let wav = common::wav_path("20_Pink Noise mono 500Hz_2kHz -20dBFS 48k.wav");
let (samples, _sr) = common::read_wav_mono(&wav);
let params = rosa::stft::StftParams::new(n_fft);
let power_spec = rosa::stft::spectrogram(&samples, ¶ms, 2.0);
let frames = ref_data["frames"].as_array().unwrap();
for frame_ref in frames {
let idx = frame_ref["index"].as_u64().unwrap() as usize;
let mag: Vec<f64> = serde_json::from_value(frame_ref["magnitude"].clone()).unwrap();
let expected_power: Vec<f64> = mag.iter().map(|m| m * m).collect();
let actual_power: Vec<f64> = (0..expected_n_bins)
.map(|bin| power_spec[(bin, idx)])
.collect();
common::assert_approx_eq(
&actual_power,
&expected_power,
1e-6,
&format!("spectrogram power frame {idx}"),
);
}
}