rosa 0.1.0

Audio feature extraction library matching librosa output
Documentation
use crate::Matrix;

/// Time-delay embedding: stack `n_steps` consecutive frames.
///
/// Input:  `data [n_features, T]`
/// Output: `[n_features * n_steps, T]`
///
/// Each output column `c` contains:
/// - Row block 0 (current): `data[:, c]`
/// - Row block 1: `data[:, c - delay]` (zero if out of bounds)
/// - Row block k: `data[:, c - k*delay]` (zero if out of bounds)
pub fn stack_memory(data: &Matrix, n_steps: usize, delay: usize) -> Matrix {
    let n_features = data.rows();
    let t = data.cols();
    let mut result = Matrix::zeros(n_features * n_steps, t);

    for step in 0..n_steps {
        for r in 0..n_features {
            let out_row = step * n_features + r;
            for c in 0..t {
                let src_col = c as isize - (step * delay) as isize;
                if src_col >= 0 {
                    *result.get_mut(out_row, c) = data.get(r, src_col as usize);
                }
                // else: already 0.0 from Matrix::zeros
            }
        }
    }

    result
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_stack_memory_n_steps_1() {
        let data = Matrix::from_rows(&[&[1.0, 2.0, 3.0]]);
        let result = stack_memory(&data, 1, 1);
        assert_eq!(result.shape(), (1, 3));
        assert_eq!(result.row(0), &[1.0, 2.0, 3.0]);
    }

    #[test]
    fn test_stack_memory_basic() {
        // n_steps=3, delay=1, data=[a,b,c,d,e]
        // expected output:
        // row 0 (current):  [a, b, c, d, e]
        // row 1 (1 back):   [0, a, b, c, d]
        // row 2 (2 back):   [0, 0, a, b, c]
        let data = Matrix::from_rows(&[&[1.0, 2.0, 3.0, 4.0, 5.0]]);
        let result = stack_memory(&data, 3, 1);
        assert_eq!(result.shape(), (3, 5));
        assert_eq!(result.row(0), &[1.0, 2.0, 3.0, 4.0, 5.0]);
        assert_eq!(result.row(1), &[0.0, 1.0, 2.0, 3.0, 4.0]);
        assert_eq!(result.row(2), &[0.0, 0.0, 1.0, 2.0, 3.0]);
    }

    #[test]
    fn test_stack_memory_delay_2() {
        // n_steps=3, delay=2
        // row 0 (current): [a, b, c, d, e]
        // row 1 (2 back):  [0, 0, a, b, c]
        // row 2 (4 back):  [0, 0, 0, 0, a]
        let data = Matrix::from_rows(&[&[1.0, 2.0, 3.0, 4.0, 5.0]]);
        let result = stack_memory(&data, 3, 2);
        assert_eq!(result.shape(), (3, 5));
        assert_eq!(result.row(0), &[1.0, 2.0, 3.0, 4.0, 5.0]);
        assert_eq!(result.row(1), &[0.0, 0.0, 1.0, 2.0, 3.0]);
        assert_eq!(result.row(2), &[0.0, 0.0, 0.0, 0.0, 1.0]);
    }

    #[test]
    fn test_stack_memory_multirow() {
        let data = Matrix::from_rows(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]]);
        let result = stack_memory(&data, 2, 1);
        assert_eq!(result.shape(), (4, 3));
        // step 0 rows: original data rows
        assert_eq!(result.row(0), &[1.0, 2.0, 3.0]);
        assert_eq!(result.row(1), &[4.0, 5.0, 6.0]);
        // step 1 rows: shifted by 1
        assert_eq!(result.row(2), &[0.0, 1.0, 2.0]);
        assert_eq!(result.row(3), &[0.0, 4.0, 5.0]);
    }
}