rosa 0.1.0

Audio feature extraction library matching librosa output
Documentation
//! Spectral centroid matching `librosa.feature.spectral_centroid`.

use crate::matrix::{Axis, Matrix};

/// Compute spectral centroid from a magnitude spectrogram.
///
/// Matches `librosa.feature.spectral_centroid(S=S, freq=freq)`.
///
/// - `s`: magnitude spectrogram (rows = frequency bins, cols = frames)
/// - `freq`: center frequencies for each bin (length = s.rows())
///
/// Returns one centroid value per frame.
///
/// # Panics
///
/// Panics if `freq.len() != s.rows()`.
///
/// # Examples
///
/// ```
/// use rosa::matrix::Matrix;
///
/// let s = Matrix::from_vec(vec![1.0, 0.0, 0.0, 1.0], 2, 2);
/// let freq = vec![100.0, 200.0];
/// let centroid = rosa::spectral::spectral_centroid(&s, &freq);
/// assert_eq!(centroid.len(), 2);
/// ```
pub fn spectral_centroid(s: &Matrix, freq: &[f64]) -> Vec<f64> {
    assert_eq!(
        s.rows(),
        freq.len(),
        "freq length {} != spectrogram rows {}",
        freq.len(),
        s.rows()
    );

    // L1-normalize each column (frame), then compute weighted mean of frequencies
    let s_norm = s.normalize_axis(Axis::Row, 1.0);

    let n_frames = s_norm.cols();
    let n_bins = s_norm.rows();
    let mut result = Vec::with_capacity(n_frames);

    for t in 0..n_frames {
        let mut centroid = 0.0;
        for (k, &f) in freq.iter().enumerate().take(n_bins) {
            centroid += s_norm.get(k, t) * f;
        }
        result.push(centroid);
    }

    result
}