use audiopus::Application;
use audiopus::ffi;
use std::ffi::c_int;
use std::ptr;
use crate::audio::AudioError;
pub(super) fn surround_mapping_family_1(
channels: u8,
) -> Result<(u8, u8, &'static [u8]), AudioError> {
match channels {
3 => Ok((2, 1, &[0, 2, 1])),
4 => Ok((2, 2, &[0, 1, 2, 3])),
5 => Ok((3, 2, &[0, 4, 1, 2, 3])),
6 => Ok((4, 2, &[0, 4, 1, 2, 3, 5])),
7 => Ok((4, 3, &[0, 4, 1, 2, 3, 5, 6])),
8 => Ok((5, 3, &[0, 6, 1, 2, 3, 4, 5, 7])),
_ => Err(AudioError::Unsupported(format!(
"Opus surround mapping family 1 only defined for 3..=8 channels; got {channels}"
))),
}
}
pub(super) struct MultistreamEncoder {
state: *mut ffi::OpusMSEncoder,
}
unsafe impl Send for MultistreamEncoder {}
impl MultistreamEncoder {
pub(super) fn new(
sample_rate: u32,
channels: u8,
streams: u8,
coupled_streams: u8,
mapping: &[u8],
application: Application,
) -> Result<Self, AudioError> {
if mapping.len() != channels as usize {
return Err(AudioError::Encode(format!(
"multistream mapping length {} != channels {channels}",
mapping.len()
)));
}
if coupled_streams > streams {
return Err(AudioError::Encode(format!(
"coupled_streams ({coupled_streams}) > streams ({streams})"
)));
}
if (streams as usize) + (coupled_streams as usize) > channels as usize {
return Err(AudioError::Encode(format!(
"streams ({streams}) + coupled_streams ({coupled_streams}) > channels ({channels})"
)));
}
let mut err: c_int = 0;
let state = unsafe {
ffi::opus_multistream_encoder_create(
sample_rate as i32,
channels as c_int,
streams as c_int,
coupled_streams as c_int,
mapping.as_ptr(),
application as c_int,
&mut err,
)
};
if state.is_null() || err != ffi::OPUS_OK {
return Err(AudioError::Encode(format!(
"opus_multistream_encoder_create failed: code={err}"
)));
}
Ok(Self { state })
}
pub(super) fn set_vbr(&mut self, vbr: bool) -> Result<(), AudioError> {
let val: c_int = if vbr { 1 } else { 0 };
let r = unsafe {
ffi::opus_multistream_encoder_ctl(self.state, ffi::OPUS_SET_VBR_REQUEST, val)
};
if r != ffi::OPUS_OK {
return Err(AudioError::Encode(format!(
"opus_multistream_encoder_ctl(SET_VBR) failed: {r}"
)));
}
Ok(())
}
pub(super) fn set_bitrate(&mut self, bps: i32) -> Result<(), AudioError> {
let r = unsafe {
ffi::opus_multistream_encoder_ctl(self.state, ffi::OPUS_SET_BITRATE_REQUEST, bps)
};
if r != ffi::OPUS_OK {
return Err(AudioError::Encode(format!(
"opus_multistream_encoder_ctl(SET_BITRATE) failed: {r}"
)));
}
Ok(())
}
pub(super) fn lookahead(&self) -> Result<u32, AudioError> {
let mut out: c_int = 0;
let r = unsafe {
ffi::opus_multistream_encoder_ctl(
self.state,
ffi::OPUS_GET_LOOKAHEAD_REQUEST,
&mut out as *mut c_int,
)
};
if r != ffi::OPUS_OK {
return Err(AudioError::Encode(format!(
"opus_multistream_encoder_ctl(GET_LOOKAHEAD) failed: {r}"
)));
}
if out < 0 {
return Err(AudioError::Encode(format!(
"opus_multistream_encoder_ctl(GET_LOOKAHEAD) returned negative: {out}"
)));
}
Ok(out as u32)
}
pub(super) fn encode_float(
&mut self,
pcm: &[f32],
frame_size: usize,
out: &mut [u8],
) -> Result<usize, AudioError> {
let max = out.len().min(i32::MAX as usize) as i32;
let n = unsafe {
ffi::opus_multistream_encode_float(
self.state,
pcm.as_ptr(),
frame_size as c_int,
out.as_mut_ptr(),
max,
)
};
if n < 0 {
return Err(AudioError::Encode(format!(
"opus_multistream_encode_float failed: code={n}"
)));
}
Ok(n as usize)
}
}
impl Drop for MultistreamEncoder {
fn drop(&mut self) {
if !self.state.is_null() {
unsafe { ffi::opus_multistream_encoder_destroy(self.state) };
self.state = ptr::null_mut();
}
}
}