rosa 0.1.0

Audio feature extraction library matching librosa output
Documentation
mod common;

#[test]
fn test_windowed_frames_match_librosa() {
    let ref_data = common::load_audio_reference("windowed_frames.json");
    let frame_length = ref_data["frame_length"].as_u64().unwrap() as usize;
    let hop_length = ref_data["hop_length"].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 cases = ref_data["cases"].as_array().unwrap();
    assert!(!cases.is_empty(), "No test cases found");

    for case in cases {
        let win_name = case["window"].as_str().unwrap();
        let frame_idx = case["frame_index"].as_u64().unwrap() as usize;
        let expected_raw: Vec<f64> = serde_json::from_value(case["raw_frame"].clone()).unwrap();
        let expected_windowed: Vec<f64> =
            serde_json::from_value(case["windowed_frame"].clone()).unwrap();

        // Extract frame from WAV
        let frame = common::extract_frame(&samples, frame_length, hop_length, frame_idx);
        common::assert_approx_eq(
            &frame,
            &expected_raw,
            1e-10,
            &format!("{win_name} frame {frame_idx} raw"),
        );

        // Apply window
        let window = match win_name {
            "hann" => rosa::windows::hann(frame_length, true),
            "hamming" => rosa::windows::hamming(frame_length, true),
            "blackman" => rosa::windows::blackman(frame_length, true),
            _ => panic!("Unknown window type: {win_name}"),
        };

        let windowed: Vec<f64> = frame
            .iter()
            .zip(window.iter())
            .map(|(s, w)| s * w)
            .collect();
        common::assert_approx_eq(
            &windowed,
            &expected_windowed,
            1e-12,
            &format!("{win_name} frame {frame_idx} windowed"),
        );
    }
}