rosa 0.1.0

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

impl Matrix {
    /// Multiply each row element-wise by `vec[row_index]`.
    /// `vec.len()` must equal `self.rows()`.
    /// Equivalent to `mat * vec[:, np.newaxis]`.
    pub fn mul_rows_by(&self, vec: &[f64]) -> Matrix {
        assert_eq!(
            vec.len(),
            self.rows(),
            "mul_rows_by: vec length {} != rows {}",
            vec.len(),
            self.rows()
        );
        let mut data = self.as_slice().to_vec();
        for (r, &factor) in vec.iter().enumerate().take(self.rows()) {
            let start = r * self.cols();
            for c in 0..self.cols() {
                data[start + c] *= factor;
            }
        }
        Matrix::from_vec(data, self.rows(), self.cols())
    }

    /// Multiply each column element-wise by `vec[col_index]`.
    /// `vec.len()` must equal `self.cols()`.
    /// Equivalent to `mat * vec[np.newaxis, :]`.
    pub fn mul_cols_by(&self, vec: &[f64]) -> Matrix {
        assert_eq!(
            vec.len(),
            self.cols(),
            "mul_cols_by: vec length {} != cols {}",
            vec.len(),
            self.cols()
        );
        let mut data = self.as_slice().to_vec();
        for r in 0..self.rows() {
            let start = r * self.cols();
            for c in 0..self.cols() {
                data[start + c] *= vec[c];
            }
        }
        Matrix::from_vec(data, self.rows(), self.cols())
    }

    /// Outer difference: `result[i][j] = a[i] - b[j]`.
    /// Returns `(a.len() × b.len())` Matrix.
    /// Used for mel filterbank ramps.
    pub fn outer_sub(a: &[f64], b: &[f64]) -> Matrix {
        Matrix::from_fn(a.len(), b.len(), |i, j| a[i] - b[j])
    }

    /// Outer product: `result[i][j] = a[i] * b[j]`.
    /// Returns `(a.len() × b.len())` Matrix.
    /// Used for harmonic frequency generation.
    pub fn outer_mul(a: &[f64], b: &[f64]) -> Matrix {
        Matrix::from_fn(a.len(), b.len(), |i, j| a[i] * b[j])
    }
}

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

    #[test]
    fn test_mul_rows_by() {
        let m = Matrix::from_rows(&[&[1.0, 2.0], &[3.0, 4.0], &[5.0, 6.0]]);
        let result = m.mul_rows_by(&[1.0, 2.0, 3.0]);
        assert_eq!(result.row(0), &[1.0, 2.0]);
        assert_eq!(result.row(1), &[6.0, 8.0]);
        assert_eq!(result.row(2), &[15.0, 18.0]);
    }

    #[test]
    fn test_mul_cols_by() {
        let m = Matrix::from_rows(&[&[1.0, 2.0], &[3.0, 4.0], &[5.0, 6.0]]);
        let result = m.mul_cols_by(&[10.0, 20.0]);
        assert_eq!(result.row(0), &[10.0, 40.0]);
        assert_eq!(result.row(1), &[30.0, 80.0]);
        assert_eq!(result.row(2), &[50.0, 120.0]);
    }

    #[test]
    fn test_outer_sub() {
        let result = Matrix::outer_sub(&[1.0, 2.0], &[10.0, 20.0, 30.0]);
        assert_eq!(result.shape(), (2, 3));
        assert_eq!(result.row(0), &[-9.0, -19.0, -29.0]);
        assert_eq!(result.row(1), &[-8.0, -18.0, -28.0]);
    }

    #[test]
    fn test_outer_mul() {
        let result = Matrix::outer_mul(&[2.0, 3.0], &[10.0, 20.0]);
        assert_eq!(result.shape(), (2, 2));
        assert_eq!(result.row(0), &[20.0, 40.0]);
        assert_eq!(result.row(1), &[30.0, 60.0]);
    }

    #[test]
    #[should_panic(expected = "mul_rows_by")]
    fn test_mul_rows_by_wrong_length() {
        let m = Matrix::zeros(3, 2);
        m.mul_rows_by(&[1.0, 2.0]);
    }

    #[test]
    #[should_panic(expected = "mul_cols_by")]
    fn test_mul_cols_by_wrong_length() {
        let m = Matrix::zeros(3, 2);
        m.mul_cols_by(&[1.0, 2.0, 3.0]);
    }
}