1use std::num::NonZeroUsize;
4
5use num_complex::Complex;
6use numpy::{PyArray1, PyArray2, PyReadonlyArray1};
7use pyo3::prelude::*;
8use pyo3::types::PyType;
9
10use crate::{
11 ChromaNorm, ChromaParams, CqtParams, ErbParams, LogHzParams, LogParams, MelNorm, MelParams,
12 MfccParams, SpectrogramParams, StftParams, StftResult, WindowType,
13};
14
15#[pyclass(name = "WindowType", from_py_object)]
20#[derive(Clone, Debug)]
21pub struct PyWindowType {
22 pub(crate) inner: WindowType,
23}
24
25impl PyWindowType {
26 #[must_use]
27 pub fn into_inner(self) -> WindowType {
28 self.inner
29 }
30 #[must_use]
31 pub const fn as_inner(&self) -> &WindowType {
32 &self.inner
33 }
34}
35
36#[pymethods]
37impl PyWindowType {
38 #[classattr]
42 const fn rectangular() -> Self {
43 Self {
44 inner: WindowType::Rectangular,
45 }
46 }
47
48 #[classattr]
52 const fn hanning() -> Self {
53 Self {
54 inner: WindowType::Hanning,
55 }
56 }
57
58 #[classattr]
62 const fn hamming() -> Self {
63 Self {
64 inner: WindowType::Hamming,
65 }
66 }
67
68 #[classattr]
72 const fn blackman() -> Self {
73 Self {
74 inner: WindowType::Blackman,
75 }
76 }
77
78 #[classmethod]
92 #[pyo3(signature = (beta: "float"), text_signature = "(beta: float) -> WindowType")]
93 const fn kaiser(_cls: &Bound<'_, PyType>, beta: f64) -> Self {
94 Self {
95 inner: WindowType::Kaiser { beta },
96 }
97 }
98
99 #[classmethod]
113 #[pyo3(signature = (std: "float"), text_signature = "(std: float) -> WindowType")]
114 const fn gaussian(_cls: &Bound<'_, PyType>, std: f64) -> Self {
115 Self {
116 inner: WindowType::Gaussian { std },
117 }
118 }
119
120 #[classmethod]
171 #[pyo3(signature = (coefficients, normalize=None), text_signature = "(coefficients, normalize=None) -> WindowType")]
172 fn custom(
173 _cls: &Bound<'_, PyType>,
174 coefficients: PyReadonlyArray1<f64>,
175 normalize: Option<&str>,
176 ) -> PyResult<Self> {
177 let vec = coefficients.as_slice()?.to_vec();
178 let inner = WindowType::custom_with_normalization(vec, normalize)?;
179 Ok(Self { inner })
180 }
181
182 #[staticmethod]
195 #[pyo3(signature = (n: "int"), text_signature = "(n: int) -> numpy.ndarray")]
196 fn make_hanning(py: Python<'_>, n: NonZeroUsize) -> Bound<'_, PyArray1<f64>> {
197 let window_vec = crate::window::hanning_window(n);
198 PyArray1::from_vec(py, window_vec.into_vec())
199 }
200
201 #[staticmethod]
214 #[pyo3(signature = (n: "int"), text_signature = "(n: int) -> numpy.ndarray")]
215 fn make_hamming(py: Python<'_>, n: NonZeroUsize) -> Bound<'_, PyArray1<f64>> {
216 let window_vec = crate::window::hamming_window(n);
217 PyArray1::from_vec(py, window_vec.into_vec())
218 }
219
220 #[staticmethod]
233 #[pyo3(signature = (n: "int"), text_signature = "(n: int) -> numpy.ndarray")]
234 fn make_blackman(py: Python<'_>, n: NonZeroUsize) -> Bound<'_, PyArray1<f64>> {
235 let window_vec = crate::window::blackman_window(n);
236 PyArray1::from_vec(py, window_vec.into_vec())
237 }
238
239 #[staticmethod]
253 #[pyo3(signature = (n: "int", beta: "float"), text_signature = "(n: int, beta: float) -> numpy.ndarray")]
254 fn make_kaiser(py: Python<'_>, n: NonZeroUsize, beta: f64) -> Bound<'_, PyArray1<f64>> {
255 let window_vec = crate::window::kaiser_window(n, beta);
256 PyArray1::from_vec(py, window_vec.into_vec())
257 }
258
259 #[staticmethod]
273 #[pyo3(signature = (n: "int", std: "float"), text_signature = "(n: int, std: float) -> numpy.ndarray")]
274 fn make_gaussian(py: Python<'_>, n: NonZeroUsize, std: f64) -> Bound<'_, PyArray1<f64>> {
275 let window_vec = crate::window::gaussian_window(n, std);
276 PyArray1::from_vec(py, window_vec.into_vec())
277 }
278
279 fn __repr__(&self) -> String {
280 format!("{}", self.inner)
281 }
282}
283
284impl From<WindowType> for PyWindowType {
285 fn from(wt: WindowType) -> Self {
286 Self { inner: wt }
287 }
288}
289
290#[pyclass(name = "StftResult", from_py_object)]
291#[derive(Clone, Debug)]
292pub struct PyStftResult {
293 pub(crate) inner: StftResult,
294}
295
296impl From<StftResult> for PyStftResult {
297 fn from(inner: StftResult) -> Self {
298 Self { inner }
299 }
300}
301
302impl From<PyStftResult> for StftResult {
303 #[inline]
304 fn from(val: PyStftResult) -> Self {
305 val.inner
306 }
307}
308
309impl PyStftResult {
310 #[must_use]
311 pub const fn from_inner(inner: StftResult) -> Self {
312 Self { inner }
313 }
314
315 #[must_use]
316 pub fn into_inner(self) -> StftResult {
317 self.inner
318 }
319}
320
321#[pymethods]
322impl PyStftResult {
323 #[getter]
324 fn n_bins(&self) -> usize {
325 self.inner.n_bins().get()
326 }
327
328 #[getter]
329 fn n_frames(&self) -> usize {
330 self.inner.n_frames().get()
331 }
332
333 #[getter]
334 fn frequency_resolution(&self) -> f64 {
335 self.inner.frequency_resolution()
336 }
337
338 #[getter]
339 fn time_resolution(&self) -> f64 {
340 self.inner.time_resolution()
341 }
342
343 #[getter]
344 fn params(&self) -> PyStftParams {
345 PyStftParams {
346 inner: self.inner.params.clone(),
347 }
348 }
349
350 #[getter]
351 const fn sample_rate(&self) -> f64 {
352 self.inner.sample_rate
353 }
354
355 fn norm<'py>(&'py self, py: Python<'py>) -> Bound<'py, PyArray2<f64>> {
356 PyArray2::from_owned_array(py, self.inner.norm())
357 }
358
359 fn data<'py>(&'py self, py: Python<'py>) -> Bound<'py, PyArray2<Complex<f64>>> {
360 PyArray2::from_owned_array(py, self.inner.data.clone())
361 }
362}
363
364#[pyclass(name = "StftParams", from_py_object)]
366#[derive(Clone, Debug)]
367pub struct PyStftParams {
368 pub inner: StftParams,
369}
370
371#[pymethods]
372impl PyStftParams {
373 #[new]
391 #[pyo3(signature = (
392 n_fft: "int",
393 hop_size: "int",
394 window: "WindowType",
395 centre: "bool" = true
396 ), text_signature = "(n_fft: int, hop_size: int, window: WindowType, centre: bool = True)")]
397 fn new(
398 n_fft: NonZeroUsize,
399 hop_size: NonZeroUsize,
400 window: PyWindowType,
401 centre: bool,
402 ) -> PyResult<Self> {
403 let inner = StftParams::new(n_fft, hop_size, window.inner, centre)?;
404 Ok(Self { inner })
405 }
406
407 #[getter]
409 const fn n_fft(&self) -> NonZeroUsize {
410 self.inner.n_fft()
411 }
412
413 #[getter]
415 const fn hop_size(&self) -> NonZeroUsize {
416 self.inner.hop_size()
417 }
418
419 #[getter]
421 fn window(&self) -> PyWindowType {
422 PyWindowType {
423 inner: self.inner.window(),
424 }
425 }
426
427 #[getter]
429 const fn centre(&self) -> bool {
430 self.inner.centre()
431 }
432
433 fn __repr__(&self) -> String {
434 format!(
435 "StftParams(n_fft={}, hop_size={}, window={}, centre={})",
436 self.n_fft(),
437 self.hop_size(),
438 self.window().__repr__(),
439 self.centre()
440 )
441 }
442}
443
444impl From<PyStftParams> for StftParams {
445 #[inline]
446 fn from(val: PyStftParams) -> Self {
447 val.inner
448 }
449}
450
451impl From<StftParams> for PyStftParams {
452 #[inline]
453 fn from(inner: StftParams) -> Self {
454 Self { inner }
455 }
456}
457
458#[pyclass(name = "LogParams", from_py_object)]
461#[derive(Debug, Copy, Clone, PartialEq)]
462pub struct PyLogParams {
463 pub(crate) inner: LogParams,
464}
465
466impl PyLogParams {
467 #[inline]
468 #[must_use]
469 pub const fn into_inner(self) -> LogParams {
470 self.inner
471 }
472
473 #[inline]
474 #[must_use]
475 pub const fn as_inner(&self) -> &LogParams {
476 &self.inner
477 }
478}
479
480#[pymethods]
481impl PyLogParams {
482 #[new]
487 #[pyo3(signature = (floor_db: "float"), text_signature = "(floor_db: float)")]
488 fn new(floor_db: f64) -> PyResult<Self> {
489 let inner = LogParams::new(floor_db)?;
490 Ok(Self { inner })
491 }
492
493 #[getter]
495 const fn floor_db(&self) -> f64 {
496 self.inner.floor_db()
497 }
498
499 fn __repr__(&self) -> String {
500 format!("LogParams(floor_db={})", self.floor_db())
501 }
502}
503
504impl From<PyLogParams> for LogParams {
505 #[inline]
506 fn from(val: PyLogParams) -> Self {
507 val.inner
508 }
509}
510
511impl From<LogParams> for PyLogParams {
512 #[inline]
513 fn from(inner: LogParams) -> Self {
514 Self { inner }
515 }
516}
517
518#[pyclass(name = "SpectrogramParams", from_py_object)]
520#[derive(Clone, Debug)]
521pub struct PySpectrogramParams {
522 pub(crate) inner: SpectrogramParams,
523}
524
525#[pymethods]
526impl PySpectrogramParams {
527 #[new]
534 #[pyo3(signature = (
535 stft: "StftParams",
536 sample_rate: "float"
537 ), text_signature = "(stft: StftParams, sample_rate: float)")]
538 fn new(stft: &PyStftParams, sample_rate: f64) -> PyResult<Self> {
539 let inner = SpectrogramParams::new(stft.inner.clone(), sample_rate)?;
540 Ok(Self { inner })
541 }
542
543 #[getter]
545 fn stft(&self) -> PyStftParams {
546 PyStftParams {
547 inner: self.inner.stft().clone(),
548 }
549 }
550
551 #[getter]
553 const fn sample_rate(&self) -> f64 {
554 self.inner.sample_rate_hz()
555 }
556
557 #[classmethod]
571 #[pyo3(signature = (sample_rate: "float"), text_signature = "(sample_rate: float)")]
572 fn speech_default(_cls: &Bound<'_, PyType>, sample_rate: f64) -> PyResult<Self> {
573 let inner = SpectrogramParams::speech_default(sample_rate)?;
574 Ok(Self { inner })
575 }
576
577 #[classmethod]
591 #[pyo3(signature = (sample_rate: "float"), text_signature = "(sample_rate: float)")]
592 fn music_default(_cls: &Bound<'_, PyType>, sample_rate: f64) -> PyResult<Self> {
593 let inner = SpectrogramParams::music_default(sample_rate)?;
594 Ok(Self { inner })
595 }
596
597 fn __repr__(&self) -> String {
598 format!(
599 "SpectrogramParams(sample_rate={}, n_fft={}, hop_size={})",
600 self.sample_rate(),
601 self.inner.stft().n_fft(),
602 self.inner.stft().hop_size()
603 )
604 }
605}
606
607impl From<SpectrogramParams> for PySpectrogramParams {
608 fn from(inner: SpectrogramParams) -> Self {
609 Self { inner }
610 }
611}
612
613impl From<PySpectrogramParams> for SpectrogramParams {
614 #[inline]
615 fn from(py_params: PySpectrogramParams) -> Self {
616 py_params.inner
617 }
618}
619
620#[pyclass(name = "MelNorm", from_py_object)]
622#[derive(Clone, Copy, Debug, PartialEq, Eq)]
623pub enum PyMelNorm {
624 None,
626 Slaney,
628 L1,
630 L2,
632}
633
634#[pymethods]
635impl PyMelNorm {
636 #[classattr]
637 const fn none() -> Self {
638 Self::None
639 }
640
641 #[classattr]
642 const fn slaney() -> Self {
643 Self::Slaney
644 }
645
646 #[classattr]
647 const fn l1() -> Self {
648 Self::L1
649 }
650
651 #[classattr]
652 const fn l2() -> Self {
653 Self::L2
654 }
655
656 fn __repr__(&self) -> String {
657 match self {
658 Self::None => "MelNorm.None".to_string(),
659 Self::Slaney => "MelNorm.Slaney".to_string(),
660 Self::L1 => "MelNorm.L1".to_string(),
661 Self::L2 => "MelNorm.L2".to_string(),
662 }
663 }
664}
665
666impl From<PyMelNorm> for MelNorm {
667 #[inline]
668 fn from(py_norm: PyMelNorm) -> Self {
669 match py_norm {
670 PyMelNorm::None => Self::None,
671 PyMelNorm::Slaney => Self::Slaney,
672 PyMelNorm::L1 => Self::L1,
673 PyMelNorm::L2 => Self::L2,
674 }
675 }
676}
677
678impl From<MelNorm> for PyMelNorm {
679 #[inline]
680 fn from(norm: MelNorm) -> Self {
681 match norm {
682 MelNorm::None => Self::None,
683 MelNorm::Slaney => Self::Slaney,
684 MelNorm::L1 => Self::L1,
685 MelNorm::L2 => Self::L2,
686 }
687 }
688}
689
690#[pyclass(name = "MelParams", from_py_object)]
692#[derive(Clone, Copy, Debug)]
693pub struct PyMelParams {
694 pub(crate) inner: MelParams,
695}
696
697#[pymethods]
698impl PyMelParams {
699 #[new]
716 #[pyo3(signature = (
717 n_mels: "int",
718 f_min: "float",
719 f_max: "float",
720 norm: "MelNorm" = None
721 ), text_signature = "(n_mels: int, f_min: float, f_max: float, norm: MelNorm | str | None = None)")]
722 fn new(
723 n_mels: NonZeroUsize,
724 f_min: f64,
725 f_max: f64,
726 norm: Option<&pyo3::Bound<'_, pyo3::PyAny>>,
727 ) -> PyResult<Self> {
728 let norm_val = if let Some(norm_arg) = norm {
729 if norm_arg.is_none() {
730 MelNorm::None
731 } else if let Ok(s) = norm_arg.extract::<String>() {
732 match s.to_lowercase().as_str() {
733 "none" => MelNorm::None,
734 "slaney" => MelNorm::Slaney,
735 "l1" => MelNorm::L1,
736 "l2" => MelNorm::L2,
737 _ => {
738 return Err(pyo3::exceptions::PyValueError::new_err(format!(
739 "Invalid norm string: '{s}'. Must be one of: 'none', 'slaney', 'l1', 'l2'"
740 )));
741 }
742 }
743 } else if let Ok(py_norm) = norm_arg.extract::<PyMelNorm>() {
744 py_norm.into()
745 } else {
746 return Err(pyo3::exceptions::PyTypeError::new_err(
747 "norm must be a MelNorm enum, a string, or None",
748 ));
749 }
750 } else {
751 MelNorm::None
752 };
753
754 let inner = MelParams::with_norm(n_mels, f_min, f_max, norm_val)?;
755 Ok(Self { inner })
756 }
757
758 #[getter]
760 const fn n_mels(&self) -> NonZeroUsize {
761 self.inner.n_mels()
762 }
763
764 #[getter]
766 const fn f_min(&self) -> f64 {
767 self.inner.f_min()
768 }
769
770 #[getter]
772 const fn f_max(&self) -> f64 {
773 self.inner.f_max()
774 }
775
776 #[getter]
778 fn norm(&self) -> PyMelNorm {
779 self.inner.norm().into()
780 }
781
782 fn __repr__(&self) -> String {
783 let norm_str = match self.inner.norm() {
784 MelNorm::None => "None",
785 MelNorm::Slaney => "slaney",
786 MelNorm::L1 => "l1",
787 MelNorm::L2 => "l2",
788 };
789 format!(
790 "MelParams(n_mels={}, f_min={}, f_max={}, norm='{}')",
791 self.n_mels(),
792 self.f_min(),
793 self.f_max(),
794 norm_str
795 )
796 }
797}
798
799impl From<PyMelParams> for MelParams {
800 #[inline]
801 fn from(val: PyMelParams) -> Self {
802 val.inner
803 }
804}
805
806impl From<MelParams> for PyMelParams {
807 #[inline]
808 fn from(inner: MelParams) -> Self {
809 Self { inner }
810 }
811}
812
813#[pyclass(name = "ErbParams", from_py_object)]
815#[derive(Clone, Copy, Debug)]
816pub struct PyErbParams {
817 pub(crate) inner: ErbParams,
818}
819
820impl PyErbParams {
821 #[inline]
822 #[must_use]
823 pub const fn into_inner(self) -> ErbParams {
824 self.inner
825 }
826 #[inline]
827 #[must_use]
828 pub const fn as_inner(&self) -> &ErbParams {
829 &self.inner
830 }
831}
832
833#[pymethods]
834impl PyErbParams {
835 #[new]
846 #[pyo3(signature = (
847 n_filters: "int",
848 f_min: "float",
849 f_max: "float"
850 ), text_signature = "(n_filters: int, f_min: float, f_max: float)")]
851 fn new(n_filters: NonZeroUsize, f_min: f64, f_max: f64) -> PyResult<Self> {
852 let inner = ErbParams::new(n_filters, f_min, f_max)?;
853 Ok(Self { inner })
854 }
855
856 #[getter]
858 const fn n_filters(&self) -> NonZeroUsize {
859 self.inner.n_filters()
860 }
861
862 #[getter]
864 const fn f_min(&self) -> f64 {
865 self.inner.f_min()
866 }
867
868 #[getter]
870 const fn f_max(&self) -> f64 {
871 self.inner.f_max()
872 }
873
874 fn __repr__(&self) -> String {
875 format!(
876 "ErbParams(n_filters={}, f_min={}, f_max={})",
877 self.n_filters(),
878 self.f_min(),
879 self.f_max()
880 )
881 }
882}
883
884impl From<ErbParams> for PyErbParams {
885 #[inline]
886 fn from(inner: ErbParams) -> Self {
887 Self { inner }
888 }
889}
890
891#[pyclass(name = "LogHzParams", from_py_object)]
893#[derive(Clone, Copy, Debug)]
894pub struct PyLogHzParams {
895 pub(crate) inner: LogHzParams,
896}
897
898#[pymethods]
899impl PyLogHzParams {
900 #[new]
911 #[pyo3(signature = (
912 n_bins: "int",
913 f_min: "float",
914 f_max: "float"
915 ), text_signature = "(n_bins: int, f_min: float, f_max: float)")]
916 fn new(n_bins: NonZeroUsize, f_min: f64, f_max: f64) -> PyResult<Self> {
917 let inner = LogHzParams::new(n_bins, f_min, f_max)?;
918 Ok(Self { inner })
919 }
920
921 #[getter]
923 const fn n_bins(&self) -> NonZeroUsize {
924 self.inner.n_bins()
925 }
926
927 #[getter]
929 const fn f_min(&self) -> f64 {
930 self.inner.f_min()
931 }
932
933 #[getter]
935 const fn f_max(&self) -> f64 {
936 self.inner.f_max()
937 }
938
939 fn __repr__(&self) -> String {
940 format!(
941 "LogHzParams(n_bins={}, f_min={}, f_max={})",
942 self.n_bins(),
943 self.f_min(),
944 self.f_max()
945 )
946 }
947}
948
949#[pyclass(name = "CqtParams", from_py_object)]
951#[derive(Clone, Debug)]
952pub struct PyCqtParams {
953 pub(crate) inner: CqtParams,
954}
955
956#[pymethods]
957impl PyCqtParams {
958 #[new]
969 #[pyo3(signature = (
970 bins_per_octave: "int",
971 n_octaves: "int",
972 f_min: "float"
973 ), text_signature = "(bins_per_octave: int, n_octaves: int, f_min: float)")]
974 fn new(bins_per_octave: NonZeroUsize, n_octaves: NonZeroUsize, f_min: f64) -> PyResult<Self> {
975 let inner = CqtParams::new(bins_per_octave, n_octaves, f_min)?;
976 Ok(Self { inner })
977 }
978
979 #[getter]
981 const fn num_bins(&self) -> NonZeroUsize {
982 self.inner.num_bins()
983 }
984
985 fn __repr__(&self) -> String {
986 format!("CqtParams(num_bins={})", self.num_bins())
987 }
988}
989
990impl From<CqtParams> for PyCqtParams {
991 #[inline]
992 fn from(inner: CqtParams) -> Self {
993 Self { inner }
994 }
995}
996
997impl From<PyCqtParams> for CqtParams {
998 #[inline]
999 fn from(val: PyCqtParams) -> Self {
1000 val.inner
1001 }
1002}
1003
1004#[pyclass(name = "ChromaNorm", from_py_object)]
1005#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
1006pub struct PyChromaNorm {
1007 pub(crate) inner: ChromaNorm,
1008}
1009
1010#[pymethods]
1011impl PyChromaNorm {
1012 #[classattr]
1014 const fn none() -> Self {
1015 Self {
1016 inner: ChromaNorm::None,
1017 }
1018 }
1019
1020 #[classattr]
1022 const fn l1() -> Self {
1023 Self {
1024 inner: ChromaNorm::L1,
1025 }
1026 }
1027
1028 #[classattr]
1030 const fn l2() -> Self {
1031 Self {
1032 inner: ChromaNorm::L2,
1033 }
1034 }
1035
1036 #[classattr]
1038 const fn max() -> Self {
1039 Self {
1040 inner: ChromaNorm::Max,
1041 }
1042 }
1043
1044 fn __repr__(&self) -> String {
1045 format!("{:?}", self.inner)
1046 }
1047}
1048
1049impl From<ChromaNorm> for PyChromaNorm {
1050 #[inline]
1051 fn from(inner: ChromaNorm) -> Self {
1052 Self { inner }
1053 }
1054}
1055
1056impl From<PyChromaNorm> for ChromaNorm {
1057 #[inline]
1058 fn from(val: PyChromaNorm) -> Self {
1059 val.inner
1060 }
1061}
1062
1063#[pyclass(name = "ChromaParams", from_py_object)]
1065#[derive(Clone, Copy, Debug)]
1066pub struct PyChromaParams {
1067 pub(crate) inner: ChromaParams,
1068}
1069
1070#[pymethods]
1071impl PyChromaParams {
1072 #[new]
1085 #[pyo3(signature = (
1086 tuning: "float" = 440.0,
1087 f_min: "float" = 32.7,
1088 f_max: "float" = 4186.0,
1089 norm: "ChromaNorm" = None
1090 ), text_signature = "(tuning: float = 440.0, f_min: float = 32.7, f_max: float = 4186.0, norm: ChromaNorm = ChromaNorm.None)")]
1091 fn new(tuning: f64, f_min: f64, f_max: f64, norm: Option<PyChromaNorm>) -> PyResult<Self> {
1092 let norm = norm.unwrap_or_default();
1093 let inner = ChromaParams::new(tuning, f_min, f_max, norm.inner)?;
1094 Ok(Self { inner })
1095 }
1096
1097 #[classmethod]
1099 const fn music_standard(_cls: &Bound<'_, PyType>) -> Self {
1100 let inner = ChromaParams::music_standard();
1101 Self { inner }
1102 }
1103
1104 #[getter]
1106 const fn tuning(&self) -> f64 {
1107 self.inner.tuning()
1108 }
1109
1110 #[getter]
1112 const fn f_min(&self) -> f64 {
1113 self.inner.f_min()
1114 }
1115
1116 #[getter]
1118 const fn f_max(&self) -> f64 {
1119 self.inner.f_max()
1120 }
1121
1122 fn __repr__(&self) -> String {
1123 format!(
1124 "ChromaParams(tuning={}, f_min={}, f_max={}, norm={:?})",
1125 self.tuning(),
1126 self.f_min(),
1127 self.f_max(),
1128 self.inner
1129 )
1130 }
1131}
1132
1133impl From<ChromaParams> for PyChromaParams {
1134 #[inline]
1135 fn from(inner: ChromaParams) -> Self {
1136 Self { inner }
1137 }
1138}
1139
1140impl From<PyChromaParams> for ChromaParams {
1141 #[inline]
1142 fn from(val: PyChromaParams) -> Self {
1143 val.inner
1144 }
1145}
1146#[pyclass(name = "MfccParams", from_py_object)]
1148#[derive(Clone, Copy, Debug)]
1149pub struct PyMfccParams {
1150 pub(crate) inner: MfccParams,
1151}
1152
1153#[pymethods]
1154impl PyMfccParams {
1155 #[new]
1162 #[pyo3(signature = (n_mfcc: "int" = 13), text_signature = "(n_mfcc: int = 13)")]
1163 fn new(n_mfcc: usize) -> PyResult<Self> {
1164 let n_mfcc = NonZeroUsize::new(n_mfcc).ok_or_else(|| {
1165 pyo3::exceptions::PyValueError::new_err("n_mfcc must be a positive integer")
1166 })?;
1167 let inner = MfccParams::new(n_mfcc);
1168 Ok(Self { inner })
1169 }
1170
1171 #[classmethod]
1173 const fn speech_standard(_cls: &Bound<'_, PyType>) -> Self {
1174 let inner = MfccParams::speech_standard();
1175 Self { inner }
1176 }
1177
1178 #[getter]
1180 const fn n_mfcc(&self) -> NonZeroUsize {
1181 self.inner.n_mfcc()
1182 }
1183
1184 fn __repr__(&self) -> String {
1185 format!("MfccParams(n_mfcc={})", self.n_mfcc())
1186 }
1187}
1188
1189impl From<PyMfccParams> for MfccParams {
1190 #[inline]
1191 fn from(val: PyMfccParams) -> Self {
1192 val.inner
1193 }
1194}
1195
1196impl From<MfccParams> for PyMfccParams {
1197 #[inline]
1198 fn from(inner: MfccParams) -> Self {
1199 Self { inner }
1200 }
1201}
1202
1203#[inline]
1205pub fn register(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
1206 m.add_class::<PyWindowType>()?;
1207 m.add_class::<PyStftResult>()?;
1208 m.add_class::<PyStftParams>()?;
1209 m.add_class::<PyLogParams>()?;
1210 m.add_class::<PySpectrogramParams>()?;
1211 m.add_class::<PyMelNorm>()?;
1212 m.add_class::<PyMelParams>()?;
1213 m.add_class::<PyErbParams>()?;
1214 m.add_class::<PyLogHzParams>()?;
1215 m.add_class::<PyCqtParams>()?;
1216 m.add_class::<PyChromaParams>()?;
1217 m.add_class::<PyMfccParams>()?;
1218 Ok(())
1219}