use std::num::NonZeroUsize;
use crate::{
AmpScaleSpec, Cqt, Decibels, Gammatone, LinearHz, LogHz, Magnitude, Mel, Power, Spectrogram,
SpectrogramPlanner, SpectrogramResult, chromagram, fft, irfft, istft, magnitude_spectrum, mfcc,
power_spectrum, rfft,
};
use numpy::{PyArray1, PyArrayMethods};
use pyo3::prelude::*;
use super::dtype::{
Dtype, array1_to_py, complex_2d_owned, parse_dtype, vec1_to_py, with_complex_1d, with_real_1d,
};
use super::params::{
PyChromaParams, PyCqtParams, PyErbParams, PyLogHzParams, PyLogParams, PyMelParams,
PyMfccParams, PySpectrogramParams, PyStftParams, PyStftResult, PyWindowType,
};
use super::spectrogram::{PyScalar, PySpectrogram};
use super::{PyChromagram, PyMfcc};
use non_empty_slice::NonEmptySlice;
fn compute_typed<F, A, T, Run>(
py: Python<'_>,
samples: &Bound<'_, PyAny>,
run: Run,
) -> PyResult<PySpectrogram>
where
F: Copy + Clone + 'static,
A: AmpScaleSpec + 'static,
T: PyScalar,
Run: FnOnce(&NonEmptySlice<T>) -> SpectrogramResult<Spectrogram<F, A, T>>,
{
let np = py.import("numpy")?;
let array_any = np.call_method1("ascontiguousarray", (samples, T::DTYPE))?;
let array = array_any.cast::<PyArray1<T>>()?;
let readonly = array.readonly();
let slice = readonly.as_slice()?;
let samples_slice = NonEmptySlice::new(slice).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err("samples array must not be empty")
})?;
let spec = run(samples_slice)?;
Ok(PySpectrogram::from_spectrogram(py, spec))
}
macro_rules! impl_py_compute_fns {
(
$(
(
freq_ty = $freq_scale:ty,
amp_ty = $amp_scale:ty,
variant = $variant:ident,
fn_name = $fn_name:ident,
freq_desc = $freq_desc:expr,
amp_desc = $amp_desc:expr
)
),+ $(,)?
) => {
$(
#[doc = concat!(
"Compute a ", $freq_desc, " ", $amp_desc, " spectrogram.\n\n",
"Parameters\n",
"----------\n",
"samples : numpy.typing.NDArray[numpy.float64]\n",
" Audio samples as a 1D NumPy array\n",
"params : SpectrogramParams\n",
" Spectrogram parameters\n",
"dtype : str, optional\n",
" Output precision: \"float64\" (default) or \"float32\". The\n",
" spectrogram is computed natively at this precision.\n\n",
"Returns\n",
"-------\n",
"Spectrogram\n",
" Spectrogram with ", $freq_desc, " frequency scale and ",
$amp_desc, " amplitude scale"
)]
#[pyfunction]
#[pyo3(signature = (samples: "numpy.typing.NDArray[numpy.float64]",
params: "SpectrogramParams", db_params: "Optional[LogParams]"=None,
dtype: "str"=None), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], params: SpectrogramParams, db_params: Option[PyLogParams]=None, dtype: str = \"float64\")")]
fn $fn_name(
py: Python,
samples: &Bound<'_, PyAny>,
params: PySpectrogramParams,
db_params: Option<PyLogParams>,
dtype: Option<&str>,
) -> PyResult<PySpectrogram> {
match parse_dtype(dtype)? {
Dtype::F32 => compute_typed::<$freq_scale, $amp_scale, f32, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<$freq_scale, $amp_scale, f32>::compute(
s,
¶ms.inner,
db_params.as_ref().map(|p| &p.inner),
)
})
}),
Dtype::F64 => compute_typed::<$freq_scale, $amp_scale, f64, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<$freq_scale, $amp_scale, f64>::compute(
s,
¶ms.inner,
db_params.as_ref().map(|p| &p.inner),
)
})
}),
}
}
)+
};
}
impl_py_compute_fns! {
(
freq_ty = LinearHz,
amp_ty = Power,
variant = LinearPower,
fn_name = compute_linear_power_spectrogram,
freq_desc = "linear",
amp_desc = "power"
),
(
freq_ty = LinearHz,
amp_ty = Magnitude,
variant = LinearMagnitude,
fn_name = compute_linear_magnitude_spectrogram,
freq_desc = "linear",
amp_desc = "magnitude"
),
(
freq_ty = LinearHz,
amp_ty = Decibels,
variant = LinearDb,
fn_name = compute_linear_db_spectrogram,
freq_desc = "linear",
amp_desc = "decibel"
),
}
macro_rules! impl_filterbank_compute_fns {
(
$(
(
freq_ty = $freq_scale:ty,
amp_ty = $amp_scale:ty,
filter_ty = $filter_ty:ty,
py_filter_ty = $py_filter_ty:ty,
variant = $variant:ident,
fn_name = $fn_name:ident,
freq_desc = $freq_desc:expr,
amp_desc = $amp_desc:expr
)
),+ $(,)?
) => {
$(
#[doc = concat!(
"Compute a ", $freq_desc, " ", $amp_desc, " spectrogram.\n\n",
"Parameters\n",
"----------\n",
"samples : numpy.typing.NDArray[numpy.float64]\n",
" Audio samples as a 1D array\n",
"params : SpectrogramParams\n",
" Spectrogram parameters\n",
"filter_params : ", stringify!($py_filter_ty), "\n",
" Filterbank parameters\n",
"db : typing.Optional[LogParams], optional\n",
" Optional decibel scaling parameters\n",
"dtype : str, optional\n",
" Output precision: \"float64\" (default) or \"float32\". The\n",
" spectrogram is computed natively at this precision.\n\n",
"Returns\n",
"-------\n",
"Spectrogram\n",
" Spectrogram with ", $freq_desc, " frequency scale and ", $amp_desc, " amplitude scale"
)]
#[pyfunction]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
params: "SpectrogramParams",
filter_params,
db: "typing.Optional[LogParams]" = None,
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], params: SpectrogramParams, filter_params: FilterParams, db: typing.Optional[LogParams] = None, dtype: str = \"float64\")")]
fn $fn_name(
py: Python,
samples: &Bound<'_, PyAny>,
params: &PySpectrogramParams,
filter_params: &$py_filter_ty,
db: Option<&PyLogParams>,
dtype: Option<&str>,
) -> PyResult<PySpectrogram> {
match parse_dtype(dtype)? {
Dtype::F32 => compute_typed::<$freq_scale, $amp_scale, f32, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<$freq_scale, $amp_scale, f32>::compute(
s,
¶ms.inner,
&filter_params.inner,
db.map(|d| &d.inner),
)
})
}),
Dtype::F64 => compute_typed::<$freq_scale, $amp_scale, f64, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<$freq_scale, $amp_scale, f64>::compute(
s,
¶ms.inner,
&filter_params.inner,
db.map(|d| &d.inner),
)
})
}),
}
}
)+
};
}
impl_filterbank_compute_fns! {
(
freq_ty = Mel,
amp_ty = Power,
filter_ty = crate::MelParams,
py_filter_ty = PyMelParams,
variant = MelPower,
fn_name = compute_mel_power_spectrogram,
freq_desc = "mel",
amp_desc = "power"
),
(
freq_ty = Mel,
amp_ty = Magnitude,
filter_ty = crate::MelParams,
py_filter_ty = PyMelParams,
variant = MelMagnitude,
fn_name = compute_mel_magnitude_spectrogram,
freq_desc = "mel",
amp_desc = "magnitude"
),
(
freq_ty = Mel,
amp_ty = Decibels,
filter_ty = crate::MelParams,
py_filter_ty = PyMelParams,
variant = MelDb,
fn_name = compute_mel_db_spectrogram,
freq_desc = "mel",
amp_desc = "decibel"
),
(
freq_ty = Gammatone,
amp_ty = Power,
filter_ty = crate::ErbParams,
py_filter_ty = PyErbParams,
variant = GammatonePower,
fn_name = compute_erb_power_spectrogram,
freq_desc = "ERB/gammatone",
amp_desc = "power"
),
(
freq_ty = Gammatone,
amp_ty = Magnitude,
filter_ty = crate::ErbParams,
py_filter_ty = PyErbParams,
variant = GammatoneMagnitude,
fn_name = compute_erb_magnitude_spectrogram,
freq_desc = "ERB/gammatone",
amp_desc = "magnitude"
),
(
freq_ty = Gammatone,
amp_ty = Decibels,
filter_ty = crate::ErbParams,
py_filter_ty = PyErbParams,
variant = GammatoneDb,
fn_name = compute_erb_db_spectrogram,
freq_desc = "ERB/gammatone",
amp_desc = "decibel"
),
(
freq_ty = LogHz,
amp_ty = Power,
filter_ty = crate::LogHzParams,
py_filter_ty = PyLogHzParams,
variant = LogHzPower,
fn_name = compute_loghz_power_spectrogram,
freq_desc = "logarithmic Hz",
amp_desc = "power"
),
(
freq_ty = LogHz,
amp_ty = Magnitude,
filter_ty = crate::LogHzParams,
py_filter_ty = PyLogHzParams,
variant = LogHzMagnitude,
fn_name = compute_loghz_magnitude_spectrogram,
freq_desc = "logarithmic Hz",
amp_desc = "magnitude"
),
(
freq_ty = LogHz,
amp_ty = Decibels,
filter_ty = crate::LogHzParams,
py_filter_ty = PyLogHzParams,
variant = LogHzDb,
fn_name = compute_loghz_db_spectrogram,
freq_desc = "logarithmic Hz",
amp_desc = "decibel"
),
}
#[pyfunction]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
params: "SpectrogramParams",
cqt: "CqtParams",
db: "typing.Optional[LogParams]" = None,
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], params: SpectrogramParams, cqt: CqtParams, db: typing.Optional[LogParams] = None, dtype: str = \"float64\")")]
pub fn compute_cqt_power_spectrogram(
py: Python,
samples: &Bound<'_, PyAny>,
params: &PySpectrogramParams,
cqt: &PyCqtParams,
db: Option<&PyLogParams>,
dtype: Option<&str>,
) -> PyResult<PySpectrogram> {
match parse_dtype(dtype)? {
Dtype::F32 => compute_typed::<Cqt, Power, f32, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<Cqt, Power, f32>::compute(
s,
¶ms.inner,
&cqt.inner,
db.map(|d| &d.inner),
)
})
}),
Dtype::F64 => compute_typed::<Cqt, Power, f64, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<Cqt, Power, f64>::compute(
s,
¶ms.inner,
&cqt.inner,
db.map(|d| &d.inner),
)
})
}),
}
}
#[pyfunction]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
params: "SpectrogramParams",
cqt: "CqtParams",
db: "typing.Optional[LogParams]" = None,
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], params: SpectrogramParams, cqt: CqtParams, db: typing.Optional[LogParams] = None, dtype: str = \"float64\")")]
pub fn compute_cqt_magnitude_spectrogram(
py: Python,
samples: &Bound<'_, PyAny>,
params: &PySpectrogramParams,
cqt: &PyCqtParams,
db: Option<&PyLogParams>,
dtype: Option<&str>,
) -> PyResult<PySpectrogram> {
match parse_dtype(dtype)? {
Dtype::F32 => compute_typed::<Cqt, Magnitude, f32, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<Cqt, Magnitude, f32>::compute(
s,
¶ms.inner,
&cqt.inner,
db.map(|d| &d.inner),
)
})
}),
Dtype::F64 => compute_typed::<Cqt, Magnitude, f64, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<Cqt, Magnitude, f64>::compute(
s,
¶ms.inner,
&cqt.inner,
db.map(|d| &d.inner),
)
})
}),
}
}
#[pyfunction]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
params: "SpectrogramParams",
cqt: "CqtParams",
db: "typing.Optional[LogParams]" = None,
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], params: SpectrogramParams, cqt: CqtParams, db: typing.Optional[LogParams] = None, dtype: str = \"float64\")")]
pub fn compute_cqt_db_spectrogram(
py: Python,
samples: &Bound<'_, PyAny>,
params: &PySpectrogramParams,
cqt: &PyCqtParams,
db: Option<&PyLogParams>,
dtype: Option<&str>,
) -> PyResult<PySpectrogram> {
match parse_dtype(dtype)? {
Dtype::F32 => compute_typed::<Cqt, Decibels, f32, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<Cqt, Decibels, f32>::compute(
s,
¶ms.inner,
&cqt.inner,
db.map(|d| &d.inner),
)
})
}),
Dtype::F64 => compute_typed::<Cqt, Decibels, f64, _>(py, samples, |s| {
py.detach(|| {
Spectrogram::<Cqt, Decibels, f64>::compute(
s,
¶ms.inner,
&cqt.inner,
db.map(|d| &d.inner),
)
})
}),
}
}
fn chromagram_typed<T: PyScalar>(
py: Python<'_>,
samples: &Bound<'_, PyAny>,
stft_params: &crate::StftParams,
sample_rate: f64,
chroma_params: &crate::ChromaParams,
) -> PyResult<PyChromagram> {
with_real_1d::<T, _, _>(py, samples, |s| {
let result = py.detach(|| chromagram::<T>(s, stft_params, sample_rate, chroma_params))?;
Ok(PyChromagram::from_chromagram(py, result))
})
}
#[pyfunction]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
stft_params: "StftParams",
sample_rate: "float",
chroma_params: "ChromaParams",
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], stft_params: StftParams, sample_rate: float, chroma_params: ChromaParams, dtype: str = \"float64\")")]
pub fn compute_chromagram(
py: Python,
samples: &Bound<'_, PyAny>,
stft_params: &PyStftParams,
sample_rate: f64,
chroma_params: &PyChromaParams,
dtype: Option<&str>,
) -> PyResult<PyChromagram> {
match parse_dtype(dtype)? {
Dtype::F32 => chromagram_typed::<f32>(
py,
samples,
&stft_params.inner,
sample_rate,
&chroma_params.inner,
),
Dtype::F64 => chromagram_typed::<f64>(
py,
samples,
&stft_params.inner,
sample_rate,
&chroma_params.inner,
),
}
}
fn mfcc_typed<T: PyScalar>(
py: Python<'_>,
samples: &Bound<'_, PyAny>,
stft_params: &crate::StftParams,
sample_rate: f64,
n_mels: NonZeroUsize,
mfcc_params: &crate::MfccParams,
) -> PyResult<PyMfcc> {
with_real_1d::<T, _, _>(py, samples, |s| {
let result = py.detach(|| mfcc::<T>(s, stft_params, sample_rate, n_mels, mfcc_params))?;
Ok(PyMfcc::from_mfcc(py, result))
})
}
#[pyfunction]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
stft_params: "StftParams",
sample_rate: "float",
n_mels: "int",
mfcc_params: "MfccParams",
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], stft_params: StftParams, sample_rate: float, n_mels: int, mfcc_params: MfccParams, dtype: str = \"float64\")")]
pub fn compute_mfcc(
py: Python,
samples: &Bound<'_, PyAny>,
stft_params: &PyStftParams,
sample_rate: f64,
n_mels: usize,
mfcc_params: &PyMfccParams,
dtype: Option<&str>,
) -> PyResult<PyMfcc> {
let n_mels = NonZeroUsize::new(n_mels).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err("n_mels must be a positive integer")
})?;
match parse_dtype(dtype)? {
Dtype::F32 => mfcc_typed::<f32>(
py,
samples,
&stft_params.inner,
sample_rate,
n_mels,
&mfcc_params.inner,
),
Dtype::F64 => mfcc_typed::<f64>(
py,
samples,
&stft_params.inner,
sample_rate,
n_mels,
&mfcc_params.inner,
),
}
}
fn stft_typed<T: PyScalar>(
py: Python<'_>,
samples: &Bound<'_, PyAny>,
params: &crate::SpectrogramParams,
) -> PyResult<PyStftResult>
where
num_complex::Complex<T>: numpy::Element,
{
with_real_1d::<T, _, _>(py, samples, |s| {
let planner = SpectrogramPlanner::new();
let result = py.detach(|| planner.compute_stft::<T>(s, params))?;
Ok(PyStftResult::from_stft_result(py, result))
})
}
fn fft_typed<T: PyScalar>(
py: Python<'_>,
samples: &Bound<'_, PyAny>,
n_fft: NonZeroUsize,
) -> PyResult<Py<PyAny>>
where
num_complex::Complex<T>: numpy::Element,
{
with_real_1d::<T, _, _>(py, samples, |s| {
let result = py.detach(|| fft::<T>(s, n_fft))?;
Ok(array1_to_py(py, result))
})
}
fn rfft_typed<T: PyScalar>(
py: Python<'_>,
samples: &Bound<'_, PyAny>,
n_fft: NonZeroUsize,
) -> PyResult<Py<PyAny>> {
with_real_1d::<T, _, _>(py, samples, |s| {
let result = py.detach(|| rfft::<T>(s, n_fft))?;
Ok(array1_to_py(py, result))
})
}
fn power_spectrum_typed<T: PyScalar>(
py: Python<'_>,
samples: &Bound<'_, PyAny>,
n_fft: NonZeroUsize,
window: Option<crate::WindowType>,
) -> PyResult<Py<PyAny>> {
with_real_1d::<T, _, _>(py, samples, |s| {
let result = py.detach(|| power_spectrum::<T>(s, n_fft, window))?;
Ok(vec1_to_py(py, result.to_vec()))
})
}
fn magnitude_spectrum_typed<T: PyScalar>(
py: Python<'_>,
samples: &Bound<'_, PyAny>,
n_fft: NonZeroUsize,
window: Option<crate::WindowType>,
) -> PyResult<Py<PyAny>> {
with_real_1d::<T, _, _>(py, samples, |s| {
let result = py.detach(|| magnitude_spectrum::<T>(s, n_fft, window))?;
Ok(vec1_to_py(py, result.to_vec()))
})
}
fn irfft_typed<T: PyScalar>(
py: Python<'_>,
spectrum: &Bound<'_, PyAny>,
n_fft: NonZeroUsize,
) -> PyResult<Py<PyAny>>
where
num_complex::Complex<T>: numpy::Element,
{
with_complex_1d::<T, _, _>(py, spectrum, |s| {
let result = py.detach(|| irfft::<T>(s, n_fft))?;
Ok(vec1_to_py(py, result.to_vec()))
})
}
fn istft_typed<T: PyScalar>(
py: Python<'_>,
stft_matrix: &Bound<'_, PyAny>,
n_fft: NonZeroUsize,
hop_size: NonZeroUsize,
window: crate::WindowType,
center: bool,
) -> PyResult<Py<PyAny>>
where
num_complex::Complex<T>: numpy::Element,
{
let matrix = complex_2d_owned::<T>(py, stft_matrix)?;
let result = py.detach(|| istft::<T>(&matrix, n_fft, hop_size, window, center))?;
Ok(vec1_to_py(py, result.to_vec()))
}
#[pyfunction]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
params: "SpectrogramParams",
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], params: SpectrogramParams, dtype: str = \"float64\")")]
pub fn compute_stft(
py: Python,
samples: &Bound<'_, PyAny>,
params: &PySpectrogramParams,
dtype: Option<&str>,
) -> PyResult<PyStftResult> {
match parse_dtype(dtype)? {
Dtype::F32 => stft_typed::<f32>(py, samples, ¶ms.inner),
Dtype::F64 => stft_typed::<f64>(py, samples, ¶ms.inner),
}
}
#[pyfunction]
#[inline]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
n_fft: "Optional[int]" = None,
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], n_fft: Optional[int]=None, dtype: str = \"float64\")")]
pub fn compute_fft(
py: Python,
samples: &Bound<'_, PyAny>,
n_fft: Option<usize>,
dtype: Option<&str>,
) -> PyResult<Py<PyAny>> {
let n_fft = match n_fft {
Some(n) => n,
None => samples.len()?,
};
let n_fft = NonZeroUsize::new(n_fft).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err("n_fft must be non-zero positive integer")
})?;
match parse_dtype(dtype)? {
Dtype::F32 => fft_typed::<f32>(py, samples, n_fft),
Dtype::F64 => fft_typed::<f64>(py, samples, n_fft),
}
}
#[pyfunction]
#[inline]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
n_fft: "int",
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], n_fft: int, dtype: str = \"float64\") -> numpy.typing.NDArray[numpy.float64]")]
pub fn compute_rfft(
py: Python,
samples: &Bound<'_, PyAny>,
n_fft: usize,
dtype: Option<&str>,
) -> PyResult<Py<PyAny>> {
let n_fft = NonZeroUsize::new(n_fft).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err("n_fft must be non-zero positive integer")
})?;
match parse_dtype(dtype)? {
Dtype::F32 => rfft_typed::<f32>(py, samples, n_fft),
Dtype::F64 => rfft_typed::<f64>(py, samples, n_fft),
}
}
#[pyfunction]
#[inline]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
n_fft: "int",
window: "typing.Optional[WindowType]" = None,
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], n_fft: int, window: typing.Optional[WindowType] = None, dtype: str = \"float64\")")]
pub fn compute_power_spectrum(
py: Python,
samples: &Bound<'_, PyAny>,
n_fft: usize,
window: Option<PyWindowType>,
dtype: Option<&str>,
) -> PyResult<Py<PyAny>> {
let n_fft = NonZeroUsize::new(n_fft).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err("n_fft must be non-zero positive integer")
})?;
let window_type = window.map(|w| w.inner);
match parse_dtype(dtype)? {
Dtype::F32 => power_spectrum_typed::<f32>(py, samples, n_fft, window_type),
Dtype::F64 => power_spectrum_typed::<f64>(py, samples, n_fft, window_type),
}
}
#[pyfunction]
#[inline]
#[pyo3(signature = (
samples: "numpy.typing.NDArray[numpy.float64]",
n_fft: "int",
window: "typing.Optional[WindowType]" = None,
dtype: "str" = None
), text_signature = "(samples: numpy.typing.NDArray[numpy.float64], n_fft: int, window: typing.Optional[WindowType] = None, dtype: str = \"float64\")")]
pub fn compute_magnitude_spectrum(
py: Python,
samples: &Bound<'_, PyAny>,
n_fft: usize,
window: Option<PyWindowType>,
dtype: Option<&str>,
) -> PyResult<Py<PyAny>> {
let n_fft = NonZeroUsize::new(n_fft).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err("n_fft must be non-zero positive integer")
})?;
let window_type = window.map(|w| w.inner);
match parse_dtype(dtype)? {
Dtype::F32 => magnitude_spectrum_typed::<f32>(py, samples, n_fft, window_type),
Dtype::F64 => magnitude_spectrum_typed::<f64>(py, samples, n_fft, window_type),
}
}
#[pyfunction]
#[inline]
#[pyo3(signature = (
spectrum: "numpy.typing.NDArray[numpy.complex128]",
n_fft: "int",
dtype: "str" = None
), text_signature = "(spectrum: numpy.typing.NDArray[numpy.complex128], n_fft: int, dtype: str = \"float64\")")]
pub fn compute_irfft(
py: Python,
spectrum: &Bound<'_, PyAny>,
n_fft: usize,
dtype: Option<&str>,
) -> PyResult<Py<PyAny>> {
let n_fft = NonZeroUsize::new(n_fft).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err("n_fft must be non-zero positive integer")
})?;
match parse_dtype(dtype)? {
Dtype::F32 => irfft_typed::<f32>(py, spectrum, n_fft),
Dtype::F64 => irfft_typed::<f64>(py, spectrum, n_fft),
}
}
#[pyfunction]
#[inline]
#[pyo3(signature = (
stft_matrix: "numpy.typing.NDArray[numpy.complex64]",
n_fft: "int",
hop_size: "int",
window: "WindowType",
center: "bool" = true,
dtype: "str" = None
), text_signature = "(stft_matrix: numpy.typing.NDArray[numpy.complex64], n_fft: int, hop_size: int, window: WindowType, center: bool = True, dtype: str = \"float64\")")]
pub fn compute_istft(
py: Python,
stft_matrix: &Bound<'_, PyAny>,
n_fft: usize,
hop_size: usize,
window: PyWindowType,
center: bool,
dtype: Option<&str>,
) -> PyResult<Py<PyAny>> {
let n_fft = NonZeroUsize::new(n_fft).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err("n_fft must be non-zero positive integer")
})?;
let hop_size = NonZeroUsize::new(hop_size).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err("hop_size must be non-zero positive integer")
})?;
let window = window.inner;
match parse_dtype(dtype)? {
Dtype::F32 => istft_typed::<f32>(py, stft_matrix, n_fft, hop_size, window, center),
Dtype::F64 => istft_typed::<f64>(py, stft_matrix, n_fft, hop_size, window, center),
}
}
pub fn register(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(compute_linear_power_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_linear_magnitude_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_linear_db_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_mel_power_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_mel_magnitude_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_mel_db_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_erb_power_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_erb_magnitude_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_erb_db_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_loghz_power_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_loghz_magnitude_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_loghz_db_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_cqt_power_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_cqt_magnitude_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_cqt_db_spectrogram, m)?)?;
m.add_function(wrap_pyfunction!(compute_chromagram, m)?)?;
m.add_function(wrap_pyfunction!(compute_mfcc, m)?)?;
m.add_function(wrap_pyfunction!(compute_stft, m)?)?;
m.add_function(wrap_pyfunction!(compute_fft, m)?)?;
m.add_function(wrap_pyfunction!(compute_rfft, m)?)?;
m.add_function(wrap_pyfunction!(compute_irfft, m)?)?;
m.add_function(wrap_pyfunction!(compute_power_spectrum, m)?)?;
m.add_function(wrap_pyfunction!(compute_magnitude_spectrum, m)?)?;
m.add_function(wrap_pyfunction!(compute_istft, m)?)?;
Ok(())
}