#![no_std]
#![deny(missing_docs)]
use az::SaturatingAs;
use core::ffi::{c_int, CStr};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use opus_embedded_sys::*;
pub mod prelude {
pub use super::{Channels, Decoder, SamplingRate};
}
unsafe trait RawOpusError {
fn numeric(&self) -> c_int;
}
pub trait OpusError {
fn message(&self) -> &'static str;
}
impl<E: RawOpusError> OpusError for E {
fn message(&self) -> &'static str {
let error = unsafe {
let error = opus_strerror(self.numeric());
if error.is_null() {
return "Unknown error";
}
CStr::from_ptr(error)
};
error.to_str().unwrap()
}
}
#[derive(Debug, PartialEq)]
pub struct DecoderError {
error_code: c_int,
}
unsafe impl RawOpusError for DecoderError {
fn numeric(&self) -> c_int {
self.error_code
}
}
impl core::fmt::Display for DecoderError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.message())
}
}
impl core::error::Error for DecoderError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
None
}
}
#[derive(Debug, PartialEq)]
pub struct InvalidPacket {}
unsafe impl RawOpusError for InvalidPacket {
fn numeric(&self) -> c_int {
OPUS_INVALID_PACKET
}
}
impl core::fmt::Display for InvalidPacket {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.message())
}
}
impl core::error::Error for InvalidPacket {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
None
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[repr(u8)]
pub enum Channels {
Mono = 1,
Stereo = 2,
}
impl Channels {
pub fn channels(&self) -> u8 {
(*self).into()
}
}
#[derive(Debug)]
pub struct Decoder {
decoder: OpusDecoder,
channels: Channels,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[repr(i32)]
pub enum SamplingRate {
F8k = 8000,
F12k = 12000,
F16k = 16000,
F24k = 24000,
F48k = 48000,
}
impl SamplingRate {
pub fn closest(value: i32) -> Self {
use SamplingRate::*;
if value <= F8k.into() {
F8k
} else if value <= F12k.into() {
F12k
} else if value <= F16k.into() {
F16k
} else if value <= F24k.into() {
F24k
} else {
F48k
}
}
}
impl Decoder {
pub fn new(freq: SamplingRate, channels: Channels) -> Result<Self, DecoderError> {
if !cfg!(feature = "stereo") && channels == Channels::Stereo {
let error_code = OPUS_ALLOC_FAIL;
return Err(DecoderError { error_code });
}
let mut decoder = Decoder {
decoder: OpusDecoder::default(),
channels,
};
let channels = channels.channels().into();
let size = unsafe { opus_decoder_get_size(channels) };
assert!(
core::mem::size_of::<OpusDecoder>() >= size.try_into().unwrap(),
"OpusDecoder struct is too small!"
);
let error_code = unsafe { opus_decoder_init(&mut decoder.decoder, freq.into(), channels) };
if error_code != OPUS_OK.try_into().unwrap() {
Err(DecoderError { error_code })
} else {
Ok(decoder)
}
}
pub fn get_nb_samples_total(&self, data: &[u8]) -> Result<usize, DecoderError> {
match self.channels {
Channels::Mono => self.get_nb_samples(data),
Channels::Stereo => Ok(self.get_nb_samples(data)? * 2),
}
}
pub fn get_nb_samples(&self, data: &[u8]) -> Result<usize, DecoderError> {
let samples = unsafe {
let len = data.len().saturating_as();
let data = if !data.is_empty() {
data.as_ptr()
} else {
core::ptr::null()
};
opus_decoder_get_nb_samples(&self.decoder, data, len)
};
if samples < 0 {
Err(DecoderError {
error_code: samples,
})
} else {
Ok(samples.saturating_as())
}
}
pub fn decode<'output>(
&mut self,
data: &[u8],
output: &'output mut [i16],
) -> Result<&'output [i16], DecoderError> {
let samples = unsafe {
let len: i32 = data.len().saturating_as();
let data = if !data.is_empty() {
data.as_ptr()
} else {
core::ptr::null()
};
let frame_size: i32 = match self.channels {
Channels::Mono => output.len(),
Channels::Stereo => output.len() / 2,
}
.saturating_as();
let output = if !output.is_empty() {
output.as_mut_ptr()
} else {
core::ptr::null_mut()
};
opus_decode(&mut self.decoder, data, len, output, frame_size, 0)
};
if samples < 0 {
Err(DecoderError {
error_code: samples,
})
} else {
let frame_size = match self.channels {
Channels::Mono => samples as usize,
Channels::Stereo => samples as usize * 2,
};
Ok(&output[..frame_size])
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Bandwidth {
Narrowband,
Mediumband,
Wideband,
Superwideband,
Fullband,
}
#[derive(Debug)]
pub struct OpusPacket<'data> {
data: &'data [u8],
}
impl<'data> OpusPacket<'data> {
pub fn new(data: &'data [u8]) -> Self {
assert!(!data.is_empty());
Self { data }
}
pub fn get_nb_channels(&self) -> Result<u8, InvalidPacket> {
let channels = unsafe {
let data = self.data.as_ptr();
opus_packet_get_nb_channels(data)
};
if channels < 0 {
debug_assert_eq!(channels, OPUS_INVALID_PACKET);
Err(InvalidPacket {})
} else {
Ok(channels.saturating_as())
}
}
pub fn get_nb_frames(&self) -> Result<u32, InvalidPacket> {
let frames = unsafe {
let len = self.data.len().saturating_as();
let data = self.data.as_ptr();
opus_packet_get_nb_frames(data, len)
};
if frames < 0 {
debug_assert_eq!(frames, OPUS_INVALID_PACKET);
Err(InvalidPacket {})
} else {
Ok(frames.saturating_as())
}
}
pub fn get_bandwidth(&self) -> Result<Bandwidth, InvalidPacket> {
let bandwidth = unsafe {
let data = self.data.as_ptr();
opus_packet_get_bandwidth(data)
};
if bandwidth < 0 {
debug_assert_eq!(bandwidth, OPUS_INVALID_PACKET);
Err(InvalidPacket {})
} else {
use Bandwidth::*;
#[allow(non_snake_case)]
Ok(match bandwidth.try_into().unwrap() {
OPUS_BANDWIDTH_NARROWBAND => Narrowband,
OPUS_BANDWIDTH_MEDIUMBAND => Mediumband,
OPUS_BANDWIDTH_WIDEBAND => Wideband,
OPUS_BANDWIDTH_SUPERWIDEBAND => Superwideband,
OPUS_BANDWIDTH_FULLBAND => Fullband,
_ => panic!("Invalid bandwidth value returned by libopus"),
})
}
}
pub fn get_samples_per_frame(&self) -> Result<u32, InvalidPacket> {
let samples = unsafe {
let len = self.data.len().saturating_as();
let data = self.data.as_ptr();
opus_packet_get_samples_per_frame(data, len)
};
if samples < 0 {
debug_assert_eq!(samples, OPUS_INVALID_PACKET);
Err(InvalidPacket {})
} else {
Ok(samples.saturating_as())
}
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use alloc::string::ToString;
use core::error::Error;
#[test]
fn create_decoder() {
let decoder = Decoder::new(SamplingRate::F8k, Channels::Mono);
assert!(decoder.is_ok());
}
#[test]
fn create_decoder_stereo() {
let decoder = Decoder::new(SamplingRate::F16k, Channels::Stereo);
if cfg!(feature = "stereo") {
assert!(decoder.is_ok());
} else {
assert!(decoder.is_err());
assert_eq!(decoder.unwrap_err().numeric(), OPUS_ALLOC_FAIL);
}
}
#[test]
fn sampling_rate() {
assert_eq!(SamplingRate::closest(8_000), SamplingRate::F8k);
assert_eq!(SamplingRate::closest(12_000), SamplingRate::F12k);
assert_eq!(SamplingRate::closest(16_000), SamplingRate::F16k);
assert_eq!(SamplingRate::closest(24_000), SamplingRate::F24k);
assert_eq!(SamplingRate::closest(48_000), SamplingRate::F48k);
}
#[test]
fn sampling_rate_from_primitive() {
assert!(SamplingRate::try_from(1_000).is_err());
assert!(SamplingRate::try_from(8_000).is_ok());
assert!(SamplingRate::try_from(12_000).is_ok());
assert!(SamplingRate::try_from(16_000).is_ok());
assert!(SamplingRate::try_from(24_000).is_ok());
assert!(SamplingRate::try_from(48_000).is_ok());
assert!(SamplingRate::try_from(64_000).is_err());
}
#[test]
fn channels_from_primitive() {
assert!(Channels::try_from(0).is_err());
assert!(Channels::try_from(1).is_ok());
assert!(Channels::try_from(2).is_ok());
for channels in 3..=255 {
assert!(Channels::try_from(channels).is_err());
}
}
#[test]
fn test_decoder_with_zero_length_packet() {
const DATA: [u8; 0] = [0u8; 0];
let mut decoder = Decoder::new(SamplingRate::F8k, Channels::Mono).unwrap();
let result = decoder.get_nb_samples(&DATA);
assert_eq!(
result,
Err(DecoderError {
error_code: OPUS_BAD_ARG
})
);
let error = result.unwrap_err();
assert_eq!(error.numeric(), OPUS_BAD_ARG);
assert!(error.source().is_none());
assert_eq!(error.to_string(), "invalid argument");
let mut output = [0i16; 100];
let result = decoder.decode(&DATA, &mut output);
assert_eq!(result.unwrap().len(), output.len());
for v in output {
assert_eq!(v, 0);
}
let mut output = [0i16; 0];
let result = decoder.decode(&[0, 0, 0, 0, 0], &mut output);
assert_eq!(
result,
Err(DecoderError {
error_code: OPUS_BAD_ARG
})
);
let error = result.unwrap_err();
assert_eq!(error.numeric(), OPUS_BAD_ARG);
assert!(error.source().is_none());
assert_eq!(error.to_string(), "invalid argument");
}
#[test]
fn test_decoder_with_zero_packet() {
const DATA: [u8; 8] = [0x00u8; 8];
let mut decoder = Decoder::new(SamplingRate::F8k, Channels::Mono).unwrap();
assert_eq!(decoder.get_nb_samples(&DATA), Ok(80));
let mut output = [0i16; 80];
assert_eq!(decoder.decode(&DATA, &mut output).unwrap().len(), 80);
}
#[test]
fn test_decoder_with_0xff_packet() {
const DATA: [u8; 8] = [0xffu8; 8];
let mut decoder = Decoder::new(SamplingRate::F8k, Channels::Mono).unwrap();
let result = decoder.get_nb_samples(&DATA);
assert_eq!(
result,
Err(DecoderError {
error_code: OPUS_INVALID_PACKET
})
);
let error = result.unwrap_err();
assert_eq!(error.numeric(), OPUS_INVALID_PACKET);
assert!(error.source().is_none());
assert_eq!(error.to_string(), "corrupted stream");
let mut output = [0i16; 80];
let result = decoder.decode(&DATA, &mut output);
assert_eq!(
result,
Err(DecoderError {
error_code: OPUS_INVALID_PACKET
})
);
let error = result.unwrap_err();
assert_eq!(error.numeric(), OPUS_INVALID_PACKET);
assert!(error.source().is_none());
assert_eq!(error.to_string(), "corrupted stream");
}
#[test]
#[should_panic]
fn test_zero_length_packet() {
const DATA: [u8; 0] = [0u8; 0];
let _packet = OpusPacket::new(&DATA);
}
#[test]
fn test_zero_packet() {
let data = [0x00u8; 8];
let packet = OpusPacket::new(&data);
assert_eq!(packet.get_nb_channels(), Ok(1));
assert_eq!(packet.get_nb_frames(), Ok(1));
assert_eq!(packet.get_bandwidth(), Ok(Bandwidth::Narrowband));
assert_eq!(packet.get_samples_per_frame(), Ok(0));
}
#[test]
fn test_0xff_packet() {
let data = [0xFFu8; 8];
let packet = OpusPacket::new(&data);
assert_eq!(packet.get_nb_channels(), Ok(2));
assert_eq!(packet.get_nb_frames(), Ok(63));
assert_eq!(packet.get_bandwidth(), Ok(Bandwidth::Fullband));
assert_eq!(packet.get_samples_per_frame(), Ok(0));
}
#[test]
fn test_one_length_packet() {
let packet = OpusPacket::new(&[0xff]);
assert_eq!(packet.get_nb_channels(), Ok(2));
let result = packet.get_nb_frames();
assert_eq!(result, Err(InvalidPacket {}));
let error = result.unwrap_err();
assert_eq!(error.numeric(), OPUS_INVALID_PACKET);
assert!(error.source().is_none());
assert_eq!(error.to_string(), "corrupted stream");
assert_eq!(packet.get_bandwidth(), Ok(Bandwidth::Fullband));
assert_eq!(packet.get_samples_per_frame(), Ok(0));
}
#[test]
fn test_packet_bandwidths() {
let packet = OpusPacket::new(&[0x00]);
assert_eq!(packet.get_bandwidth(), Ok(Bandwidth::Narrowband));
let packet = OpusPacket::new(&[0x20]);
assert_eq!(packet.get_bandwidth(), Ok(Bandwidth::Mediumband));
let packet = OpusPacket::new(&[0xB0]);
assert_eq!(packet.get_bandwidth(), Ok(Bandwidth::Wideband));
let packet = OpusPacket::new(&[0xC0]);
assert_eq!(packet.get_bandwidth(), Ok(Bandwidth::Superwideband));
let packet = OpusPacket::new(&[0xF0]);
assert_eq!(packet.get_bandwidth(), Ok(Bandwidth::Fullband));
}
}