use numpy::PyArray2;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use super::numpy_io::interleaved_f32_to_numpy;
#[pyclass(module = "ruopus", name = "MultistreamDecoder")]
pub struct MultistreamDecoder {
inner: crate::multistream::MultistreamDecoder,
channels: usize,
sample_rate: u32,
}
#[pymethods]
impl MultistreamDecoder {
#[new]
#[pyo3(signature = (streams, coupled, mapping, *, sample_rate = 48_000))]
fn new(streams: usize, coupled: usize, mapping: Vec<u8>, sample_rate: u32) -> PyResult<Self> {
if streams < 1 || coupled > streams || streams + coupled > 255 {
return Err(PyValueError::new_err(
"require streams >= 1, coupled <= streams, streams + coupled <= 255",
));
}
if !matches!(sample_rate, 48_000 | 24_000 | 16_000 | 12_000 | 8_000) {
return Err(PyValueError::new_err(
"sample_rate must be one of 48000, 24000, 16000, 12000, 8000",
));
}
let decoded_channels = (streams + coupled) as u8;
if !mapping.iter().all(|&m| m == 255 || m < decoded_channels) {
return Err(PyValueError::new_err(
"every mapping entry must be 255 or less than streams + coupled",
));
}
if mapping.is_empty() {
return Err(PyValueError::new_err("mapping must have at least one channel"));
}
Ok(Self {
channels: mapping.len(),
inner: crate::multistream::MultistreamDecoder::with_rate(sample_rate, streams, coupled, &mapping),
sample_rate,
})
}
#[getter]
fn channels(&self) -> usize {
self.channels
}
#[getter]
fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[pyo3(signature = (data) -> "numpy.typing.NDArray[numpy.float32]")]
fn decode_packet<'py>(&mut self, py: Python<'py>, data: &[u8]) -> PyResult<Bound<'py, PyArray2<f32>>> {
let owned = data.to_vec();
let pcm = py.detach(|| self.inner.decode_packet(&owned))?;
interleaved_f32_to_numpy(py, pcm, self.channels)
}
fn __repr__(&self) -> String {
format!(
"MultistreamDecoder(channels={}, sample_rate={})",
self.channels, self.sample_rate
)
}
}