use pyo3::prelude::*;
mod binaural;
mod dlpack;
mod dtype;
mod error;
mod fft2d;
mod functions;
mod mdct;
mod mfcc;
mod params;
mod planner;
mod spectrogram;
use crate::{ChromaParams, Chromagram};
use spectrogram::{PyArrayData, PyScalar, real_dlpack};
pub use error::*;
pub use mfcc::PyMfcc;
pub use params::*;
#[pyclass(name = "Chromagram", skip_from_py_object)]
pub struct PyChromagram {
data: PyArrayData,
params: ChromaParams,
n_bins: usize,
n_frames: usize,
}
impl PyChromagram {
pub(crate) fn from_chromagram<T: PyScalar>(py: Python<'_>, chroma: Chromagram<T>) -> Self {
let n_bins = chroma.data.nrows();
let n_frames = chroma.data.ncols();
let params = *chroma.params();
let data = T::into_array_data(py, chroma.data);
Self {
data,
params,
n_bins,
n_frames,
}
}
}
#[pymethods]
impl PyChromagram {
#[getter]
fn data<'py>(&self, py: Python<'py>) -> Bound<'py, PyAny> {
self.data.bind(py)
}
#[getter]
const fn dtype(&self) -> &'static str {
self.data.dtype()
}
#[getter]
const fn n_frames(&self) -> usize {
self.n_frames
}
#[getter]
const fn n_bins(&self) -> usize {
self.n_bins
}
#[getter]
const fn shape(&self) -> (usize, usize) {
(self.n_bins, self.n_frames)
}
#[getter]
fn params(&self) -> PyChromaParams {
PyChromaParams::from(self.params)
}
#[classattr]
fn labels() -> [&'static str; 12] {
Chromagram::<f64>::labels()
}
#[pyo3(signature = (dtype=None))]
fn __array__<'py>(
&self,
py: Python<'py>,
dtype: Option<&Bound<'py, PyAny>>,
) -> PyResult<Bound<'py, PyAny>> {
let arr = self.data.bind(py);
if let Some(dt) = dtype {
arr.call_method1("astype", (dt,))
} else {
Ok(arr)
}
}
#[staticmethod]
const fn __dlpack_device__() -> (i32, i32) {
(1, 0) }
#[pyo3(signature = (*, stream=None, max_version=None, dl_device=None, copy=None))]
fn __dlpack__<'py>(
&self,
py: Python<'py>,
stream: Option<&Bound<'py, PyAny>>,
max_version: Option<(u32, u32)>,
dl_device: Option<(i32, i32)>,
copy: Option<bool>,
) -> PyResult<Bound<'py, pyo3::types::PyCapsule>> {
real_dlpack(py, &self.data, stream, max_version, dl_device, copy)
}
fn __repr__(&self) -> String {
format!(
"Chromagram(shape=({}, {}), dtype={})",
self.n_bins,
self.n_frames,
self.dtype()
)
}
}
#[inline]
pub fn register_module(py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add("SpectrogramError", py.get_type::<PySpectrogramError>())?;
m.add("InvalidInputError", py.get_type::<PyInvalidInputError>())?;
m.add(
"DimensionMismatchError",
py.get_type::<PyDimensionMismatchError>(),
)?;
m.add("FFTBackendError", py.get_type::<PyFFTBackendError>())?;
m.add("InternalError", py.get_type::<PyInternalError>())?;
params::register(py, m)?;
spectrogram::register(py, m)?;
m.add_class::<PyChromagram>()?;
m.add_class::<PyMfcc>()?;
planner::register(py, m)?;
functions::register(py, m)?;
fft2d::register(py, m)?;
dlpack::register(py, m)?;
binaural::register(py, m)?;
mdct::register(py, m)?;
#[cfg(feature = "realfft")]
{
m.add_function(wrap_pyfunction!(clear_fft_plan_cache, m)?)?;
m.add_function(wrap_pyfunction!(fft_plan_cache_info, m)?)?;
}
Ok(())
}
#[pyfunction]
#[cfg(feature = "realfft")]
fn clear_fft_plan_cache() {
crate::fft_backend::clear_plan_cache();
}
#[pyfunction]
#[cfg(feature = "realfft")]
fn fft_plan_cache_info() -> (usize, usize) {
crate::fft_backend::cache_stats()
}