use numpy::PyArray2;
use pyo3::prelude::*;
use pyo3::types::PyBytes;
use super::numpy_io::{borrow_interleaved_f32, interleaved_f32_to_numpy};
#[pyclass(module = "ruopus", name = "OpusHead", frozen)]
pub struct OpusHead {
inner: crate::ogg::OpusHead,
}
#[pymethods]
impl OpusHead {
#[getter]
fn version(&self) -> u8 {
self.inner.version
}
#[getter]
fn channel_count(&self) -> u8 {
self.inner.channel_count
}
#[getter]
fn pre_skip(&self) -> u16 {
self.inner.pre_skip
}
#[getter]
fn input_sample_rate(&self) -> u32 {
self.inner.input_sample_rate
}
#[getter]
fn output_gain_q8(&self) -> i16 {
self.inner.output_gain_q8
}
#[getter]
fn mapping_family(&self) -> u8 {
match &self.inner.channel_mapping {
crate::ogg::ChannelMapping::Family0 => 0,
crate::ogg::ChannelMapping::Table { family, .. } => *family,
}
}
#[getter]
fn stream_count(&self) -> Option<u8> {
match &self.inner.channel_mapping {
crate::ogg::ChannelMapping::Family0 => None,
crate::ogg::ChannelMapping::Table { stream_count, .. } => Some(*stream_count),
}
}
#[getter]
fn coupled_count(&self) -> Option<u8> {
match &self.inner.channel_mapping {
crate::ogg::ChannelMapping::Family0 => None,
crate::ogg::ChannelMapping::Table { coupled_count, .. } => Some(*coupled_count),
}
}
#[getter]
fn channel_mapping(&self) -> Option<Vec<u8>> {
match &self.inner.channel_mapping {
crate::ogg::ChannelMapping::Family0 => None,
crate::ogg::ChannelMapping::Table { mapping, .. } => Some(mapping.clone()),
}
}
#[pyo3(signature = () -> "bytes")]
fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.inner.to_bytes())
}
fn __repr__(&self) -> String {
format!(
"OpusHead(version={}, channel_count={}, pre_skip={}, input_sample_rate={}, mapping_family={})",
self.inner.version,
self.inner.channel_count,
self.inner.pre_skip,
self.inner.input_sample_rate,
self.mapping_family(),
)
}
}
impl From<crate::ogg::OpusHead> for OpusHead {
fn from(inner: crate::ogg::OpusHead) -> Self {
Self { inner }
}
}
#[pyfunction]
#[pyo3(signature = (pcm: "numpy.typing.NDArray[numpy.float32]", channels, bitrate))]
pub fn encode_ogg_opus<'py>(
py: Python<'py>,
pcm: numpy::PyReadonlyArrayDyn<'_, f32>,
channels: usize,
bitrate: u32,
) -> PyResult<Bound<'py, PyBytes>> {
let pcm = borrow_interleaved_f32(&pcm, channels)?;
let out = py.detach(|| crate::encoder::encode_ogg_opus(&pcm, channels, bitrate));
Ok(PyBytes::new(py, &out))
}
#[pyfunction]
#[pyo3(signature = (data) -> "tuple[numpy.typing.NDArray[numpy.float32], OpusHead]")]
pub fn decode_ogg_opus<'py>(py: Python<'py>, data: &[u8]) -> PyResult<(Bound<'py, PyArray2<f32>>, OpusHead)> {
let owned = data.to_vec();
let (pcm, head) = py.detach(|| crate::decoder::decode_ogg_opus(&owned))?;
let channels = head.channel_count as usize;
let arr = interleaved_f32_to_numpy(py, pcm, channels)?;
Ok((arr, OpusHead::from(head)))
}