use super::GenericCtl;
use crate::{
error::try_map_opus_error, ffi, packet::Packet, Channels, ErrorCode, MutSignals, Result,
SampleRate, TryFrom, TryInto,
};
#[derive(Debug)]
pub struct Decoder {
pointer: *mut ffi::OpusDecoder,
channels: Channels,
}
unsafe impl Send for Decoder {}
impl GenericCtl for Decoder {
fn final_range(&self) -> Result<u32> {
self.decoder_ctl_request(ffi::OPUS_GET_FINAL_RANGE_REQUEST)
.map(|v| v as u32)
}
fn phase_inversion_disabled(&self) -> Result<bool> {
self.decoder_ctl_request(ffi::OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST)
.map(|b| b == 1)
}
fn set_phase_inversion_disabled(&mut self, disabled: bool) -> Result<()> {
let disable_phase_inversion = if disabled { 1 } else { 0 };
self.set_decoder_ctl_request(
ffi::OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST,
disable_phase_inversion,
)
.map(|_| ())
}
fn sample_rate(&self) -> Result<SampleRate> {
self.decoder_ctl_request(ffi::OPUS_GET_SAMPLE_RATE_REQUEST)
.and_then(SampleRate::try_from)
}
fn reset_state(&mut self) -> Result<()> {
self.decoder_ctl_request(ffi::OPUS_RESET_STATE).map(|_| ())
}
}
impl Decoder {
pub fn new(sample_rate: SampleRate, channels: Channels) -> Result<Decoder> {
let mut opus_code = 0;
let pointer = unsafe {
ffi::opus_decoder_create(sample_rate as i32, channels as i32, &mut opus_code)
};
if opus_code == ffi::OPUS_OK || !pointer.is_null() {
return Ok(Decoder { pointer, channels });
}
Err(ErrorCode::from(opus_code))?
}
pub fn decode<'a, TP, TS>(&mut self, input: Option<TP>, output: TS, fec: bool) -> Result<usize>
where
TP: TryInto<Packet<'a>>,
TS: TryInto<MutSignals<'a, i16>>,
{
let (input_pointer, input_len) = if let Some(value) = input {
let value = value.try_into()?;
(value.as_ptr(), value.i32_len())
} else {
(std::ptr::null(), 0)
};
let mut output = output.try_into()?;
try_map_opus_error(unsafe {
ffi::opus_decode(
self.pointer,
input_pointer,
input_len,
output.as_mut_ptr(),
output.i32_len() / self.channels as i32,
fec as i32,
)
})
.map(|n| n as usize)
}
pub fn decode_float<'a, TP, TS>(&mut self, input: Option<TP>, output: TS, fec: bool) -> Result<usize>
where
TP: TryInto<Packet<'a>>,
TS: TryInto<MutSignals<'a, f32>>,
{
let (input_pointer, input_len) = if let Some(value) = input {
let value = value.try_into()?;
(value.as_ptr(), value.i32_len())
} else {
(std::ptr::null(), 0)
};
let mut output = output.try_into()?;
try_map_opus_error(unsafe {
ffi::opus_decode_float(
self.pointer,
input_pointer,
input_len,
output.as_mut_ptr(),
output.i32_len() / self.channels as i32,
fec as i32,
)
})
.map(|n| n as usize)
}
fn decoder_ctl_request(&self, request: i32) -> Result<i32> {
let mut value = 0;
let ffi_result = unsafe { ffi::opus_decoder_ctl(self.pointer, request, &mut value) };
try_map_opus_error(ffi_result)?;
Ok(value)
}
fn set_decoder_ctl_request(&self, request: i32, value: i32) -> Result<()> {
try_map_opus_error(unsafe { ffi::opus_decoder_ctl(self.pointer, request, value) })?;
Ok(())
}
pub fn last_packet_duration(&self) -> Result<u32> {
self.decoder_ctl_request(ffi::OPUS_GET_LAST_PACKET_DURATION_REQUEST)
.map(|v| v as u32)
}
pub fn pitch(&self) -> Result<i32> {
self.decoder_ctl_request(ffi::OPUS_GET_PITCH_REQUEST)
}
pub fn gain(&self) -> Result<i32> {
self.decoder_ctl_request(ffi::OPUS_GET_GAIN_REQUEST)
}
pub fn set_gain(&self, gain: i32) -> Result<()> {
self.set_decoder_ctl_request(ffi::OPUS_SET_GAIN_REQUEST, gain)
}
pub fn size(&self) -> usize {
unsafe { ffi::opus_decoder_get_size(self.channels as i32) as usize }
}
}
pub fn size(channels: Channels) -> usize {
unsafe { ffi::opus_decoder_get_size(channels as i32) as usize }
}
impl Drop for Decoder {
fn drop(&mut self) {
unsafe { ffi::opus_decoder_destroy(self.pointer) }
}
}
#[cfg(test)]
mod tests {
use super::Decoder;
use crate::{Channels, Error, ErrorCode, SampleRate};
use matches::assert_matches;
#[test]
fn set_and_get_gain() {
let decoder = Decoder::new(SampleRate::Hz48000, Channels::Stereo).unwrap();
assert_matches!(decoder.gain(), Ok(0));
assert_matches!(decoder.set_gain(10), Ok(()));
assert_matches!(decoder.gain(), Ok(10));
let lower_limit = -32768;
let upper_limit = 32767;
assert_matches!(decoder.set_gain(lower_limit), Ok(()));
assert_matches!(
decoder.set_gain(lower_limit - 1),
Err(Error::Opus(ErrorCode::BadArgument))
);
assert_matches!(decoder.set_gain(upper_limit), Ok(()));
assert_matches!(
decoder.set_gain(upper_limit + 1),
Err(Error::Opus(ErrorCode::BadArgument))
);
}
}