#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(test), warn(clippy::undocumented_unsafe_blocks))]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::needless_range_loop)]
#![cfg_attr(all(test, not(feature = "std")), allow(unused_imports))]
#![cfg_attr(not(feature = "__internals"), allow(dead_code, unused_imports))]
#[cfg(all(target_arch = "aarch64", not(target_feature = "neon")))]
compile_error!(
"opus1 needs NEON (Advanced SIMD) on aarch64; targets without it, such as \
aarch64-unknown-none-softfloat, are not supported"
);
#[cfg(all(test, not(feature = "std")))]
#[macro_use]
extern crate std;
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(all(feature = "heap", not(feature = "std")))]
use alloc::boxed::Box;
mod compat;
mod error;
pub use error::{Error, ErrorKind};
mod fixedvec;
mod in_place;
mod mathops;
#[cfg(all(feature = "std", feature = "__internals"))]
mod eval;
mod analysis;
mod bands;
mod celt;
mod celt_lpc;
mod deep_plc;
#[cfg(feature = "dnn")]
mod dnn;
#[cfg(feature = "deep-plc")]
pub use dnn::{DnnBlob, DnnModels};
#[cfg(feature = "dred")]
pub use dnn::dred::decoder::{DredParse, DredProcessing, OpusDred, OpusDredDecoder};
mod hp_cutoff;
#[cfg(feature = "__internals")]
#[doc(hidden)]
pub mod internals;
mod kiss_fft;
mod mdct;
mod modes;
mod osce_args;
#[cfg(feature = "alloc")]
pub mod multistream;
pub mod extensions;
pub mod packet;
mod pitch;
mod pvq;
mod quant_bands;
mod range_coder;
mod rate;
mod silk;
mod simd_mem;
#[cfg(all(doctest, feature = "std"))]
mod doc_examples {
#[doc = include_str!("../README.md")]
struct Readme;
#[doc = include_str!("../docs/guide/01-what-is-opus.md")]
struct WhatIsOpus;
#[doc = include_str!("../docs/guide/02-packets.md")]
struct Packets;
#[doc = include_str!("../docs/guide/03-encoder.md")]
struct Encoder;
#[doc = include_str!("../docs/guide/04-decoder.md")]
struct Decoder;
#[doc = include_str!("../docs/guide/05-multichannel.md")]
struct Multichannel;
#[doc = include_str!("../docs/guide/06-using-the-library.md")]
struct UsingTheLibrary;
#[doc = include_str!("../docs/guide/07-architecture.md")]
struct Architecture;
#[doc = include_str!("../docs/guide/08-performance.md")]
struct Performance;
#[doc = include_str!("../docs/guide/09-testing.md")]
struct Testing;
#[doc = include_str!("../docs/guide/10-differences-from-libopus.md")]
struct DifferencesFromLibopus;
}
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[cfg(not(feature = "std"))]
use crate::compat::Math;
use crate::fixedvec::FixedVec;
use crate::in_place::{HeapMemory, write_fields};
use core::mem::MaybeUninit;
use core::num::NonZeroU32;
use analysis::{AnalysisGate, AnalysisInfo, AnalysisPcm, PcmRef, SecondChannel, is_digital_silence};
use celt::{CeltDecoder, CeltEncoder};
use hp_cutoff::{dc_reject_float, hp_cutoff, hp_cutoff_float};
use mathops::mac;
use range_coder::RangeCoder;
use silk::define::{TYPE_NO_VOICE_ACTIVITY, VAD_NO_ACTIVITY, VAD_NO_DECISION};
use silk::enc_api::{SilkControl, SilkEncoder, SilkInput, SilkPrefill};
use silk::lin2log::silk_lin2log;
use silk::log2lin::silk_log2lin;
use silk::macros::*;
const OPUS_MAX_CHANNELS: usize = 2;
const OPUS_MAX_FRAME: usize = packet::MAX_PACKET_SAMPLES;
const OPUS_MAX_SUBFRAME: usize = 2880;
const OPUS_HP_MEM: usize = OPUS_MAX_CHANNELS * 2;
const OPUS_PCM_I16: usize = 960 * OPUS_MAX_CHANNELS;
const OPUS_PCM_PACKET: usize = OPUS_MAX_FRAME * OPUS_MAX_CHANNELS;
const OPUS_PCM_TAIL: usize = 240 * OPUS_MAX_CHANNELS;
const CELT_PREFILL: usize = 120 * OPUS_MAX_CHANNELS;
pub(crate) const OPUS_MAX_PACKET_BYTES: usize = 1 + packet::MAX_FRAME_BYTES;
#[cfg(feature = "dred")]
const MULTIFRAME_BYTES: usize = 6 * OPUS_MAX_PACKET_BYTES + dnn::dred::DRED_MAX_DATA_SIZE + 6;
#[cfg(not(feature = "dred"))]
const MULTIFRAME_BYTES: usize = 6 * OPUS_MAX_PACKET_BYTES;
const OPUS_DELAY_BUF: usize = 480 * OPUS_MAX_CHANNELS;
const OPUS_PCM_BUF: usize = (192 + OPUS_MAX_SUBFRAME) * OPUS_MAX_CHANNELS;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum SampleRate {
Hz8000 = 8000,
Hz12000 = 12000,
Hz16000 = 16000,
Hz24000 = 24000,
Hz48000 = 48000,
}
impl SampleRate {
pub const ALL: &'static [SampleRate] = &[
SampleRate::Hz8000,
SampleRate::Hz12000,
SampleRate::Hz16000,
SampleRate::Hz24000,
SampleRate::Hz48000,
];
pub const fn hz(self) -> u32 {
self as u32
}
pub const fn max_frame_size(self) -> usize {
self as usize / 25 * 3
}
pub(crate) const fn from_valid_hz(hz: i32) -> SampleRate {
match hz {
8000 => SampleRate::Hz8000,
12000 => SampleRate::Hz12000,
16000 => SampleRate::Hz16000,
24000 => SampleRate::Hz24000,
_ => {
debug_assert!(hz == 48000);
SampleRate::Hz48000
}
}
}
}
impl TryFrom<u32> for SampleRate {
type Error = crate::Error;
fn try_from(value: u32) -> Result<Self, Self::Error> {
SampleRate::ALL
.iter()
.copied()
.find(|rate| rate.hz() == value)
.ok_or(Error::INVALID_SAMPLING_RATE)
}
}
impl TryFrom<i32> for SampleRate {
type Error = crate::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
u32::try_from(value)
.map_err(|_| Error::INVALID_SAMPLING_RATE)
.and_then(SampleRate::try_from)
}
}
impl From<SampleRate> for u32 {
fn from(rate: SampleRate) -> u32 {
rate.hz()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Channels {
Mono = 1,
Stereo = 2,
}
impl Channels {
pub const ALL: [Channels; 2] = [Channels::Mono, Channels::Stereo];
pub const fn count(self) -> usize {
self as usize
}
pub(crate) const fn from_valid_count(count: usize) -> Channels {
if count == 1 {
Channels::Mono
} else {
debug_assert!(count == 2);
Channels::Stereo
}
}
}
impl TryFrom<usize> for Channels {
type Error = crate::Error;
fn try_from(value: usize) -> Result<Self, Self::Error> {
match value {
1 => Ok(Channels::Mono),
2 => Ok(Channels::Stereo),
_ => Err(Error::INVALID_CHANNELS),
}
}
}
impl From<Channels> for usize {
fn from(channels: Channels) -> usize {
channels.count()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Application {
Voip = 2048,
Audio = 2049,
RestrictedLowDelay = 2051,
RestrictedSilk = 2052,
RestrictedCelt = 2053,
}
impl Application {
pub const fn to_raw(self) -> i32 {
self as i32
}
const fn only_at_creation(self) -> bool {
matches!(self, Application::RestrictedSilk | Application::RestrictedCelt)
}
const fn without_delay_buffer(self) -> bool {
matches!(
self,
Application::RestrictedLowDelay | Application::RestrictedSilk | Application::RestrictedCelt
)
}
}
impl TryFrom<i32> for Application {
type Error = crate::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
2048 => Ok(Application::Voip),
2049 => Ok(Application::Audio),
2051 => Ok(Application::RestrictedLowDelay),
2052 => Ok(Application::RestrictedSilk),
2053 => Ok(Application::RestrictedCelt),
_ => Err(Error::bad_arg("invalid application")),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Bandwidth {
Narrowband = 1101,
Mediumband = 1102,
Wideband = 1103,
Superwideband = 1104,
Fullband = 1105,
}
impl TryFrom<i32> for Bandwidth {
type Error = crate::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
1101 => Ok(Bandwidth::Narrowband),
1102 => Ok(Bandwidth::Mediumband),
1103 => Ok(Bandwidth::Wideband),
1104 => Ok(Bandwidth::Superwideband),
1105 => Ok(Bandwidth::Fullband),
_ => Err(Error::bad_arg("invalid bandwidth")),
}
}
}
impl Bandwidth {
pub const fn to_raw(self) -> i32 {
self as i32
}
fn max_by_index(self, other: Bandwidth) -> Bandwidth {
if bandwidth_fec_index(other) > bandwidth_fec_index(self) { other } else { self }
}
#[inline]
pub(crate) fn celt_end_band(self) -> usize {
match self {
Bandwidth::Narrowband => 13,
Bandwidth::Mediumband | Bandwidth::Wideband => 17,
Bandwidth::Superwideband => 19,
Bandwidth::Fullband => 21,
}
}
#[inline]
pub(crate) fn silk_khz(self) -> i32 {
match self {
Bandwidth::Narrowband => 8,
Bandwidth::Mediumband => 12,
_ => 16,
}
}
}
const HYBRID_START_BAND: usize = 17;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OpusMode {
SilkOnly,
Hybrid,
CeltOnly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ForcedMode {
SilkOnly = 1000,
Hybrid = 1001,
CeltOnly = 1002,
}
impl ForcedMode {
pub const fn to_raw(self) -> i32 {
self as i32
}
}
impl TryFrom<i32> for ForcedMode {
type Error = crate::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
1000 => Ok(ForcedMode::SilkOnly),
1001 => Ok(ForcedMode::Hybrid),
1002 => Ok(ForcedMode::CeltOnly),
_ => Err(Error::bad_arg("invalid forced mode")),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Bitrate {
#[default]
Auto,
Max,
BitsPerSecond(NonZeroU32),
}
impl Bitrate {
pub(crate) fn within_limits(bps: i32) -> Bitrate {
let bps = u32::try_from(bps).unwrap_or(0);
Bitrate::BitsPerSecond(NonZeroU32::new(bps).unwrap_or(NonZeroU32::MIN))
}
pub(crate) fn bps_to_i32(bps: NonZeroU32) -> i32 {
i32::try_from(bps.get()).unwrap_or(i32::MAX)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum FrameDuration {
#[default]
Argument = 5000,
Ms2_5 = 5001,
Ms5 = 5002,
Ms10 = 5003,
Ms20 = 5004,
Ms40 = 5005,
Ms60 = 5006,
Ms80 = 5007,
Ms100 = 5008,
Ms120 = 5009,
}
impl FrameDuration {
pub const ALL: [FrameDuration; 10] = [
FrameDuration::Argument,
FrameDuration::Ms2_5,
FrameDuration::Ms5,
FrameDuration::Ms10,
FrameDuration::Ms20,
FrameDuration::Ms40,
FrameDuration::Ms60,
FrameDuration::Ms80,
FrameDuration::Ms100,
FrameDuration::Ms120,
];
pub const fn to_raw(self) -> i32 {
self as i32
}
pub fn samples(self, sampling_rate: SampleRate) -> Option<usize> {
let fs = sampling_rate.hz() as usize;
Some(match self {
FrameDuration::Argument => return None,
FrameDuration::Ms2_5 => fs / 400,
FrameDuration::Ms5 => fs / 400 * 2,
FrameDuration::Ms10 => fs / 400 * 4,
FrameDuration::Ms20 => fs / 400 * 8,
FrameDuration::Ms40 => fs / 400 * 16,
FrameDuration::Ms60 => 3 * fs / 50,
FrameDuration::Ms80 => 4 * fs / 50,
FrameDuration::Ms100 => 5 * fs / 50,
FrameDuration::Ms120 => 6 * fs / 50,
})
}
}
impl TryFrom<i32> for FrameDuration {
type Error = crate::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
FrameDuration::ALL
.into_iter()
.find(|d| d.to_raw() == value)
.ok_or(Error::bad_arg("invalid frame duration"))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum Signal {
#[default]
Auto = -1000,
Voice = 3001,
Music = 3002,
}
impl Signal {
pub const fn to_raw(self) -> i32 {
self as i32
}
}
impl TryFrom<i32> for Signal {
type Error = crate::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
OPUS_AUTO => Ok(Signal::Auto),
3001 => Ok(Signal::Voice),
3002 => Ok(Signal::Music),
_ => Err(Error::bad_arg("invalid signal type")),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum InbandFec {
#[default]
Off = 0,
On = 1,
OnUnlessMusic = 2,
}
impl InbandFec {
pub const fn to_raw(self) -> i32 {
self as i32
}
}
impl TryFrom<i32> for InbandFec {
type Error = crate::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0 => Ok(InbandFec::Off),
1 => Ok(InbandFec::On),
2 => Ok(InbandFec::OnUnlessMusic),
_ => Err(Error::bad_arg("invalid in-band FEC setting")),
}
}
}
#[derive(Debug, Clone, Copy)]
struct FrameDecisions {
mode: OpusMode,
bandwidth: Bandwidth,
lbrr_coded: i32,
redundancy: bool,
celt_to_silk: bool,
to_celt: bool,
prefill: bool,
equiv_rate: i32,
}
#[derive(Debug, Clone, Copy, Default)]
struct FrameAnalysis {
info: AnalysisInfo,
is_silence: bool,
}
const DTX_ACTIVITY_THRESHOLD: f32 = 0.1;
const PSEUDO_SNR_THRESHOLD: f32 = 316.23;
fn frame_energy(pcm: &[f32]) -> f32 {
pitch::inner_prod(pcm, pcm, pcm.len()) / pcm.len() as f32
}
fn decide_dtx_mode(activity: i32, nb_no_activity_ms_q1: &mut i32, frame_ms_q1: i32) -> bool {
use silk::define::{MAX_CONSECUTIVE_DTX, NB_SPEECH_FRAMES_BEFORE_DTX};
if activity != 0 {
*nb_no_activity_ms_q1 = 0;
return false;
}
*nb_no_activity_ms_q1 += frame_ms_q1;
if *nb_no_activity_ms_q1 > NB_SPEECH_FRAMES_BEFORE_DTX * 20 * 2 {
if *nb_no_activity_ms_q1 <= (NB_SPEECH_FRAMES_BEFORE_DTX + MAX_CONSECUTIVE_DTX) * 20 * 2 {
return true;
}
*nb_no_activity_ms_q1 = NB_SPEECH_FRAMES_BEFORE_DTX * 20 * 2;
}
false
}
fn compute_redundancy_bytes(
max_data_bytes: usize,
bitrate_bps: i32,
frame_rate: i32,
channels: usize,
) -> usize {
let channels = channels as i32;
let base_bits = 40 * channels + 20;
let redundancy_rate = bitrate_bps + base_bits * (200 - frame_rate);
let redundancy_rate = 3 * redundancy_rate / 2;
let mut redundancy_bytes = redundancy_rate / 1600;
let available_bits = max_data_bytes as i32 * 8 - 2 * base_bits;
let cap = (available_bits * 240 / (240 + 48000 / frame_rate) + base_bits) / 8;
redundancy_bytes = redundancy_bytes.min(cap);
if redundancy_bytes > 4 + 8 * channels {
redundancy_bytes.min(257) as usize
} else {
0
}
}
fn stereo_fade(pcm: &mut [f32], g1: f32, g2: f32, frame_size: usize, fs: i32) {
let window = modes::default_mode().window;
let inc = celt::resampling_factor(fs).unwrap_or(1);
let overlap = (window.len() / inc).min(frame_size);
let (g1, g2) = (1.0 - g1, 1.0 - g2);
let pcm = &mut pcm[..2 * frame_size];
for i in 0..frame_size {
let g = if i < overlap {
let w = window[i * inc] * window[i * inc];
mac((1.0 - w) * g1, w, g2)
} else {
g2
};
let diff = g * (0.5 * (pcm[2 * i] - pcm[2 * i + 1]));
pcm[2 * i] -= diff;
pcm[2 * i + 1] += diff;
}
}
#[inline]
fn input_is_sane(x: &[f32]) -> bool {
let sum = pitch::inner_prod(x, x, x.len());
sum < 1e9
}
fn encode_celt_redundancy(
celt: &mut CeltEncoder,
pcm: &[f32],
n: usize,
out: &mut [u8],
) -> Result<u32, Error> {
let mut rc = RangeCoder::new_encoder(out.len() as u32);
let _ = celt.set_start_band(0);
celt.set_vbr(false);
let _ = celt.set_bitrate(OPUS_BITRATE_MAX);
celt.encode(pcm, n, &mut rc);
rc.done();
if rc.error != 0 || rc.buf.len() < out.len() {
return Err(Error::internal("CELT redundancy frame does not fit"));
}
out.copy_from_slice(&rc.buf[..out.len()]);
Ok(rc.rng)
}
pub struct OpusEncoder {
#[cfg(not(feature = "heap"))]
celt_enc: CeltEncoder,
#[cfg(feature = "heap")]
celt_enc: Box<CeltEncoder>,
silk: SilkEncoder,
application: Application,
sampling_rate: i32,
channels: usize,
bandwidth: Bandwidth,
auto_bandwidth: Bandwidth,
user_bandwidth: Option<Bandwidth>,
max_bandwidth: Bandwidth,
user_forced_mode: Option<OpusMode>,
lfe: bool,
energy_mask: Option<[f32; 21 * OPUS_MAX_CHANNELS]>,
range_final: u32,
first: bool,
silk_allow_bandwidth_switch: bool,
silk_in_wb_mode_without_variable_lp: bool,
silk_opus_can_switch: bool,
silk_lbrr_coded: i32,
nonfinal_frame: bool,
stream_channels: usize,
force_channels: Option<usize>,
prev_channels: usize,
silk_to_mono: bool,
silk_bw_switch: bool,
hybrid_stereo_width_q14: i32,
silk_stereo_width_q14: i32,
width_mem: StereoWidthState,
stereo_width: f32,
user_bitrate: Bitrate,
packet_bitrate_bps: i32,
complexity: i32,
use_cbr: bool,
vbr_constraint: bool,
fec_config: InbandFec,
packet_loss_perc: i32,
prev_frame_size: usize,
prediction_disabled: bool,
variable_duration: FrameDuration,
#[cfg(not(feature = "heap"))]
celt_prefill: [f32; CELT_PREFILL],
#[cfg(feature = "heap")]
celt_prefill: Box<[f32; CELT_PREFILL]>,
mode: OpusMode,
prev_enc_mode: Option<OpusMode>,
variable_hp_smth2_q15: i32,
hp_mem: FixedVec<i32, OPUS_HP_MEM>,
integer_hp_at_48k: bool,
fixed_point_res_nrg: bool,
#[cfg(not(feature = "heap"))]
buf_filtered: FixedVec<i16, OPUS_MAX_FRAME>,
#[cfg(feature = "heap")]
buf_filtered: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
#[cfg(not(feature = "heap"))]
delay_buffer: FixedVec<f32, OPUS_DELAY_BUF>,
#[cfg(feature = "heap")]
delay_buffer: Box<FixedVec<f32, OPUS_DELAY_BUF>>,
#[cfg(not(feature = "heap"))]
buf_celt_pcm: FixedVec<f32, OPUS_PCM_BUF>,
#[cfg(feature = "heap")]
buf_celt_pcm: Box<FixedVec<f32, OPUS_PCM_BUF>>,
encoder_buffer: usize,
delay_compensation: usize,
hp_mem_float: [f32; 4],
prev_hb_gain: f32,
#[cfg(all(feature = "analysis", feature = "heap"))]
analysis: Box<analysis::TonalityAnalysisState>,
#[cfg(all(feature = "analysis", not(feature = "heap")))]
analysis: analysis::TonalityAnalysisState,
analysis_gate: AnalysisGate,
float_api: bool,
last_analysis: AnalysisInfo,
voice_ratio: i32,
signal: Signal,
lsb_depth: i32,
peak_signal_energy: f32,
use_dtx: bool,
silk_use_dtx: bool,
silk_channels_internal: usize,
nb_no_activity_ms_q1: i32,
rc: RangeCoder,
#[cfg(all(feature = "dred", feature = "heap"))]
dred: Box<dnn::dred::encoder::DredEncoder>,
#[cfg(all(feature = "dred", not(feature = "heap")))]
dred: dnn::dred::encoder::DredEncoder,
}
fn gain_fade(pcm: &mut [f32], g1: f32, g2: f32, frame_size: usize, channels: usize, fs: i32) {
let window = modes::default_mode().window;
let inc = celt::resampling_factor(fs).unwrap_or(1);
let overlap = (window.len() / inc).min(frame_size);
for i in 0..overlap {
let w = window[i * inc] * window[i * inc];
let g = mac((1.0 - w) * g1, w, g2);
for c in 0..channels {
pcm[i * channels + c] *= g;
}
}
for v in &mut pcm[overlap * channels..frame_size * channels] {
*v *= g2;
}
}
fn compute_equiv_rate(
bitrate: i32,
channels: usize,
frame_rate: i32,
vbr: bool,
mode: Option<OpusMode>,
complexity: i32,
loss: i32,
) -> i32 {
let mut equiv = bitrate;
if frame_rate > 50 {
equiv -= (40 * channels as i32 + 20) * (frame_rate - 50);
}
if !vbr {
equiv -= equiv / 12;
}
equiv = equiv * (90 + complexity) / 100;
match mode {
Some(OpusMode::SilkOnly) | Some(OpusMode::Hybrid) => {
if complexity < 2 {
equiv = equiv * 4 / 5;
}
equiv -= equiv * loss / (6 * loss + 10);
}
Some(OpusMode::CeltOnly) => {
if complexity < 5 {
equiv = equiv * 9 / 10;
}
}
None => equiv -= equiv * loss / (12 * loss + 20),
}
equiv
}
const VOICE_BANDWIDTH_THRESHOLDS: [i32; 8] = [
9000, 700, 9000, 700, 13500, 1000, 14000, 2000, ];
const MUSIC_BANDWIDTH_THRESHOLDS: [i32; 8] = [
9000, 700, 9000, 700, 11000, 1000, 12000, 2000, ];
const FEC_THRESHOLDS: [i32; 10] = [
12000, 1000, 14000, 1000, 16000, 1000, 20000, 1000, 22000, 1000, ];
fn bandwidth_fec_index(bw: Bandwidth) -> usize {
match bw {
Bandwidth::Narrowband => 0,
Bandwidth::Mediumband => 1,
Bandwidth::Wideband => 2,
Bandwidth::Superwideband => 3,
Bandwidth::Fullband => 4,
}
}
fn bandwidth_from_fec_index(idx: usize) -> Bandwidth {
match idx {
0 => Bandwidth::Narrowband,
1 => Bandwidth::Mediumband,
2 => Bandwidth::Wideband,
3 => Bandwidth::Superwideband,
_ => Bandwidth::Fullband,
}
}
fn decide_fec(
use_in_band_fec: i32,
packet_loss_perc: i32,
last_fec: i32,
mode: OpusMode,
bandwidth: &mut Bandwidth,
rate: i32,
) -> i32 {
if use_in_band_fec == 0 || packet_loss_perc == 0 || mode == OpusMode::CeltOnly {
return 0;
}
let orig_bandwidth = *bandwidth;
loop {
let idx = bandwidth_fec_index(*bandwidth);
let mut lbrr_rate_thres_bps = FEC_THRESHOLDS[2 * idx];
let hysteresis = FEC_THRESHOLDS[2 * idx + 1];
if last_fec == 1 {
lbrr_rate_thres_bps -= hysteresis;
}
if last_fec == 0 {
lbrr_rate_thres_bps += hysteresis;
}
lbrr_rate_thres_bps = silk_smulwb(
lbrr_rate_thres_bps * (125 - packet_loss_perc.min(25)),
655,
);
if rate > lbrr_rate_thres_bps {
return 1;
} else if packet_loss_perc <= 5 {
return 0;
} else if bandwidth_fec_index(*bandwidth) > 0 {
let lower = bandwidth_fec_index(*bandwidth) - 1;
*bandwidth = bandwidth_from_fec_index(lower);
} else {
break;
}
}
*bandwidth = orig_bandwidth;
0
}
#[derive(Clone, Copy, Default)]
struct StereoWidthState {
xx: f32,
xy: f32,
yy: f32,
smoothed_width: f32,
max_follower: f32,
}
fn compute_stereo_width(
pcm: &[f32],
frame_size: usize,
fs: i32,
mem: &mut StereoWidthState,
) -> f32 {
let frame_rate = fs / frame_size as i32;
let short_alpha = 25.0 / frame_rate.max(50) as f32;
let (mut xx, mut xy, mut yy) = (0.0f32, 0.0f32, 0.0f32);
let (min, max) = (|a: f32, b: f32| if a < b { a } else { b }, |a: f32, b: f32| if a > b { a } else { b });
let mut i = 0;
while i + 3 < frame_size {
let (x, y) = (pcm[2 * i], pcm[2 * i + 1]);
let (mut pxx, mut pxy, mut pyy) = (x * x, x * y, y * y);
for k in 1..4 {
let (x, y) = (pcm[2 * (i + k)], pcm[2 * (i + k) + 1]);
pxx = mac(pxx, x, x);
pxy = mac(pxy, x, y);
pyy = mac(pyy, y, y);
}
xx += pxx;
xy += pxy;
yy += pyy;
i += 4;
}
#[allow(clippy::neg_cmp_op_on_partial_ord)]
if !(xx < 1e9) || xx.is_nan() || !(yy < 1e9) || yy.is_nan() {
xx = 0.0;
xy = 0.0;
yy = 0.0;
}
mem.xx = mac(mem.xx, short_alpha, xx - mem.xx);
mem.xy = mac(short_alpha * xy, 1.0 - short_alpha, mem.xy);
mem.yy = mac(mem.yy, short_alpha, yy - mem.yy);
mem.xx = max(0.0, mem.xx);
mem.xy = max(0.0, mem.xy);
mem.yy = max(0.0, mem.yy);
if max(mem.xx, mem.yy) > 8e-4 {
let sqrt_xx = mem.xx.sqrt();
let sqrt_yy = mem.yy.sqrt();
let qrrt_xx = sqrt_xx.sqrt();
let qrrt_yy = sqrt_yy.sqrt();
mem.xy = min(mem.xy, sqrt_xx * sqrt_yy);
let corr = mem.xy / mac(1e-15, sqrt_xx, sqrt_yy);
let ldiff = (qrrt_xx - qrrt_yy).abs() / (1e-15 + qrrt_xx + qrrt_yy);
let width = min(1.0, mac(1.0, -corr, corr).sqrt()) * ldiff;
mem.smoothed_width += (width - mem.smoothed_width) / frame_rate as f32;
mem.max_follower = max(mem.max_follower - 0.02 / frame_rate as f32, mem.smoothed_width);
}
min(1.0, 20.0 * mem.max_follower)
}
fn compute_mode_threshold(
application: Application,
stereo_width: f32,
prev_was_celt: bool,
has_prev_mode: bool,
voice_est: i32,
) -> i32 {
let mode_voice = ((1.0 - stereo_width) * 64000.0 + stereo_width * 44000.0) as i32;
let mode_music = ((1.0 - stereo_width) * 10000.0 + stereo_width * 10000.0) as i32;
let diff = mode_voice - mode_music;
let offset = (voice_est * voice_est * diff) >> 14;
let mut threshold = mode_music + offset;
if application == Application::Voip {
threshold += 8000;
}
if has_prev_mode {
if prev_was_celt {
threshold -= 4000;
} else {
threshold += 4000;
}
}
if application == Application::RestrictedLowDelay {
threshold = 0;
}
threshold
}
pub(crate) const OPUS_AUTO: i32 = -1000;
pub(crate) const OPUS_BITRATE_MAX: i32 = -1;
#[inline]
fn frame_rate_x6(fs: i32, frame_size: usize) -> i64 {
6 * fs as i64 / frame_size.min(i32::MAX as usize) as i64
}
#[inline]
pub(crate) fn bitrate_to_bits(bitrate: i32, fs: i32, frame_size: usize) -> i32 {
let bits = bitrate as i64 * 6 / frame_rate_x6(fs, frame_size);
bits.clamp(i32::MIN as i64, i32::MAX as i64) as i32
}
#[inline]
pub(crate) fn bits_to_bitrate(bits: i32, fs: i32, frame_size: usize) -> i32 {
let bitrate = (bits as i64).saturating_mul(frame_rate_x6(fs, frame_size)) / 6;
bitrate.clamp(i32::MIN as i64, i32::MAX as i64) as i32
}
fn compute_silk_rate_for_hybrid(
rate_bps: i32,
bandwidth: Bandwidth,
frame20ms: bool,
vbr: bool,
fec: bool,
channels: usize,
) -> i32 {
const RATE_TABLE: [[i32; 5]; 7] = [
[0, 0, 0, 0, 0],
[12000, 10000, 10000, 11000, 11000],
[16000, 13500, 13500, 15000, 15000],
[20000, 16000, 16000, 18000, 18000],
[24000, 18000, 18000, 21000, 21000],
[32000, 22000, 22000, 28000, 28000],
[64000, 38000, 38000, 50000, 50000],
];
let channels = channels as i32;
let rate = rate_bps / channels;
let entry = 1 + frame20ms as usize + 2 * fec as usize;
let n = RATE_TABLE.len();
let mut i = 1;
while i < n && RATE_TABLE[i][0] <= rate {
i += 1;
}
let mut silk_rate = if i == n {
RATE_TABLE[n - 1][entry] + (rate - RATE_TABLE[n - 1][0]) / 2
} else {
let (lo, hi) = (RATE_TABLE[i - 1][entry], RATE_TABLE[i][entry]);
let (x0, x1) = (RATE_TABLE[i - 1][0], RATE_TABLE[i][0]);
(lo * (x1 - rate) + hi * (rate - x0)) / (x1 - x0)
};
if !vbr {
silk_rate += 100;
}
if bandwidth == Bandwidth::Superwideband {
silk_rate += 300;
}
silk_rate *= channels;
if channels == 2 && rate >= 12000 {
silk_rate -= 1000;
}
silk_rate
}
#[cfg(all(test, feature = "std"))]
mod silk_rate_tests {
use super::{Bandwidth, compute_silk_rate_for_hybrid};
fn plain(rate: i32, channels: usize) -> i32 {
compute_silk_rate_for_hybrid(rate, Bandwidth::Fullband, true, true, false, channels)
}
#[test]
fn test_reference_table_exact_entries() {
assert_eq!(plain(12000, 1), 10000);
assert_eq!(plain(16000, 1), 13500);
assert_eq!(plain(20000, 1), 16000);
assert_eq!(plain(24000, 1), 18000);
assert_eq!(plain(32000, 1), 22000);
assert_eq!(plain(64000, 1), 38000);
}
#[test]
fn test_interpolation_between_table_entries() {
assert_eq!(plain(18000, 1), 14750);
}
#[test]
fn test_above_table_max_gives_half_extra() {
assert_eq!(plain(72000, 1), 38000 + (72000 - 64000) / 2);
}
#[test]
fn test_stereo_allocates_per_channel() {
assert_eq!(plain(32000, 2), 2 * 13500 - 1000);
assert_eq!(plain(24000, 2), 2 * 10000 - 1000);
assert_eq!(plain(20000, 2), 2 * 8333);
assert_eq!(plain(64000, 2), 2 * 22000 - 1000);
}
#[test]
fn test_boosts_and_fec_column() {
let swb = Bandwidth::Superwideband;
let fb = Bandwidth::Fullband;
assert_eq!(compute_silk_rate_for_hybrid(24000, swb, true, true, false, 1), 18300);
assert_eq!(compute_silk_rate_for_hybrid(24000, fb, true, false, false, 1), 18100);
assert_eq!(compute_silk_rate_for_hybrid(24000, swb, true, false, false, 1), 18400);
assert_eq!(compute_silk_rate_for_hybrid(24000, fb, true, true, true, 1), 21000);
assert_eq!(compute_silk_rate_for_hybrid(32000, swb, true, true, true, 1), 28300);
assert_eq!(
compute_silk_rate_for_hybrid(32000, swb, true, true, false, 2),
2 * (13500 + 300) - 1000
);
}
}
#[cfg(feature = "heap")]
#[inline(always)]
fn state_mut<T: core::ops::DerefMut>(b: &mut T) -> &mut T::Target {
b
}
#[cfg(not(feature = "heap"))]
#[inline(always)]
fn state_mut<T: ?Sized>(b: &mut T) -> &mut T {
b
}
impl core::fmt::Debug for OpusEncoder {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut d = f.debug_struct("OpusEncoder");
d.field("sampling_rate", &self.sampling_rate())
.field("channels", &self.channels())
.field("application", &self.application)
.field("bitrate_setting", &self.user_bitrate)
.field("complexity", &self.complexity())
.field("vbr", &self.vbr())
.field("vbr_constraint", &self.vbr_constraint)
.field("bandwidth_setting", &self.user_bandwidth)
.field("max_bandwidth", &self.max_bandwidth)
.field("force_mode", &self.force_mode())
.field("force_channels", &self.force_channels())
.field("signal", &self.signal)
.field("inband_fec", &self.fec_config)
.field("packet_loss_perc", &self.packet_loss_perc())
.field("dtx", &self.use_dtx)
.field("lsb_depth", &self.lsb_depth())
.field("expert_frame_duration", &self.variable_duration);
#[cfg(feature = "dred")]
d.field("dred_duration", &self.dred_duration());
d.finish_non_exhaustive()
}
}
macro_rules! kept_on_reset {
($( $(#[$doc:meta])* $field:ident: $ty:ty, )*) => {
struct KeptOnReset {
$( $(#[$doc])* $field: $ty, )*
phase_inversion_disabled: bool,
}
impl KeptOnReset {
fn of(enc: &OpusEncoder) -> Self {
Self {
$( $field: enc.$field, )*
phase_inversion_disabled: enc.celt_enc.phase_inversion_disabled(),
}
}
fn restore(self, enc: &mut OpusEncoder) {
$( enc.$field = self.$field; )*
enc.celt_enc.set_phase_inversion_disabled(self.phase_inversion_disabled);
}
}
};
}
kept_on_reset! {
user_bitrate: Bitrate,
complexity: i32,
use_cbr: bool,
vbr_constraint: bool,
fec_config: InbandFec,
packet_loss_perc: i32,
user_forced_mode: Option<OpusMode>,
user_bandwidth: Option<Bandwidth>,
max_bandwidth: Bandwidth,
lfe: bool,
prediction_disabled: bool,
variable_duration: FrameDuration,
force_channels: Option<usize>,
silk_to_mono: bool,
silk_lbrr_coded: i32,
silk_allow_bandwidth_switch: bool,
silk_in_wb_mode_without_variable_lp: bool,
integer_hp_at_48k: bool,
fixed_point_res_nrg: bool,
signal: Signal,
voice_ratio: i32,
lsb_depth: i32,
analysis_gate: AnalysisGate,
use_dtx: bool,
silk_use_dtx: bool,
silk_channels_internal: usize,
}
macro_rules! with_deep_plc {
($self:ident, |$p:ident| $body:expr) => {{
#[cfg(feature = "deep-plc")]
let mut args = crate::deep_plc::DeepPlcArgs {
state: state_mut(&mut $self.deep_plc),
models: $self.dnn.deep_plc(),
arch: $self.dnn.arch(),
};
#[cfg(feature = "deep-plc")]
let $p = Some(&mut args);
#[cfg(not(feature = "deep-plc"))]
let $p: Option<&mut crate::deep_plc::DeepPlcArgs> = None;
$body
}};
}
impl OpusEncoder {
pub fn new(
sampling_rate: SampleRate,
channels: Channels,
application: Application,
) -> Result<Self, Error> {
let mut enc = MaybeUninit::<Self>::uninit();
Self::init(&mut enc, sampling_rate, channels, application)?;
Ok(unsafe { enc.assume_init() })
}
pub fn init(
slot: &mut MaybeUninit<Self>,
sampling_rate: SampleRate,
channels: Channels,
application: Application,
) -> Result<&mut Self, Error> {
unsafe {
Self::write_new(
slot.as_mut_ptr(),
sampling_rate.hz() as i32,
channels.count(),
application,
HeapMemory::Allocate,
);
Ok(slot.assume_init_mut())
}
}
unsafe fn write_new(
this: *mut Self,
sampling_rate: i32,
channels: usize,
application: Application,
memory: HeapMemory,
) {
let mode = modes::default_mode();
let (opus_mode, bw) = (OpusMode::Hybrid, Bandwidth::Fullband);
let low_delay = application.without_delay_buffer();
unsafe {
write_fields!(this, {
#[cfg(not(feature = "heap"))]
celt_enc = |p| CeltEncoder::write_new(p, mode, channels, sampling_rate),
#[cfg(feature = "heap")]
celt_enc = |p: *mut Box<CeltEncoder>| {
let celt = |q| CeltEncoder::write_new(q, mode, channels, sampling_rate);
in_place::write_box(p, memory, celt)
},
silk = |p| SilkEncoder::write_new(p, sampling_rate, channels, memory),
application: application,
sampling_rate: sampling_rate,
channels: channels,
bandwidth: bw,
auto_bandwidth: bw,
user_bandwidth: None,
max_bandwidth: Bandwidth::Fullband,
user_forced_mode: None,
lfe: false,
energy_mask: None,
range_final: 0,
first: true,
silk_allow_bandwidth_switch: false,
silk_in_wb_mode_without_variable_lp: false,
silk_opus_can_switch: false,
silk_lbrr_coded: 0,
nonfinal_frame: false,
stream_channels: channels,
force_channels: None,
prev_channels: 0,
silk_to_mono: false,
silk_bw_switch: false,
hybrid_stereo_width_q14: 1 << 14,
silk_stereo_width_q14: 1 << 14,
width_mem: StereoWidthState::default(),
stereo_width: 0.0,
user_bitrate: Bitrate::Auto,
packet_bitrate_bps: 3000 + sampling_rate * channels as i32,
complexity: 9,
use_cbr: false,
vbr_constraint: true,
fec_config: InbandFec::Off,
packet_loss_perc: 0,
prev_frame_size: 0,
prediction_disabled: false,
variable_duration: FrameDuration::Argument,
#[cfg(not(feature = "heap"))]
celt_prefill = |p| in_place::write_zero(p),
#[cfg(feature = "heap")]
celt_prefill = |p: *mut Box<[f32; CELT_PREFILL]>| {
in_place::write_box(p, memory, |q| in_place::write_zero(q))
},
mode: opus_mode,
prev_enc_mode: None,
variable_hp_smth2_q15: silk_lin2log(60) << 8,
hp_mem = |p| FixedVec::write_from_value(p, 0, channels * 2),
integer_hp_at_48k: false,
fixed_point_res_nrg: false,
#[cfg(not(feature = "heap"))]
buf_filtered = |p| FixedVec::write_new(p),
#[cfg(feature = "heap")]
buf_filtered = |p: *mut Box<FixedVec<i16, OPUS_MAX_FRAME>>| {
in_place::write_box(p, memory, |q| FixedVec::write_new(q))
},
#[cfg(not(feature = "heap"))]
delay_buffer = |p| FixedVec::write_from_value(p, 0.0, OPUS_DELAY_BUF),
#[cfg(feature = "heap")]
delay_buffer = |p: *mut Box<FixedVec<f32, OPUS_DELAY_BUF>>| {
in_place::write_box(p, memory, |q| FixedVec::write_from_value(q, 0.0, OPUS_DELAY_BUF))
},
#[cfg(not(feature = "heap"))]
buf_celt_pcm = |p| FixedVec::write_new(p),
#[cfg(feature = "heap")]
buf_celt_pcm = |p: *mut Box<FixedVec<f32, OPUS_PCM_BUF>>| {
in_place::write_box(p, memory, |q| FixedVec::write_new(q))
},
encoder_buffer: if low_delay { 0 } else { (sampling_rate / 100) as usize },
delay_compensation: if low_delay { 0 } else { (sampling_rate / 250) as usize },
hp_mem_float: [0.0; 4],
prev_hb_gain: 1.0,
#[cfg(all(feature = "analysis", not(feature = "heap")))]
analysis = |p| analysis::TonalityAnalysisState::write_new(p, sampling_rate),
#[cfg(all(feature = "analysis", feature = "heap"))]
analysis = |p: *mut Box<analysis::TonalityAnalysisState>| {
let state = |q| analysis::TonalityAnalysisState::write_new(q, sampling_rate);
in_place::write_box(p, memory, state)
},
analysis_gate: AnalysisGate::FloatBuild,
float_api: true,
last_analysis: AnalysisInfo::default(),
voice_ratio: -1,
signal: Signal::Auto,
lsb_depth: 24,
peak_signal_energy: 0.0,
use_dtx: false,
silk_use_dtx: false,
silk_channels_internal: channels,
nb_no_activity_ms_q1: 0,
rc: RangeCoder::new_encoder(1),
#[cfg(all(feature = "dred", not(feature = "heap")))]
dred = |p| dnn::dred::encoder::DredEncoder::write_new(p, sampling_rate, channels),
#[cfg(all(feature = "dred", feature = "heap"))]
dred = |p: *mut Box<dnn::dred::encoder::DredEncoder>| {
let dred = |q| dnn::dred::encoder::DredEncoder::write_new(q, sampling_rate, channels);
in_place::write_box(p, memory, dred)
},
});
}
}
fn decide_mode_and_bandwidth(
&mut self,
frame_size: usize,
frame_rate: i32,
n_bytes: usize,
detected_bandwidth: Option<Bandwidth>,
) -> FrameDecisions {
let bitrate = self.packet_bitrate_bps;
let max_rate = bits_to_bitrate(8 * n_bytes as i32, self.sampling_rate, frame_size);
let voice_est = match self.signal {
Signal::Voice => 127,
Signal::Music => 0,
Signal::Auto if self.voice_ratio >= 0 => {
let v = (self.voice_ratio * 327) >> 8;
if self.application == Application::Audio { v.min(115) } else { v }
}
Signal::Auto if self.application == Application::Voip => 115,
Signal::Auto => 48,
};
self.decide_stream_channels(bitrate, frame_rate, voice_est);
let mut mode = if self.application == Application::RestrictedSilk {
OpusMode::SilkOnly
} else if matches!(self.application, Application::RestrictedLowDelay | Application::RestrictedCelt) {
OpusMode::CeltOnly
} else if let Some(forced) = self.user_forced_mode {
forced
} else {
let equiv = compute_equiv_rate(
bitrate,
self.stream_channels,
frame_rate,
!self.use_cbr,
None,
self.complexity,
self.packet_loss_perc,
);
let threshold = compute_mode_threshold(
self.application,
self.stereo_width,
self.prev_enc_mode == Some(OpusMode::CeltOnly),
self.prev_enc_mode.is_some(),
voice_est,
);
let mut mode = if equiv >= threshold {
OpusMode::CeltOnly
} else {
OpusMode::SilkOnly
};
if self.fec_config != InbandFec::Off
&& self.packet_loss_perc > (128 - voice_est) >> 4
&& (self.fec_config != InbandFec::OnUnlessMusic || voice_est > 25)
{
mode = OpusMode::SilkOnly;
}
if self.silk_use_dtx && voice_est > 100 {
mode = OpusMode::SilkOnly;
}
let tiny_budget_rate = if frame_rate > 50 { 9000 } else { 6000 };
if (n_bytes as i32) < bitrate_to_bits(tiny_budget_rate, self.sampling_rate, frame_size) / 8 {
mode = OpusMode::CeltOnly;
}
mode
};
if mode != OpusMode::CeltOnly && frame_size < self.sampling_rate as usize / 100 {
mode = OpusMode::CeltOnly;
}
if self.lfe && self.application != Application::RestrictedSilk {
mode = OpusMode::CeltOnly;
}
let (mut redundancy, mut celt_to_silk, mut to_celt) = (false, false, false);
if let Some(prev) = self.prev_enc_mode {
let (is_celt, was_celt) = (mode == OpusMode::CeltOnly, prev == OpusMode::CeltOnly);
if is_celt != was_celt {
redundancy = true;
celt_to_silk = !is_celt;
if !celt_to_silk {
if frame_size >= self.sampling_rate as usize / 100 {
mode = prev;
to_celt = true;
} else {
redundancy = false;
}
}
}
}
if self.stream_channels == 1
&& self.prev_channels == 2
&& !self.silk_to_mono
&& mode != OpusMode::CeltOnly
&& self.prev_enc_mode != Some(OpusMode::CeltOnly)
{
self.silk_to_mono = true;
self.stream_channels = 2;
} else {
self.silk_to_mono = false;
}
let prefill =
mode != OpusMode::CeltOnly && self.prev_enc_mode == Some(OpusMode::CeltOnly);
let equiv_rate = compute_equiv_rate(
bitrate,
self.stream_channels,
frame_rate,
!self.use_cbr,
Some(mode),
self.complexity,
self.packet_loss_perc,
);
if mode == OpusMode::CeltOnly || self.first || self.silk_allow_bandwidth_switch {
let mut bandwidth = 4usize;
loop {
let entry = 2 * (bandwidth - 1);
let interpolate = |i: usize| {
MUSIC_BANDWIDTH_THRESHOLDS[i]
+ ((voice_est
* voice_est
* (VOICE_BANDWIDTH_THRESHOLDS[i] - MUSIC_BANDWIDTH_THRESHOLDS[i]))
>> 14)
};
let (mut threshold, hysteresis) = (interpolate(entry), interpolate(entry + 1));
if !self.first {
if bandwidth_fec_index(self.auto_bandwidth) >= bandwidth {
threshold -= hysteresis;
} else {
threshold += hysteresis;
}
}
if equiv_rate >= threshold {
break;
}
bandwidth -= 1;
if bandwidth == 0 {
break;
}
}
if bandwidth == 1 {
bandwidth = 2;
}
self.auto_bandwidth = bandwidth_from_fec_index(bandwidth);
self.bandwidth = self.auto_bandwidth;
if !self.first
&& mode != OpusMode::CeltOnly
&& !self.silk_in_wb_mode_without_variable_lp
&& bandwidth > 2
{
self.bandwidth = Bandwidth::Wideband;
}
}
if bandwidth_fec_index(self.bandwidth) > bandwidth_fec_index(self.max_bandwidth) {
self.bandwidth = self.max_bandwidth;
}
if let Some(user) = self.user_bandwidth {
self.bandwidth = user;
}
if mode != OpusMode::CeltOnly
&& max_rate < 15000
&& bandwidth_fec_index(self.bandwidth) > 2
{
self.bandwidth = Bandwidth::Wideband;
}
let nyquist_bandwidth = match self.sampling_rate {
8000 => Bandwidth::Narrowband,
12000 => Bandwidth::Mediumband,
16000 => Bandwidth::Wideband,
24000 => Bandwidth::Superwideband,
_ => Bandwidth::Fullband,
};
if bandwidth_fec_index(self.bandwidth) > bandwidth_fec_index(nyquist_bandwidth) {
self.bandwidth = nyquist_bandwidth;
}
if let (Some(detected), None) = (detected_bandwidth, self.user_bandwidth) {
let sc = self.stream_channels as i32;
let celt = mode == OpusMode::CeltOnly;
let min_detected = if equiv_rate <= 18000 * sc && celt {
Bandwidth::Narrowband
} else if equiv_rate <= 24000 * sc && celt {
Bandwidth::Mediumband
} else if equiv_rate <= 30000 * sc {
Bandwidth::Wideband
} else if equiv_rate <= 44000 * sc {
Bandwidth::Superwideband
} else {
Bandwidth::Fullband
};
let detected = detected.max_by_index(min_detected);
if bandwidth_fec_index(self.bandwidth) > bandwidth_fec_index(detected) {
self.bandwidth = detected;
}
}
let lbrr_coded = decide_fec(
(self.fec_config != InbandFec::Off) as i32,
self.packet_loss_perc,
self.silk_lbrr_coded,
mode,
&mut self.bandwidth,
equiv_rate,
);
self.silk_lbrr_coded = lbrr_coded;
if mode == OpusMode::CeltOnly && self.bandwidth == Bandwidth::Mediumband {
self.bandwidth = Bandwidth::Wideband;
}
if self.lfe {
self.bandwidth = Bandwidth::Narrowband;
}
if self.application == Application::RestrictedSilk && bandwidth_fec_index(self.bandwidth) > 2 {
self.bandwidth = Bandwidth::Wideband;
}
let above_wideband = bandwidth_fec_index(self.bandwidth) > 2;
if mode == OpusMode::SilkOnly && above_wideband {
mode = OpusMode::Hybrid;
}
if mode == OpusMode::Hybrid && !above_wideband {
mode = OpusMode::SilkOnly;
}
self.mode = mode;
FrameDecisions {
mode,
bandwidth: self.bandwidth,
lbrr_coded,
redundancy,
celt_to_silk,
to_celt,
prefill,
equiv_rate,
}
}
fn decide_stream_channels(&mut self, bitrate: i32, frame_rate: i32, voice_est: i32) {
const STEREO_VOICE_THRESHOLD: i32 = 19000;
const STEREO_MUSIC_THRESHOLD: i32 = 17000;
if let (Some(forced), 2) = (self.force_channels, self.channels) {
self.stream_channels = forced;
} else if self.channels == 2 {
let equiv_rate = compute_equiv_rate(
bitrate,
self.channels,
frame_rate,
!self.use_cbr,
None,
self.complexity,
self.packet_loss_perc,
);
let mut threshold = STEREO_MUSIC_THRESHOLD
+ ((voice_est * voice_est * (STEREO_VOICE_THRESHOLD - STEREO_MUSIC_THRESHOLD))
>> 14);
if self.stream_channels == 2 {
threshold -= 1000;
} else {
threshold += 1000;
}
self.stream_channels = if equiv_rate > threshold { 2 } else { 1 };
} else {
self.stream_channels = self.channels;
}
}
pub fn set_bitrate(&mut self, bitrate: Bitrate) {
self.user_bitrate = match bitrate {
Bitrate::BitsPerSecond(bps) => Bitrate::within_limits(
Bitrate::bps_to_i32(bps).clamp(500, 750_000 * self.channels as i32),
),
other => other,
};
}
pub fn bitrate_setting(&self) -> Bitrate {
self.user_bitrate
}
pub fn bitrate(&self) -> i32 {
self.user_bitrate_to_bitrate(self.prev_frame_size, OPUS_MAX_PACKET_BYTES)
}
fn user_bitrate_to_bitrate(&self, frame_size: usize, max_data_bytes: usize) -> i32 {
let fs = self.sampling_rate;
let frame_size = if frame_size == 0 { fs as usize / 400 } else { frame_size };
let max_bitrate = bits_to_bitrate(8 * max_data_bytes as i32, fs, frame_size);
let user_bitrate = match self.user_bitrate {
Bitrate::Auto => 60 * fs / frame_size as i32 + fs * self.channels as i32,
Bitrate::Max => 1_500_000,
Bitrate::BitsPerSecond(bps) => Bitrate::bps_to_i32(bps),
};
user_bitrate.min(max_bitrate)
}
pub fn set_complexity(&mut self, complexity: u8) -> Result<(), Error> {
if complexity > 10 {
return Err(Error::INVALID_COMPLEXITY);
}
self.complexity = i32::from(complexity);
Ok(())
}
pub fn complexity(&self) -> u8 {
self.complexity as u8
}
pub fn set_vbr(&mut self, vbr: bool) {
self.use_cbr = !vbr;
}
pub fn vbr(&self) -> bool {
!self.use_cbr
}
pub fn set_vbr_constraint(&mut self, constrained: bool) {
self.vbr_constraint = constrained;
}
pub fn vbr_constraint(&self) -> bool {
self.vbr_constraint
}
pub fn set_inband_fec(&mut self, fec: InbandFec) {
self.fec_config = fec;
}
pub fn inband_fec(&self) -> InbandFec {
self.fec_config
}
pub fn set_signal(&mut self, signal: Signal) {
self.signal = signal;
}
pub fn signal(&self) -> Signal {
self.signal
}
pub fn set_lsb_depth(&mut self, depth: u8) -> Result<(), Error> {
if !(8..=24).contains(&depth) {
return Err(Error::INVALID_LSB_DEPTH);
}
self.lsb_depth = i32::from(depth);
Ok(())
}
pub fn lsb_depth(&self) -> u8 {
self.lsb_depth as u8
}
pub fn set_dtx(&mut self, enabled: bool) {
self.use_dtx = enabled;
}
pub fn dtx(&self) -> bool {
self.use_dtx
}
pub fn in_dtx(&self) -> bool {
use silk::define::NB_SPEECH_FRAMES_BEFORE_DTX;
let silk_frame = matches!(self.prev_enc_mode, Some(OpusMode::SilkOnly | OpusMode::Hybrid));
if self.silk_use_dtx && silk_frame {
let mid = self.silk.mid();
let mut in_dtx = mid.s_cmn.no_speech_counter >= NB_SPEECH_FRAMES_BEFORE_DTX;
if in_dtx && self.silk_channels_internal == 2 && mid.stereo.prev_decode_only_middle == 0 {
in_dtx = self
.silk
.side()
.is_some_and(|side| side.s_cmn.no_speech_counter >= NB_SPEECH_FRAMES_BEFORE_DTX);
}
in_dtx
} else {
self.use_dtx && self.nb_no_activity_ms_q1 >= NB_SPEECH_FRAMES_BEFORE_DTX * 20 * 2
}
}
#[cfg(feature = "__internals")]
pub(crate) fn set_analysis_gate(&mut self, gate: AnalysisGate) {
self.analysis_gate = gate;
}
#[cfg(feature = "__internals")]
pub(crate) fn analysis_gate(&self) -> AnalysisGate {
self.analysis_gate
}
#[cfg(feature = "__internals")]
pub(crate) fn last_analysis_info(&self) -> AnalysisInfo {
self.last_analysis
}
#[cfg(feature = "analysis")]
fn analysis_runs(&self) -> bool {
self.analysis_gate.min_complexity().is_some_and(|c| self.complexity >= c)
&& (16000..=48000).contains(&self.sampling_rate)
&& self.application != Application::RestrictedSilk
}
pub fn set_packet_loss_perc(&mut self, percent: u8) -> Result<(), Error> {
if percent > 100 {
return Err(Error::INVALID_PACKET_LOSS);
}
self.packet_loss_perc = i32::from(percent);
Ok(())
}
pub fn packet_loss_perc(&self) -> u8 {
self.packet_loss_perc as u8
}
#[cfg(feature = "dred")]
pub fn set_dred_duration(&mut self, frames: u8) -> Result<(), Error> {
self.dred.set_duration(i32::from(frames))
}
#[cfg(feature = "dred")]
pub fn dred_duration(&self) -> u8 {
self.dred.duration() as u8
}
#[cfg(feature = "dred")]
pub fn set_dnn_blob(&mut self, blob: &DnnBlob) -> Result<DnnModels, Error> {
self.dred.load(blob)
}
pub fn set_force_mode(&mut self, mode: Option<ForcedMode>) {
self.user_forced_mode = mode.map(|mode| match mode {
ForcedMode::SilkOnly => OpusMode::SilkOnly,
ForcedMode::Hybrid => OpusMode::Hybrid,
ForcedMode::CeltOnly => OpusMode::CeltOnly,
});
}
pub fn force_mode(&self) -> Option<ForcedMode> {
self.user_forced_mode.map(|mode| match mode {
OpusMode::SilkOnly => ForcedMode::SilkOnly,
OpusMode::Hybrid => ForcedMode::Hybrid,
OpusMode::CeltOnly => ForcedMode::CeltOnly,
})
}
pub fn set_bandwidth(&mut self, bandwidth: Option<Bandwidth>) {
self.user_bandwidth = bandwidth;
}
pub fn bandwidth_setting(&self) -> Option<Bandwidth> {
self.user_bandwidth
}
pub fn bandwidth(&self) -> Bandwidth {
self.bandwidth
}
pub fn set_max_bandwidth(&mut self, bandwidth: Bandwidth) {
self.max_bandwidth = bandwidth;
}
pub fn max_bandwidth(&self) -> Bandwidth {
self.max_bandwidth
}
pub fn set_lfe(&mut self, lfe: bool) {
self.lfe = lfe;
}
pub fn lfe(&self) -> bool {
self.lfe
}
pub fn set_prediction_disabled(&mut self, disabled: bool) {
self.prediction_disabled = disabled;
}
pub fn prediction_disabled(&self) -> bool {
self.prediction_disabled
}
pub fn set_phase_inversion_disabled(&mut self, disabled: bool) {
if self.application != Application::RestrictedSilk {
self.celt_enc.set_phase_inversion_disabled(disabled);
}
}
pub fn phase_inversion_disabled(&self) -> bool {
self.application != Application::RestrictedSilk && self.celt_enc.phase_inversion_disabled()
}
#[cfg(feature = "__internals")]
pub(crate) fn set_integer_hp_at_48k(&mut self, on: bool) {
self.integer_hp_at_48k = on;
}
#[cfg(feature = "__internals")]
pub(crate) fn set_fixed_point_res_nrg(&mut self, on: bool) {
self.fixed_point_res_nrg = on;
}
pub fn set_expert_frame_duration(&mut self, duration: FrameDuration) {
self.variable_duration = duration;
}
pub fn expert_frame_duration(&self) -> FrameDuration {
self.variable_duration
}
pub fn set_force_channels(&mut self, channels: Option<Channels>) {
self.force_channels = channels.map(Channels::count);
}
pub fn force_channels(&self) -> Option<Channels> {
self.force_channels.map(Channels::from_valid_count)
}
pub fn set_application(&mut self, application: Application) -> Result<(), Error> {
if self.application.only_at_creation() || application.only_at_creation() {
return Err(Error::bad_arg("application can only be chosen when the encoder is created"));
}
if !self.first && application != self.application {
return Err(Error::bad_arg("application cannot change after the first frame"));
}
self.application = application;
let restricted = application.without_delay_buffer();
let fs = self.sampling_rate as usize;
self.encoder_buffer = if restricted { 0 } else { fs / 100 };
self.delay_compensation = if restricted { 0 } else { fs / 250 };
Ok(())
}
pub fn application(&self) -> Application {
self.application
}
pub fn set_voice_ratio(&mut self, ratio: Option<u8>) -> Result<(), Error> {
self.voice_ratio = match ratio {
None => -1,
Some(percent) if percent <= 100 => i32::from(percent),
Some(_) => return Err(Error::bad_arg("invalid voice ratio")),
};
Ok(())
}
pub fn voice_ratio(&self) -> Option<u8> {
u8::try_from(self.voice_ratio).ok()
}
pub fn set_energy_mask(&mut self, mask: Option<&[f32]>) -> Result<(), Error> {
let n = 21 * self.channels;
self.energy_mask = match mask {
None => None,
Some(m) if m.len() == n => {
let mut values = [0.0; 21 * OPUS_MAX_CHANNELS];
values[..n].copy_from_slice(m);
Some(values)
}
Some(_) => return Err(Error::bad_arg("invalid energy mask length")),
};
Ok(())
}
pub fn energy_mask(&self) -> Option<&[f32]> {
self.energy_mask.as_ref().map(|m| &m[..21 * self.channels])
}
pub fn final_range(&self) -> u32 {
self.range_final
}
pub fn reset_state(&mut self) {
let kept = KeptOnReset::of(self);
#[cfg(feature = "dred")]
let models = self.dred.take_models();
let (sampling_rate, channels, application) = (self.sampling_rate, self.channels, self.application);
#[cfg(not(feature = "heap"))]
const { assert!(!core::mem::needs_drop::<Self>()) };
unsafe { Self::write_new(self, sampling_rate, channels, application, HeapMemory::Reuse) };
kept.restore(self);
#[cfg(feature = "dred")]
self.dred.set_models(models);
}
pub fn sampling_rate(&self) -> SampleRate {
SampleRate::from_valid_hz(self.sampling_rate)
}
pub fn channels(&self) -> Channels {
Channels::from_valid_count(self.channels)
}
pub fn lookahead(&self) -> usize {
let fs = self.sampling_rate as usize;
match self.application {
Application::RestrictedLowDelay | Application::RestrictedCelt => fs / 400,
_ => fs / 400 + fs / 250,
}
}
pub fn encode(
&mut self,
input: &[f32],
frame_size: usize,
output: &mut [u8],
) -> Result<usize, Error> {
let analysis_frame_size = frame_size;
let frame_size = self.select_frame_size(input.len(), frame_size)?;
let analysis_pcm = AnalysisPcm {
pcm: PcmRef::F32(input),
channels: self.channels,
c1: 0,
c2: SecondChannel::AllOthers,
frame_size: analysis_frame_size,
};
self.encode_impl(input, frame_size, output, analysis_pcm, 24, true)
}
pub fn encode_i16(
&mut self,
input: &[i16],
frame_size: usize,
output: &mut [u8],
) -> Result<usize, Error> {
let analysis_frame_size = frame_size;
let frame_size = self.select_frame_size(input.len(), frame_size)?;
let n = frame_size * self.channels;
let mut pcm: FixedVec<f32, OPUS_PCM_PACKET> = FixedVec::new();
pcm.extend_map(&input[..n], int16_to_res);
let analysis_pcm = AnalysisPcm {
pcm: PcmRef::I16(input),
channels: self.channels,
c1: 0,
c2: SecondChannel::AllOthers,
frame_size: analysis_frame_size,
};
self.encode_impl(&pcm, frame_size, output, analysis_pcm, 16, true)
}
pub fn encode24(
&mut self,
input: &[i32],
frame_size: usize,
output: &mut [u8],
) -> Result<usize, Error> {
let analysis_frame_size = frame_size;
let frame_size = self.select_frame_size(input.len(), frame_size)?;
let n = frame_size * self.channels;
let mut pcm: FixedVec<f32, OPUS_PCM_PACKET> = FixedVec::new();
pcm.extend_map(&input[..n], int24_to_res);
let analysis_pcm = AnalysisPcm {
pcm: PcmRef::I32(input),
channels: self.channels,
c1: 0,
c2: SecondChannel::AllOthers,
frame_size: analysis_frame_size,
};
self.encode_impl(&pcm, frame_size, output, analysis_pcm, 24, true)
}
fn select_frame_size(&self, input_len: usize, frame_size: usize) -> Result<usize, Error> {
let selected =
frame_size_select(self.application, frame_size, self.variable_duration, self.sampling_rate)
.ok_or(Error::INVALID_FRAME_SIZE)?;
if input_len < frame_size.saturating_mul(self.channels) {
return Err(Error::INPUT_TOO_SHORT);
}
Ok(selected)
}
pub(crate) fn encode_impl(
&mut self,
input: &[f32],
frame_size: usize,
output: &mut [u8],
analysis_pcm: AnalysisPcm<'_>,
lsb_depth: i32,
float_api: bool,
) -> Result<usize, Error> {
self.float_api = float_api;
self.range_final = 0;
let out_data_bytes = output.len();
let mut max_data_bytes = out_data_bytes.min(6 * OPUS_MAX_PACKET_BYTES);
if max_data_bytes == 0 {
return Err(Error::bad_arg("output buffer is empty"));
}
let frame_rate = frame_rate_from_params(self.sampling_rate, frame_size)
.ok_or(Error::INVALID_FRAME_SIZE)?;
if max_data_bytes == 1 && frame_size * 10 == self.sampling_rate as usize {
return Err(Error::BUFFER_TOO_SMALL);
}
if input.len() < frame_size * self.channels {
return Err(Error::INPUT_TOO_SHORT);
}
let lsb_depth = lsb_depth.min(self.lsb_depth);
let pcm = &input[..frame_size * self.channels];
let is_silence = is_digital_silence(pcm, lsb_depth);
let (analysis_info, analysis_cursor) = self.run_analysis(analysis_pcm, frame_size, lsb_depth);
if !is_silence {
self.voice_ratio = -1;
}
let mut detected_bandwidth = None;
if analysis_info.valid {
if self.signal == Signal::Auto {
let prob = match self.prev_enc_mode {
None => analysis_info.music_prob,
Some(OpusMode::CeltOnly) => analysis_info.music_prob_max,
Some(_) => analysis_info.music_prob_min,
};
self.voice_ratio = (0.5 + (100.0 * (1.0 - prob)) as f64).floor() as i32;
}
detected_bandwidth = Some(match analysis_info.bandwidth {
..=12 => Bandwidth::Narrowband,
13..=14 => Bandwidth::Mediumband,
15..=16 => Bandwidth::Wideband,
17..=18 => Bandwidth::Superwideband,
_ => Bandwidth::Fullband,
});
}
if (!analysis_info.valid || analysis_info.activity_probability > DTX_ACTIVITY_THRESHOLD)
&& !is_silence
{
let decayed = 0.999 * self.peak_signal_energy;
let energy = frame_energy(pcm);
self.peak_signal_energy = if decayed > energy { decayed } else { energy };
}
self.stereo_width = if self.channels == 2 && self.force_channels != Some(1) {
compute_stereo_width(input, frame_size, self.sampling_rate, &mut self.width_mem)
} else {
0.0
};
let fs = self.sampling_rate;
let mut bitrate = self.user_bitrate_to_bitrate(frame_size, max_data_bytes);
let mut cbr_bytes = 0usize;
if self.use_cbr {
let nominal = (bitrate_to_bits(bitrate, fs, frame_size) + 4) / 8;
cbr_bytes = (nominal.max(0) as usize).min(max_data_bytes);
bitrate = bits_to_bitrate(8 * cbr_bytes as i32, fs, frame_size);
max_data_bytes = cbr_bytes.max(1);
}
#[cfg(feature = "dred")]
{
let fec = self.fec_config != InbandFec::Off;
bitrate -= self.dred.compute_bitrate(bitrate, frame_size, fec, self.packet_loss_perc);
}
self.packet_bitrate_bps = bitrate;
if max_data_bytes < 3
|| bitrate < 3 * frame_rate * 8
|| (frame_rate < 50 && (max_data_bytes as i32 * frame_rate < 300 || bitrate < 2400))
{
return self.write_placeholder(frame_rate, max_data_bytes, output);
}
self.silk_use_dtx = self.use_dtx && !(analysis_info.valid || is_silence);
let decisions = self.decide_mode_and_bandwidth(
frame_size,
frame_rate,
max_data_bytes,
detected_bandwidth,
);
let mode = decisions.mode;
if self.application != Application::RestrictedSilk {
self.celt_enc.set_lsb_depth(lsb_depth);
}
let fs_u = fs as usize;
if (mode != OpusMode::SilkOnly && frame_size > fs_u / 50) || frame_size > 3 * fs_u / 50 {
return self.encode_multiframe(
input,
frame_size,
decisions,
cbr_bytes,
output,
analysis_cursor,
lsb_depth,
);
}
let frame_analysis = FrameAnalysis {
info: analysis_info,
is_silence,
};
self.encode_frame(input, frame_size, &mut output[..max_data_bytes], decisions, frame_analysis, true)
}
fn run_analysis(
&mut self,
pcm: AnalysisPcm<'_>,
frame_size: usize,
lsb_depth: i32,
) -> (AnalysisInfo, Option<(usize, i32)>) {
#[cfg(feature = "analysis")]
{
if self.analysis_runs() {
let cursor = self.analysis.read_cursor();
let info = self.analysis.run(pcm, frame_size, lsb_depth);
return (info, Some(cursor));
}
if self.analysis.initialized() {
self.analysis.reset();
}
}
#[cfg(not(feature = "analysis"))]
let _ = (pcm, frame_size, lsb_depth);
(AnalysisInfo::default(), None)
}
fn frame_analysis_info(&mut self, cursor: Option<(usize, i32)>, len: usize) -> AnalysisInfo {
#[cfg(feature = "analysis")]
{
if let Some(cursor) = cursor {
self.analysis.set_read_cursor(cursor);
}
self.analysis.get_info(len)
}
#[cfg(not(feature = "analysis"))]
{
let _ = (cursor, len);
AnalysisInfo::default()
}
}
fn encode_frame(
&mut self,
input: &[f32],
frame_size: usize,
output: &mut [u8],
d: FrameDecisions,
analysis: FrameAnalysis,
first_frame: bool,
) -> Result<usize, Error> {
let orig_max_data_bytes = output.len();
let n_bytes = orig_max_data_bytes.min(OPUS_MAX_PACKET_BYTES);
self.range_final = 0;
if n_bytes < 3 {
return Err(Error::BUFFER_TOO_SMALL);
}
let fs = self.sampling_rate;
let ch = self.channels;
let sch = self.stream_channels;
let frame_rate = fs / frame_size as i32;
let mode = d.mode;
let frame_bandwidth = d.bandwidth;
let lbrr_coded = d.lbrr_coded;
self.last_analysis = analysis.info;
let mut activity = if analysis.is_silence {
VAD_NO_ACTIVITY
} else if analysis.info.valid {
let active = analysis.info.activity_probability >= DTX_ACTIVITY_THRESHOLD
|| self.peak_signal_energy
< PSEUDO_SNR_THRESHOLD * frame_energy(&input[..frame_size * ch]);
active as i32
} else if d.mode == OpusMode::CeltOnly && ((self.use_dtx && !self.silk_use_dtx) || self.dred_on()) {
let noise_energy = frame_energy(&input[..frame_size * ch]);
(self.peak_signal_energy < PSEUDO_SNR_THRESHOLD * (0.5 * noise_energy)) as i32
} else {
VAD_NO_DECISION
};
let f2_5 = fs as usize / 400;
let f5 = 2 * f2_5;
match (mode, frame_rate) {
(OpusMode::CeltOnly, 400 | 200 | 100 | 50) => {}
(OpusMode::CeltOnly, _) => {
return Err(Error::internal("unsupported frame size for CELT-only mode"));
}
(OpusMode::Hybrid, 100 | 50) => {}
(OpusMode::Hybrid, _) => {
return Err(Error::internal("unsupported frame size for Hybrid mode"));
}
(OpusMode::SilkOnly, 100 | 50 | 25 | 16) => {}
(OpusMode::SilkOnly, _) => {
return Err(Error::internal("unsupported frame size for SILK-only mode"));
}
}
let (mut redundancy, mut celt_to_silk) = (d.redundancy, d.celt_to_silk);
let mut prefill = d.prefill as u8;
if self.silk_bw_switch {
redundancy = true;
celt_to_silk = true;
self.silk_bw_switch = false;
if prefill == 0 {
prefill = 2;
}
}
if mode == OpusMode::CeltOnly {
redundancy = false;
}
let mut redundancy_bytes = 0usize;
if redundancy {
redundancy_bytes = compute_redundancy_bytes(
n_bytes,
self.packet_bitrate_bps,
frame_rate,
self.stream_channels,
);
if redundancy_bytes == 0 {
redundancy = false;
}
}
let silk_only_app = self.application == Application::RestrictedSilk;
if silk_only_app {
redundancy = false;
redundancy_bytes = 0;
}
self.rc.reset_for_encode((n_bytes - 1) as u32);
let mut hybrid_silk_rate = 0i32;
let mut hb_gain = 1.0f32;
let hp_freq_smth1 = if mode == OpusMode::CeltOnly {
silk_lin2log(60) << 8
} else {
self.silk.mid().s_cmn.variable_hp_smth1_q15
};
const VARIABLE_HP_SMTH_COEF2_Q16: i32 = 983;
self.variable_hp_smth2_q15 = silk_smlawb(
self.variable_hp_smth2_q15,
hp_freq_smth1 - self.variable_hp_smth2_q15,
VARIABLE_HP_SMTH_COEF2_Q16,
);
let cutoff_hz = silk_log2lin(silk_rshift(self.variable_hp_smth2_q15, 8));
let delay = self.delay_compensation;
let ebuf = self.encoder_buffer;
let prefix = delay * ch;
let mut frame_cleared = false;
{
self.buf_celt_pcm.resize((delay + frame_size) * ch, 0.0);
let src_start = (ebuf - delay) * ch;
self.buf_celt_pcm[..prefix]
.copy_from_slice(&self.delay_buffer[src_start..src_start + prefix]);
let out = &mut self.buf_celt_pcm[prefix..];
if self.application == Application::Voip {
hp_cutoff_float(
input,
cutoff_hz,
out,
&mut self.hp_mem_float,
frame_size,
ch,
fs,
);
} else {
dc_reject_float(input, 3, out, &mut self.hp_mem_float, frame_size, ch, fs);
}
if self.float_api && !input_is_sane(&self.buf_celt_pcm[prefix..]) {
self.buf_celt_pcm[prefix..].fill(0.0);
self.hp_mem_float = [0.0; 4];
frame_cleared = true;
}
}
#[cfg(feature = "dred")]
self.dred.analyze_frame(&self.buf_celt_pcm[prefix..], frame_size, delay, activity);
let integer_hp = self.application == Application::Voip
&& (self.sampling_rate != 48000 || self.integer_hp_at_48k);
if integer_hp {
let required_size = frame_size * ch;
self.buf_filtered.resize(required_size, 0);
if frame_cleared {
state_mut(&mut self.buf_filtered)[..required_size].fill(0);
self.hp_mem.fill(0);
} else {
hp_cutoff(
input,
cutoff_hz,
state_mut(&mut self.buf_filtered),
&mut self.hp_mem,
frame_size,
ch,
fs,
);
}
}
let mut silk_fs_khz = 0;
if mode == OpusMode::SilkOnly || mode == OpusMode::Hybrid {
let frame_ms = (frame_size as i32 * 1000) / fs;
let rate_bits = bitrate_to_bits(self.packet_bitrate_bps, fs, frame_size);
let bits_target = (8 * (n_bytes - redundancy_bytes) as i32).min(rate_bits) - 8;
let total_bitrate = bits_to_bitrate(bits_target, fs, frame_size);
let silk_bitrate = if mode == OpusMode::Hybrid {
let rate = compute_silk_rate_for_hybrid(
total_bitrate,
frame_bandwidth,
frame_ms == 20,
!self.use_cbr,
lbrr_coded != 0,
self.stream_channels,
);
if self.energy_mask.is_none() {
let celt_rate = total_bitrate - rate;
let x = -celt_rate as f32 * (1.0 / 1024.0);
hb_gain = 1.0 - mathops::celt_exp2(x);
}
rate
} else {
total_bitrate
};
let silk_bitrate = if let Some(energy_mask) = &self.energy_mask
&& !self.use_cbr
&& !self.lfe
{
let (end, srate) = match frame_bandwidth {
Bandwidth::Narrowband => (13usize, 8000.0f32),
Bandwidth::Mediumband => (15, 12000.0),
_ => (17, 16000.0),
};
let mut mask_sum = 0.0f32;
for c in 0..ch {
for i in 0..end {
#[allow(clippy::manual_clamp)]
let mut mask = energy_mask[21 * c + i].min(0.5).max(-2.0);
if mask > 0.0 {
mask *= 0.5;
}
mask_sum += mask;
}
}
let mut masking_depth = mask_sum / end as f32 * ch as f32;
masking_depth += 0.2;
let rate_offset = ((srate * masking_depth) as i32).max(-2 * silk_bitrate / 3);
if matches!(frame_bandwidth, Bandwidth::Superwideband | Bandwidth::Fullband) {
silk_bitrate + 3 * rate_offset / 5
} else {
silk_bitrate + rate_offset
}
} else {
silk_bitrate
};
if mode == OpusMode::Hybrid {
hybrid_silk_rate = silk_bitrate;
}
let mut silk_max_bits = ((n_bytes - 1) * 8) as i32;
if redundancy && redundancy_bytes >= 2 {
silk_max_bits -= redundancy_bytes as i32 * 8 + 1;
if mode == OpusMode::Hybrid {
silk_max_bits -= 20;
}
}
let silk_vbr_with_cap = mode == OpusMode::Hybrid || self.dred_bitrate_bps() > 0;
if self.use_cbr {
if silk_vbr_with_cap {
let silk_bits =
(silk_bitrate as i64 * frame_size as i64 / fs as i64) as i32;
let other_bits = 0i32.max(silk_max_bits - silk_bits);
silk_max_bits = 0i32.max(silk_max_bits - other_bits * 3 / 4);
}
} else if mode == OpusMode::Hybrid {
let max_bit_rate = compute_silk_rate_for_hybrid(
silk_max_bits * fs / frame_size as i32,
frame_bandwidth,
frame_ms == 20,
true,
lbrr_coded != 0,
self.stream_channels,
);
silk_max_bits = bitrate_to_bits(max_bit_rate, fs, frame_size);
}
let mut desired_khz = frame_bandwidth.silk_khz();
let min_khz = if mode == OpusMode::Hybrid { 16 } else { 8 };
let mut max_khz = 16;
if mode == OpusMode::SilkOnly {
let mut effective_max_rate = bits_to_bitrate(8 * n_bytes as i32, fs, frame_size);
if frame_rate > 50 {
effective_max_rate = effective_max_rate * 2 / 3;
}
if effective_max_rate < 8000 {
max_khz = 12;
desired_khz = desired_khz.min(12);
}
if effective_max_rate < 7000 {
max_khz = 8;
desired_khz = desired_khz.min(8);
}
}
let required_size = frame_size * ch;
let silk_input = if integer_hp {
SilkInput::Int16(&self.buf_filtered[..required_size])
} else {
SilkInput::Float(&self.buf_celt_pcm[prefix..prefix + required_size])
};
let mut silk_prefill = None;
if prefill == 1 && self.prev_enc_mode == Some(OpusMode::CeltOnly) {
self.silk.reset();
}
if prefill != 0 && !silk_only_app {
let mut prefill_input = None;
if ebuf > 0 && delay > 0 && ebuf >= delay + f2_5 {
let offset = ebuf - delay - f2_5;
let faded = &mut self.delay_buffer[..ebuf * ch];
gain_fade(&mut faded[offset * ch..], 0.0, 1.0, f2_5, ch, fs);
faded[..offset * ch].fill(0.0);
prefill_input = Some(SilkInput::Float(&self.delay_buffer[..ebuf * ch]));
}
silk_prefill = Some(SilkPrefill { rate_switch: prefill == 2, input: prefill_input });
}
self.silk_channels_internal = sch;
let mut silk_ctl = SilkControl {
channels_internal: sch,
payload_size_ms: frame_ms,
min_khz,
max_khz,
desired_khz,
bit_rate: silk_bitrate,
max_bits: silk_max_bits,
use_cbr: self.use_cbr && !silk_vbr_with_cap,
complexity: self.complexity,
packet_loss_perc: self.packet_loss_perc,
lbrr_coded: lbrr_coded != 0,
activity,
use_dtx: self.silk_use_dtx,
to_mono: self.silk_to_mono,
reduced_dependency: self.prediction_disabled,
opus_can_switch: self.silk_opus_can_switch,
allow_bandwidth_switch: self.silk_allow_bandwidth_switch,
fixed_point_res_nrg: self.fixed_point_res_nrg,
..SilkControl::default()
};
let prefilled = silk_prefill.is_some();
let result = self.silk.encode(&mut silk_ctl, silk_input, silk_prefill, &mut self.rc);
if prefilled {
self.silk_opus_can_switch = false;
}
result?;
self.silk_allow_bandwidth_switch = silk_ctl.allow_bandwidth_switch;
self.silk_in_wb_mode_without_variable_lp = silk_ctl.in_wb_mode_without_variable_lp;
silk_fs_khz = silk_ctl.internal_khz;
if ch == 2 {
self.silk_stereo_width_q14 = silk_ctl.stereo_width_q14;
}
self.silk_opus_can_switch = silk_ctl.switch_ready && !self.nonfinal_frame;
if activity == VAD_NO_DECISION {
activity = (self.silk.mid().s_cmn.indices.signal_type != TYPE_NO_VOICE_ACTIVITY as i8) as i32;
#[cfg(feature = "dred")]
self.dred.set_activity(frame_size, activity);
}
if silk_ctl.zero_bytes {
output[0] = gen_toc(
mode,
frame_rate,
toc_bandwidth(mode, silk_fs_khz, frame_bandwidth),
self.stream_channels,
);
return Ok(1);
}
if self.silk_opus_can_switch {
if !silk_only_app {
redundancy_bytes = compute_redundancy_bytes(
n_bytes,
self.packet_bitrate_bps,
frame_rate,
self.stream_channels,
);
redundancy = redundancy_bytes != 0;
}
celt_to_silk = false;
self.silk_bw_switch = true;
}
}
let toc_bandwidth = toc_bandwidth(mode, silk_fs_khz, frame_bandwidth);
let toc = gen_toc(mode, frame_rate, toc_bandwidth, self.stream_channels);
if !silk_only_app {
self.celt_enc.complexity = self.complexity;
self.celt_enc.set_end_band(toc_bandwidth.celt_end_band());
let _ = self.celt_enc.set_stream_channels(self.stream_channels);
if mode != OpusMode::SilkOnly {
let _ = self
.celt_enc
.set_prediction(if self.prediction_disabled { 0 } else { 2 });
}
let _ = self.celt_enc.set_packet_loss_perc(self.packet_loss_perc);
self.celt_enc.set_lfe(self.lfe);
let mask = self.energy_mask.as_ref().map(|m| &m[..21 * ch]);
self.celt_enc.set_energy_mask(mask);
}
let celt_restart = mode != OpusMode::SilkOnly
&& self.prev_enc_mode.is_some()
&& self.prev_enc_mode != Some(mode)
&& self.application != Application::RestrictedCelt;
if celt_restart {
let start = ebuf.saturating_sub(delay + f2_5) * ch;
self.celt_prefill[..f2_5 * ch]
.copy_from_slice(&self.delay_buffer[start..start + f2_5 * ch]);
}
{
let keep = ebuf as i64 - (frame_size as i64 + delay as i64);
if keep > 0 {
let keep = keep as usize;
self.delay_buffer
.copy_within(ch * frame_size..ch * (frame_size + keep), 0);
let dst = ch * keep;
let n = (frame_size + delay) * ch;
self.delay_buffer[dst..dst + n].copy_from_slice(&self.buf_celt_pcm[..n]);
} else {
let n = ebuf * ch;
let src = (frame_size + delay - ebuf) * ch;
self.delay_buffer[..n].copy_from_slice(&self.buf_celt_pcm[src..src + n]);
}
if (self.prev_hb_gain < 1.0 || hb_gain < 1.0) && !silk_only_app {
gain_fade(
&mut self.buf_celt_pcm[..frame_size * ch],
self.prev_hb_gain,
hb_gain,
frame_size,
ch,
fs,
);
}
}
self.prev_hb_gain = hb_gain;
if mode != OpusMode::Hybrid || self.stream_channels == 1 {
self.silk_stereo_width_q14 = if d.equiv_rate > 32000 {
16384
} else if d.equiv_rate < 16000 {
0
} else {
16384 - 2048 * (32000 - d.equiv_rate) / (d.equiv_rate - 14000)
};
}
if self.energy_mask.is_none()
&& ch == 2
&& (self.hybrid_stereo_width_q14 < (1 << 14) || self.silk_stereo_width_q14 < (1 << 14))
{
let g1 = self.hybrid_stereo_width_q14 as f32 * (1.0 / 16384.0);
let g2 = self.silk_stereo_width_q14 as f32 * (1.0 / 16384.0);
if !silk_only_app {
stereo_fade(&mut self.buf_celt_pcm, g1, g2, frame_size, fs);
}
self.hybrid_stereo_width_q14 = self.silk_stereo_width_q14;
}
if mode != OpusMode::CeltOnly
&& self.rc.tell() + 17 + 20 * (mode == OpusMode::Hybrid) as i32
<= 8 * (n_bytes as i32 - 1)
{
if mode == OpusMode::Hybrid {
self.rc.encode_bit_logp(redundancy, 12);
}
if redundancy {
self.rc.encode_bit_logp(celt_to_silk, 1);
let max_redundancy = if mode == OpusMode::Hybrid {
(n_bytes as i32 - 1) - ((self.rc.tell() + 8 + 3 + 7) >> 3)
} else {
(n_bytes as i32 - 1) - ((self.rc.tell() + 7) >> 3)
};
let bytes = (redundancy_bytes as i32).min(max_redundancy);
redundancy_bytes = bytes.clamp(2, 257) as usize;
if mode == OpusMode::Hybrid {
self.rc.enc_uint(redundancy_bytes as u32 - 2, 256);
}
}
} else {
redundancy = false;
}
if !redundancy {
self.silk_bw_switch = false;
redundancy_bytes = 0;
}
let mut nb_compr_bytes = if mode == OpusMode::SilkOnly {
let used = ((self.rc.tell() + 7) >> 3).max(0) as u32;
if used < self.rc.storage {
self.rc.shrink(used);
}
(used as usize).min(n_bytes - 1 - redundancy_bytes)
} else {
#[allow(unused_mut)]
let mut nb = n_bytes - 1 - redundancy_bytes;
#[cfg(feature = "dred")]
if self.dred.duration() > 0 {
let dred_bytes = bitrate_to_bits(self.dred.bitrate_bps(), fs, frame_size) / 8;
let max_celt_bytes = nb as i32 - dred_bytes * 3 / 4;
let max_celt_bytes = max_celt_bytes.max((self.rc.tell() + 7) / 8 + 5);
nb = nb.min(max_celt_bytes as usize);
}
self.rc.shrink(nb as u32);
nb
};
let fits = self.rc.error == 0 && self.rc.tell() <= 8 * nb_compr_bytes as i32;
let mut redundant = [0u8; 257];
let mut redundant_rng = 0u32;
if redundancy || mode != OpusMode::SilkOnly {
self.celt_enc.set_analysis(&analysis.info);
}
if mode == OpusMode::Hybrid {
const QUANTIZATION_OFFSETS_Q10: [[i32; 2]; 2] = [[100, 240], [32, 100]];
let indices = &self.silk.mid().s_cmn.indices;
self.celt_enc.set_silk_info(
indices.signal_type as i32,
QUANTIZATION_OFFSETS_Q10[(indices.signal_type >> 1) as usize & 1]
[indices.quant_offset_type as usize & 1],
);
}
if redundancy && celt_to_silk && fits {
redundant_rng = encode_celt_redundancy(
state_mut(&mut self.celt_enc),
&self.buf_celt_pcm[..f5 * ch],
f5,
&mut redundant[..redundancy_bytes],
)?;
self.celt_enc.reset_state();
}
if mode != OpusMode::SilkOnly {
let start_band = if mode == OpusMode::Hybrid { HYBRID_START_BAND } else { 0 };
let celt_bitrate = if self.use_cbr {
OPUS_BITRATE_MAX
} else if mode == OpusMode::Hybrid {
(self.packet_bitrate_bps - hybrid_silk_rate).max(501)
} else {
self.packet_bitrate_bps.max(501)
};
let _ = self.celt_enc.set_bitrate(celt_bitrate);
self.celt_enc.set_vbr(!self.use_cbr);
self.celt_enc
.set_constrained_vbr(mode == OpusMode::CeltOnly && self.vbr_constraint);
#[cfg(feature = "dred")]
if self.use_cbr && self.dred.duration() > 0 {
let mut celt_bitrate = self.packet_bitrate_bps;
if mode == OpusMode::Hybrid {
celt_bitrate -= hybrid_silk_rate;
}
self.celt_enc.set_vbr(true);
self.celt_enc.set_constrained_vbr(false);
let _ = self.celt_enc.set_bitrate(celt_bitrate);
}
let _ = self.celt_enc.set_start_band(start_band);
if celt_restart {
self.celt_enc.reset_state();
let mut prefill_rc = RangeCoder::new_encoder(2);
self.celt_enc
.encode(&self.celt_prefill[..f2_5 * ch], f2_5, &mut prefill_rc);
let _ = self.celt_enc.set_prediction(0);
}
if fits {
self.celt_enc
.encode(&self.buf_celt_pcm[..frame_size * ch], frame_size, &mut self.rc);
}
}
self.rc.done();
self.prev_enc_mode = Some(if d.to_celt { OpusMode::CeltOnly } else { mode });
self.prev_channels = sch;
self.prev_frame_size = frame_size;
self.first = false;
if self.use_dtx && !self.silk_use_dtx {
let frame_ms_q1 = 2 * 1000 * frame_size as i32 / fs;
if decide_dtx_mode(activity, &mut self.nb_no_activity_ms_q1, frame_ms_q1) {
if redundancy && !celt_to_silk {
let mut redundant = [0u8; 257];
self.encode_silk_to_celt_redundancy(frame_size, &mut redundant[..redundancy_bytes])?;
} else if d.to_celt {
self.celt_enc.reset_state();
}
self.range_final = 0;
output[0] = toc;
return Ok(1);
}
} else {
self.nb_no_activity_ms_q1 = 0;
}
if !fits || self.rc.error != 0 || self.rc.tell() > 8 * nb_compr_bytes as i32 {
output[0] = toc;
output[1] = 0;
self.silk_bw_switch = false;
if d.to_celt {
self.celt_enc.reset_state();
}
return self.finish_frame(output, 2, first_frame);
}
self.range_final = self.rc.rng;
if mode == OpusMode::SilkOnly {
if !redundancy {
while nb_compr_bytes > 2 && self.rc.buf[nb_compr_bytes - 1] == 0 {
nb_compr_bytes -= 1;
}
}
} else {
nb_compr_bytes = (self.rc.storage as usize).min(nb_compr_bytes);
}
if redundancy && !celt_to_silk {
redundant_rng =
self.encode_silk_to_celt_redundancy(frame_size, &mut redundant[..redundancy_bytes])?;
} else if d.to_celt {
self.celt_enc.reset_state();
}
output[0] = toc;
output[1..1 + nb_compr_bytes].copy_from_slice(&self.rc.buf[..nb_compr_bytes]);
let mut ret = 1 + nb_compr_bytes;
output[ret..ret + redundancy_bytes].copy_from_slice(&redundant[..redundancy_bytes]);
ret += redundancy_bytes;
self.range_final ^= redundant_rng;
self.finish_frame(output, ret, first_frame)
}
fn finish_frame(&mut self, output: &mut [u8], ret: usize, first_frame: bool) -> Result<usize, Error> {
let orig_max_data_bytes = output.len();
#[cfg(feature = "dred")]
if first_frame && self.dred_on() {
use crate::dnn::dred::{DRED_EXTENSION_ID, DRED_MAX_DATA_SIZE};
let mut buf = [0u8; DRED_MAX_DATA_SIZE];
let room = orig_max_data_bytes as i32 - ret as i32 - 3;
let n = self.dred.write_extension(&mut self.rc, &mut buf, room, !self.use_cbr);
if n > 0 {
let ext = [extensions::Extension::new(DRED_EXTENSION_ID, 0, &buf[..n])];
let padding = packet::Padding::from_flag(self.use_cbr);
return packet::pad_with_extensions(output, ret, orig_max_data_bytes, padding, &ext)
.map_err(|_| Error::INTERNAL_ERROR);
}
}
#[cfg(not(feature = "dred"))]
let _ = first_frame;
if self.use_cbr {
packet::pad(output, ret, orig_max_data_bytes)?;
return Ok(orig_max_data_bytes);
}
Ok(ret)
}
#[inline]
fn dred_on(&self) -> bool {
#[cfg(feature = "dred")]
{
self.dred.duration() > 0
}
#[cfg(not(feature = "dred"))]
false
}
#[inline]
fn dred_bitrate_bps(&self) -> i32 {
#[cfg(feature = "dred")]
{
self.dred.bitrate_bps()
}
#[cfg(not(feature = "dred"))]
0
}
fn encode_silk_to_celt_redundancy(
&mut self,
frame_size: usize,
out: &mut [u8],
) -> Result<u32, Error> {
let ch = self.channels;
let f2_5 = self.sampling_rate as usize / 400;
let f5 = 2 * f2_5;
self.celt_enc.reset_state();
let _ = self.celt_enc.set_prediction(0);
let warm_up = (frame_size - f5 - f2_5) * ch..(frame_size - f5) * ch;
let mut dummy = [0u8; 2];
let _ = encode_celt_redundancy(
state_mut(&mut self.celt_enc),
&self.buf_celt_pcm[warm_up],
f2_5,
&mut dummy,
);
encode_celt_redundancy(
state_mut(&mut self.celt_enc),
&self.buf_celt_pcm[(frame_size - f5) * ch..frame_size * ch],
f5,
out,
)
}
fn write_placeholder(
&self,
frame_rate: i32,
max_data_bytes: usize,
output: &mut [u8],
) -> Result<usize, Error> {
let mut toc_mode = self.mode;
let mut bw = self.bandwidth;
let mut frame_rate = frame_rate;
let mut packet_code = 0u8;
let mut num_multiframes = 0u8;
if frame_rate > 100 {
toc_mode = OpusMode::CeltOnly;
}
if frame_rate == 25 && toc_mode != OpusMode::SilkOnly {
frame_rate = 50;
packet_code = 1;
}
if frame_rate <= 16 {
if output.len() == 1 || (toc_mode == OpusMode::SilkOnly && frame_rate != 10) {
toc_mode = OpusMode::SilkOnly;
packet_code = (frame_rate <= 12) as u8;
frame_rate = if frame_rate == 12 { 25 } else { 16 };
} else {
num_multiframes = (50 / frame_rate) as u8;
frame_rate = 50;
packet_code = 3;
}
}
let index = bandwidth_fec_index(bw);
if toc_mode == OpusMode::SilkOnly && index > 2 {
bw = Bandwidth::Wideband;
} else if toc_mode == OpusMode::CeltOnly && bw == Bandwidth::Mediumband {
bw = Bandwidth::Narrowband;
} else if toc_mode == OpusMode::Hybrid && index <= 3 {
bw = Bandwidth::Superwideband;
}
output[0] = gen_toc(toc_mode, frame_rate, bw, self.stream_channels) | packet_code;
let mut ret = if packet_code <= 1 { 1 } else { 2 };
let max_data_bytes = max_data_bytes.max(ret);
if packet_code == 3 {
output[1] = num_multiframes;
}
if self.use_cbr {
packet::pad(output, ret, max_data_bytes)?;
ret = max_data_bytes;
}
Ok(ret)
}
#[allow(clippy::too_many_arguments)]
fn encode_multiframe(
&mut self,
input: &[f32],
frame_size: usize,
decisions: FrameDecisions,
cbr_bytes: usize,
output: &mut [u8],
analysis_cursor: Option<(usize, i32)>,
lsb_depth: i32,
) -> Result<usize, Error> {
const MAX_SUB: usize = 6;
let ch = self.channels;
let fs = self.sampling_rate;
let fs_u = fs as usize;
let enc_frame = if decisions.mode == OpusMode::SilkOnly {
if frame_size == 2 * fs_u / 25 {
fs_u / 25
} else if frame_size == 3 * fs_u / 25 {
3 * fs_u / 50
} else {
fs_u / 50
}
} else {
fs_u / 50
};
if enc_frame == 0 || !frame_size.is_multiple_of(enc_frame) {
return Err(Error::internal("invalid multi-frame size"));
}
let nb = frame_size / enc_frame;
if !(2..=MAX_SUB).contains(&nb) {
return Err(Error::internal("unsupported multi-frame size"));
}
let max_header_bytes = if nb == 2 { 3 } else { 2 + (nb - 1) * 2 };
let repacketize_len = if self.use_cbr && self.user_bitrate != Bitrate::Max {
cbr_bytes.min(output.len())
} else {
output.len()
};
let max_len_sum = (nb + repacketize_len).saturating_sub(max_header_bytes);
let nominal_part =
(bitrate_to_bits(self.packet_bitrate_bps, fs, enc_frame) / 8).max(0) as usize;
let mut tmp: FixedVec<u8, MULTIFRAME_BYTES> = FixedVec::new();
let mut lens = [0usize; MAX_SUB];
let mut tot_size = 0usize;
let mut dtx_count = 0usize;
let mut result = Ok(());
let bak_to_mono = self.silk_to_mono;
if bak_to_mono {
self.force_channels = Some(1);
} else {
self.prev_channels = self.stream_channels;
}
for i in 0..nb {
let start = i * enc_frame * ch;
let end = start + enc_frame * ch;
let sub_input = &input[start..end];
let info = match analysis_cursor {
Some(cursor) => self.frame_analysis_info((i == 0).then_some(cursor), enc_frame),
None => AnalysisInfo::default(),
};
let frame_analysis = FrameAnalysis {
info,
is_silence: is_digital_silence(sub_input, lsb_depth),
};
self.silk_to_mono = false;
self.nonfinal_frame = i + 1 < nb;
let frame_to_celt = decisions.to_celt && i == nb - 1;
let frame_decisions = FrameDecisions {
to_celt: frame_to_celt,
redundancy: decisions.redundancy
&& (frame_to_celt || (!decisions.to_celt && i == 0)),
..decisions
};
let first_frame = i == 0 || i == dtx_count;
#[allow(unused_mut)]
let mut curr_max = max_len_sum / nb;
if self.use_cbr {
curr_max = curr_max.min(nominal_part);
}
#[cfg(feature = "dred")]
{
let dred_bytes = (bitrate_to_bits(self.dred.bitrate_bps(), fs, frame_size) / 8).max(0) as usize;
curr_max = curr_max.min(max_len_sum.saturating_sub(dred_bytes) / nb);
if first_frame {
curr_max += dred_bytes;
}
}
let curr_max = curr_max.min(max_len_sum.saturating_sub(tot_size));
let room = MULTIFRAME_BYTES - tot_size;
if self.use_cbr && curr_max > room {
result = Err(Error::internal("a CBR frame does not fit the multi-frame buffer"));
break;
}
let curr_max = curr_max.min(room);
if tmp.len() < tot_size + curr_max {
tmp.resize(tot_size + curr_max, 0);
}
match self.encode_frame(
sub_input,
enc_frame,
&mut tmp[tot_size..tot_size + curr_max],
frame_decisions,
frame_analysis,
first_frame,
) {
Ok(n) => {
lens[i] = n;
tot_size += n;
if n == 1 {
dtx_count += 1;
}
}
Err(e) => {
result = Err(e);
break;
}
}
}
self.nonfinal_frame = false;
self.silk_to_mono = bak_to_mono;
result?;
let mut rp = packet::Repacketizer::new();
let mut start = 0;
for &len in lens.iter().take(nb) {
let part = &tmp[start..start + len];
start += len;
if packet::nb_frames(part) != Ok(1) {
return Err(Error::internal("unexpected frame count in a part of a packet"));
}
rp.cat(part).map_err(|_| Error::INTERNAL_ERROR)?;
}
rp.out_range_ext(
0,
nb,
&mut output[..repacketize_len],
packet::Framing::Normal,
packet::Padding::from_flag(self.use_cbr && dtx_count != nb),
&[],
)
.map_err(|_| Error::INTERNAL_ERROR)
}
}
use celt::PcmLayout;
#[cfg(feature = "osce-bwe")]
use dnn::osce::OsceExtMode;
pub struct OpusDecoder {
#[cfg(not(feature = "heap"))]
celt_dec: CeltDecoder,
#[cfg(feature = "heap")]
celt_dec: Box<CeltDecoder>,
#[cfg(not(feature = "heap"))]
silk_dec: silk::dec_api::SilkDecoder,
#[cfg(feature = "heap")]
silk_dec: Box<silk::dec_api::SilkDecoder>,
sampling_rate: i32,
channels: usize,
mode: Option<OpusMode>,
prev_mode: Option<OpusMode>,
bandwidth: Option<Bandwidth>,
prev_redundancy: bool,
frame_size: usize,
stream_channels: usize,
range_final: u32,
last_packet_duration: usize,
softclip_mem: [f32; OPUS_MAX_CHANNELS],
silk_channels_internal: usize,
silk_internal_rate: i32,
#[cfg(not(feature = "heap"))]
w_pcm_i16: FixedVec<i16, OPUS_PCM_I16>,
#[cfg(feature = "heap")]
w_pcm_i16: Box<FixedVec<i16, OPUS_PCM_I16>>,
#[cfg(feature = "heap")]
w_pcm_float: Option<Box<FixedVec<f32, OPUS_PCM_PACKET>>>,
complexity: i32,
decode_gain: i32,
silk_prev_pitch_lag: i32,
#[cfg(feature = "osce-bwe")]
osce_bwe: bool,
#[cfg(all(feature = "dnn", feature = "heap"))]
dnn: Box<dnn::DecoderDnn>,
#[cfg(all(feature = "dnn", not(feature = "heap")))]
dnn: dnn::DecoderDnn,
#[cfg(all(feature = "deep-plc", feature = "heap"))]
deep_plc: Box<dnn::plc::DeepPlc>,
#[cfg(all(feature = "deep-plc", not(feature = "heap")))]
deep_plc: dnn::plc::DeepPlc,
}
impl core::fmt::Debug for OpusDecoder {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut d = f.debug_struct("OpusDecoder");
d.field("sampling_rate", &self.sampling_rate())
.field("channels", &self.channels())
.field("complexity", &self.complexity())
.field("gain", &self.gain())
.field("phase_inversion_disabled", &self.phase_inversion_disabled());
#[cfg(feature = "osce-bwe")]
d.field("osce_bwe", &self.osce_bwe);
d.finish_non_exhaustive()
}
}
impl OpusDecoder {
pub fn new(sampling_rate: SampleRate, channels: Channels) -> Result<Self, Error> {
let mut dec = MaybeUninit::<Self>::uninit();
Self::init(&mut dec, sampling_rate, channels)?;
Ok(unsafe { dec.assume_init() })
}
pub fn init(
slot: &mut MaybeUninit<Self>,
sampling_rate: SampleRate,
channels: Channels,
) -> Result<&mut Self, Error> {
unsafe {
Self::write_new(slot.as_mut_ptr(), sampling_rate.hz() as i32, channels.count());
Ok(slot.assume_init_mut())
}
}
unsafe fn write_new(this: *mut Self, sampling_rate: i32, channels: usize) {
let mode = modes::default_mode();
unsafe {
write_fields!(this, {
#[cfg(not(feature = "heap"))]
celt_dec = |p| CeltDecoder::write_new(p, mode, channels, sampling_rate),
#[cfg(feature = "heap")]
celt_dec = |p: *mut Box<CeltDecoder>| {
let celt = |q| CeltDecoder::write_new(q, mode, channels, sampling_rate);
p.write(in_place::boxed(celt))
},
#[cfg(not(feature = "heap"))]
silk_dec = |p| silk::dec_api::SilkDecoder::write_new(p),
#[cfg(feature = "heap")]
silk_dec = |p: *mut Box<silk::dec_api::SilkDecoder>| {
p.write(in_place::boxed(|q| silk::dec_api::SilkDecoder::write_new(q)))
},
sampling_rate: sampling_rate,
channels: channels,
mode: None,
prev_mode: None,
bandwidth: None,
prev_redundancy: false,
frame_size: sampling_rate as usize / 400,
stream_channels: channels,
range_final: 0,
last_packet_duration: 0,
softclip_mem: [0.0; OPUS_MAX_CHANNELS],
silk_channels_internal: 0,
silk_internal_rate: 0,
#[cfg(not(feature = "heap"))]
w_pcm_i16 = |p| FixedVec::write_from_value(p, 0i16, 960 * channels),
#[cfg(feature = "heap")]
w_pcm_i16 = |p: *mut Box<FixedVec<i16, OPUS_PCM_I16>>| {
p.write(in_place::boxed(|q| FixedVec::write_from_value(q, 0i16, 960 * channels)))
},
#[cfg(feature = "heap")]
w_pcm_float: Some(in_place::boxed(|q| FixedVec::write_new(q))),
complexity: 0,
decode_gain: 0,
silk_prev_pitch_lag: 0,
#[cfg(feature = "osce-bwe")]
osce_bwe: false,
#[cfg(all(feature = "dnn", not(feature = "heap")))]
dnn = |p| dnn::DecoderDnn::write_new(p),
#[cfg(all(feature = "dnn", feature = "heap"))]
dnn = |p: *mut Box<dnn::DecoderDnn>| {
p.write(in_place::boxed(|q| dnn::DecoderDnn::write_new(q)))
},
#[cfg(all(feature = "deep-plc", not(feature = "heap")))]
deep_plc = |p| dnn::plc::DeepPlc::write_new(p),
#[cfg(all(feature = "deep-plc", feature = "heap"))]
deep_plc = |p: *mut Box<dnn::plc::DeepPlc>| {
p.write(in_place::boxed(|q| dnn::plc::DeepPlc::write_new(q)))
},
});
}
}
pub fn decode(
&mut self,
input: &[u8],
frame_size: usize,
output: &mut [f32],
) -> Result<usize, Error> {
let (samples, _) = self.decode_native(input, frame_size, output, false, false, false)?;
Ok(samples)
}
pub fn decode_fec(
&mut self,
data: &[u8],
frame_size: usize,
output: &mut [f32],
) -> Result<usize, Error> {
self.decode_native(data, frame_size, output, true, false, false)
.map(|(samples, _)| samples)
}
pub fn decode_i16(
&mut self,
input: &[u8],
frame_size: usize,
output: &mut [i16],
) -> Result<usize, Error> {
self.decode_i16_native(input, frame_size, output, false)
}
pub fn decode_fec_i16(
&mut self,
data: &[u8],
frame_size: usize,
output: &mut [i16],
) -> Result<usize, Error> {
self.decode_i16_native(data, frame_size, output, true)
}
pub fn decode24(
&mut self,
input: &[u8],
frame_size: usize,
output: &mut [i32],
) -> Result<usize, Error> {
self.decode_int_native(input, frame_size, output, false, false, res_to_int24)
}
pub fn decode_fec24(
&mut self,
data: &[u8],
frame_size: usize,
output: &mut [i32],
) -> Result<usize, Error> {
self.decode_int_native(data, frame_size, output, true, false, res_to_int24)
}
#[cfg(feature = "dred")]
pub fn dred_decode(
&mut self,
dred: &OpusDred,
dred_offset: i32,
frame_size: usize,
output: &mut [f32],
) -> Result<usize, Error> {
self.check_dred_decode(frame_size, output.len())?;
self.queue_dred_features(dred, dred_offset, frame_size);
self.decode_native(&[], frame_size, output, false, false, false)
.map(|(samples, _)| samples)
}
#[cfg(feature = "dred")]
pub fn dred_decode_i16(
&mut self,
dred: &OpusDred,
dred_offset: i32,
frame_size: usize,
output: &mut [i16],
) -> Result<usize, Error> {
self.dred_decode_int(dred, dred_offset, frame_size, output, float2int16)
}
#[cfg(feature = "dred")]
pub fn dred_decode24(
&mut self,
dred: &OpusDred,
dred_offset: i32,
frame_size: usize,
output: &mut [i32],
) -> Result<usize, Error> {
self.dred_decode_int(dred, dred_offset, frame_size, output, float2int24)
}
#[cfg(feature = "dred")]
fn check_dred_decode(&self, frame_size: usize, output_len: usize) -> Result<(), Error> {
if frame_size == 0 {
return Err(Error::INVALID_FRAME_SIZE);
}
if !is_whole_2_5ms(frame_size, self.sampling_rate) {
return Err(Error::LOST_FRAME_SIZE);
}
if frame_size.checked_mul(self.channels).is_none_or(|len| len > output_len) {
return Err(Error::BUFFER_TOO_SMALL);
}
Ok(())
}
#[cfg(feature = "dred")]
fn queue_dred_features(&mut self, dred: &OpusDred, dred_offset: i32, frame_size: usize) {
use dnn::models::dred_rdovae_stats::DRED_NUM_FEATURES;
if !dred.is_processed() {
return;
}
let plc = &mut self.deep_plc;
plc.fec_clear();
let f10 = self.sampling_rate / 100;
let init_frames: i32 = if plc.blend() { 0 } else { 2 };
let features_per_frame = (frame_size / f10 as usize).max(1) as i32;
let needed_feature_frames = init_frames + features_per_frame;
let newest = (dred_offset as f32 + (dred.dred_offset * f10 / 4) as f32) / f10 as f32;
let newest = newest.floor() as i32;
for i in 0..needed_feature_frames {
let feature_offset = (init_frames - i - 2).wrapping_add(newest);
if feature_offset >= 0 && feature_offset < 4 * dred.nb_latents {
let start = feature_offset as usize * DRED_NUM_FEATURES;
let features = dred.fec_features[start..start + DRED_NUM_FEATURES].try_into().ok();
plc.fec_add(features);
} else if feature_offset >= 0 {
plc.fec_add(None);
}
}
}
#[cfg(feature = "dred")]
fn dred_decode_int<T>(
&mut self,
dred: &OpusDred,
dred_offset: i32,
frame_size: usize,
output: &mut [T],
convert: fn(f32) -> T,
) -> Result<usize, Error> {
self.check_dred_decode(frame_size, output.len())?;
self.queue_dred_features(dred, dred_offset, frame_size);
let saved_duration = self.last_packet_duration;
let channels = self.channels;
let max_samples = packet::max_packet_samples(self.sampling_rate);
let step = max_samples / 2;
#[cfg(feature = "heap")]
let mut pcm = self.w_pcm_float.take().unwrap_or_default();
#[cfg(not(feature = "heap"))]
let mut pcm: FixedVec<f32, OPUS_PCM_PACKET> = FixedVec::new();
let mut done = 0;
let result = loop {
let left = frame_size - done;
let piece = if left > max_samples { step } else { left };
let len = piece * channels;
if pcm.len() < len {
pcm.resize(len, 0.0);
}
match self.decode_native(&[], piece, &mut pcm[..len], false, false, true) {
Ok((samples, _)) => {
let out = &mut output[done * channels..(done + samples) * channels];
for (o, &x) in out.iter_mut().zip(&pcm[..samples * channels]) {
*o = convert(x);
}
done += samples;
}
Err(e) => break Err(e),
}
if piece == left {
break Ok(done);
}
};
#[cfg(feature = "heap")]
{
self.w_pcm_float = Some(pcm);
}
self.last_packet_duration = if result.is_ok() { frame_size } else { saved_duration };
result
}
fn decode_i16_native(
&mut self,
data: &[u8],
frame_size: usize,
output: &mut [i16],
decode_fec: bool,
) -> Result<usize, Error> {
self.decode_int_native(data, frame_size, output, decode_fec, true, celt_float2int16)
}
fn decode_int_native<T>(
&mut self,
data: &[u8],
frame_size: usize,
output: &mut [T],
decode_fec: bool,
soft_clip: bool,
convert: fn(&[f32], &mut [T]),
) -> Result<usize, Error> {
if frame_size == 0 {
return Err(Error::INVALID_FRAME_SIZE);
}
let channels = self.channels;
let lost = decode_fec || data.is_empty();
let max_samples = packet::max_packet_samples(self.sampling_rate);
let step = max_samples / 2;
if lost && frame_size > max_samples {
if !is_whole_2_5ms(frame_size, self.sampling_rate) {
return Err(Error::LOST_FRAME_SIZE);
}
if frame_size.checked_mul(channels).is_none_or(|len| len > output.len()) {
return Err(Error::BUFFER_TOO_SMALL);
}
if !data.is_empty() {
packet::parse(data, packet::Framing::Normal)?;
}
}
let split = lost && frame_size > max_samples;
let saved_duration = self.last_packet_duration;
#[cfg(feature = "heap")]
let mut pcm = self.w_pcm_float.take().unwrap_or_default();
#[cfg(not(feature = "heap"))]
let mut pcm: FixedVec<f32, OPUS_PCM_PACKET> = FixedVec::new();
let mut done = 0;
let result = loop {
let last = !(lost && frame_size - done > max_samples);
let (data, piece, decode_fec) = if last {
(data, frame_size - done, decode_fec)
} else {
(&[][..], step, false)
};
let output = &mut output[done * channels..];
let needed = if lost {
piece
} else {
match packet::nb_samples(data, self.sampling_rate()) {
Ok(samples) => piece.min(samples),
Err(e) => break Err(e),
}
};
let len = output.len().min(needed * channels);
if pcm.len() < len {
pcm.resize(len, 0.0);
}
match self.decode_native(data, piece, &mut pcm[..len], decode_fec, false, soft_clip) {
Ok((samples, _)) => {
convert(&pcm[..samples * channels], output);
done += samples;
}
Err(e) => break Err(e),
}
if last {
break Ok(done);
}
};
#[cfg(feature = "heap")]
{
self.w_pcm_float = Some(pcm);
}
if split {
self.last_packet_duration = if result.is_ok() { frame_size } else { saved_duration };
}
result
}
pub(crate) fn decode_native(
&mut self,
data: &[u8],
frame_size: usize,
output: &mut [f32],
decode_fec: bool,
self_delimited: bool,
soft_clip: bool,
) -> Result<(usize, usize), Error> {
if frame_size == 0 {
return Err(Error::INVALID_FRAME_SIZE);
}
let channels = self.channels;
if (decode_fec || data.is_empty()) && !is_whole_2_5ms(frame_size, self.sampling_rate) {
return Err(Error::LOST_FRAME_SIZE);
}
let lost_len = frame_size.checked_mul(channels);
if data.is_empty() {
let lost_len = lost_len
.filter(|&len| len <= output.len())
.ok_or(Error::BUFFER_TOO_SMALL)?;
self.conceal(frame_size, &mut output[..lost_len])?;
self.last_packet_duration = frame_size;
return Ok((frame_size, 0));
}
let toc = data[0];
let packet_mode = mode_from_toc(toc);
let packet_bandwidth = packet::bandwidth(toc);
let packet_frame_size = packet::samples_per_frame(toc, self.sampling_rate());
let packet_channels = packet::nb_channels(toc).count();
let parsed = packet::parse(data, packet::Framing::from_flag(self_delimited))?;
if decode_fec {
if lost_len.is_none_or(|len| len > output.len()) {
return Err(Error::BUFFER_TOO_SMALL);
}
if frame_size < packet_frame_size
|| packet_mode == OpusMode::CeltOnly
|| self.mode == Some(OpusMode::CeltOnly)
{
self.conceal(frame_size, &mut output[..frame_size * channels])?;
self.last_packet_duration = frame_size;
return Ok((frame_size, parsed.packet_offset()));
}
let frame = parsed.frame(data, 0);
let lead = frame_size - packet_frame_size;
if lead != 0 {
self.conceal(lead, &mut output[..lead * channels])?;
self.last_packet_duration = lead;
}
self.mode = Some(packet_mode);
self.bandwidth = Some(packet_bandwidth);
self.frame_size = packet_frame_size;
self.stream_channels = packet_channels;
self.decode_or_conceal_frame(
packet_mode,
packet_bandwidth,
frame,
packet_frame_size,
&mut output[lead * channels..frame_size * channels],
true,
)?;
self.last_packet_duration = frame_size;
return Ok((frame_size, parsed.packet_offset()));
}
let decoded_total = parsed.nb_frames() * packet_frame_size;
if decoded_total > frame_size {
return Err(Error::FRAME_SIZE_TOO_SMALL);
}
if output.len() < decoded_total * channels {
return Err(Error::BUFFER_TOO_SMALL);
}
self.mode = Some(packet_mode);
self.bandwidth = Some(packet_bandwidth);
self.frame_size = packet_frame_size;
self.stream_channels = packet_channels;
let frame_len = packet_frame_size * channels;
for (frame, out) in parsed.frames(data).zip(output.chunks_exact_mut(frame_len)) {
self.decode_or_conceal_frame(
packet_mode,
packet_bandwidth,
frame,
packet_frame_size,
out,
false,
)?;
}
self.last_packet_duration = decoded_total;
if soft_clip {
crate::soft_clip(
&mut output[..decoded_total * channels],
decoded_total,
channels,
&mut self.softclip_mem,
);
} else {
self.softclip_mem = [0.0; OPUS_MAX_CHANNELS];
}
Ok((decoded_total, parsed.packet_offset()))
}
fn decode_or_conceal_frame(
&mut self,
mode: OpusMode,
bandwidth: Bandwidth,
payload: &[u8],
frame_size: usize,
out: &mut [f32],
decode_fec: bool,
) -> Result<(), Error> {
if payload.len() <= 1 {
self.conceal(frame_size, out)
} else {
self.decode_frame(mode, bandwidth, payload, frame_size, out, decode_fec)
}
}
pub fn final_range(&self) -> u32 {
self.range_final
}
pub fn last_packet_duration(&self) -> usize {
self.last_packet_duration
}
pub fn bandwidth(&self) -> Option<Bandwidth> {
self.bandwidth
}
pub fn nb_samples(&self, packet: &[u8]) -> Result<usize, Error> {
packet::nb_samples(packet, self.sampling_rate())
}
pub fn reset_state(&mut self) {
self.celt_dec.reset_state();
self.silk_dec.reset();
self.mode = None;
self.prev_mode = None;
self.bandwidth = None;
self.prev_redundancy = false;
self.frame_size = self.sampling_rate as usize / 400;
self.stream_channels = self.channels;
self.range_final = 0;
self.last_packet_duration = 0;
self.softclip_mem = [0.0; OPUS_MAX_CHANNELS];
#[cfg(feature = "deep-plc")]
self.deep_plc.reset();
}
pub fn sampling_rate(&self) -> SampleRate {
SampleRate::from_valid_hz(self.sampling_rate)
}
pub fn set_complexity(&mut self, complexity: u8) -> Result<(), Error> {
if complexity > 10 {
return Err(Error::INVALID_COMPLEXITY);
}
let complexity = i32::from(complexity);
self.complexity = complexity;
self.celt_dec.set_complexity(complexity)?;
Ok(())
}
pub fn complexity(&self) -> u8 {
self.complexity as u8
}
pub fn set_phase_inversion_disabled(&mut self, disabled: bool) {
self.celt_dec.set_phase_inversion_disabled(disabled);
}
pub fn phase_inversion_disabled(&self) -> bool {
self.celt_dec.phase_inversion_disabled()
}
pub fn set_gain(&mut self, gain: i16) {
self.decode_gain = i32::from(gain);
}
pub fn gain(&self) -> i16 {
self.decode_gain as i16
}
pub fn pitch(&self) -> i32 {
if self.prev_mode == Some(OpusMode::CeltOnly) {
self.celt_dec.pitch()
} else {
self.silk_prev_pitch_lag
}
}
#[allow(clippy::excessive_precision)]
fn apply_gain(&self, pcm: &mut [f32]) {
if self.decode_gain != 0 {
let gain = mathops::celt_exp2(6.488_140_81e-4_f32 * self.decode_gain as f32);
for x in pcm {
*x *= gain;
}
}
}
#[cfg(feature = "osce-bwe")]
pub fn set_osce_bwe(&mut self, enabled: bool) {
self.osce_bwe = enabled;
}
#[cfg(feature = "osce-bwe")]
pub fn osce_bwe(&self) -> bool {
self.osce_bwe
}
#[cfg(feature = "deep-plc")]
pub fn set_dnn_blob(&mut self, blob: &DnnBlob) -> Result<DnnModels, Error> {
self.dnn.load(blob)
}
#[cfg(feature = "deep-plc")]
pub fn dnn_models(&self) -> DnnModels {
self.dnn.models()
}
pub fn channels(&self) -> Channels {
Channels::from_valid_count(self.channels)
}
fn decode_frame(
&mut self,
mode: OpusMode,
bandwidth: Bandwidth,
payload: &[u8],
frame_size: usize,
out: &mut [f32],
decode_fec: bool,
) -> Result<(), Error> {
let channels = self.channels;
let f2_5 = self.sampling_rate as usize / 400;
let f5 = 2 * f2_5;
let frame_len = frame_size * channels;
let prev_mode = self.prev_mode;
let mut len = payload.len();
let mut rc = RangeCoder::new_decoder(payload);
let mut transition = match prev_mode {
Some(prev) => {
(mode == OpusMode::CeltOnly
&& prev != OpusMode::CeltOnly
&& !self.prev_redundancy)
|| (mode != OpusMode::CeltOnly && prev == OpusMode::CeltOnly)
}
None => false,
};
let mut pcm_transition: FixedVec<f32, OPUS_PCM_TAIL> = FixedVec::new();
let transition_len = f5.min(frame_size);
if transition && mode == OpusMode::CeltOnly {
pcm_transition.resize(f5 * channels, 0.0);
self.conceal(transition_len, &mut pcm_transition[..transition_len * channels])?;
}
if mode != OpusMode::CeltOnly {
if prev_mode == Some(OpusMode::CeltOnly) {
self.silk_dec.reset();
}
self.silk_channels_internal = self.stream_channels;
self.silk_internal_rate = if mode == OpusMode::SilkOnly {
1000 * bandwidth.silk_khz()
} else {
16000
};
let lost_flag = if decode_fec {
silk::decode_frame::FLAG_DECODE_LBRR
} else {
silk::decode_frame::FLAG_DECODE_NORMAL
};
self.decode_silk(&mut rc, lost_flag, frame_size, mode, out)?;
}
let mut redundancy = false;
let mut celt_to_silk = false;
let mut redundancy_bytes = 0usize;
let mut redundant_rng = 0u32;
let hybrid = mode == OpusMode::Hybrid;
if !decode_fec
&& mode != OpusMode::CeltOnly
&& rc.tell() + 17 + if hybrid { 20 } else { 0 } <= 8 * len as i32
{
redundancy = if hybrid {
rc.decode_bit_logp(12)
} else {
true
};
if redundancy {
celt_to_silk = rc.decode_bit_logp(1);
redundancy_bytes = if hybrid {
rc.dec_uint(256) as usize + 2
} else {
len - ((rc.tell() + 7) >> 3) as usize
};
if redundancy_bytes > len || ((len - redundancy_bytes) * 8) < rc.tell() as usize {
len = 0;
redundancy_bytes = 0;
redundancy = false;
} else {
len -= redundancy_bytes;
rc.storage -= redundancy_bytes as u32;
}
}
}
let start_band = if mode != OpusMode::CeltOnly { HYBRID_START_BAND } else { 0 };
if redundancy {
transition = false;
}
if transition && mode != OpusMode::CeltOnly {
pcm_transition.resize(f5 * channels, 0.0);
self.conceal(transition_len, &mut pcm_transition[..transition_len * channels])?;
}
self.celt_dec.set_end_band(bandwidth.celt_end_band())?;
self.celt_dec.set_stream_channels(self.stream_channels)?;
let mut redundant_audio: FixedVec<f32, OPUS_PCM_TAIL> = FixedVec::new();
if redundancy {
redundant_audio.resize(f5 * channels, 0.0);
}
if redundancy && celt_to_silk {
let mut rc_red = RangeCoder::new_decoder(&payload[len..len + redundancy_bytes]);
let bits = (redundancy_bytes * 8) as i32;
self.celt_dec.set_start_band(0)?;
let layout = PcmLayout::Interleaved;
self.celt_dec.decode(&mut rc_red, bits, f5, &mut redundant_audio, layout);
redundant_rng = self.celt_dec.final_range();
}
if mode != OpusMode::SilkOnly {
if prev_mode.is_some() && prev_mode != Some(mode) && !self.prev_redundancy {
self.celt_dec.reset_state();
}
self.celt_dec.set_start_band(start_band)?;
let layout = if hybrid { PcmLayout::InterleavedAdd } else { PcmLayout::Interleaved };
if decode_fec {
with_deep_plc!(self, |p| {
self.celt_dec.decode_lost_with_deep_plc(frame_size, out, layout, p)
})?;
} else {
let bits = (len * 8) as i32;
let decoded = with_deep_plc!(self, |p| {
self.celt_dec.decode_with_deep_plc(&mut rc, bits, frame_size, out, layout, p)
});
if decoded != frame_size {
return Err(Error::internal("CELT decoding failed"));
}
}
} else if prev_mode == Some(OpusMode::Hybrid)
&& !(redundancy && celt_to_silk && self.prev_redundancy)
{
let mut rc_silence = RangeCoder::new_decoder(&[0xFF, 0xFF]);
let fade = &mut out[..f2_5 * channels];
self.celt_dec.set_start_band(0)?;
self.celt_dec.decode(&mut rc_silence, 16, f2_5, fade, PcmLayout::InterleavedAdd);
}
let frame_rng = if mode != OpusMode::SilkOnly {
self.celt_dec.final_range()
} else {
rc.rng
};
let window = modes::default_mode().window;
let inc = celt::resampling_factor(self.sampling_rate).unwrap_or(1);
let f2_5_ch = f2_5 * channels;
if redundancy && !celt_to_silk {
self.celt_dec.reset_state();
let mut rc_red = RangeCoder::new_decoder(&payload[len..len + redundancy_bytes]);
let bits = (redundancy_bytes * 8) as i32;
self.celt_dec.set_start_band(0)?;
let layout = PcmLayout::Interleaved;
self.celt_dec.decode(&mut rc_red, bits, f5, &mut redundant_audio, layout);
redundant_rng = self.celt_dec.final_range();
let tail = frame_len - f2_5_ch;
let fade = &redundant_audio[f2_5_ch..];
smooth_fade(fade, &mut out[tail..], false, f2_5, channels, window, inc);
}
if redundancy
&& celt_to_silk
&& (prev_mode != Some(OpusMode::SilkOnly) || self.prev_redundancy)
{
out[..f2_5_ch].copy_from_slice(&redundant_audio[..f2_5_ch]);
let fade = &redundant_audio[f2_5_ch..];
smooth_fade(fade, &mut out[f2_5_ch..2 * f2_5_ch], true, f2_5, channels, window, inc);
}
if transition {
if frame_size >= f5 {
out[..f2_5_ch].copy_from_slice(&pcm_transition[..f2_5_ch]);
let fade = &pcm_transition[f2_5_ch..];
let main = &mut out[f2_5_ch..2 * f2_5_ch];
smooth_fade(fade, main, true, f2_5, channels, window, inc);
} else {
let main = &mut out[..f2_5_ch];
smooth_fade(&pcm_transition, main, true, f2_5, channels, window, inc);
}
}
self.apply_gain(&mut out[..frame_len]);
self.range_final = if len <= 1 { 0 } else { frame_rng ^ redundant_rng };
self.prev_mode = Some(mode);
self.prev_redundancy = redundancy && !celt_to_silk;
Ok(())
}
fn conceal(&mut self, total: usize, output: &mut [f32]) -> Result<(), Error> {
let f2_5 = self.sampling_rate as usize / 400;
let (f5, f10, f20) = (2 * f2_5, 4 * f2_5, 8 * f2_5);
let channels = self.channels;
let plc_mode = if self.prev_redundancy {
Some(OpusMode::CeltOnly)
} else {
self.prev_mode
};
let Some(plc_mode) = plc_mode else {
output[..total * channels].fill(0.0);
return Ok(());
};
let mut pos = 0usize;
while pos < total {
let mut left = (total - pos).min(self.frame_size);
while left > 0 {
let limit = left.min(f20);
let piece = if limit > f10 && limit < f20 {
f10
} else if plc_mode != OpusMode::SilkOnly && limit > f5 && limit < f10 {
f5
} else {
limit
};
let dst = &mut output[pos * channels..(pos + piece) * channels];
if plc_mode == OpusMode::CeltOnly {
self.conceal_celt(piece, 0, dst, PcmLayout::Interleaved)?;
} else {
let mut rc = RangeCoder::new_decoder(&[]);
let lost = silk::decode_frame::FLAG_PACKET_LOST;
self.decode_silk(&mut rc, lost, piece, plc_mode, dst)?;
if plc_mode == OpusMode::Hybrid && self.silk_bwe_replaces_celt() {
self.celt_dec.set_stream_channels(self.stream_channels)?;
self.celt_dec.set_start_band(0)?;
let mut rc_silence = RangeCoder::new_decoder(&[0xFF, 0xFF]);
let fade = &mut dst[..f2_5 * channels];
self.celt_dec.decode(&mut rc_silence, 16, f2_5, fade, PcmLayout::InterleavedAdd);
} else if plc_mode == OpusMode::Hybrid {
let layout = PcmLayout::InterleavedAdd;
self.conceal_celt(piece, HYBRID_START_BAND, dst, layout)?;
}
}
pos += piece;
left -= piece;
}
}
self.apply_gain(&mut output[..total * channels]);
self.prev_mode = Some(plc_mode);
self.prev_redundancy = false;
self.range_final = 0;
Ok(())
}
fn conceal_celt(
&mut self,
n: usize,
start_band: usize,
out: &mut [f32],
layout: PcmLayout,
) -> Result<(), Error> {
self.celt_dec.set_stream_channels(self.stream_channels)?;
self.celt_dec.set_start_band(start_band)?;
with_deep_plc!(self, |p| self.celt_dec.decode_lost_with_deep_plc(n, out, layout, p))?;
Ok(())
}
}
impl OpusDecoder {
fn decode_silk(
&mut self,
rc: &mut RangeCoder,
lost_flag: i32,
frame_size: usize,
mode: OpusMode,
out: &mut [f32],
) -> Result<(), Error> {
#[cfg(feature = "osce-bwe")]
let extended_mode = self.osce_extended_mode(mode, lost_flag != silk::decode_frame::FLAG_PACKET_LOST);
#[cfg(feature = "osce-bwe")]
if self.prev_mode == Some(OpusMode::CeltOnly) {
self.silk_dec.prev_osce_extended_mode = OsceExtMode::CeltOnly;
}
#[cfg(feature = "osce")]
let osce_args = osce_args::OsceArgs {
models: dnn::osce::OsceModels::from_decoder(&self.dnn),
method: dnn::osce::OsceMethod::for_complexity(self.complexity),
#[cfg(feature = "osce-bwe")]
extended_mode,
};
#[cfg(feature = "osce")]
let osce = Some(&osce_args);
#[cfg(not(feature = "osce"))]
let osce: Option<&osce_args::OsceArgs> = None;
#[cfg(not(feature = "osce-bwe"))]
let _ = mode;
let channels = self.channels;
let f10 = self.sampling_rate as usize / 100;
let control = silk::decoder_structs::SilkDecControl {
n_channels_api: channels as i32,
n_channels_internal: self.silk_channels_internal as i32,
api_sample_rate: self.sampling_rate,
internal_sample_rate: self.silk_internal_rate,
payload_size_ms: (1000 * frame_size as i32 / self.sampling_rate).max(10),
};
let enable_deep_plc = self.complexity >= 5;
let room = frame_size.max(f10);
let mut done = 0usize;
while done < frame_size {
let decoded = with_deep_plc!(self, |p| {
let (silk_dec, pcm) = (
state_mut(&mut self.silk_dec),
state_mut(&mut self.w_pcm_i16),
);
silk_dec.decode_with_dnn(
&control,
rc,
pcm,
lost_flag,
done == 0,
p,
enable_deep_plc,
osce,
)
});
match decoded {
Ok(n) if n > 0 && done + n <= room => {
self.silk_prev_pitch_lag = self.silk_dec.prev_pitch_lag();
let end = (done + n).min(frame_size);
let silk_out = &mut out[done * channels..end * channels];
for (o, &v) in silk_out.iter_mut().zip(self.w_pcm_i16.iter()) {
*o = int16_to_res(v);
}
done += n;
}
_ if lost_flag != silk::decode_frame::FLAG_DECODE_NORMAL => {
out[done * channels..frame_size * channels].fill(0.0);
done = frame_size;
}
_ => return Err(Error::internal("SILK decoding failed")),
}
}
Ok(())
}
}
impl OpusDecoder {
#[cfg(feature = "osce-bwe")]
fn osce_extended_mode(&self, mode: OpusMode, has_data: bool) -> OsceExtMode {
let bbwe = self.complexity >= 4
&& self.osce_bwe
&& self.sampling_rate == 48000
&& self.silk_internal_rate == 16000
&& (mode == OpusMode::SilkOnly || !has_data)
&& self.dnn.bbwenet().is_some();
if bbwe {
OsceExtMode::SilkBbwe
} else if mode == OpusMode::SilkOnly {
OsceExtMode::SilkOnly
} else {
OsceExtMode::Hybrid
}
}
fn silk_bwe_replaces_celt(&self) -> bool {
#[cfg(feature = "osce-bwe")]
{
self.osce_extended_mode(OpusMode::Hybrid, false) == OsceExtMode::SilkBbwe
}
#[cfg(not(feature = "osce-bwe"))]
{
false
}
}
}
#[inline(always)]
pub(crate) fn int16_to_res(x: i16) -> f32 {
x as f32 * (1.0 / 32768.0)
}
#[inline]
pub(crate) fn float2int16(x: f32) -> i16 {
let x = x * 32768.0;
let x = if x > -32768.0 { x } else { -32768.0 };
let x = if x < 32767.0 { x } else { 32767.0 };
mathops::float2int(x) as i16
}
fn res_to_int24(input: &[f32], output: &mut [i32]) {
let n = input.len().min(output.len());
let (input, output) = (&input[..n], &mut output[..n]);
#[cfg(target_arch = "aarch64")]
let blocks = n / 4 * 4;
#[cfg(not(target_arch = "aarch64"))]
let blocks = 0;
#[cfg(target_arch = "aarch64")]
unsafe {
res_to_int24_blocks_neon(&input[..blocks], &mut output[..blocks]);
}
for (o, &x) in output[blocks..].iter_mut().zip(&input[blocks..]) {
*o = float2int24(x);
}
}
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
#[inline]
fn res_to_int24_blocks_neon(input: &[f32], output: &mut [i32]) {
use crate::simd_mem::neon::{load_f32x4, store_i32x4};
use core::arch::aarch64::{vcvtnq_s32_f32, vmulq_n_f32};
for (src, dst) in input.as_chunks::<4>().0.iter().zip(output.as_chunks_mut::<4>().0) {
store_i32x4(dst, vcvtnq_s32_f32(vmulq_n_f32(load_f32x4(src), 32768.0 * 256.0)));
}
}
#[inline(always)]
pub(crate) fn int24_to_res(x: i32) -> f32 {
x as f32 * (1.0 / 8_388_608.0)
}
pub(crate) fn float2int24(x: f32) -> i32 {
mathops::float2int(32768.0f32 * 256.0 * x)
}
pub(crate) fn celt_float2int16(input: &[f32], output: &mut [i16]) {
let n = input.len().min(output.len());
let (input, output) = (&input[..n], &mut output[..n]);
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
let blocks = n / 16 * 16;
#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
let blocks = n / 8 * 8;
#[cfg(not(any(
target_arch = "aarch64",
target_arch = "arm",
all(target_arch = "x86_64", target_feature = "sse2")
)))]
let blocks = 0;
#[cfg(target_arch = "aarch64")]
unsafe {
celt_float2int16_blocks_neon(&input[..blocks], &mut output[..blocks]);
}
#[cfg(target_arch = "arm")]
for (dst, &src) in output[..blocks].iter_mut().zip(&input[..blocks]) {
*dst = float2int16_neon_arm32(src);
}
#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
unsafe {
celt_float2int16_blocks_sse2(&input[..blocks], &mut output[..blocks]);
}
for (dst, &src) in output[blocks..].iter_mut().zip(&input[blocks..]) {
*dst = float2int16(src);
}
}
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
#[inline]
fn celt_float2int16_blocks_neon(input: &[f32], output: &mut [i16]) {
use crate::simd_mem::neon::{load_f32x4, store_i16x4};
use core::arch::aarch64::{vcvtaq_s32_f32, vmulq_n_f32, vqmovn_s32};
for (src, dst) in input.as_chunks::<4>().0.iter().zip(output.as_chunks_mut::<4>().0) {
let x = vmulq_n_f32(load_f32x4(src), 32768.0);
store_i16x4(dst, vqmovn_s32(vcvtaq_s32_f32(x)));
}
}
#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
#[target_feature(enable = "sse2")]
#[inline]
fn celt_float2int16_blocks_sse2(input: &[f32], output: &mut [i16]) {
use crate::simd_mem::array_at;
use crate::simd_mem::x86::{load_f32x4, store_i16x8};
use core::arch::x86_64::*;
for (src, dst) in input.as_chunks::<8>().0.iter().zip(output.as_chunks_mut::<8>().0) {
let scale = _mm_set1_ps(32768.0);
let low = _mm_set1_ps(-32768.0);
let high = _mm_set1_ps(32767.0);
let limit = |x: __m128| _mm_min_ps(_mm_max_ps(_mm_mul_ps(x, scale), low), high);
let a = limit(load_f32x4(array_at(src, 0)));
let b = limit(load_f32x4(array_at(src, 4)));
let packed = _mm_packs_epi32(_mm_cvtps_epi32(a), _mm_cvtps_epi32(b));
store_i16x8(dst, packed);
}
}
#[cfg(any(target_arch = "arm", test))]
#[inline]
fn float2int16_neon_arm32(x: f32) -> i16 {
let x = x * 32768.0;
let half = f32::from_bits(0x3F00_0000 | (x.to_bits() & 0x8000_0000));
((x + half) as i32).clamp(i16::MIN as i32, i16::MAX as i32) as i16
}
pub fn pcm_soft_clip(pcm: &mut [f32], channels: usize, declip_mem: &mut [f32]) -> Result<(), Error> {
if channels == 0 || !pcm.len().is_multiple_of(channels) || declip_mem.len() < channels {
return Err(Error::BAD_ARG);
}
soft_clip(pcm, pcm.len() / channels, channels, declip_mem);
Ok(())
}
fn soft_clip(pcm: &mut [f32], frame_size: usize, channels: usize, declip_mem: &mut [f32]) {
let (n, nc) = (frame_size, channels);
let fits = n.checked_mul(nc).is_some_and(|len| len <= pcm.len());
if nc < 1 || n < 1 || !fits || declip_mem.len() < nc {
return;
}
let all_within_neg1pos1 = limit2_checkwithin1(&mut pcm[..n * nc]);
for c in 0..nc {
let x = &mut pcm[c..];
let mut a = declip_mem[c];
#[cfg(not(target_arch = "aarch64"))]
let mut all_within_neg1pos1 = all_within_neg1pos1;
for i in 0..n {
let v = x[i * nc];
let av = a * v;
if av >= 0.0 {
break;
}
let bent = mac(v, av, v);
x[i * nc] = bent;
#[cfg(not(target_arch = "aarch64"))]
{
all_within_neg1pos1 &= !(bent > 1.0 || bent < -1.0);
}
}
let mut curr = 0usize;
let x0 = x[0];
loop {
let mut i = curr;
if all_within_neg1pos1 {
i = n;
} else {
while i < n {
let v = x[i * nc];
if v > 1.0 || v < -1.0 {
break;
}
i += 1;
}
}
if i == n {
a = 0.0;
break;
}
let clip = x[i * nc];
let mut peak_pos = i;
let (mut start, mut end) = (i, i);
let mut maxval = clip.abs();
while start > 0 && clip * x[(start - 1) * nc] >= 0.0 {
start -= 1;
}
while end < n && clip * x[end * nc] >= 0.0 {
let v = x[end * nc].abs();
if v > maxval {
maxval = v;
peak_pos = end;
}
end += 1;
}
let special = start == 0 && clip * x[0] >= 0.0;
a = (maxval - 1.0) / (maxval * maxval);
a = mac(a, a, 2.4e-7);
if clip > 0.0 {
a = -a;
}
for k in start..end {
let v = x[k * nc];
x[k * nc] = mac(v, a * v, v);
}
if special && peak_pos >= 2 {
let mut offset = x0 - x[0];
let delta = offset / peak_pos as f32;
for k in curr..peak_pos {
offset -= delta;
x[k * nc] = (x[k * nc] + offset).clamp(-1.0, 1.0);
}
}
curr = end;
if curr == n {
break;
}
}
declip_mem[c] = a;
}
}
fn limit2_checkwithin1(samples: &mut [f32]) -> bool {
#[cfg(target_arch = "aarch64")]
let (blocks, mut exceeding1) = {
let blocks = samples.len() / 16 * 16;
(blocks, unsafe { limit2_checkwithin1_blocks_neon(&mut samples[..blocks]) })
};
#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
let (blocks, mut exceeding1) = {
let blocks = samples.len() / 4 * 4;
(blocks, unsafe { limit2_checkwithin1_blocks_sse2(&mut samples[..blocks]) })
};
#[cfg(not(any(target_arch = "aarch64", all(target_arch = "x86_64", target_feature = "sse2"))))]
let (blocks, mut exceeding1) = (0, false);
for v in samples[blocks..].iter_mut() {
exceeding1 |= (*v > 1.0) | (*v < -1.0);
*v = v.clamp(-2.0, 2.0);
}
!exceeding1
}
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
#[inline]
fn limit2_checkwithin1_blocks_neon(samples: &mut [f32]) -> bool {
use crate::simd_mem::{array_at, neon::load_f32x4};
use core::arch::aarch64::*;
let blocks = samples.as_chunks_mut::<16>().0;
let zero = vdupq_n_f32(0.0);
let (mut max0, mut max1, mut min0, mut min1) = (zero, zero, zero, zero);
for b in blocks.iter() {
let x0 = load_f32x4(array_at(b, 0));
let x1 = load_f32x4(array_at(b, 4));
let x2 = load_f32x4(array_at(b, 8));
let x3 = load_f32x4(array_at(b, 12));
max0 = vmaxq_f32(max0, vmaxq_f32(x0, x1));
max1 = vmaxq_f32(max1, vmaxq_f32(x2, x3));
min0 = vminq_f32(min0, vminq_f32(x0, x1));
min1 = vminq_f32(min1, vminq_f32(x2, x3));
}
let max = vmaxvq_f32(vmaxq_f32(max0, max1));
let min = vminvq_f32(vminq_f32(min0, min1));
if min < -2.0 || max > 2.0 {
for v in blocks.as_flattened_mut() {
*v = v.clamp(-2.0, 2.0);
}
}
max > 1.0 || min < -1.0
}
#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
#[target_feature(enable = "sse2")]
#[inline]
fn limit2_checkwithin1_blocks_sse2(samples: &mut [f32]) -> bool {
use crate::simd_mem::x86::{load_f32x4, store_f32x4};
use core::arch::x86_64::*;
let (lo, hi) = (_mm_set1_ps(-2.0), _mm_set1_ps(2.0));
let (one, minus_one) = (_mm_set1_ps(1.0), _mm_set1_ps(-1.0));
let mut exceeding = _mm_setzero_ps();
for block in samples.as_chunks_mut::<4>().0 {
let x = load_f32x4(block);
let beyond = _mm_or_ps(_mm_cmpgt_ps(x, one), _mm_cmplt_ps(x, minus_one));
exceeding = _mm_or_ps(exceeding, beyond);
store_f32x4(block, _mm_min_ps(hi, _mm_max_ps(lo, x)));
}
_mm_movemask_ps(exceeding) != 0
}
#[inline]
fn is_whole_2_5ms(frame_size: usize, fs: i32) -> bool {
frame_size != 0 && frame_size.is_multiple_of(fs as usize / 400)
}
pub(crate) fn frame_rate_from_params(sampling_rate: i32, frame_size: usize) -> Option<i32> {
let frame_size = i32::try_from(frame_size).ok()?;
if frame_size <= 0 || frame_size > sampling_rate {
return None;
}
let (fs, n) = (sampling_rate, frame_size);
let valid = 400 * n == fs
|| 200 * n == fs
|| 100 * n == fs
|| 50 * n == fs
|| 25 * n == fs
|| 50 * n == 3 * fs
|| 50 * n == 4 * fs
|| 50 * n == 5 * fs
|| 50 * n == 6 * fs;
if !valid {
return None;
}
Some(fs / frame_size)
}
pub(crate) fn frame_size_select(
application: Application,
frame_size: usize,
duration: FrameDuration,
sampling_rate: i32,
) -> Option<usize> {
if frame_size < sampling_rate as usize / 400 {
return None;
}
let new_size = duration.samples(SampleRate::from_valid_hz(sampling_rate)).unwrap_or(frame_size);
if new_size > frame_size {
return None;
}
frame_rate_from_params(sampling_rate, new_size)?;
if application == Application::RestrictedSilk && new_size < sampling_rate as usize / 100 {
return None;
}
Some(new_size)
}
fn toc_bandwidth(mode: OpusMode, silk_fs_khz: i32, bandwidth: Bandwidth) -> Bandwidth {
match (mode, silk_fs_khz) {
(OpusMode::SilkOnly, 8) => Bandwidth::Narrowband,
(OpusMode::SilkOnly, 12) => Bandwidth::Mediumband,
(OpusMode::SilkOnly, _) => Bandwidth::Wideband,
_ => bandwidth,
}
}
fn gen_toc(mode: OpusMode, frame_rate: i32, bandwidth: Bandwidth, channels: usize) -> u8 {
let mut rate = frame_rate;
let mut period = 0;
while rate < 400 {
rate <<= 1;
period += 1;
}
let mut toc = match mode {
OpusMode::SilkOnly => {
let bw = (bandwidth as i32 - Bandwidth::Narrowband as i32) << 5;
let per = (period - 2) << 3;
(bw | per) as u8
}
OpusMode::CeltOnly => {
let mut tmp = bandwidth as i32 - Bandwidth::Mediumband as i32;
if tmp < 0 {
tmp = 0;
}
let per = period << 3;
(0x80 | (tmp << 5) | per) as u8
}
OpusMode::Hybrid => {
let base_config = if bandwidth == Bandwidth::Superwideband {
12
} else {
14
};
let period_offset = if frame_rate >= 100 { 0 } else { 1 };
((base_config + period_offset) << 3) as u8
}
};
if channels == 2 {
toc |= 0x04;
}
toc
}
fn mode_from_toc(toc: u8) -> OpusMode {
if toc & 0x80 != 0 {
OpusMode::CeltOnly
} else if toc & 0x60 == 0x60 {
OpusMode::Hybrid
} else {
OpusMode::SilkOnly
}
}
#[cfg(all(test, feature = "std"))]
fn frame_duration_ms_from_toc(toc: u8) -> i32 {
let mode = mode_from_toc(toc);
match mode {
OpusMode::SilkOnly => {
let config = (toc >> 3) & 0x03;
match config {
0 => 10,
1 => 20,
2 => 40,
3 => 60,
_ => 20,
}
}
OpusMode::Hybrid => {
let config = (toc >> 3) & 0x01;
if config == 0 { 10 } else { 20 }
}
OpusMode::CeltOnly => {
let config = (toc >> 3) & 0x03;
match config {
0 => 2,
1 => 5,
2 => 10,
3 => 20,
_ => 20,
}
}
}
}
fn smooth_fade(
other: &[f32],
out: &mut [f32],
out_fades_in: bool,
overlap: usize,
channels: usize,
window: &[f32],
inc: usize,
) {
for (i, &win) in window.iter().step_by(inc).take(overlap).enumerate() {
let w = win * win;
for c in 0..channels {
let k = i * channels + c;
let (in1, in2) = if out_fades_in { (other[k], out[k]) } else { (out[k], other[k]) };
out[k] = mac((1.0 - w) * in1, w, in2);
}
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use crate::packet::{Framing, Padding};
#[test]
fn debug_shows_the_settings_only() {
let mut enc = OpusEncoder::new(SampleRate::Hz48000, Channels::Stereo, Application::Audio).unwrap();
enc.set_force_mode(Some(ForcedMode::CeltOnly));
let text = format!("{enc:?}");
assert!(text.starts_with("OpusEncoder { sampling_rate: Hz48000, channels: Stereo, application: Audio, bitrate_setting: Auto, complexity: 9,"), "{text}");
assert!(text.contains("force_mode: Some(CeltOnly)") && text.ends_with(", .. }"), "{text}");
assert!(text.len() < 600, "{text}");
let dec = OpusDecoder::new(SampleRate::Hz16000, Channels::Mono).unwrap();
let text = format!("{dec:?}");
assert!(text.starts_with("OpusDecoder { sampling_rate: Hz16000, channels: Mono, complexity: 0, gain: 0, phase_inversion_disabled: true"), "{text}");
assert!(text.len() < 200, "{text}");
let packet = [0xfc, 1, 2, 3];
let mut rp = packet::Repacketizer::new();
rp.cat(&packet).unwrap();
assert_eq!(format!("{rp:?}"), "Repacketizer { toc: 252, nb_frames: 1, .. }");
let ms = multistream::MultistreamEncoder::new(SampleRate::Hz48000, 2, 1, 1, &[0, 1], Application::Voip).unwrap();
let text = format!("{ms:?}");
assert!(text.starts_with("MultistreamEncoder { sampling_rate: Hz48000, layout: "), "{text}");
assert!(text.contains("application: Voip") && text.len() < 600, "{text}");
let ms = multistream::MultistreamDecoder::new(SampleRate::Hz24000, 2, 2, 0, &[0, 1]).unwrap();
let text = format!("{ms:?}");
assert!(text.starts_with("MultistreamDecoder { sampling_rate: Hz24000, layout: "), "{text}");
assert!(text.len() < 400, "{text}");
#[cfg(feature = "dred")]
{
assert_eq!(
format!("{:?}", OpusDred::new()),
"OpusDred { parsed: false, processed: false, nb_latents: 0, dred_offset: 0, .. }"
);
assert_eq!(format!("{:?}", OpusDredDecoder::new()), "OpusDredDecoder { loaded: true, .. }");
}
}
#[test]
fn the_new_getters_return_what_was_set() {
let mut enc = OpusEncoder::new(SampleRate::Hz24000, Channels::Mono, Application::Voip).unwrap();
assert_eq!(enc.force_mode(), None);
for mode in [ForcedMode::SilkOnly, ForcedMode::Hybrid, ForcedMode::CeltOnly] {
enc.set_force_mode(Some(mode));
assert_eq!(enc.force_mode(), Some(mode));
}
enc.set_force_mode(None);
assert_eq!(enc.force_mode(), None);
assert_eq!(enc.max_bandwidth(), Bandwidth::Fullband);
enc.set_max_bandwidth(Bandwidth::Wideband);
assert_eq!(enc.max_bandwidth(), Bandwidth::Wideband);
assert_eq!(enc.bandwidth(), Bandwidth::Fullband);
let mut packet = [0u8; 1275];
enc.encode(&[0.1f32; 480], 480, &mut packet).unwrap();
assert!(matches!(enc.bandwidth(), Bandwidth::Narrowband | Bandwidth::Mediumband | Bandwidth::Wideband));
let mut dec = OpusDecoder::new(SampleRate::Hz24000, Channels::Mono).unwrap();
assert_eq!(dec.bandwidth(), None);
assert_eq!(dec.nb_samples(&packet), Ok(480));
let mut pcm = [0.0f32; 480];
dec.decode(&packet, 480, &mut pcm).unwrap();
assert_eq!(dec.bandwidth(), Some(packet::bandwidth(packet[0])));
dec.decode(&[], 480, &mut pcm).unwrap();
assert_eq!(dec.bandwidth(), Some(packet::bandwidth(packet[0])), "a loss keeps it");
dec.reset_state();
assert_eq!(dec.bandwidth(), None);
}
#[test]
fn sample_rates_convert_to_and_from_numbers() {
let hz = [8000, 12000, 16000, 24000, 48000];
assert!(SampleRate::ALL.iter().map(|rate| rate.hz()).eq(hz));
for &rate in SampleRate::ALL {
assert_eq!(SampleRate::try_from(rate.hz()), Ok(rate));
assert_eq!(SampleRate::try_from(rate.hz() as i32), Ok(rate));
assert_eq!(u32::from(rate), rate.hz());
assert_eq!(SampleRate::from_valid_hz(rate.hz() as i32), rate);
assert_eq!(rate.max_frame_size(), packet::max_packet_samples(rate.hz() as i32));
assert_eq!(rate.max_frame_size(), rate.hz() as usize * 120 / 1000);
}
assert!(SampleRate::ALL.windows(2).all(|w| w[0] < w[1]));
for hz in [0, 1, 4000, 11025, 22050, 32000, 44100, 96000, u32::MAX] {
let err = SampleRate::try_from(hz).unwrap_err();
assert_eq!((err.kind(), err.message()), (ErrorKind::BadArg, "invalid sampling rate"));
}
for hz in [-1, -48000, i32::MIN, i32::MAX] {
assert_eq!(SampleRate::try_from(hz), Err(Error::INVALID_SAMPLING_RATE));
}
}
#[test]
fn channels_convert_to_and_from_numbers() {
assert_eq!(Channels::ALL.map(Channels::count), [1, 2]);
for channels in Channels::ALL {
assert_eq!(Channels::try_from(channels.count()), Ok(channels));
assert_eq!(usize::from(channels), channels.count());
assert_eq!(Channels::from_valid_count(channels.count()), channels);
}
for count in [0, 3, 8, 255, usize::MAX] {
let err = Channels::try_from(count).unwrap_err();
assert_eq!((err.kind(), err.message()), (ErrorKind::BadArg, "invalid number of channels"));
}
}
#[test]
fn codecs_report_their_rate_and_channels() {
for &rate in SampleRate::ALL {
for channels in Channels::ALL {
let enc = OpusEncoder::new(rate, channels, Application::Audio).unwrap();
assert_eq!((enc.sampling_rate(), enc.channels()), (rate, channels));
let dec = OpusDecoder::new(rate, channels).unwrap();
assert_eq!((dec.sampling_rate(), dec.channels()), (rate, channels));
}
}
}
#[test]
fn forced_channels_are_kept_on_a_mono_encoder() {
let mut enc =
OpusEncoder::new(SampleRate::Hz48000, Channels::Mono, Application::Audio).unwrap();
assert_eq!(enc.force_channels(), None);
enc.set_force_channels(Some(Channels::Stereo));
assert_eq!(enc.force_channels(), Some(Channels::Stereo));
let mut plain =
OpusEncoder::new(SampleRate::Hz48000, Channels::Mono, Application::Audio).unwrap();
let (mut out, mut plain_out) = ([0u8; 1500], [0u8; 1500]);
for k in 0..10 {
let pcm: Vec<f32> = (0..960).map(|i| (0.01 * (k * 960 + i) as f32).sin() * 0.3).collect();
let len = enc.encode(&pcm, 960, &mut out).unwrap();
let plain_len = plain.encode(&pcm, 960, &mut plain_out).unwrap();
assert_eq!(out[..len], plain_out[..plain_len], "packet {k}");
assert_eq!(packet::nb_channels(out[0]), Channels::Mono);
}
enc.set_force_channels(None);
assert_eq!(enc.force_channels(), None);
}
fn frame_size_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
let mode = mode_from_toc(toc);
match mode {
OpusMode::CeltOnly => {
let period = ((toc >> 3) & 0x03) as i32;
let frame_rate = 400 >> period;
if frame_rate == 0 || sampling_rate % frame_rate != 0 {
return None;
}
Some((sampling_rate / frame_rate) as usize)
}
OpusMode::SilkOnly => {
let duration_ms = frame_duration_ms_from_toc(toc);
Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
}
OpusMode::Hybrid => {
let duration_ms = frame_duration_ms_from_toc(toc);
Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
}
}
}
#[test]
fn gen_toc_matches_celt_reference_values() {
let sampling_rate = 48_000;
let cases = [
(120usize, 0xE0u8),
(240usize, 0xE8u8),
(480usize, 0xF0u8),
(960usize, 0xF8u8),
];
for (frame_size, expected_toc) in cases {
let frame_rate = frame_rate_from_params(sampling_rate, frame_size).unwrap();
let toc = gen_toc(OpusMode::CeltOnly, frame_rate, Bandwidth::Fullband, 1);
assert_eq!(
toc, expected_toc,
"frame_size {} expected TOC {:02X} got {:02X}",
frame_size, expected_toc, toc
);
let decoded_size = frame_size_from_toc(toc, sampling_rate).unwrap();
assert_eq!(decoded_size, frame_size);
}
let stereo_toc = gen_toc(
OpusMode::CeltOnly,
frame_rate_from_params(sampling_rate, 960).unwrap(),
Bandwidth::Fullband,
2,
);
assert_eq!(packet::nb_channels(stereo_toc), Channels::Stereo);
}
#[test]
fn test_celt_decoder_large_frame_sizes() {
let sampling_rate = 48000;
let channels = 1;
let mut decoder =
OpusDecoder::new(sampling_rate.try_into().unwrap(), channels.try_into().unwrap()).unwrap();
let frame_sizes = [120, 240, 480, 960];
for frame_size in frame_sizes {
let toc = gen_toc(
OpusMode::CeltOnly,
frame_rate_from_params(sampling_rate, frame_size).unwrap(),
Bandwidth::Fullband,
channels,
);
let packet = [toc, 0, 0, 0, 0];
let mut output = vec![0.0f32; frame_size * channels];
let _ = decoder.decode(&packet, frame_size, &mut output);
}
let channels = 2;
let mut decoder =
OpusDecoder::new(sampling_rate.try_into().unwrap(), channels.try_into().unwrap()).unwrap();
for frame_size in frame_sizes {
let toc = gen_toc(
OpusMode::CeltOnly,
frame_rate_from_params(sampling_rate, frame_size).unwrap(),
Bandwidth::Fullband,
channels,
);
let packet = [toc, 0, 0, 0, 0];
let mut output = vec![0.0f32; frame_size * channels];
let _ = decoder.decode(&packet, frame_size, &mut output);
}
}
#[test]
fn test_celt_decoder_edge_case_frame_sizes() {
let channels = 1;
let mut decoder = OpusDecoder::new(SampleRate::Hz48000, Channels::Mono).unwrap();
let edge_sizes = [2048, 2167, 2168, 2169, 2880, 3072];
for frame_size in edge_sizes {
let mut output = vec![0.0f32; frame_size * channels];
let _ = decoder.decode(&[0x80, 0, 0, 0], frame_size, &mut output);
}
}
#[test]
fn test_invalid_small_frame_size_returns_error_not_panic() {
let mut enc =
OpusEncoder::new(SampleRate::Hz48000, Channels::Stereo, Application::Voip).unwrap();
enc.set_bitrate(Bitrate::BitsPerSecond(NonZeroU32::new(64000).unwrap()));
enc.set_complexity(5).unwrap();
enc.set_vbr(false);
let input = vec![0.0f32; 48 * 2]; let mut output = vec![0u8; 256];
let result = enc.encode(&input, 48, &mut output);
assert!(
result.is_err(),
"encode with invalid frame_size=48 should return Err, not panic"
);
}
#[test]
fn float2int16_rounds_and_saturates() {
let lsb = 1.0 / 32768.0;
assert_eq!(float2int16(0.0), 0);
assert_eq!(float2int16(100.4 * lsb), 100);
assert_eq!(float2int16(100.6 * lsb), 101);
assert_eq!(float2int16(-100.6 * lsb), -101);
assert_eq!(float2int16(0.5 * lsb), 0);
assert_eq!(float2int16(1.5 * lsb), 2);
assert_eq!(float2int16(2.5 * lsb), 2);
assert_eq!(float2int16(-0.5 * lsb), 0);
assert_eq!(float2int16(-1.5 * lsb), -2);
assert_eq!(float2int16(1.0), 32767);
assert_eq!(float2int16(32767.4 * lsb), 32767);
assert_eq!(float2int16(-1.0), -32768);
assert_eq!(float2int16(7.0), 32767);
assert_eq!(float2int16(-7.0), -32768);
assert_eq!(float2int16(f32::INFINITY), 32767);
assert_eq!(float2int16(f32::NEG_INFINITY), -32768);
assert_eq!(float2int16(f32::NAN), -32768);
}
#[test]
fn celt_float2int16_rounds_blocks_as_libopus() {
let lsb = 1.0 / 32768.0;
let input: Vec<f32> = (0..35).map(|i| (i as f32 - 17.0 + 0.5) * lsb).collect();
let mut output = vec![0i16; 35];
celt_float2int16(&input, &mut output);
let arm = cfg!(any(target_arch = "aarch64", target_arch = "arm"));
for (i, (&x, &y)) in input.iter().zip(&output).enumerate() {
let v = x * 32768.0;
let expected = if arm && i < 32 { v.round() } else { v.round_ties_even() };
assert_eq!(y, expected as i16, "value {i}: {v}");
}
let mut input = vec![0f32; 17];
input[..5].copy_from_slice(&[1.0, -1.0, 7.0, f32::NEG_INFINITY, f32::NAN]);
input[12..].copy_from_slice(&[32767.5 * lsb, -32768.5 * lsb, f32::INFINITY, -7.0, f32::NAN]);
let mut output = vec![1i16; 17];
celt_float2int16(&input, &mut output);
assert_eq!(output[..5], [32767, -32768, 32767, -32768, if arm { 0 } else { -32768 }]);
assert_eq!(output[12..], [32767, -32768, 32767, -32768, -32768]);
let mut output = [5i16; 3];
celt_float2int16(&[0.5 * lsb, 1.5 * lsb], &mut output);
assert_eq!(output, [0, 2, 5]);
}
#[test]
fn celt_float2int16_blocks_match_float2int16_off_arm() {
if cfg!(any(target_arch = "aarch64", target_arch = "arm")) {
return;
}
let lsb = 1.0 / 32768.0;
let special = [
0.0f32,
-0.0,
0.5 * lsb,
-0.5 * lsb,
1.5 * lsb,
-2.5 * lsb,
32766.5 * lsb,
32767.5 * lsb,
-32767.5 * lsb,
-32768.5 * lsb,
1.0,
-1.0,
1e30,
-1e30,
f32::INFINITY,
f32::NEG_INFINITY,
f32::NAN,
-f32::NAN,
f32::MIN_POSITIVE,
1e-40,
];
let mut seed = 0x0f12_1600u32;
let mut values: Vec<f32> = Vec::new();
for i in 0..400 {
seed = seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
values.push(if i % 3 == 0 {
special[(seed >> 8) as usize % special.len()]
} else {
((seed >> 8) as f32 / 8_388_608.0 - 1.0) * 1.2
});
}
for n in (0..=33).chain([399, 400]) {
let mut output = vec![0x5555i16; n + 2];
celt_float2int16(&values[..n], &mut output);
for (i, &x) in values[..n].iter().enumerate() {
assert_eq!(output[i], float2int16(x), "n {n} value {i}: {x:e}");
}
assert_eq!(output[n..], [0x5555; 2], "n {n}: wrote past the input");
}
}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[test]
fn celt_float2int16_matches_libopus() {
use audiopus_sys as _;
unsafe extern "C" {
#[cfg_attr(target_arch = "aarch64", link_name = "celt_float2int16_neon")]
#[cfg_attr(not(target_arch = "aarch64"), link_name = "celt_float2int16_c")]
fn c_celt_float2int16(input: *const f32, output: *mut i16, cnt: i32);
}
let lsb = 1.0 / 32768.0;
let special = [
0.0f32,
-0.0,
0.5 * lsb,
-0.5 * lsb,
1.5 * lsb,
-2.5 * lsb,
32766.5 * lsb,
32767.5 * lsb,
-32767.5 * lsb,
-32768.5 * lsb,
1.0,
-1.0,
1e30,
-1e30,
f32::INFINITY,
f32::NEG_INFINITY,
f32::NAN,
-f32::NAN,
f32::MIN_POSITIVE,
1e-40,
];
let mut seed = 0x0f12_1601u32;
let mut values: Vec<f32> = Vec::new();
for i in 0..1000 {
seed = seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
values.push(if i % 4 == 0 {
special[(seed >> 8) as usize % special.len()]
} else {
((seed >> 8) as f32 / 8_388_608.0 - 1.0) * 1.2
});
}
for n in (0..=40).chain([999, 1000]) {
let mut want = vec![0x5555i16; n];
unsafe { c_celt_float2int16(values.as_ptr(), want.as_mut_ptr(), n as i32) };
let mut got = vec![0x5555i16; n];
celt_float2int16(&values[..n], &mut got);
assert_eq!(got, want, "n {n}");
}
}
#[test]
fn float2int16_neon_arm32_adds_half_and_truncates() {
let lsb = 1.0 / 32768.0;
for (v, expected) in [
(0.5f32, 1i16),
(1.5, 2),
(2.5, 3),
(-0.5, -1),
(-2.5, -3),
(2.4999998, 2),
(100.49, 100),
(-100.51, -101),
(0.49999997, 1),
(-0.49999997, -1),
(32767.5, 32767),
(-32768.5, -32768),
(1e9, 32767),
(f32::NEG_INFINITY, -32768),
(f32::NAN, 0),
] {
assert_eq!(float2int16_neon_arm32(v * lsb), expected, "{v}");
}
}
fn encode_tone(enc: &mut OpusEncoder, frame_size: usize, frames: usize) -> Vec<Vec<u8>> {
let mut packets = Vec::new();
for f in 0..frames {
let pcm: Vec<f32> = (0..frame_size)
.map(|i| 0.4 * ((f * frame_size + i) as f32 * 0.05).sin())
.collect();
let mut out = vec![0u8; 1500];
let n = enc.encode(&pcm, frame_size, &mut out).unwrap();
out.truncate(n);
packets.push(out);
}
packets
}
#[test]
fn decode_native_reads_self_delimited_packets() {
for frame_size in [960usize, 2880] {
let mut enc = OpusEncoder::new(
SampleRate::Hz48000,
Channels::Mono,
Application::RestrictedLowDelay,
)
.unwrap();
let mut plain = OpusDecoder::new(SampleRate::Hz48000, Channels::Mono).unwrap();
let mut delimited = OpusDecoder::new(SampleRate::Hz48000, Channels::Mono).unwrap();
let (mut a, mut b) = (vec![0f32; frame_size], vec![0f32; frame_size]);
for p in encode_tone(&mut enc, frame_size, 12) {
let parsed = packet::parse(&p, Framing::Normal).unwrap();
assert_eq!(parsed.nb_frames(), frame_size / 960);
let frames: Vec<&[u8]> = parsed.frames(&p).collect();
let mut stream = vec![0u8; p.len() + 16];
let len = packet::write(parsed.toc(), &frames, &mut stream, Framing::SelfDelimited, Padding::Shortest).unwrap();
stream.truncate(len);
stream.extend_from_slice(&[0xF8, 1, 2, 3, 4]);
assert_eq!(plain.decode(&p, frame_size, &mut a), Ok(frame_size));
assert_eq!(
delimited.decode_native(&stream, frame_size, &mut b, false, true, false),
Ok((frame_size, len))
);
assert_eq!(a, b);
assert_eq!(plain.final_range(), delimited.final_range());
assert_ne!(plain.final_range(), 0);
}
assert_eq!(delimited.decode_native(&[], 960, &mut b, false, true, false), Ok((960, 0)));
assert_eq!(delimited.final_range(), 0);
let stream = [0xF8, 3, 1, 2, 3, 0xF8, 9];
assert_eq!(
delimited.decode_native(&stream, 960, &mut b, true, true, false),
Ok((960, 5))
);
let cut = [0xF8, 3, 1, 2];
assert!(delimited.decode_native(&cut, 960, &mut b, false, true, false).is_err());
}
}
#[test]
fn decode_native_handles_fec_and_padding_in_self_delimited_packets() {
let frame_size = 320usize;
let mut enc =
OpusEncoder::new(SampleRate::Hz16000, Channels::Mono, Application::Voip).unwrap();
enc.set_bitrate(Bitrate::BitsPerSecond(NonZeroU32::new(28000).unwrap()));
enc.set_inband_fec(InbandFec::On);
enc.set_packet_loss_perc(25).unwrap();
let packets = encode_tone(&mut enc, frame_size, 40);
assert!(packets.iter().all(|p| p[0] & 0x80 == 0));
let with_copy = packets.iter().filter(|p| p[1] & 0x40 != 0).count();
assert!(with_copy >= 15, "{with_copy} of 40 packets carry FEC data");
let mut plain = OpusDecoder::new(SampleRate::Hz16000, Channels::Mono).unwrap();
let mut delimited = OpusDecoder::new(SampleRate::Hz16000, Channels::Mono).unwrap();
let (mut a, mut b) = (vec![0f32; 2 * frame_size], vec![0f32; 2 * frame_size]);
let mut a16 = vec![0i16; frame_size];
let mut fec_calls = 0;
for (i, p) in packets.iter().enumerate() {
let parsed = packet::parse(p, Framing::Normal).unwrap();
let frames: Vec<&[u8]> = parsed.frames(p).collect();
let extra = if i % 3 == 0 { [1usize, 2, 300][(i / 3) % 3] } else { 0 };
let mut stream = vec![0u8; p.len() + 2 + extra];
let len = if extra == 0 {
packet::write(parsed.toc(), &frames, &mut stream, Framing::SelfDelimited, Padding::Shortest).unwrap()
} else {
packet::write(parsed.toc(), &frames, &mut stream, Framing::SelfDelimited, Padding::FillBuffer).unwrap()
};
stream.truncate(len);
stream.extend_from_slice(&[0x0B, 0xFF, 7]);
if i % 7 == 5 {
let lost = if fec_calls % 2 == 0 { frame_size } else { 2 * frame_size };
assert_eq!(plain.decode_fec(p, lost, &mut a), Ok(lost));
assert_eq!(
delimited.decode_native(&stream, lost, &mut b, true, true, false),
Ok((lost, len))
);
assert_eq!(a[..lost], b[..lost], "packet {i}: FEC decode");
assert_eq!(plain.final_range(), delimited.final_range());
fec_calls += 1;
}
if i % 4 == 1 {
assert_eq!(plain.decode_i16(p, frame_size, &mut a16), Ok(frame_size));
assert_eq!(
delimited.decode_native(&stream, frame_size, &mut b, false, true, true),
Ok((frame_size, len))
);
let mut converted = vec![0i16; frame_size];
celt_float2int16(&b[..frame_size], &mut converted);
assert_eq!(a16, converted, "packet {i}: 16-bit decode");
} else {
assert_eq!(plain.decode(p, frame_size, &mut a), Ok(frame_size));
assert_eq!(
delimited.decode_native(&stream, frame_size, &mut b, false, true, false),
Ok((frame_size, len))
);
assert_eq!(a[..frame_size], b[..frame_size], "packet {i}");
}
assert_eq!(plain.final_range(), delimited.final_range(), "packet {i}");
assert_ne!(plain.final_range(), 0, "packet {i}");
}
assert!(fec_calls >= 4);
}
#[test]
fn test_invalid_small_frame_size_audio_application_returns_error() {
let mut enc =
OpusEncoder::new(SampleRate::Hz48000, Channels::Mono, Application::Audio).unwrap();
let input = vec![0.0f32; 48];
let mut output = vec![0u8; 256];
let result = enc.encode(&input, 48, &mut output);
assert!(
result.is_err(),
"Audio/48kHz encoder with frame_size=48 should return Err"
);
}
#[test]
fn settings_enums_convert_to_and_from_libopus_numbers() {
fn check<T>(values: &[T], raw: fn(T) -> i32)
where
T: Copy + PartialEq + core::fmt::Debug + TryFrom<i32, Error = Error>,
{
for &v in values {
assert_eq!(T::try_from(raw(v)), Ok(v));
}
let numbers: Vec<i32> = values.iter().map(|&v| raw(v)).collect();
for &n in &numbers {
for other in [n - 1, n + 1] {
if !numbers.contains(&other) {
assert_eq!(T::try_from(other).map_err(|e| e.kind()), Err(ErrorKind::BadArg));
}
}
}
for other in [i32::MIN, -1, 0, i32::MAX] {
if !numbers.contains(&other) {
assert!(T::try_from(other).is_err(), "{other}");
}
}
}
let apps = [
Application::Voip,
Application::Audio,
Application::RestrictedLowDelay,
Application::RestrictedSilk,
Application::RestrictedCelt,
];
assert_eq!(apps.map(Application::to_raw), [2048, 2049, 2051, 2052, 2053]);
check(&apps, Application::to_raw);
let bandwidths = [
Bandwidth::Narrowband,
Bandwidth::Mediumband,
Bandwidth::Wideband,
Bandwidth::Superwideband,
Bandwidth::Fullband,
];
assert_eq!(bandwidths.map(Bandwidth::to_raw), [1101, 1102, 1103, 1104, 1105]);
check(&bandwidths, Bandwidth::to_raw);
assert!(Bandwidth::try_from(OPUS_AUTO).is_err());
let modes = [ForcedMode::SilkOnly, ForcedMode::Hybrid, ForcedMode::CeltOnly];
assert_eq!(modes.map(ForcedMode::to_raw), [1000, 1001, 1002]);
check(&modes, ForcedMode::to_raw);
let signals = [Signal::Auto, Signal::Voice, Signal::Music];
assert_eq!(signals.map(Signal::to_raw), [OPUS_AUTO, 3001, 3002]);
check(&signals, Signal::to_raw);
let fec = [InbandFec::Off, InbandFec::On, InbandFec::OnUnlessMusic];
assert_eq!(fec.map(InbandFec::to_raw), [0, 1, 2]);
check(&fec, InbandFec::to_raw);
check(&FrameDuration::ALL, FrameDuration::to_raw);
assert_eq!(Bitrate::default(), Bitrate::Auto);
assert_eq!(Signal::default(), Signal::Auto);
assert_eq!(InbandFec::default(), InbandFec::Off);
assert_eq!(FrameDuration::default(), FrameDuration::Argument);
}
#[test]
fn narrow_settings_refuse_what_libopus_refuses() {
let mut enc =
OpusEncoder::new(SampleRate::Hz48000, Channels::Stereo, Application::Audio).unwrap();
for v in 0..=u8::MAX {
assert_eq!(enc.set_complexity(v).is_ok(), v <= 10, "complexity {v}");
assert_eq!(enc.complexity(), v.min(10), "complexity {v}");
assert_eq!(enc.set_packet_loss_perc(v).is_ok(), v <= 100, "loss {v}");
assert_eq!(enc.packet_loss_perc(), v.min(100), "loss {v}");
assert_eq!(enc.set_voice_ratio(Some(v)).is_ok(), v <= 100, "voice ratio {v}");
assert_eq!(enc.voice_ratio(), Some(v.min(100)), "voice ratio {v}");
}
for v in [0, 7, 8, 16, 24, 25, u8::MAX] {
let ok = (8..=24).contains(&v);
assert_eq!(enc.set_lsb_depth(v).is_ok(), ok, "LSB depth {v}");
assert_eq!(enc.lsb_depth(), if ok { v } else { 24 }, "LSB depth {v}");
}
enc.set_voice_ratio(None).unwrap();
assert_eq!(enc.voice_ratio(), None);
assert_eq!(enc.voice_ratio, -1);
let bps = |v| Bitrate::BitsPerSecond(NonZeroU32::new(v).unwrap());
for (set, kept) in [(1, 500), (499, 500), (64_000, 64_000), (1_500_001, 1_500_000), (u32::MAX, 1_500_000)] {
enc.set_bitrate(bps(set));
assert_eq!(enc.bitrate_setting(), bps(kept), "bitrate {set}");
}
let mut dec = OpusDecoder::new(SampleRate::Hz48000, Channels::Mono).unwrap();
for v in 0..=u8::MAX {
assert_eq!(dec.set_complexity(v).is_ok(), v <= 10, "decoder complexity {v}");
assert_eq!(dec.complexity(), v.min(10), "decoder complexity {v}");
}
for gain in [i16::MIN, -1, 0, 1, i16::MAX] {
dec.set_gain(gain);
assert_eq!(dec.gain(), gain);
}
}
#[test]
fn pcm_soft_clip_checks_its_arguments() {
let mut pcm = [1.5f32, -1.5, 1.5, -1.5, 0.0, 0.0];
let mut memory = [0.0f32; 2];
let kind = |r: Result<(), Error>| r.map_err(|e| e.kind());
assert_eq!(kind(pcm_soft_clip(&mut pcm, 0, &mut memory)), Err(ErrorKind::BadArg));
assert_eq!(kind(pcm_soft_clip(&mut pcm, 4, &mut memory)), Err(ErrorKind::BadArg));
assert_eq!(kind(pcm_soft_clip(&mut pcm, 3, &mut memory)), Err(ErrorKind::BadArg));
assert_eq!(pcm, [1.5, -1.5, 1.5, -1.5, 0.0, 0.0]);
let mut expected = pcm;
let mut expected_memory = memory;
soft_clip(&mut expected, 3, 2, &mut expected_memory);
pcm_soft_clip(&mut pcm, 2, &mut memory).unwrap();
assert_eq!(pcm, expected);
assert_eq!(memory, expected_memory);
assert!(pcm.iter().all(|x| x.abs() <= 1.0));
}
}