use alloc::vec;
use alloc::vec::Vec;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::CELT_SIG_SCALE;
use crate::celt::CeltDecodeError;
#[cfg(feature = "deep_plc")]
use crate::celt::LpcNetPlcState;
use crate::celt::celt_decode_with_ec_dred;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::float2int;
use crate::celt::select_celt_float2int16_impl;
use crate::celt::{CeltCoef, OpusCustomMode};
use crate::celt::{
CeltDecoderCtlError, DecoderCtlRequest as CeltDecoderCtlRequest, EcDec, OpusRes,
OwnedCeltDecoder, canonical_mode, celt_decoder_get_size, celt_exp2, opus_custom_decoder_create,
opus_custom_decoder_ctl, opus_select_arch, resampling_factor,
};
#[cfg(not(feature = "fixed_point"))]
use crate::opus::opus_pcm_soft_clip_impl;
use crate::packet::{
Bandwidth, Mode, PacketError, ParsedPacket, opus_packet_get_bandwidth, opus_packet_get_mode,
opus_packet_get_nb_channels, opus_packet_get_nb_samples, opus_packet_get_samples_per_frame,
opus_packet_parse_impl,
};
use crate::silk::SilkRangeDecoder;
use crate::silk::dec_api::{
DECODER_NUM_CHANNELS, DecControl, Decoder as SilkDecoder, reset_decoder as silk_reset_decoder,
silk_decode,
};
use crate::silk::decode_frame::DecodeFlag;
use crate::silk::decoder_state::DecoderState;
use crate::silk::errors::SilkError;
use crate::silk::get_decoder_size::get_decoder_size;
use crate::silk::init_decoder::init_decoder as silk_init_channel;
use crate::silk::load_osce_models::load_osce_models;
#[cfg(feature = "deep_plc")]
type PlcHandle<'a> = Option<&'a mut LpcNetPlcState>;
#[cfg(not(feature = "deep_plc"))]
type PlcHandle<'a> = ();
const MAX_CHANNELS: usize = 2;
const MAX_DECODE_SAMPLES_PER_CHANNEL: usize = 5760;
const DECODE_GAIN_SCALE: f32 = core::f32::consts::LOG2_10 / 5120.0;
pub(crate) const MODE_SILK_ONLY: i32 = 1000;
pub(crate) const MODE_HYBRID: i32 = 1001;
pub(crate) const MODE_CELT_ONLY: i32 = 1002;
#[cfg(feature = "fixed_point")]
const OPTIONAL_CLIP: bool = false;
#[cfg(not(feature = "fixed_point"))]
const OPTIONAL_CLIP: bool = true;
#[inline]
fn align(value: usize) -> usize {
#[repr(C)]
struct AlignProbe {
_tag: u8,
_union: AlignUnion,
}
#[repr(C)]
union AlignUnion {
_ptr: *const (),
_i32: i32,
_f32: f32,
}
let alignment = core::mem::align_of::<AlignProbe>();
value.div_ceil(alignment) * alignment
}
#[inline]
fn opus_mode_to_int(mode: Mode) -> i32 {
match mode {
Mode::SILK => MODE_SILK_ONLY,
Mode::HYBRID => MODE_HYBRID,
Mode::CELT => MODE_CELT_ONLY,
}
}
#[inline]
fn decode_as_celt_only(mode: i32) -> bool {
mode == MODE_CELT_ONLY
}
fn smooth_fade(
in1: &[OpusRes],
in2: &[OpusRes],
out: &mut [OpusRes],
overlap: usize,
channels: usize,
window: &[CeltCoef],
fs: i32,
) {
if channels == 0 || overlap == 0 || fs <= 0 {
return;
}
let inc = match 48_000i32.checked_div(fs) {
Some(step) if step > 0 => step as usize,
_ => return,
};
let Some(required) = overlap.checked_mul(channels) else {
return;
};
if in1.len() < required || in2.len() < required || out.len() < required {
return;
}
for c in 0..channels {
for i in 0..overlap {
let w_idx = i.saturating_mul(inc);
if w_idx >= window.len() {
break;
}
let weight = window[w_idx] * window[w_idx];
let idx = i * channels + c;
out[idx] = weight * in2[idx] + (1.0 - weight) * in1[idx];
}
}
}
#[repr(C)]
struct OpusDecoderLayout {
celt_dec_offset: i32,
silk_dec_offset: i32,
channels: i32,
fs: i32,
dec_control: DecControlLayout,
decode_gain: i32,
complexity: i32,
arch: i32,
#[cfg(feature = "deep_plc")]
lpcnet: LpcNetPlcState,
stream_channels: i32,
bandwidth: i32,
mode: i32,
prev_mode: i32,
frame_size: i32,
prev_redundancy: i32,
last_packet_duration: i32,
softclip_mem: [f32; 2],
range_final: u32,
}
#[repr(C)]
struct DecControlLayout {
n_channels_api: i32,
n_channels_internal: i32,
api_sample_rate: i32,
internal_sample_rate: i32,
payload_size_ms: i32,
prev_pitch_lag: i32,
enable_deep_plc: i32,
}
#[must_use]
pub fn opus_decoder_get_size(channels: usize) -> Option<usize> {
if channels == 0 || channels > MAX_CHANNELS {
return None;
}
let mut silk_size = 0usize;
get_decoder_size(&mut silk_size).ok()?;
let silk_size = align(silk_size);
let celt_size = celt_decoder_get_size(channels)?;
let header_size = align(core::mem::size_of::<OpusDecoderLayout>());
Some(header_size + silk_size + celt_size)
}
#[derive(Debug)]
pub struct OpusDecoder<'mode> {
pub(crate) celt: OwnedCeltDecoder<'mode>,
pub(crate) silk: SilkDecoder,
pub(crate) fs: i32,
pub(crate) channels: i32,
dec_control: DecControl,
decode_gain: i32,
complexity: i32,
arch: i32,
#[cfg(feature = "deep_plc")]
lpcnet: LpcNetPlcState,
stream_channels: i32,
bandwidth: i32,
mode: i32,
prev_mode: i32,
frame_size: i32,
prev_redundancy: i32,
last_packet_duration: i32,
#[cfg(not(feature = "fixed_point"))]
softclip_mem: [f32; 2],
range_final: u32,
decode_scratch: Vec<OpusRes>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OpusDecoderInitError {
BadArgument,
CeltInit,
SilkInit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OpusDecoderCtlError {
BadArgument,
Unimplemented,
Silk(SilkError),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OpusDecodeError {
BadArgument,
BufferTooSmall,
InvalidPacket,
InternalError,
Unimplemented,
}
impl OpusDecodeError {
#[inline]
pub const fn code(&self) -> i32 {
match self {
Self::BadArgument => -1,
Self::BufferTooSmall => -2,
Self::InternalError => -3,
Self::InvalidPacket => -4,
Self::Unimplemented => -5,
}
}
}
impl From<CeltDecoderCtlError> for OpusDecoderCtlError {
fn from(value: CeltDecoderCtlError) -> Self {
match value {
CeltDecoderCtlError::InvalidArgument => Self::BadArgument,
CeltDecoderCtlError::Unimplemented => Self::Unimplemented,
}
}
}
impl From<SilkError> for OpusDecoderCtlError {
fn from(value: SilkError) -> Self {
Self::Silk(value)
}
}
impl From<PacketError> for OpusDecodeError {
#[inline]
fn from(value: PacketError) -> Self {
match value {
PacketError::BadArgument => Self::BadArgument,
PacketError::InvalidPacket => Self::InvalidPacket,
}
}
}
pub enum OpusDecoderCtlRequest<'req> {
SetGain(i32),
GetGain(&'req mut i32),
SetComplexity(i32),
GetComplexity(&'req mut i32),
GetBandwidth(&'req mut i32),
GetSampleRate(&'req mut i32),
GetPitch(&'req mut i32),
GetFinalRange(&'req mut u32),
ResetState,
GetLastPacketDuration(&'req mut i32),
SetPhaseInversionDisabled(bool),
GetPhaseInversionDisabled(&'req mut bool),
SetDnnBlob(&'req [u8]),
}
#[derive(Debug, Clone)]
pub struct ParsedPacketMetadata<'a> {
pub mode: Mode,
pub bandwidth: Bandwidth,
pub frame_size: usize,
pub stream_channels: usize,
pub parsed: ParsedPacket<'a>,
}
impl<'mode> OpusDecoder<'mode> {
pub fn init(&mut self, fs: i32, channels: i32) -> Result<(), OpusDecoderInitError> {
if !matches!(fs, 48_000 | 24_000 | 16_000 | 12_000 | 8_000) || !matches!(channels, 1 | 2) {
return Err(OpusDecoderInitError::BadArgument);
}
self.fs = fs;
self.channels = channels;
self.reset_dec_control();
self.decode_gain = 0;
self.complexity = 0;
self.bandwidth = 0;
self.mode = 0;
self.prev_mode = 0;
self.prev_redundancy = 0;
self.last_packet_duration = 0;
#[cfg(not(feature = "fixed_point"))]
{
self.softclip_mem = [0.0; 2];
}
self.range_final = 0;
self.decode_scratch.clear();
#[cfg(feature = "deep_plc")]
{
self.lpcnet.reset();
}
for (idx, state) in self
.silk
.channel_states
.iter_mut()
.enumerate()
.take(DECODER_NUM_CHANNELS)
{
silk_init_channel(state).map_err(|_| OpusDecoderInitError::SilkInit)?;
if idx as i32 >= channels {
*state = DecoderState::default();
}
}
self.silk.n_channels_api = channels;
self.silk.n_channels_internal = channels;
let mode = canonical_mode().ok_or(OpusDecoderInitError::CeltInit)?;
self.celt = opus_custom_decoder_create(mode, channels as usize)
.map_err(|_| OpusDecoderInitError::CeltInit)?;
let downsample = resampling_factor(fs);
if downsample == 0 {
return Err(OpusDecoderInitError::BadArgument);
}
self.celt.decoder().downsample = downsample as i32;
opus_custom_decoder_ctl(self.celt.decoder(), CeltDecoderCtlRequest::SetSignalling(0))
.map_err(|_| OpusDecoderInitError::CeltInit)?;
self.arch = opus_select_arch();
self.reset_runtime_fields();
Ok(())
}
#[inline]
pub fn get_nb_samples(&self, packet: &[u8], len: usize) -> Result<usize, PacketError> {
debug_assert!(matches!(self.fs, 48_000 | 24_000 | 16_000 | 12_000 | 8_000));
opus_packet_get_nb_samples(packet, len, self.fs as u32)
}
pub fn parse_packet<'a>(
&self,
packet: &'a [u8],
len: usize,
self_delimited: bool,
) -> Result<ParsedPacketMetadata<'a>, PacketError> {
if len == 0 || len > packet.len() {
return Err(PacketError::BadArgument);
}
debug_assert!(matches!(self.fs, 48_000 | 24_000 | 16_000 | 12_000 | 8_000));
debug_assert!(matches!(self.channels, 1 | 2));
let parsed = opus_packet_parse_impl(packet, len, self_delimited)?;
let mode = opus_packet_get_mode(packet)?;
let bandwidth = opus_packet_get_bandwidth(packet)?;
let fs = u32::try_from(self.fs).map_err(|_| PacketError::BadArgument)?;
let frame_size = opus_packet_get_samples_per_frame(packet, fs)?;
let stream_channels = opus_packet_get_nb_channels(packet)?;
Ok(ParsedPacketMetadata {
mode,
bandwidth,
frame_size,
stream_channels,
parsed,
})
}
#[cfg_attr(not(test), allow(dead_code))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn decode_frame(
&mut self,
data: Option<&[u8]>,
len: usize,
pcm: &mut [OpusRes],
frame_size: usize,
decode_fec: bool,
) -> Result<usize, OpusDecodeError> {
let channels = usize::try_from(self.channels).map_err(|_| OpusDecodeError::BadArgument)?;
if !(1..=MAX_CHANNELS).contains(&channels) {
return Err(OpusDecodeError::BadArgument);
}
if !matches!(self.fs, 48_000 | 24_000 | 16_000 | 12_000 | 8_000) {
return Err(OpusDecodeError::BadArgument);
}
let fs = usize::try_from(self.fs).map_err(|_| OpusDecodeError::BadArgument)?;
let f20 = fs / 50;
let f10 = f20 / 2;
let f5 = f10 / 2;
let f2_5 = f5 / 2;
if frame_size < f2_5 {
return Err(OpusDecodeError::BufferTooSmall);
}
let max_frame = (fs / 25) * 3;
let mut frame_size = frame_size.min(max_frame);
let mut packet_len = len;
if let Some(packet) = data
&& packet_len > packet.len()
{
return Err(OpusDecodeError::BadArgument);
}
let mut packet = data.map(|packet| &packet[..packet_len]);
if packet.is_none() || packet_len <= 1 {
packet = None;
let current_size =
usize::try_from(self.frame_size).map_err(|_| OpusDecodeError::BadArgument)?;
frame_size = frame_size.min(current_size);
}
let mut audiosize;
let mode;
let bandwidth;
let mut transition = false;
let mut redundancy = false;
let mut celt_to_silk = false;
let mut redundant_rng = 0u32;
let mut pcm_transition: Option<Vec<OpusRes>> = None;
let mut redundant_audio: Option<Vec<OpusRes>> = None;
let mut redundant_packet: Option<&[u8]> = None;
let mut range_decoder: Option<EcDec<'_>> = None;
let celt_only = if packet.is_some() {
audiosize =
usize::try_from(self.frame_size).map_err(|_| OpusDecodeError::BadArgument)?;
mode = self.mode;
bandwidth = self.bandwidth;
decode_as_celt_only(mode)
} else {
audiosize = frame_size;
mode = if self.prev_redundancy != 0 {
MODE_CELT_ONLY
} else {
self.prev_mode
};
bandwidth = 0;
let celt_only = mode == MODE_CELT_ONLY;
if mode == 0 {
let samples = audiosize
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if pcm.len() < samples {
return Err(OpusDecodeError::BufferTooSmall);
}
pcm[..samples].fill(0.0);
self.prev_mode = 0;
self.prev_redundancy = 0;
self.range_final = 0;
return Ok(audiosize);
}
if audiosize > f20 {
let mut decoded = 0usize;
while decoded < audiosize {
let chunk = (audiosize - decoded).min(f20);
let offset = decoded
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let ret = self.decode_frame(None, 0, &mut pcm[offset..], chunk, false)?;
if ret != chunk {
return Err(OpusDecodeError::InternalError);
}
decoded = decoded
.checked_add(ret)
.ok_or(OpusDecodeError::BadArgument)?;
}
self.prev_mode = mode;
self.prev_redundancy = 0;
self.range_final = 0;
return Ok(audiosize);
} else if audiosize < f20 {
if audiosize > f10 {
audiosize = f10;
} else if !celt_only && audiosize > f5 && audiosize < f10 {
audiosize = f5;
}
}
celt_only
};
if celt_only && let Some(packet) = packet {
range_decoder = Some(EcDec::new(packet));
}
let prev_celt_only = decode_as_celt_only(self.prev_mode);
if packet.is_some()
&& self.prev_mode > 0
&& ((celt_only && !prev_celt_only && self.prev_redundancy == 0)
|| (!celt_only && prev_celt_only))
{
transition = true;
if celt_only {
let transition_len = f5
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let mut buffer = vec![0.0; transition_len];
let transition_size = audiosize.min(f5);
let ret = self.decode_frame(None, 0, &mut buffer, transition_size, false)?;
if ret != transition_size {
return Err(OpusDecodeError::InternalError);
}
pcm_transition = Some(buffer);
}
}
if audiosize > frame_size {
return Err(OpusDecodeError::BadArgument);
}
let celt_accum = !celt_only;
let mut range_final: Option<u32> = None;
let mut celt_final_range: Option<u32> = None;
if !celt_only {
let pcm_too_small = audiosize < f10;
let pcm_silk_len = if pcm_too_small { f10 } else { audiosize };
let mut silk_pcm: Option<Vec<OpusRes>> =
pcm_too_small.then(|| vec![0.0; pcm_silk_len.saturating_mul(channels)]);
let payload_ms = audiosize
.checked_mul(1000)
.and_then(|value| value.checked_div(fs))
.ok_or(OpusDecodeError::BadArgument)?
.max(10);
let payload_ms = i32::try_from(payload_ms).map_err(|_| OpusDecodeError::BadArgument)?;
let control = &mut self.dec_control;
control.n_channels_api = self.channels;
control.api_sample_rate = self.fs;
control.payload_size_ms = payload_ms;
control.enable_deep_plc = self.complexity >= 5;
if packet.is_some() {
control.n_channels_internal = self.stream_channels;
control.internal_sample_rate = Bandwidth::from_opus_int(bandwidth).map_or(
16_000,
|packet_bandwidth| match mode {
MODE_SILK_ONLY => match packet_bandwidth {
Bandwidth::Narrow => 8_000,
Bandwidth::Medium => 12_000,
Bandwidth::Wide => 16_000,
_ => 16_000,
},
_ => 16_000,
},
);
} else if control.internal_sample_rate == 0
&& let Some(fs_khz) = self
.silk
.channel_states
.first()
.map(|state| state.sample_rate.fs_khz)
.filter(|&fs_khz| fs_khz > 0)
{
control.internal_sample_rate = fs_khz * 1000;
}
if control.n_channels_internal == 0 {
control.n_channels_internal = self.stream_channels;
}
if prev_celt_only {
silk_reset_decoder(&mut self.silk).map_err(|_| OpusDecodeError::InternalError)?;
}
if range_decoder.is_none() {
range_decoder = Some(EcDec::new(packet.unwrap_or(&[])));
}
{
let range_dec = range_decoder
.as_mut()
.ok_or(OpusDecodeError::InternalError)?;
let mut silk_output = vec![0i16; pcm_silk_len.saturating_mul(channels)];
let mut decoded_samples = 0usize;
let mut write_offset = 0usize;
while decoded_samples < audiosize {
let new_packet = decoded_samples == 0;
let max_chunk = audiosize
.checked_sub(decoded_samples)
.ok_or(OpusDecodeError::BadArgument)?;
let samples_available = max_chunk
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if write_offset + samples_available > silk_output.len() {
return Err(OpusDecodeError::BadArgument);
}
let result = silk_decode(
&mut self.silk,
control,
if packet.is_some() {
if decode_fec {
DecodeFlag::Lbrr
} else {
DecodeFlag::Normal
}
} else {
DecodeFlag::PacketLoss
},
new_packet,
range_dec,
&mut silk_output[write_offset..write_offset + samples_available],
self.arch,
);
let written = match result {
Ok(value) => value,
Err(_) if packet.is_none() => {
silk_output[write_offset..write_offset + samples_available].fill(0);
max_chunk
}
Err(_) => return Err(OpusDecodeError::InternalError),
};
if written == 0 {
return Err(OpusDecodeError::InternalError);
}
decoded_samples = decoded_samples
.checked_add(written)
.ok_or(OpusDecodeError::BadArgument)?;
write_offset = decoded_samples
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
}
let silk_samples = decoded_samples
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if let Some(ref mut temp) = silk_pcm {
if temp.len() < silk_samples {
return Err(OpusDecodeError::BufferTooSmall);
}
for (dst, &src) in temp.iter_mut().zip(silk_output.iter().take(silk_samples)) {
*dst = silk_int16_to_res(src);
}
} else {
if pcm.len() < silk_samples {
return Err(OpusDecodeError::BufferTooSmall);
}
for (dst, &src) in pcm
.iter_mut()
.take(silk_samples)
.zip(silk_output.iter().take(silk_samples))
{
*dst = silk_int16_to_res(src);
}
}
if let Some(temp) = silk_pcm {
let copy_len = audiosize
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if temp.len() < copy_len || pcm.len() < copy_len {
return Err(OpusDecodeError::BufferTooSmall);
}
pcm[..copy_len].copy_from_slice(&temp[..copy_len]);
}
if !decode_fec && packet.is_some() && mode != MODE_CELT_ONLY {
let tell = range_dec.tell();
let threshold = 17 + if mode == MODE_HYBRID { 20 } else { 0 };
if tell + threshold <= (8 * packet_len) as i32 {
redundancy = if mode == MODE_HYBRID {
range_dec.decode_symbol_logp(12) != 0
} else {
true
};
if redundancy {
celt_to_silk = range_dec.decode_symbol_logp(1) != 0;
let bytes = if mode == MODE_HYBRID {
usize::try_from(range_dec.decode_uint(256).saturating_add(2))
.map_err(|_| OpusDecodeError::BadArgument)?
} else {
let used_bytes = ((range_dec.tell() + 7) >> 3) as usize;
packet_len
.checked_sub(used_bytes)
.ok_or(OpusDecodeError::BadArgument)?
};
if bytes > packet_len {
return Err(OpusDecodeError::BadArgument);
}
let mut redundancy_bytes = bytes;
let cutoff = packet_len
.checked_sub(bytes)
.ok_or(OpusDecodeError::BadArgument)?;
if let Some(data) = packet {
redundant_packet = data.get(cutoff..cutoff + bytes);
}
packet_len = cutoff;
if packet_len
.checked_mul(8)
.is_none_or(|value| value < range_dec.tell() as usize)
{
packet_len = 0;
redundancy = false;
redundant_packet = None;
redundancy_bytes = 0;
} else if redundancy && redundant_packet.is_none() {
return Err(OpusDecodeError::BadArgument);
}
if redundancy_bytes > 0 {
let bytes_u32 = u32::try_from(redundancy_bytes)
.map_err(|_| OpusDecodeError::BadArgument)?;
let storage = range_dec.ctx().storage;
if storage < bytes_u32 {
return Err(OpusDecodeError::BadArgument);
}
range_dec.ctx_mut().storage = storage - bytes_u32;
}
}
}
}
if packet.is_some() && packet_len > 1 && (mode == MODE_SILK_ONLY || decode_fec) {
range_final = Some(range_dec.range_final());
}
}
if redundancy {
transition = false;
} else if transition {
let transition_len = f5
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let mut buffer = vec![0.0; transition_len];
let transition_size = audiosize.min(f5);
let ret = self.decode_frame(None, 0, &mut buffer, transition_size, false)?;
if ret != transition_size {
return Err(OpusDecodeError::InternalError);
}
pcm_transition = Some(buffer);
}
}
if let Some(data) = packet {
if packet_len > data.len() {
return Err(OpusDecodeError::BadArgument);
}
packet = Some(&data[..packet_len]);
}
if packet_len > 1
&& let Some(range_dec) = range_decoder.as_ref()
{
debug_assert_eq!(
range_dec.ctx().storage as usize,
packet_len,
"range decoder storage must match packet length",
);
}
let mut start_band = 0;
if !celt_only {
start_band = 17;
}
if let Some(packet) = Bandwidth::from_opus_int(bandwidth) {
let end_band = match packet {
Bandwidth::Narrow => 13,
Bandwidth::Medium | Bandwidth::Wide => 17,
Bandwidth::SuperWide => 19,
Bandwidth::Full => 21,
};
opus_custom_decoder_ctl(
self.celt.decoder(),
CeltDecoderCtlRequest::SetEndBand(end_band),
)
.map_err(|_| OpusDecodeError::BadArgument)?;
}
let stream_channels =
usize::try_from(self.stream_channels).map_err(|_| OpusDecodeError::BadArgument)?;
if stream_channels == 0 || stream_channels > MAX_CHANNELS {
return Err(OpusDecodeError::BadArgument);
}
opus_custom_decoder_ctl(
self.celt.decoder(),
CeltDecoderCtlRequest::SetChannels(stream_channels),
)
.map_err(|_| OpusDecodeError::BadArgument)?;
if redundancy && celt_to_silk {
let redundant_len = f5
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let mut buffer = vec![0.0; redundant_len];
opus_custom_decoder_ctl(self.celt.decoder(), CeltDecoderCtlRequest::SetStartBand(0))
.map_err(|_| OpusDecodeError::BadArgument)?;
if let Some(data) = redundant_packet {
#[cfg(feature = "deep_plc")]
let (celt, plc) = {
let celt = &mut self.celt;
let plc = Some(&mut self.lpcnet);
(celt, plc)
};
#[cfg(not(feature = "deep_plc"))]
let (celt, plc) = (&mut self.celt, ());
let ret = decode_celt_frame(celt, Some(data), &mut buffer, f5, false, plc)?;
if ret != f5 {
return Err(OpusDecodeError::InternalError);
}
opus_custom_decoder_ctl(
self.celt.decoder(),
CeltDecoderCtlRequest::GetFinalRange(&mut redundant_rng),
)
.map_err(|_| OpusDecodeError::BadArgument)?;
} else {
return Err(OpusDecodeError::BadArgument);
}
redundant_audio = Some(buffer);
}
opus_custom_decoder_ctl(
self.celt.decoder(),
CeltDecoderCtlRequest::SetStartBand(start_band),
)
.map_err(|_| OpusDecodeError::BadArgument)?;
if mode == MODE_SILK_ONLY {
if !celt_accum {
let total = audiosize
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if pcm.len() < total {
return Err(OpusDecodeError::BufferTooSmall);
}
pcm[..total].fill(0.0);
}
if self.prev_mode == MODE_HYBRID
&& !(redundancy && celt_to_silk && self.prev_redundancy != 0)
{
opus_custom_decoder_ctl(
self.celt.decoder(),
CeltDecoderCtlRequest::SetStartBand(0),
)
.map_err(|_| OpusDecodeError::BadArgument)?;
let silence = [0xFFu8, 0xFF];
#[cfg(feature = "deep_plc")]
let (celt, plc) = {
let celt = &mut self.celt;
let plc = Some(&mut self.lpcnet);
(celt, plc)
};
#[cfg(not(feature = "deep_plc"))]
let (celt, plc) = (&mut self.celt, ());
let ret = decode_celt_frame(celt, Some(&silence), pcm, f2_5, celt_accum, plc)?;
if ret != f2_5 {
return Err(OpusDecodeError::InternalError);
}
}
} else {
if mode != self.prev_mode && self.prev_mode > 0 && self.prev_redundancy == 0 {
opus_custom_decoder_ctl(self.celt.decoder(), CeltDecoderCtlRequest::ResetState)
.map_err(|_| OpusDecodeError::BadArgument)?;
}
let celt_frame = audiosize.min(f20);
let celt_packet = if decode_fec { None } else { packet };
let celt_ret = if celt_packet.is_some() && range_decoder.is_some() {
#[cfg(feature = "deep_plc")]
let (celt, plc) = {
let celt = &mut self.celt;
let plc = Some(&mut self.lpcnet);
(celt, plc)
};
#[cfg(not(feature = "deep_plc"))]
let (celt, plc) = (&mut self.celt, ());
decode_celt_frame_with_ec(
celt,
celt_packet,
pcm,
celt_frame,
range_decoder.as_mut(),
celt_accum,
plc,
)?
} else {
#[cfg(feature = "deep_plc")]
let (celt, plc) = {
let celt = &mut self.celt;
let plc = Some(&mut self.lpcnet);
(celt, plc)
};
#[cfg(not(feature = "deep_plc"))]
let (celt, plc) = (&mut self.celt, ());
decode_celt_frame(celt, celt_packet, pcm, celt_frame, celt_accum, plc)?
};
if celt_ret != celt_frame {
return Err(OpusDecodeError::InternalError);
}
if packet.is_some() && packet_len > 1 && celt_packet.is_some() {
let mut final_range = 0u32;
opus_custom_decoder_ctl(
self.celt.decoder(),
CeltDecoderCtlRequest::GetFinalRange(&mut final_range),
)
.map_err(|_| OpusDecodeError::BadArgument)?;
celt_final_range = Some(final_range);
}
}
let mut mode_slot: Option<&OpusCustomMode<'_>> = None;
opus_custom_decoder_ctl(
self.celt.decoder(),
CeltDecoderCtlRequest::GetMode(&mut mode_slot),
)
.map_err(|_| OpusDecodeError::BadArgument)?;
let Some(celt_mode) = mode_slot else {
return Err(OpusDecodeError::InternalError);
};
let window = celt_mode.window;
let fade_len = f2_5
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if redundancy && !celt_to_silk && redundant_audio.is_none() {
let redundant_len = f5
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let mut buffer = vec![0.0; redundant_len];
opus_custom_decoder_ctl(self.celt.decoder(), CeltDecoderCtlRequest::ResetState)
.map_err(|_| OpusDecodeError::BadArgument)?;
opus_custom_decoder_ctl(self.celt.decoder(), CeltDecoderCtlRequest::SetStartBand(0))
.map_err(|_| OpusDecodeError::BadArgument)?;
if let Some(data) = redundant_packet {
#[cfg(feature = "deep_plc")]
let (celt, plc) = {
let celt = &mut self.celt;
let plc = Some(&mut self.lpcnet);
(celt, plc)
};
#[cfg(not(feature = "deep_plc"))]
let (celt, plc) = (&mut self.celt, ());
let ret = decode_celt_frame(celt, Some(data), &mut buffer, f5, false, plc)?;
if ret != f5 {
return Err(OpusDecodeError::InternalError);
}
opus_custom_decoder_ctl(
self.celt.decoder(),
CeltDecoderCtlRequest::GetFinalRange(&mut redundant_rng),
)
.map_err(|_| OpusDecodeError::BadArgument)?;
} else {
return Err(OpusDecodeError::BadArgument);
}
redundant_audio = Some(buffer);
}
if redundancy {
if !celt_to_silk {
if let Some(buffer) = redundant_audio.as_ref() {
let offset = audiosize
.checked_sub(f2_5)
.and_then(|value| value.checked_mul(channels))
.ok_or(OpusDecodeError::BadArgument)?;
let mut current = vec![0.0; fade_len];
current.copy_from_slice(&pcm[offset..offset + fade_len]);
smooth_fade(
¤t,
&buffer[fade_len..],
&mut pcm[offset..offset + fade_len],
f2_5,
channels,
window,
self.fs,
);
}
} else if (self.prev_mode != MODE_SILK_ONLY || self.prev_redundancy != 0)
&& let Some(buffer) = redundant_audio.as_ref()
{
if pcm.len() < fade_len.saturating_mul(2) {
return Err(OpusDecodeError::BufferTooSmall);
}
for (dst, &src) in pcm.iter_mut().take(fade_len).zip(buffer.iter()) {
*dst = src;
}
let mut tail = vec![0.0; fade_len];
tail.copy_from_slice(&pcm[fade_len..fade_len + fade_len]);
smooth_fade(
&buffer[fade_len..],
&tail,
&mut pcm[fade_len..fade_len + fade_len],
f2_5,
channels,
window,
self.fs,
);
}
} else if transition && let Some(transition_pcm) = pcm_transition.as_ref() {
if audiosize >= f5 {
if transition_pcm.len() < fade_len || pcm.len() < fade_len {
return Err(OpusDecodeError::BufferTooSmall);
}
pcm[..fade_len].copy_from_slice(&transition_pcm[..fade_len]);
if pcm.len() < fade_len.saturating_mul(2) {
return Err(OpusDecodeError::BufferTooSmall);
}
let mut tail = vec![0.0; fade_len];
tail.copy_from_slice(&pcm[fade_len..fade_len + fade_len]);
smooth_fade(
&transition_pcm[fade_len..],
&tail,
&mut pcm[fade_len..fade_len + fade_len],
f2_5,
channels,
window,
self.fs,
);
} else {
if pcm.len() < fade_len {
return Err(OpusDecodeError::BufferTooSmall);
}
let mut current = vec![0.0; fade_len];
current.copy_from_slice(&pcm[..fade_len]);
smooth_fade(
transition_pcm,
¤t,
&mut pcm[..fade_len],
f2_5,
channels,
window,
self.fs,
);
}
}
let final_range = if packet_len > 1 {
if let Some(range_value) = range_final {
range_value
} else {
celt_final_range.unwrap_or_default()
}
} else {
0
};
self.prev_mode = mode;
self.prev_redundancy = i32::from(redundancy && !celt_to_silk);
self.range_final = if packet_len > 1 {
final_range ^ redundant_rng
} else {
0
};
Ok(audiosize)
}
#[cfg_attr(not(test), allow(dead_code))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn decode_native_with<F>(
&mut self,
data: Option<&[u8]>,
len: usize,
pcm: &mut [OpusRes],
frame_size: usize,
decode_fec: bool,
self_delimited: bool,
packet_offset: Option<&mut usize>,
soft_clip: bool,
decode_frame: &mut F,
) -> Result<usize, OpusDecodeError>
where
F: FnMut(
&mut OpusDecoder<'mode>,
Option<&[u8]>,
usize,
&mut [OpusRes],
usize,
bool,
) -> Result<usize, OpusDecodeError>,
{
if frame_size == 0 {
return Err(OpusDecodeError::BadArgument);
}
let channels = usize::try_from(self.channels).map_err(|_| OpusDecodeError::BadArgument)?;
if channels == 0 || channels > MAX_CHANNELS {
return Err(OpusDecodeError::BadArgument);
}
let total_samples = frame_size
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if pcm.len() < total_samples {
return Err(OpusDecodeError::BufferTooSmall);
}
let samples_per_2_5_ms =
usize::try_from(self.fs / 400).map_err(|_| OpusDecodeError::BadArgument)?;
if (decode_fec || len == 0 || data.is_none())
&& samples_per_2_5_ms != 0
&& !frame_size.is_multiple_of(samples_per_2_5_ms)
{
return Err(OpusDecodeError::BadArgument);
}
if len == 0 || data.is_none() {
let mut pcm_count = 0usize;
while pcm_count < frame_size {
let offset = pcm_count
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let remaining = frame_size
.checked_sub(pcm_count)
.ok_or(OpusDecodeError::BadArgument)?;
let pcm_slice_end = offset
.checked_add(
remaining
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?,
)
.ok_or(OpusDecodeError::BadArgument)?;
let ret = decode_frame(
self,
None,
0,
&mut pcm[offset..pcm_slice_end],
remaining,
false,
)?;
if ret == 0 {
return Err(OpusDecodeError::InternalError);
}
pcm_count = pcm_count
.checked_add(ret)
.ok_or(OpusDecodeError::BadArgument)?;
}
debug_assert_eq!(pcm_count, frame_size);
self.last_packet_duration =
i32::try_from(pcm_count).map_err(|_| OpusDecodeError::BadArgument)?;
self.apply_decode_gain_and_soft_clip(pcm, pcm_count, false);
return Ok(pcm_count);
}
let packet = data.unwrap_or(&[]);
if len > packet.len() {
return Err(OpusDecodeError::BadArgument);
}
let packet = &packet[..len];
let packet_mode = opus_packet_get_mode(packet)?;
let packet_bandwidth = opus_packet_get_bandwidth(packet)?;
let fs = u32::try_from(self.fs).map_err(|_| OpusDecodeError::BadArgument)?;
let packet_frame_size = opus_packet_get_samples_per_frame(packet, fs)?;
let packet_stream_channels = opus_packet_get_nb_channels(packet)?;
let parsed = opus_packet_parse_impl(packet, len, self_delimited)?;
if let Some(slot) = packet_offset {
*slot = parsed.packet_offset;
}
if decode_fec {
if frame_size < packet_frame_size
|| decode_as_celt_only(opus_mode_to_int(packet_mode))
|| decode_as_celt_only(self.mode)
{
return self.decode_native_with(
None,
0,
pcm,
frame_size,
false,
false,
None,
soft_clip,
decode_frame,
);
}
let duration_copy = self.last_packet_duration;
if frame_size != packet_frame_size {
let leading = frame_size
.checked_sub(packet_frame_size)
.ok_or(OpusDecodeError::BadArgument)?;
let ret = self.decode_native_with(
None,
0,
pcm,
leading,
false,
false,
None,
soft_clip,
decode_frame,
);
if let Err(err) = ret {
self.last_packet_duration = duration_copy;
return Err(err);
}
let ret = ret?;
if ret != leading {
self.last_packet_duration = duration_copy;
return Err(OpusDecodeError::InternalError);
}
}
self.mode = opus_mode_to_int(packet_mode);
self.bandwidth = packet_bandwidth.to_opus_int();
self.frame_size =
i32::try_from(packet_frame_size).map_err(|_| OpusDecodeError::BadArgument)?;
self.stream_channels =
i32::try_from(packet_stream_channels).map_err(|_| OpusDecodeError::BadArgument)?;
let offset = (frame_size - packet_frame_size)
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let frame_samples = packet_frame_size
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let end = offset
.checked_add(frame_samples)
.ok_or(OpusDecodeError::BadArgument)?;
let ret = decode_frame(
self,
Some(parsed.frames[0]),
usize::from(parsed.frame_sizes[0]),
&mut pcm[offset..end],
packet_frame_size,
true,
)?;
debug_assert_eq!(ret, packet_frame_size);
self.last_packet_duration =
i32::try_from(frame_size).map_err(|_| OpusDecodeError::BadArgument)?;
self.apply_decode_gain_and_soft_clip(pcm, frame_size, false);
return Ok(frame_size);
}
if parsed.frame_count * packet_frame_size > frame_size {
return Err(OpusDecodeError::BufferTooSmall);
}
self.mode = opus_mode_to_int(packet_mode);
self.bandwidth = packet_bandwidth.to_opus_int();
self.frame_size =
i32::try_from(packet_frame_size).map_err(|_| OpusDecodeError::BadArgument)?;
self.stream_channels =
i32::try_from(packet_stream_channels).map_err(|_| OpusDecodeError::BadArgument)?;
let mut nb_samples = 0usize;
for (frame, &size_bytes) in parsed
.frames
.iter()
.zip(parsed.frame_sizes.iter())
.take(parsed.frame_count)
{
let offset = nb_samples
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let remaining = frame_size
.checked_sub(nb_samples)
.ok_or(OpusDecodeError::BadArgument)?;
let pcm_samples = remaining
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
let end = offset
.checked_add(pcm_samples)
.ok_or(OpusDecodeError::BadArgument)?;
let ret = decode_frame(
self,
Some(*frame),
usize::from(size_bytes),
&mut pcm[offset..end],
remaining,
false,
)?;
debug_assert_eq!(ret, packet_frame_size);
nb_samples = nb_samples
.checked_add(ret)
.ok_or(OpusDecodeError::BadArgument)?;
}
self.last_packet_duration =
i32::try_from(nb_samples).map_err(|_| OpusDecodeError::BadArgument)?;
self.apply_decode_gain_and_soft_clip(pcm, nb_samples, soft_clip);
Ok(nb_samples)
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn apply_decode_gain_and_soft_clip(
&mut self,
pcm: &mut [OpusRes],
frame_size: usize,
soft_clip: bool,
) {
let channels = usize::try_from(self.channels).unwrap_or_default();
debug_assert!(matches!(channels, 1 | 2));
let Some(total_samples) = frame_size.checked_mul(channels) else {
return;
};
debug_assert!(pcm.len() >= total_samples);
if pcm.len() < total_samples {
return;
}
let pcm = &mut pcm[..total_samples];
if self.decode_gain != 0 {
let gain = celt_exp2(DECODE_GAIN_SCALE * self.decode_gain as f32);
for sample in pcm.iter_mut() {
*sample *= gain;
}
}
#[cfg(not(feature = "fixed_point"))]
{
if soft_clip {
opus_pcm_soft_clip_impl(
pcm,
frame_size,
channels,
&mut self.softclip_mem,
self.arch,
);
} else {
self.softclip_mem = [0.0; 2];
}
}
#[cfg(feature = "fixed_point")]
{
let _ = soft_clip;
}
}
#[inline]
pub(crate) fn arch(&self) -> i32 {
self.arch
}
#[inline]
pub(crate) fn sample_rate(&self) -> i32 {
self.fs
}
#[cfg(feature = "deep_plc")]
#[inline]
pub(crate) fn lpcnet_mut(&mut self) -> &mut LpcNetPlcState {
&mut self.lpcnet
}
fn reset_dec_control(&mut self) {
self.dec_control = DecControl {
n_channels_api: self.channels,
n_channels_internal: 0,
api_sample_rate: self.fs,
internal_sample_rate: 0,
payload_size_ms: 0,
prev_pitch_lag: 0,
enable_deep_plc: false,
};
}
fn reset_runtime_fields(&mut self) {
self.stream_channels = self.channels;
self.bandwidth = 0;
self.mode = 0;
self.prev_mode = 0;
self.frame_size = self.fs / 400;
self.prev_redundancy = 0;
self.last_packet_duration = 0;
#[cfg(not(feature = "fixed_point"))]
{
self.softclip_mem = [0.0; 2];
}
self.range_final = 0;
}
fn reset_state(&mut self) -> Result<(), OpusDecoderCtlError> {
opus_custom_decoder_ctl(self.celt.decoder(), CeltDecoderCtlRequest::ResetState)?;
silk_reset_decoder(&mut self.silk)?;
#[cfg(feature = "deep_plc")]
{
self.lpcnet.reset();
}
self.reset_runtime_fields();
Ok(())
}
}
fn map_celt_error(err: CeltDecodeError) -> OpusDecodeError {
match err {
CeltDecodeError::BadArgument => OpusDecodeError::BadArgument,
CeltDecodeError::InvalidPacket | CeltDecodeError::PacketLoss => {
OpusDecodeError::InvalidPacket
}
}
}
fn decode_celt_frame_with_ec<'mode, 'pkt>(
decoder: &mut OwnedCeltDecoder<'mode>,
packet: Option<&'pkt [u8]>,
pcm: &mut [OpusRes],
frame_size: usize,
range_decoder: Option<&'pkt mut EcDec<'pkt>>,
accum: bool,
plc: PlcHandle<'_>,
) -> Result<usize, OpusDecodeError>
where
'mode: 'pkt,
{
celt_decode_with_ec_dred(
decoder.decoder(),
packet,
pcm,
frame_size,
range_decoder,
accum,
plc,
)
.map_err(map_celt_error)
}
fn decode_celt_frame(
decoder: &mut OwnedCeltDecoder<'_>,
packet: Option<&[u8]>,
pcm: &mut [OpusRes],
frame_size: usize,
accum: bool,
plc: PlcHandle<'_>,
) -> Result<usize, OpusDecodeError> {
decode_celt_frame_with_ec(decoder, packet, pcm, frame_size, None, accum, plc)
}
#[inline]
fn silk_int16_to_res(sample: i16) -> OpusRes {
#[cfg(feature = "fixed_point")]
{
crate::celt::res2float(crate::celt::int16tores(sample))
}
#[cfg(not(feature = "fixed_point"))]
{
OpusRes::from(sample) * (1.0 / CELT_SIG_SCALE)
}
}
#[inline]
fn res_to_int24(sample: OpusRes) -> i32 {
#[cfg(feature = "fixed_point")]
{
crate::celt::res2int24(crate::celt::float2res(sample))
}
#[cfg(not(feature = "fixed_point"))]
{
let scale = CELT_SIG_SCALE * 256.0;
let scaled = (sample * scale).clamp(-8_388_608.0, 8_388_607.0);
float2int(scaled)
}
}
pub fn opus_decoder_create(
fs: i32,
channels: i32,
) -> Result<OpusDecoder<'static>, OpusDecoderInitError> {
if !matches!(fs, 48_000 | 24_000 | 16_000 | 12_000 | 8_000) || !matches!(channels, 1 | 2) {
return Err(OpusDecoderInitError::BadArgument);
}
let silk = SilkDecoder::default();
let mode = canonical_mode().ok_or(OpusDecoderInitError::CeltInit)?;
let celt = opus_custom_decoder_create(mode, channels as usize)
.map_err(|_| OpusDecoderInitError::CeltInit)?;
let mut decoder = OpusDecoder {
celt,
silk,
fs,
channels,
dec_control: DecControl::default(),
decode_gain: 0,
complexity: 0,
arch: 0,
#[cfg(feature = "deep_plc")]
lpcnet: LpcNetPlcState::default(),
stream_channels: 0,
bandwidth: 0,
mode: 0,
prev_mode: 0,
frame_size: 0,
prev_redundancy: 0,
last_packet_duration: 0,
#[cfg(not(feature = "fixed_point"))]
softclip_mem: [0.0; 2],
range_final: 0,
decode_scratch: Vec::with_capacity(MAX_DECODE_SAMPLES_PER_CHANNEL * MAX_CHANNELS),
};
decoder.init(fs, channels)?;
Ok(decoder)
}
#[inline]
pub fn opus_decoder_get_nb_samples(
decoder: &OpusDecoder<'_>,
packet: &[u8],
len: usize,
) -> Result<usize, PacketError> {
decoder.get_nb_samples(packet, len)
}
#[cfg_attr(not(test), allow(dead_code))]
#[allow(clippy::too_many_arguments)]
pub fn opus_decode_native(
decoder: &mut OpusDecoder<'_>,
data: Option<&[u8]>,
len: usize,
pcm: &mut [OpusRes],
frame_size: usize,
decode_fec: bool,
self_delimited: bool,
packet_offset: Option<&mut usize>,
soft_clip: bool,
) -> Result<usize, OpusDecodeError> {
decoder.decode_native_with(
data,
len,
pcm,
frame_size,
decode_fec,
self_delimited,
packet_offset,
soft_clip,
&mut |st, frame_data, frame_len, out, size, fec| {
st.decode_frame(frame_data, frame_len, out, size, fec)
},
)
}
pub fn opus_decode(
decoder: &mut OpusDecoder<'_>,
data: Option<&[u8]>,
len: usize,
pcm: &mut [i16],
frame_size: usize,
decode_fec: bool,
) -> Result<usize, OpusDecodeError> {
if frame_size == 0 {
return Err(OpusDecodeError::BadArgument);
}
let channels = usize::try_from(decoder.channels).map_err(|_| OpusDecodeError::BadArgument)?;
if channels == 0 || channels > MAX_CHANNELS {
return Err(OpusDecodeError::BadArgument);
}
if decode_fec || len == 0 || data.is_none() {
let fs = usize::try_from(decoder.fs).map_err(|_| OpusDecodeError::BadArgument)?;
let f2_5 = fs / 400;
if f2_5 == 0 || !frame_size.is_multiple_of(f2_5) {
return Err(OpusDecodeError::BadArgument);
}
}
let mut frame_size = frame_size;
if let Some(packet) = data {
if len > packet.len() {
return Err(OpusDecodeError::BadArgument);
}
if len > 0 && !decode_fec {
let nb_samples = opus_decoder_get_nb_samples(decoder, packet, len)?;
if nb_samples == 0 {
return Err(OpusDecodeError::InvalidPacket);
}
frame_size = frame_size.min(nb_samples);
}
}
let total_samples = frame_size
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if pcm.len() < total_samples {
return Err(OpusDecodeError::BufferTooSmall);
}
let mut out = core::mem::take(&mut decoder.decode_scratch);
if out.len() < total_samples {
out.resize(total_samples, OpusRes::default());
}
let decoded = match opus_decode_native(
decoder,
data,
len,
&mut out[..total_samples],
frame_size,
decode_fec,
false,
None,
OPTIONAL_CLIP,
) {
Ok(decoded) => decoded,
Err(err) => {
decoder.decode_scratch = out;
return Err(err);
}
};
let decoded_samples = decoded
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if pcm.len() < decoded_samples {
return Err(OpusDecodeError::BufferTooSmall);
}
select_celt_float2int16_impl(decoder.arch)(
&out[..decoded_samples],
&mut pcm[..decoded_samples],
);
decoder.decode_scratch = out;
Ok(decoded)
}
pub fn opus_decode24(
decoder: &mut OpusDecoder<'_>,
data: Option<&[u8]>,
len: usize,
pcm: &mut [i32],
frame_size: usize,
decode_fec: bool,
) -> Result<usize, OpusDecodeError> {
if frame_size == 0 {
return Err(OpusDecodeError::BadArgument);
}
let channels = usize::try_from(decoder.channels).map_err(|_| OpusDecodeError::BadArgument)?;
if channels == 0 || channels > MAX_CHANNELS {
return Err(OpusDecodeError::BadArgument);
}
if decode_fec || len == 0 || data.is_none() {
let fs = usize::try_from(decoder.fs).map_err(|_| OpusDecodeError::BadArgument)?;
let f2_5 = fs / 400;
if f2_5 == 0 || !frame_size.is_multiple_of(f2_5) {
return Err(OpusDecodeError::BadArgument);
}
}
let mut frame_size = frame_size;
if let Some(packet) = data {
if len > packet.len() {
return Err(OpusDecodeError::BadArgument);
}
if len > 0 && !decode_fec {
let nb_samples = opus_decoder_get_nb_samples(decoder, packet, len)?;
if nb_samples == 0 {
return Err(OpusDecodeError::InvalidPacket);
}
frame_size = frame_size.min(nb_samples);
}
}
let total_samples = frame_size
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if pcm.len() < total_samples {
return Err(OpusDecodeError::BufferTooSmall);
}
let mut out = core::mem::take(&mut decoder.decode_scratch);
if out.len() < total_samples {
out.resize(total_samples, OpusRes::default());
}
let decoded = match opus_decode_native(
decoder,
data,
len,
&mut out[..total_samples],
frame_size,
decode_fec,
false,
None,
false,
) {
Ok(decoded) => decoded,
Err(err) => {
decoder.decode_scratch = out;
return Err(err);
}
};
let decoded_samples = decoded
.checked_mul(channels)
.ok_or(OpusDecodeError::BadArgument)?;
if pcm.len() < decoded_samples {
return Err(OpusDecodeError::BufferTooSmall);
}
for (dst, &src) in pcm.iter_mut().take(decoded_samples).zip(out.iter()) {
*dst = res_to_int24(src);
}
decoder.decode_scratch = out;
Ok(decoded)
}
pub fn opus_decode_float(
decoder: &mut OpusDecoder<'_>,
data: Option<&[u8]>,
len: usize,
pcm: &mut [OpusRes],
frame_size: usize,
decode_fec: bool,
) -> Result<usize, OpusDecodeError> {
if frame_size == 0 {
return Err(OpusDecodeError::BadArgument);
}
if decode_fec || len == 0 || data.is_none() {
let fs = usize::try_from(decoder.fs).map_err(|_| OpusDecodeError::BadArgument)?;
let f2_5 = fs / 400;
if f2_5 == 0 || !frame_size.is_multiple_of(f2_5) {
return Err(OpusDecodeError::BadArgument);
}
}
opus_decode_native(
decoder, data, len, pcm, frame_size, decode_fec, false, None, false,
)
}
pub fn opus_decoder_ctl<'req>(
decoder: &mut OpusDecoder<'_>,
request: OpusDecoderCtlRequest<'req>,
) -> Result<(), OpusDecoderCtlError> {
match request {
OpusDecoderCtlRequest::GetBandwidth(slot) => {
*slot = decoder.bandwidth;
}
OpusDecoderCtlRequest::GetSampleRate(slot) => {
*slot = decoder.fs;
}
OpusDecoderCtlRequest::GetPitch(slot) => {
if decode_as_celt_only(decoder.prev_mode) {
opus_custom_decoder_ctl(
decoder.celt.decoder(),
CeltDecoderCtlRequest::GetPitch(slot),
)?;
} else {
*slot = decoder.dec_control.prev_pitch_lag;
}
}
OpusDecoderCtlRequest::SetGain(value) => {
if !(-32_768..=32_767).contains(&value) {
return Err(OpusDecoderCtlError::BadArgument);
}
decoder.decode_gain = value;
}
OpusDecoderCtlRequest::GetGain(slot) => {
*slot = decoder.decode_gain;
}
OpusDecoderCtlRequest::SetComplexity(value) => {
if !(0..=10).contains(&value) {
return Err(OpusDecoderCtlError::BadArgument);
}
opus_custom_decoder_ctl(
decoder.celt.decoder(),
CeltDecoderCtlRequest::SetComplexity(value),
)?;
decoder.complexity = value;
}
OpusDecoderCtlRequest::GetComplexity(slot) => {
*slot = decoder.complexity;
}
OpusDecoderCtlRequest::ResetState => decoder.reset_state()?,
OpusDecoderCtlRequest::GetLastPacketDuration(slot) => {
*slot = decoder.last_packet_duration;
}
OpusDecoderCtlRequest::GetFinalRange(slot) => {
*slot = decoder.range_final;
}
OpusDecoderCtlRequest::SetPhaseInversionDisabled(value) => {
opus_custom_decoder_ctl(
decoder.celt.decoder(),
CeltDecoderCtlRequest::SetPhaseInversionDisabled(value),
)?;
}
OpusDecoderCtlRequest::GetPhaseInversionDisabled(slot) => {
opus_custom_decoder_ctl(
decoder.celt.decoder(),
CeltDecoderCtlRequest::GetPhaseInversionDisabled(slot),
)?;
}
OpusDecoderCtlRequest::SetDnnBlob(data) => {
if data.is_empty() {
return Err(OpusDecoderCtlError::BadArgument);
}
#[cfg(feature = "deep_plc")]
{
decoder
.lpcnet
.load_model(data)
.map_err(|_| OpusDecoderCtlError::BadArgument)?;
}
load_osce_models(&mut decoder.silk, Some(data))?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
extern crate std;
use super::MODE_HYBRID;
use super::{
DecControl, MODE_CELT_ONLY, MODE_SILK_ONLY, OpusDecodeError, OpusDecoderCtlError,
OpusDecoderCtlRequest, opus_decode, opus_decode_float, opus_decode_native, opus_decode24,
opus_decoder_create, opus_decoder_ctl, opus_decoder_get_size, smooth_fade,
};
use crate::celt::{
OpusRes, canonical_mode, celt_decoder_get_size, celt_exp2, opus_custom_decoder_create,
};
#[cfg(feature = "fixed_point")]
use crate::packet::{
Bandwidth, Mode, PacketError, opus_packet_get_bandwidth, opus_packet_get_nb_channels,
};
use crate::silk::dec_api::Decoder as SilkDecoder;
use crate::silk::get_decoder_size::get_decoder_size;
use alloc::vec;
use alloc::vec::Vec;
#[cfg(feature = "deep_plc_weights")]
use mousiki_deep_plc_weights::DNN_BLOB;
#[cfg(feature = "fixed_point")]
include!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/fixtures/hybrid_decode_vectors.rs"
));
fn simple_packet(toc: u8, payload_len: usize) -> Vec<u8> {
let mut packet = Vec::with_capacity(payload_len + 1);
packet.push(toc);
packet.extend(core::iter::repeat(0u8).take(payload_len));
packet
}
fn read_be_u32(bytes: &[u8]) -> Option<u32> {
let chunk = bytes.get(..4)?;
Some(u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
}
#[test]
fn smooth_fade_blends_with_squared_window() {
let in1 = [1.0f32, -1.0, 0.5, -0.5];
let in2 = [0.0f32, 0.5, 1.0, -1.0];
let mut out = [0.0f32; 4];
let window = [0.0f32, 0.5, 1.0, 1.0];
smooth_fade(&in1, &in2, &mut out, 2, 2, &window, 48_000);
assert!((out[0] - 1.0).abs() < 1e-7);
assert!((out[1] + 1.0).abs() < 1e-7);
assert!((out[2] - 0.625).abs() < 1e-7);
assert!((out[3] + 0.625).abs() < 1e-7);
}
#[test]
fn rejects_invalid_channel_counts() {
assert!(opus_decoder_get_size(0).is_none());
assert!(opus_decoder_get_size(3).is_none());
}
#[test]
fn matches_component_size_sum_for_mono_and_stereo() {
for &channels in &[1usize, 2] {
let mut silk_size = 0usize;
get_decoder_size(&mut silk_size).unwrap();
let celt_size = celt_decoder_get_size(channels).unwrap();
let expected = opus_decoder_get_size(channels).unwrap();
assert!(expected >= silk_size + celt_size);
}
}
#[test]
fn init_resets_silk_and_recreates_celt() {
let mode = canonical_mode().expect("canonical mode");
let celt = opus_custom_decoder_create(mode, 1).expect("celt decoder");
let silk = SilkDecoder::default();
let mut decoder = super::OpusDecoder {
celt,
silk,
fs: 48_000,
channels: 1,
dec_control: DecControl::default(),
decode_gain: 0,
complexity: 0,
arch: 0,
#[cfg(feature = "deep_plc")]
lpcnet: crate::celt::LpcNetPlcState::default(),
stream_channels: 0,
bandwidth: 0,
mode: 0,
prev_mode: 0,
frame_size: 0,
prev_redundancy: 0,
last_packet_duration: 0,
#[cfg(not(feature = "fixed_point"))]
softclip_mem: [0.0; 2],
range_final: 0,
decode_scratch: Vec::with_capacity(
super::MAX_DECODE_SAMPLES_PER_CHANNEL * super::MAX_CHANNELS,
),
};
decoder.init(48_000, 1).expect("init succeeds");
assert_eq!(decoder.silk.n_channels_api, 1);
assert_eq!(decoder.silk.n_channels_internal, 1);
}
#[test]
fn create_rejects_invalid_arguments() {
assert!(super::opus_decoder_create(44_100, 1).is_err());
assert!(super::opus_decoder_create(48_000, 3).is_err());
}
#[test]
fn decoder_gain_round_trips_and_validates_range() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::SetGain(-15)).unwrap();
let mut gain = 0;
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::GetGain(&mut gain)).unwrap();
assert_eq!(gain, -15);
let err =
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::SetGain(40_000)).unwrap_err();
assert_eq!(err, OpusDecoderCtlError::BadArgument);
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::GetGain(&mut gain)).unwrap();
assert_eq!(gain, -15);
}
#[test]
fn complexity_ctl_round_trips_and_validates_range() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::SetComplexity(7)).unwrap();
let mut complexity = 0;
opus_decoder_ctl(
&mut decoder,
OpusDecoderCtlRequest::GetComplexity(&mut complexity),
)
.unwrap();
assert_eq!(complexity, 7);
let err =
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::SetComplexity(11)).unwrap_err();
assert_eq!(err, OpusDecoderCtlError::BadArgument);
opus_decoder_ctl(
&mut decoder,
OpusDecoderCtlRequest::GetComplexity(&mut complexity),
)
.unwrap();
assert_eq!(complexity, 7);
}
#[test]
fn reset_state_preserves_gain_and_resets_runtime_fields() {
let mut decoder = opus_decoder_create(48_000, 2).expect("decoder should initialise");
decoder.decode_gain = 123;
decoder.stream_channels = 1;
decoder.prev_mode = 1;
decoder.prev_redundancy = 1;
decoder.last_packet_duration = 960;
decoder.range_final = 42;
decoder.silk.prev_decode_only_middle = true;
#[cfg(not(feature = "fixed_point"))]
{
decoder.softclip_mem = [0.5, -0.25];
}
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::ResetState).unwrap();
assert_eq!(decoder.decode_gain, 123);
assert_eq!(decoder.stream_channels, decoder.channels);
assert_eq!(decoder.prev_mode, 0);
assert_eq!(decoder.prev_redundancy, 0);
assert_eq!(decoder.last_packet_duration, 0);
assert_eq!(decoder.range_final, 0);
assert_eq!(decoder.frame_size, decoder.fs / 400);
assert!(!decoder.silk.prev_decode_only_middle);
#[cfg(not(feature = "fixed_point"))]
{
assert_eq!(decoder.softclip_mem, [0.0, 0.0]);
}
}
#[test]
#[cfg(not(feature = "fixed_point"))]
fn apply_decode_gain_scales_pcm_and_resets_softclip_mem_when_disabled() {
let mut decoder = opus_decoder_create(48_000, 2).expect("decoder should initialise");
decoder.decode_gain = 256; decoder.softclip_mem = [0.5, -0.25];
let mut pcm: [OpusRes; 4] = [0.5, -0.25, -0.75, 0.1];
decoder.apply_decode_gain_and_soft_clip(&mut pcm, 2, false);
let gain = celt_exp2(super::DECODE_GAIN_SCALE * 256.0);
assert!((pcm[0] - 0.5 * gain).abs() < 1e-6);
assert!((pcm[1] + 0.25 * gain).abs() < 1e-6);
assert!((pcm[2] + 0.75 * gain).abs() < 1e-6);
assert!((pcm[3] - 0.1 * gain).abs() < 1e-6);
assert_eq!(decoder.softclip_mem, [0.0, 0.0]);
}
#[test]
#[cfg(not(feature = "fixed_point"))]
fn apply_decode_gain_invokes_soft_clip_before_returning() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
decoder.decode_gain = 256;
let mut pcm: [OpusRes; 1] = [0.9];
decoder.apply_decode_gain_and_soft_clip(&mut pcm, 1, true);
let gain = celt_exp2(super::DECODE_GAIN_SCALE * 256.0);
let mut expected = [0.9 * gain];
let mut softclip_mem = [0.0; 2];
crate::opus::opus_pcm_soft_clip_impl(&mut expected, 1, 1, &mut softclip_mem, decoder.arch);
assert!((pcm[0] - expected[0]).abs() < 1e-6);
assert!((decoder.softclip_mem[0] - softclip_mem[0]).abs() < 1e-6);
assert_eq!(decoder.softclip_mem[1], 0.0);
}
#[test]
#[cfg(feature = "fixed_point")]
fn apply_decode_gain_scales_pcm_in_fixed_point_builds() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
decoder.decode_gain = 128;
let mut pcm: [OpusRes; 1] = [0.5];
decoder.apply_decode_gain_and_soft_clip(&mut pcm, 1, false);
let gain = celt_exp2(super::DECODE_GAIN_SCALE * 128.0);
assert!((pcm[0] - 0.5 * gain).abs() < 1e-6);
}
#[test]
fn exposes_sample_rate_bandwidth_and_final_range() {
let mut decoder = opus_decoder_create(48_000, 2).expect("decoder should initialise");
decoder.bandwidth = 1105;
decoder.range_final = 42;
let mut fs = 0;
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::GetSampleRate(&mut fs)).unwrap();
assert_eq!(fs, 48_000);
let mut bandwidth = 0;
opus_decoder_ctl(
&mut decoder,
OpusDecoderCtlRequest::GetBandwidth(&mut bandwidth),
)
.unwrap();
assert_eq!(bandwidth, 1105);
let mut final_range = 0;
opus_decoder_ctl(
&mut decoder,
OpusDecoderCtlRequest::GetFinalRange(&mut final_range),
)
.unwrap();
assert_eq!(final_range, 42);
}
#[test]
fn reports_last_packet_duration() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
decoder.last_packet_duration = 960;
let mut duration = 0;
opus_decoder_ctl(
&mut decoder,
OpusDecoderCtlRequest::GetLastPacketDuration(&mut duration),
)
.unwrap();
assert_eq!(duration, 960);
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::ResetState).unwrap();
opus_decoder_ctl(
&mut decoder,
OpusDecoderCtlRequest::GetLastPacketDuration(&mut duration),
)
.unwrap();
assert_eq!(duration, 0);
}
#[test]
fn phase_inversion_ctl_forwards_to_celt() {
let mut decoder = opus_decoder_create(48_000, 2).expect("decoder should initialise");
opus_decoder_ctl(
&mut decoder,
OpusDecoderCtlRequest::SetPhaseInversionDisabled(true),
)
.unwrap();
let mut disabled = false;
opus_decoder_ctl(
&mut decoder,
OpusDecoderCtlRequest::GetPhaseInversionDisabled(&mut disabled),
)
.unwrap();
assert!(disabled);
}
#[test]
fn dnn_blob_ctl_rejects_empty_payload() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
assert_eq!(
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::SetDnnBlob(&[])).unwrap_err(),
OpusDecoderCtlError::BadArgument
);
}
#[cfg(feature = "deep_plc")]
#[test]
fn dnn_blob_ctl_rejects_invalid_plc_blob() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let loaded_before = decoder.lpcnet.loaded;
assert_eq!(
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::SetDnnBlob(&[1, 2, 3]))
.unwrap_err(),
OpusDecoderCtlError::BadArgument
);
assert_eq!(decoder.lpcnet.loaded, loaded_before);
}
#[cfg(feature = "deep_plc_weights")]
#[test]
fn dnn_blob_ctl_accepts_embedded_plc_blob() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::SetDnnBlob(DNN_BLOB))
.expect("embedded PLC blob should load");
assert!(decoder.lpcnet.loaded);
}
#[test]
fn get_pitch_reports_silk_prev_pitch_lag() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
decoder.prev_mode = MODE_SILK_ONLY;
decoder.dec_control.prev_pitch_lag = 123;
let mut pitch = 0;
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::GetPitch(&mut pitch)).unwrap();
assert_eq!(pitch, 123);
}
#[test]
fn get_pitch_forwards_to_celt_when_prev_mode_is_celt() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
decoder.prev_mode = MODE_CELT_ONLY;
{
let celt = decoder.celt.decoder();
celt.postfilter_period = 321;
}
let mut pitch = 0;
opus_decoder_ctl(&mut decoder, OpusDecoderCtlRequest::GetPitch(&mut pitch)).unwrap();
assert_eq!(pitch, 321);
}
#[test]
fn parse_packet_reports_self_delimited_metadata() {
let decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let packet: [u8; 7] = [0x00, 0x05, 1, 2, 3, 4, 5];
let parsed = decoder
.parse_packet(&packet, packet.len(), true)
.expect("parse succeeds");
assert_eq!(parsed.mode, Mode::SILK);
assert_eq!(parsed.bandwidth, Bandwidth::Narrow);
assert_eq!(parsed.frame_size, 480);
assert_eq!(parsed.stream_channels, 1);
assert_eq!(parsed.parsed.frame_count, 1);
assert_eq!(parsed.parsed.frame_sizes[0], 5);
assert_eq!(parsed.parsed.payload_offset, 2);
assert_eq!(parsed.parsed.packet_offset, packet.len());
assert!(parsed.parsed.padding.is_empty());
}
#[test]
fn parse_packet_validates_length() {
let decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let packet: [u8; 2] = [0x00, 0x00];
let err = decoder
.parse_packet(&packet, packet.len() + 1, true)
.unwrap_err();
assert_eq!(err, PacketError::BadArgument);
}
#[test]
fn decode_native_rejects_misaligned_plc_frame_size() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let mut pcm = [0.0; 240];
let err = decoder
.decode_native_with(
None,
0,
&mut pcm,
100,
false,
false,
None,
false,
&mut |_st, _data, _len, _pcm, _frame_size, _decode_fec| {
unreachable!("decode_frame should not be called on invalid input")
},
)
.unwrap_err();
assert_eq!(err, OpusDecodeError::BadArgument);
}
#[test]
fn decode_frame_rejects_frame_sizes_shorter_than_2_5_ms() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let mut pcm = [0.0; 10];
let err = decoder
.decode_frame(None, 0, &mut pcm, 60, false)
.unwrap_err();
assert_eq!(err, OpusDecodeError::BufferTooSmall);
}
#[test]
fn hybrid_plc_frames_decode_without_unimplemented() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
decoder.prev_mode = MODE_HYBRID;
decoder.frame_size = 960;
let mut pcm = vec![0.0; 960];
let decoded = decoder
.decode_frame(None, 0, &mut pcm, 960, false)
.expect("hybrid PLC decode should succeed");
assert_eq!(decoded, 960);
assert_eq!(decoder.prev_mode, MODE_HYBRID);
assert_eq!(decoder.prev_redundancy, 0);
}
#[test]
fn decode_native_runs_plc_via_frame_decoder() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let mut pcm = [0.0; 480];
let decoded = opus_decode_native(
&mut decoder,
None,
0,
&mut pcm,
480,
false,
false,
None,
false,
)
.expect("PLC decode should succeed");
assert_eq!(decoded, 480);
assert_eq!(decoder.last_packet_duration, 480);
assert!(pcm.iter().all(|&sample| sample == 0.0));
}
#[test]
fn decode_native_runs_plc_path_when_packet_missing() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let mut pcm = [0.0; 480];
let mut calls = 0usize;
let decoded = decoder
.decode_native_with(
None,
0,
&mut pcm,
480,
false,
false,
None,
false,
&mut |st, data, len, out, requested, decode_fec| {
assert!(data.is_none());
assert_eq!(len, 0);
assert_eq!(requested, 480);
assert!(!decode_fec);
calls += 1;
let channels = st.channels as usize;
for sample in out.iter_mut().take(requested * channels) {
*sample = 1.0;
}
Ok(requested)
},
)
.expect("PLC decode should succeed");
assert_eq!(decoded, 480);
assert_eq!(calls, 1);
assert_eq!(decoder.last_packet_duration, 480);
assert!(pcm[..480].iter().all(|&sample| (sample - 1.0).abs() < 1e-6));
}
#[test]
fn decode_native_handles_partial_fec_and_updates_state() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let packet = simple_packet(0x00, 2);
let mut pcm = [0.0; 960];
let mut calls = Vec::new();
let decoded = decoder
.decode_native_with(
Some(&packet),
packet.len(),
&mut pcm,
960,
true,
false,
None,
false,
&mut |st, data, len, out, requested, decode_fec| {
calls.push((data.is_some(), len, requested, decode_fec));
let channels = st.channels as usize;
for sample in out.iter_mut().take(requested * channels) {
*sample = if data.is_none() { -1.0 } else { 2.0 };
}
Ok(requested)
},
)
.expect("FEC decode should succeed");
assert_eq!(decoded, 960);
assert_eq!(decoder.mode, MODE_SILK_ONLY);
assert_eq!(decoder.bandwidth, Bandwidth::Narrow.to_opus_int());
assert_eq!(decoder.frame_size, 480);
assert_eq!(decoder.stream_channels, 1);
assert!(pcm[..480].iter().all(|&sample| (sample + 1.0).abs() < 1e-6));
assert!(
pcm[480..960]
.iter()
.all(|&sample| (sample - 2.0).abs() < 1e-6)
);
assert_eq!(
calls,
vec![(false, 0, 480, false), (true, packet.len() - 1, 480, true)]
);
}
#[test]
fn decode_native_fec_falls_back_to_plc_when_celt_only() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
decoder.mode = MODE_CELT_ONLY;
let packet = simple_packet(0x80, 2);
let mut pcm = [0.0; 240];
let mut calls = 0usize;
let decoded = decoder
.decode_native_with(
Some(&packet),
packet.len(),
&mut pcm,
240,
true,
false,
None,
false,
&mut |st, data, _len, out, requested, decode_fec| {
assert!(data.is_none());
assert!(!decode_fec);
calls += 1;
let channels = st.channels as usize;
for sample in out.iter_mut().take(requested * channels) {
*sample = 3.0;
}
Ok(requested)
},
)
.expect("PLC fallback should succeed");
assert_eq!(decoded, 240);
assert_eq!(calls, 1);
assert_eq!(decoder.mode, MODE_CELT_ONLY);
assert!(pcm[..240].iter().all(|&sample| (sample - 3.0).abs() < 1e-6));
}
#[test]
fn decode_native_restores_last_duration_when_plc_fails_during_fec() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
decoder.last_packet_duration = 320;
let packet = simple_packet(0x00, 2);
let mut pcm = [0.0; 960];
let err = decoder
.decode_native_with(
Some(&packet),
packet.len(),
&mut pcm,
960,
true,
false,
None,
false,
&mut |_st, data, _len, _out, _requested, _decode_fec| {
if data.is_none() {
Err(OpusDecodeError::InvalidPacket)
} else {
Ok(0)
}
},
)
.unwrap_err();
assert_eq!(err, OpusDecodeError::InvalidPacket);
assert_eq!(decoder.last_packet_duration, 320);
}
#[test]
fn opus_decode_rejects_zero_frame_size() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let mut pcm = [0i16; 1];
let err = opus_decode(&mut decoder, None, 0, &mut pcm, 0, false).unwrap_err();
assert_eq!(err, OpusDecodeError::BadArgument);
}
#[test]
fn opus_decode_runs_plc_and_converts_to_int16() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let mut pcm = vec![0i16; 480];
let decoded = opus_decode(&mut decoder, None, 0, &mut pcm, 480, false)
.expect("PLC decode should succeed");
assert_eq!(decoded, 480);
assert!(pcm.iter().all(|&sample| sample == 0));
}
#[test]
fn opus_decode24_runs_plc_and_converts_to_int24() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let mut pcm = vec![0i32; 480];
let decoded = opus_decode24(&mut decoder, None, 0, &mut pcm, 480, false)
.expect("PLC decode should succeed");
assert_eq!(decoded, 480);
assert!(pcm.iter().all(|&sample| sample == 0));
}
#[test]
fn opus_decode_float_forwards_to_native_plc() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let mut pcm: Vec<OpusRes> = vec![1.0; 480];
let decoded = opus_decode_float(&mut decoder, None, 0, &mut pcm, 480, false)
.expect("PLC decode should succeed");
assert_eq!(decoded, 480);
assert!(pcm.iter().all(|&sample| sample == 0.0));
}
#[test]
fn opus_decode_reports_buffer_too_small() {
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder should initialise");
let mut pcm = [0i16; 100];
let err = opus_decode(&mut decoder, None, 0, &mut pcm, 480, false).unwrap_err();
assert_eq!(err, OpusDecodeError::BufferTooSmall);
}
#[test]
fn decode_fuzz_crash_input_does_not_panic() {
const SETUP_BYTE_COUNT: usize = 8;
const MAX_FRAME_SAMP: usize = 5760;
const MAX_PACKET: usize = 1500;
const FUZZ_CRASH_INPUT: [u8; 23] = [
0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x00, 0xb8, 0x7c, 0x35, 0x21, 0x75, 0xe5,
0x67, 0xd5, 0x1c, 0xac, 0xa2, 0x54, 0xfa, 0xff, 0xbf,
];
let data = &FUZZ_CRASH_INPUT[..];
assert!(data.len() > SETUP_BYTE_COUNT);
let toc = &data[SETUP_BYTE_COUNT..];
let bandwidth = opus_packet_get_bandwidth(toc).expect("bandwidth");
let channels = opus_packet_get_nb_channels(toc).expect("channels");
let sample_rate = bandwidth.sample_rate() as i32;
let mut decoder =
opus_decoder_create(sample_rate, channels as i32).expect("decoder should initialise");
let len = read_be_u32(data).expect("packet length") as usize;
assert!(len <= MAX_PACKET);
let packet_offset = SETUP_BYTE_COUNT;
let end = packet_offset + len;
assert!(end <= data.len());
assert!(len > 0);
let packet = &data[packet_offset..end];
let mut pcm = vec![0i16; MAX_FRAME_SAMP.saturating_mul(channels)];
let _ = opus_decode(
&mut decoder,
Some(packet),
len,
&mut pcm,
MAX_FRAME_SAMP,
false,
);
}
}