essentia-core 0.1.5

Core Rust library wrapping Essentia functionality
Documentation
use essentia_sys::ffi;
use ndarray::{Array2, Array4};
use std::collections::HashMap;

use crate::{ConversionError, DataContainer, phantom};

pub trait IntoDataContainer<T> {
    fn into_data_container(self) -> DataContainer<'static, T>;
}

pub trait TryIntoDataContainer<T> {
    fn try_into_data_container(self) -> Result<DataContainer<'static, T>, ConversionError>;
}

impl<T, V> TryIntoDataContainer<T> for V
where
    V: IntoDataContainer<T>,
{
    fn try_into_data_container(self) -> Result<DataContainer<'static, T>, ConversionError> {
        Ok(self.into_data_container())
    }
}

impl<'a, T> IntoDataContainer<T> for DataContainer<'a, T> {
    fn into_data_container(self) -> DataContainer<'static, T> {
        let owned_ptr = self.into_owned_ptr();
        DataContainer::new_owned(owned_ptr)
    }
}

impl IntoDataContainer<phantom::Bool> for bool {
    fn into_data_container(self) -> DataContainer<'static, phantom::Bool> {
        DataContainer::new_owned(ffi::create_data_container_from_bool(self))
    }
}

impl IntoDataContainer<phantom::String> for &str {
    fn into_data_container(self) -> DataContainer<'static, phantom::String> {
        DataContainer::new_owned(ffi::create_data_container_from_string(self))
    }
}

impl IntoDataContainer<phantom::Int> for i32 {
    fn into_data_container(self) -> DataContainer<'static, phantom::Int> {
        DataContainer::new_owned(ffi::create_data_container_from_int(self))
    }
}

impl IntoDataContainer<phantom::Float> for f32 {
    fn into_data_container(self) -> DataContainer<'static, phantom::Float> {
        DataContainer::new_owned(ffi::create_data_container_from_float(self))
    }
}

impl IntoDataContainer<phantom::UnsignedInt> for u32 {
    fn into_data_container(self) -> DataContainer<'static, phantom::UnsignedInt> {
        DataContainer::new_owned(ffi::create_data_container_from_unsigned_int(self))
    }
}

impl IntoDataContainer<phantom::Long> for i64 {
    fn into_data_container(self) -> DataContainer<'static, phantom::Long> {
        DataContainer::new_owned(ffi::create_data_container_from_long(self))
    }
}

impl IntoDataContainer<phantom::StereoSample> for ffi::StereoSample {
    fn into_data_container(self) -> DataContainer<'static, phantom::StereoSample> {
        DataContainer::new_owned(ffi::create_data_container_from_stereo_sample(self))
    }
}

impl IntoDataContainer<phantom::Complex> for num::Complex<f32> {
    fn into_data_container(self) -> DataContainer<'static, phantom::Complex> {
        DataContainer::new_owned(ffi::create_data_container_from_complex(ffi::Complex {
            real: self.re,
            imag: self.im,
        }))
    }
}

impl IntoDataContainer<phantom::TensorFloat> for &Array4<f32> {
    fn into_data_container(self) -> DataContainer<'static, phantom::TensorFloat> {
        let slice = self.as_slice().expect("Array must be contiguous");
        let shape = [
            self.shape()[0],
            self.shape()[1],
            self.shape()[2],
            self.shape()[3],
        ];

        DataContainer::new_owned(ffi::create_data_container_from_tensor_float(
            ffi::TensorFloat {
                slice,
                shape: &shape,
            },
        ))
    }
}

impl IntoDataContainer<phantom::VectorBool> for &[bool] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorBool> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_bool(self))
    }
}

impl IntoDataContainer<phantom::VectorInt> for &[i32] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorInt> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_int(self))
    }
}

impl IntoDataContainer<phantom::VectorString> for &[&str] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorString> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_string(self))
    }
}

impl IntoDataContainer<phantom::VectorFloat> for &[f32] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorFloat> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_float(self))
    }
}

impl IntoDataContainer<phantom::VectorStereoSample> for &[ffi::StereoSample] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorStereoSample> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_stereo_sample(self))
    }
}

impl IntoDataContainer<phantom::VectorComplex> for &[num::Complex<f32>] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorComplex> {
        let ffi_vec: Vec<ffi::Complex> = self
            .iter()
            .map(|c| ffi::Complex {
                real: c.re,
                imag: c.im,
            })
            .collect();
        DataContainer::new_owned(ffi::create_data_container_from_vector_complex(&ffi_vec))
    }
}

impl IntoDataContainer<phantom::VectorVectorFloat> for &[Vec<f32>] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorVectorFloat> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_vector_float(
            self.iter()
                .map(|item| ffi::SliceFloat {
                    slice: item.as_slice(),
                })
                .collect(),
        ))
    }
}

impl IntoDataContainer<phantom::MatrixFloat> for &Array2<f32> {
    fn into_data_container(self) -> DataContainer<'static, phantom::MatrixFloat> {
        let slice = self.as_slice().expect("Array must be contiguous");
        let (dim1, dim2) = self.dim();

        DataContainer::new_owned(ffi::create_data_container_from_matrix_float(
            ffi::MatrixFloat { slice, dim1, dim2 },
        ))
    }
}

impl IntoDataContainer<phantom::VectorVectorString> for &[&[&str]] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorVectorString> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_vector_string(
            self.iter()
                .map(|item| ffi::VecString {
                    vec: item.iter().map(|s| s.to_string()).collect(),
                })
                .collect(),
        ))
    }
}

impl IntoDataContainer<phantom::VectorVectorStereoSample> for &[&[ffi::StereoSample]] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorVectorStereoSample> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_vector_stereo_sample(
            self.iter()
                .map(|item| ffi::SliceStereoSample { slice: item })
                .collect(),
        ))
    }
}

impl IntoDataContainer<phantom::VectorVectorComplex> for &[Vec<num::Complex<f32>>] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorVectorComplex> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_vector_complex(
            self.iter()
                .map(|item| ffi::VecComplex {
                    vec: item
                        .iter()
                        .map(|c| ffi::Complex {
                            real: c.re,
                            imag: c.im,
                        })
                        .collect(),
                })
                .collect(),
        ))
    }
}

impl IntoDataContainer<phantom::VectorMatrixFloat> for &[Array2<f32>] {
    fn into_data_container(self) -> DataContainer<'static, phantom::VectorMatrixFloat> {
        DataContainer::new_owned(ffi::create_data_container_from_vector_matrix_float(
            self.iter()
                .map(|array| {
                    let slice = array.as_slice().expect("Array must be contiguous");
                    let (dim1, dim2) = array.dim();
                    ffi::MatrixFloat { slice, dim1, dim2 }
                })
                .collect(),
        ))
    }
}

impl IntoDataContainer<phantom::MapVectorFloat> for &HashMap<String, Vec<f32>> {
    fn into_data_container(self) -> DataContainer<'static, phantom::MapVectorFloat> {
        DataContainer::new_owned(ffi::create_data_container_from_map_vector_float(
            self.iter()
                .map(|(key, vec)| ffi::MapEntryVectorFloat {
                    key: key.clone(),
                    value: vec.as_slice(),
                })
                .collect(),
        ))
    }
}

impl IntoDataContainer<phantom::MapVectorString> for &HashMap<String, Vec<String>> {
    fn into_data_container(self) -> DataContainer<'static, phantom::MapVectorString> {
        DataContainer::new_owned(ffi::create_data_container_from_map_vector_string(
            self.iter()
                .map(|(key, vec)| ffi::MapEntryVectorString {
                    key: key.clone(),
                    value: vec.clone(),
                })
                .collect(),
        ))
    }
}

impl IntoDataContainer<phantom::MapVectorInt> for &HashMap<String, Vec<i32>> {
    fn into_data_container(self) -> DataContainer<'static, phantom::MapVectorInt> {
        DataContainer::new_owned(ffi::create_data_container_from_map_vector_int(
            self.iter()
                .map(|(key, vec)| ffi::MapEntryVectorInt {
                    key: key.clone(),
                    value: vec.as_slice(),
                })
                .collect(),
        ))
    }
}

impl IntoDataContainer<phantom::MapVectorComplex> for &HashMap<String, Vec<num::Complex<f32>>> {
    fn into_data_container(self) -> DataContainer<'static, phantom::MapVectorComplex> {
        let converted_data: Vec<(String, Vec<ffi::Complex>)> = self
            .iter()
            .map(|(key, vec)| {
                (
                    key.clone(),
                    vec.iter()
                        .map(|c| ffi::Complex {
                            real: c.re,
                            imag: c.im,
                        })
                        .collect(),
                )
            })
            .collect();

        let entries: Vec<ffi::MapEntryVectorComplex> = converted_data
            .iter()
            .map(|(key, ffi_vec)| ffi::MapEntryVectorComplex {
                key: key.clone(),
                value: ffi_vec.as_slice(),
            })
            .collect();

        DataContainer::new_owned(ffi::create_data_container_from_map_vector_complex(entries))
    }
}

impl IntoDataContainer<phantom::MapFloat> for &HashMap<String, f32> {
    fn into_data_container(self) -> DataContainer<'static, phantom::MapFloat> {
        DataContainer::new_owned(ffi::create_data_container_from_map_float(
            self.iter()
                .map(|(key, &val)| ffi::MapEntryFloat {
                    key: key.clone(),
                    value: val,
                })
                .collect(),
        ))
    }
}

impl TryIntoDataContainer<phantom::MatrixFloat> for &[Vec<f32>] {
    fn try_into_data_container(
        self,
    ) -> Result<DataContainer<'static, phantom::MatrixFloat>, ConversionError> {
        if self.is_empty() {
            return Err(ConversionError::InvalidFormat {
                message: "Cannot create matrix from empty vector".to_string(),
            });
        }

        let expected_cols = self[0].len();
        if expected_cols == 0 {
            return Err(ConversionError::InvalidFormat {
                message: "Cannot create matrix from empty rows".to_string(),
            });
        }

        for (row_idx, row) in self.iter().enumerate() {
            if row.len() != expected_cols {
                return Err(ConversionError::InvalidFormat {
                    message: format!(
                        "Non-rectangular matrix: row {} has {} elements, expected {}",
                        row_idx,
                        row.len(),
                        expected_cols
                    ),
                });
            }
        }

        let mut flat_data = Vec::with_capacity(self.len() * expected_cols);
        for row in self {
            flat_data.extend(row);
        }

        let dim1 = flat_data.len() / expected_cols;
        let dim2 = expected_cols;

        Ok(DataContainer::new_owned(
            ffi::create_data_container_from_matrix_float(ffi::MatrixFloat {
                slice: &flat_data,
                dim1,
                dim2,
            }),
        ))
    }
}

impl IntoDataContainer<phantom::Pool> for crate::pool::Pool {
    fn into_data_container(self) -> DataContainer<'static, phantom::Pool> {
        DataContainer::new_owned(ffi::create_data_container_from_pool(self.into_owned_ptr()))
    }
}