#![allow(dead_code)]
#![allow(clippy::field_reassign_with_default)]
#![allow(clippy::needless_range_loop)]
use alloc::vec;
use alloc::vec::Vec;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::bands::normalise_bands;
use crate::celt::bands::{
BandCodingState, compute_band_energies, haar1, hysteresis_decision, quant_all_bands,
spreading_decision,
};
#[cfg(feature = "fixed_point")]
use crate::celt::bands::{compute_band_energies_fixed, normalise_bands_fixed};
#[cfg(feature = "fixed_point")]
use crate::celt::celt::comb_filter_fixed;
use crate::celt::celt::{
COMBFILTER_MINPERIOD, TF_SELECT_TABLE, comb_filter, init_caps, resampling_factor,
};
use crate::celt::cpu_support::opus_select_arch;
use crate::celt::entcode::{BITRES, ec_ilog, ec_tell, ec_tell_frac};
use crate::celt::entenc::EcEnc;
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_arch::{DB_SHIFT, EPSILON as FIXED_EPSILON, SIG_SAT, SIG_SHIFT, float2sig};
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_ops::{
abs32, add32, mult16_16_q15, mult16_32_q15, qconst16, qconst32, shl32, shr32, sub32,
};
use crate::celt::float_cast::CELT_SIG_SCALE;
#[cfg(feature = "fixed_point")]
use crate::celt::math::celt_ilog2;
use crate::celt::math::{celt_exp2, celt_log2, celt_maxabs16, celt_rcp, celt_sqrt, frac_div32_q29};
#[cfg(feature = "fixed_point")]
use crate::celt::math_fixed::celt_sqrt as celt_sqrt_fixed;
use crate::celt::mdct::clt_mdct_forward;
#[cfg(test)]
use crate::celt::mdct::mdct_trace as mdct_input_trace;
#[cfg(feature = "fixed_point")]
use crate::celt::mdct_fixed::{FixedMdctLookup, clt_mdct_forward_fixed};
use crate::celt::modes::{opus_custom_mode_find_static, opus_custom_mode_find_static_ref};
use crate::celt::pitch::{celt_inner_prod, pitch_downsample, pitch_search, remove_doubling};
#[cfg(feature = "fixed_point")]
use crate::celt::pitch::{pitch_downsample_fixed, pitch_search_fixed, remove_doubling_fixed};
use crate::celt::quant_bands::E_MEANS;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::quant_bands::{
amp2_log2, quant_coarse_energy, quant_energy_finalise, quant_fine_energy,
};
#[cfg(feature = "fixed_point")]
use crate::celt::quant_bands::{
amp2_log2_fixed, quant_coarse_energy_fixed, quant_energy_finalise_fixed,
quant_fine_energy_fixed,
};
use crate::celt::rate::clt_compute_allocation;
use crate::celt::types::{
AnalysisInfo, CeltGlog, CeltNorm, CeltSig, OpusCustomEncoder, OpusCustomMode, OpusInt16,
OpusInt32, OpusRes, OpusUint32, OpusVal16, OpusVal32, SilkInfo,
};
#[cfg(feature = "fixed_point")]
use crate::celt::types::{
FixedCeltCoef, FixedCeltEner, FixedCeltGlog, FixedCeltNorm, FixedCeltSig, FixedOpusVal16,
};
use crate::celt::vq::{SPREAD_AGGRESSIVE, SPREAD_NONE, SPREAD_NORMAL};
use core::cmp::{max, min};
use core::f32::consts::FRAC_1_SQRT_2;
#[cfg(not(feature = "fixed_point"))]
use libm::acosf;
use libm::{floor, floorf};
#[cfg(test)]
extern crate std;
#[cfg(feature = "fixed_point")]
fn glog_from_fixed(value: FixedCeltGlog) -> f32 {
value as f32 / (1u32 << DB_SHIFT) as f32
}
#[cfg(feature = "fixed_point")]
fn glog_to_fixed(value: f32) -> FixedCeltGlog {
qconst32(value as f64, DB_SHIFT)
}
#[cfg(feature = "fixed_point")]
fn sync_loge_from_fixed(dst: &mut [CeltGlog], src: &[FixedCeltGlog]) {
for (out, &value) in dst.iter_mut().zip(src.iter()) {
*out = glog_from_fixed(value);
}
}
#[cfg(feature = "fixed_point")]
fn sync_loge_to_fixed(dst: &mut [FixedCeltGlog], src: &[CeltGlog]) {
for (out, &value) in dst.iter_mut().zip(src.iter()) {
*out = glog_to_fixed(value);
}
}
#[cfg(feature = "fixed_point")]
fn fill_fixed_sig(dst: &mut [FixedCeltSig], src: &[CeltSig]) {
for (out, &value) in dst.iter_mut().zip(src.iter()) {
let sample = float2sig(value);
*out = sample.clamp(-SIG_SAT, SIG_SAT);
}
}
#[cfg(feature = "fixed_point")]
fn fixed_sig_to_float(value: FixedCeltSig) -> f32 {
let scale = CELT_SIG_SCALE * (1u32 << SIG_SHIFT) as f32;
value as f32 / scale
}
#[cfg(feature = "fixed_point")]
fn fill_float_sig(dst: &mut [CeltSig], src: &[FixedCeltSig]) {
assert_eq!(
dst.len(),
src.len(),
"float buffer must mirror fixed buffer length",
);
for (out, &value) in dst.iter_mut().zip(src.iter()) {
*out = fixed_sig_to_float(value);
}
}
#[cfg(feature = "fixed_point")]
fn fixed_norm_to_float(value: FixedCeltNorm) -> f32 {
value as f32 / 32_768.0
}
#[cfg(feature = "fixed_point")]
fn fill_float_norm(dst: &mut [CeltNorm], src: &[FixedCeltNorm]) {
assert_eq!(
dst.len(),
src.len(),
"float norm buffer must mirror fixed buffer length",
);
for (out, &value) in dst.iter_mut().zip(src.iter()) {
*out = fixed_norm_to_float(value);
}
}
#[cfg(feature = "fixed_point")]
fn lm_offset_fixed(lm: usize) -> FixedCeltGlog {
shl32(lm as i32, DB_SHIFT - 1)
}
#[cfg(test)]
mod celt_alloc_trace {
extern crate std;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
pub(crate) struct TraceConfig {
frame: Option<usize>,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
Some(FRAME_INDEX.fetch_add(1, Ordering::Relaxed))
} else {
None
}
}
fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_ALLOC") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_ALLOC_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
Some(TraceConfig { frame })
})
.as_ref()
}
pub(crate) fn dump_if_match(
frame_idx: usize,
use_external: bool,
header_bytes: usize,
nb_compressed_bytes: usize,
tell0_frac: u32,
tell: i32,
nb_filled_bytes: i32,
tell_frac: u32,
start: usize,
end: usize,
bits: i32,
coded_bands: i32,
balance: i32,
intensity: i32,
dual_stereo: i32,
pulses: &[i32],
fine_quant: &[i32],
fine_priority: &[i32],
) {
let Some(cfg) = config() else {
return;
};
if let Some(frame) = cfg.frame {
if frame != frame_idx {
return;
}
}
crate::test_trace::trace_println!(
"celt_alloc[{frame_idx}].use_external={}",
if use_external { 1 } else { 0 }
);
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].header_bytes={header_bytes}");
crate::test_trace::trace_println!(
"celt_alloc[{frame_idx}].nb_compressed_bytes={nb_compressed_bytes}"
);
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].tell0_frac={tell0_frac}");
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].tell={tell}");
crate::test_trace::trace_println!(
"celt_alloc[{frame_idx}].nb_filled_bytes={nb_filled_bytes}"
);
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].tell_frac={tell_frac}");
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].start={start}");
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].end={end}");
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].bits={bits}");
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].coded_bands={coded_bands}");
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].balance={balance}");
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].intensity={intensity}");
crate::test_trace::trace_println!("celt_alloc[{frame_idx}].dual_stereo={dual_stereo}");
for band in start..end {
crate::test_trace::trace_println!(
"celt_alloc[{frame_idx}].band[{band}].pulses={}",
pulses[band]
);
crate::test_trace::trace_println!(
"celt_alloc[{frame_idx}].band[{band}].fine_quant={}",
fine_quant[band]
);
crate::test_trace::trace_println!(
"celt_alloc[{frame_idx}].band[{band}].fine_priority={}",
fine_priority[band]
);
}
}
}
#[cfg(test)]
mod celt_ctrl_trace {
extern crate std;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
pub(crate) struct TraceConfig {
frame: Option<usize>,
want_bits: bool,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
Some(FRAME_INDEX.fetch_add(1, Ordering::Relaxed))
} else {
None
}
}
fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_CTRL") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_CTRL_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
let want_bits = match env::var("CELT_TRACE_CTRL_BITS") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
Some(TraceConfig { frame, want_bits })
})
.as_ref()
}
pub(crate) fn dump_if_match(
frame_idx: usize,
use_external: bool,
header_bytes: usize,
nb_compressed_bytes: usize,
nb_filled_bytes: i32,
nb_available_bytes: i32,
effective_bytes: i32,
vbr_rate: i32,
equiv_rate: i32,
total_bits: i32,
tell0_frac: u32,
tell: i32,
tell_frac: u32,
tf_estimate: f32,
tf_chan: usize,
is_transient: bool,
short_blocks: usize,
spread_decision: i32,
intensity: i32,
alloc_trim: i32,
signal_bandwidth: i32,
start: usize,
end: usize,
bits: i32,
) {
let Some(cfg) = config() else {
return;
};
if let Some(frame) = cfg.frame {
if frame != frame_idx {
return;
}
}
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].use_external={}",
if use_external { 1 } else { 0 }
);
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].header_bytes={header_bytes}");
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].nb_compressed_bytes={nb_compressed_bytes}"
);
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].nb_filled_bytes={nb_filled_bytes}"
);
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].nb_available_bytes={nb_available_bytes}"
);
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].effective_bytes={effective_bytes}"
);
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].vbr_rate={vbr_rate}");
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].equiv_rate={equiv_rate}");
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].total_bits={total_bits}");
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].tell0_frac={tell0_frac}");
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].tell={tell}");
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].tell_frac={tell_frac}");
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].tf_estimate={:.9e}",
tf_estimate as f64
);
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].tf_chan={tf_chan}");
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].is_transient={}",
if is_transient { 1 } else { 0 }
);
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].short_blocks={short_blocks}");
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].spread_decision={spread_decision}"
);
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].intensity={intensity}");
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].alloc_trim={alloc_trim}");
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].signal_bandwidth={signal_bandwidth}"
);
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].start={start}");
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].end={end}");
crate::test_trace::trace_println!("celt_ctrl[{frame_idx}].bits={bits}");
if cfg.want_bits {
crate::test_trace::trace_println!(
"celt_ctrl[{frame_idx}].tf_estimate_bits=0x{:08x}",
tf_estimate.to_bits()
);
}
}
}
#[cfg(test)]
mod celt_vbr_budget_trace {
extern crate std;
use core::cell::Cell;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
use std::thread_local;
pub(crate) struct TraceConfig {
frame: Option<usize>,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
thread_local! { static CURRENT_FRAME: Cell<Option<usize>> = Cell::new(None); }
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
let frame = FRAME_INDEX.fetch_add(1, Ordering::Relaxed);
CURRENT_FRAME.with(|current| current.set(Some(frame)));
Some(frame)
} else {
CURRENT_FRAME.with(|current| current.set(None));
None
}
}
pub(crate) fn current_frame_idx() -> Option<usize> {
CURRENT_FRAME.with(|current| current.get())
}
pub(crate) fn should_dump(frame_idx: usize) -> bool {
config().map_or(false, |cfg| {
cfg.frame.map_or(true, |frame| frame == frame_idx)
})
}
fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_VBR_BUDGET") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_VBR_BUDGET_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
Some(TraceConfig { frame })
})
.as_ref()
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn dump_if_match(
frame_idx: usize,
stage: &str,
use_external: bool,
constrained_vbr: bool,
vbr_rate: i32,
vbr_reservoir: i32,
vbr_offset: i32,
vbr_drift: i32,
nb_compressed_bytes: usize,
nb_available_bytes: i32,
nb_filled_bytes: i32,
min_bytes: i32,
max_allowed: i32,
base_target: i32,
target: i32,
delta: i32,
min_allowed: i32,
tell: i32,
tell_frac: i32,
) {
let Some(cfg) = config() else {
return;
};
if let Some(frame) = cfg.frame {
if frame != frame_idx {
return;
}
}
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].stage={stage}");
crate::test_trace::trace_println!(
"celt_vbr_budget[{frame_idx}].use_external={}",
if use_external { 1 } else { 0 }
);
crate::test_trace::trace_println!(
"celt_vbr_budget[{frame_idx}].constrained_vbr={}",
if constrained_vbr { 1 } else { 0 }
);
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].vbr_rate={vbr_rate}");
crate::test_trace::trace_println!(
"celt_vbr_budget[{frame_idx}].vbr_reservoir={vbr_reservoir}"
);
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].vbr_offset={vbr_offset}");
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].vbr_drift={vbr_drift}");
crate::test_trace::trace_println!(
"celt_vbr_budget[{frame_idx}].nb_compressed_bytes={nb_compressed_bytes}"
);
crate::test_trace::trace_println!(
"celt_vbr_budget[{frame_idx}].nb_available_bytes={nb_available_bytes}"
);
crate::test_trace::trace_println!(
"celt_vbr_budget[{frame_idx}].nb_filled_bytes={nb_filled_bytes}"
);
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].min_bytes={min_bytes}");
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].max_allowed={max_allowed}");
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].base_target={base_target}");
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].target={target}");
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].delta={delta}");
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].min_allowed={min_allowed}");
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].tell={tell}");
crate::test_trace::trace_println!("celt_vbr_budget[{frame_idx}].tell_frac={tell_frac}");
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn dump_inputs_if_match(
frame_idx: usize,
tell_frac: i32,
tot_boost: i32,
tf_estimate: f32,
stereo_saving: f32,
intensity: i32,
last_coded_bands: i32,
pitch_change: i32,
max_depth: f32,
surround_masking: f32,
temporal_vbr: f32,
) {
let Some(cfg) = config() else {
return;
};
if let Some(frame) = cfg.frame {
if frame != frame_idx {
return;
}
}
crate::test_trace::trace_println!("celt_vbr_inputs[{frame_idx}].tell_frac={tell_frac}");
crate::test_trace::trace_println!("celt_vbr_inputs[{frame_idx}].tot_boost={tot_boost}");
crate::test_trace::trace_println!(
"celt_vbr_inputs[{frame_idx}].tf_estimate={:.9e}",
tf_estimate as f64
);
crate::test_trace::trace_println!(
"celt_vbr_inputs[{frame_idx}].stereo_saving={:.9e}",
stereo_saving as f64
);
crate::test_trace::trace_println!("celt_vbr_inputs[{frame_idx}].intensity={intensity}");
crate::test_trace::trace_println!(
"celt_vbr_inputs[{frame_idx}].last_coded_bands={last_coded_bands}"
);
crate::test_trace::trace_println!(
"celt_vbr_inputs[{frame_idx}].pitch_change={pitch_change}"
);
crate::test_trace::trace_println!(
"celt_vbr_inputs[{frame_idx}].max_depth={:.9e}",
max_depth as f64
);
crate::test_trace::trace_println!(
"celt_vbr_inputs[{frame_idx}].surround_masking={:.9e}",
surround_masking as f64
);
crate::test_trace::trace_println!(
"celt_vbr_inputs[{frame_idx}].temporal_vbr={:.9e}",
temporal_vbr as f64
);
}
}
#[cfg(test)]
mod celt_rc_trace {
extern crate std;
use core::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
use crate::celt::entcode::EcCtx;
pub(crate) struct TraceConfig {
frame: Option<usize>,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
static CURRENT_FRAME: AtomicIsize = AtomicIsize::new(-1);
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
let idx = FRAME_INDEX.fetch_add(1, Ordering::Relaxed);
CURRENT_FRAME.store(idx as isize, Ordering::Relaxed);
Some(idx)
} else {
CURRENT_FRAME.store(-1, Ordering::Relaxed);
None
}
}
pub(crate) fn should_dump(frame_idx: usize) -> bool {
config().map_or(false, |cfg| {
cfg.frame.map_or(true, |frame| frame == frame_idx)
})
}
pub(crate) fn current_frame_idx() -> Option<usize> {
let value = CURRENT_FRAME.load(Ordering::Relaxed);
if value >= 0 {
Some(value as usize)
} else {
None
}
}
fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_RC") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_RC_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
Some(TraceConfig { frame })
})
.as_ref()
}
pub(crate) fn dump_if_match(frame_idx: usize, stage: &str, ctx: &EcCtx<'_>) {
if !should_dump(frame_idx) {
return;
}
crate::test_trace::trace_println!("celt_rc[{frame_idx}].stage={stage}");
crate::test_trace::trace_println!("celt_rc[{frame_idx}].offs={}", ctx.offs);
crate::test_trace::trace_println!("celt_rc[{frame_idx}].end_offs={}", ctx.end_offs);
crate::test_trace::trace_println!("celt_rc[{frame_idx}].nbits_total={}", ctx.nbits_total);
crate::test_trace::trace_println!("celt_rc[{frame_idx}].nend_bits={}", ctx.nend_bits);
crate::test_trace::trace_println!("celt_rc[{frame_idx}].rng=0x{:08x}", ctx.rng);
crate::test_trace::trace_println!("celt_rc[{frame_idx}].val=0x{:08x}", ctx.val);
crate::test_trace::trace_println!("celt_rc[{frame_idx}].ext=0x{:08x}", ctx.ext);
crate::test_trace::trace_println!("celt_rc[{frame_idx}].rem={}", ctx.rem);
crate::test_trace::trace_println!(
"celt_rc[{frame_idx}].end_window=0x{:08x}",
ctx.end_window
);
crate::test_trace::trace_println!("celt_rc[{frame_idx}].error={}", ctx.error);
crate::test_trace::trace_println!(
"celt_rc[{frame_idx}].tell={}",
crate::celt::entcode::ec_tell(ctx)
);
crate::test_trace::trace_println!(
"celt_rc[{frame_idx}].tell_frac={}",
crate::celt::entcode::ec_tell_frac(ctx)
);
let buffer = ctx.buffer();
for i in 0..(ctx.offs as usize) {
let value = buffer[i];
crate::test_trace::trace_println!("celt_rc[{frame_idx}].buf[{i}]=0x{value:02x}");
}
if ctx.end_offs > 0 {
let start = (ctx.storage - ctx.end_offs) as usize;
for i in 0..(ctx.end_offs as usize) {
let value = buffer[start + i];
crate::test_trace::trace_println!(
"celt_rc[{frame_idx}].end_buf[{i}]=0x{value:02x}"
);
}
}
}
}
#[cfg(test)]
mod celt_band_energy_trace {
extern crate std;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
use crate::celt::types::CeltGlog;
pub(crate) struct TraceConfig {
frame: Option<usize>,
want_bits: bool,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
Some(FRAME_INDEX.fetch_add(1, Ordering::Relaxed))
} else {
None
}
}
pub(crate) fn should_dump(frame_idx: usize) -> bool {
config().map_or(false, |cfg| {
cfg.frame.map_or(true, |frame| frame == frame_idx)
})
}
pub(crate) fn want_bits() -> bool {
config().map_or(false, |cfg| cfg.want_bits)
}
fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_BAND_ENERGY") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_BAND_ENERGY_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
let want_bits = match env::var("CELT_TRACE_BAND_ENERGY_BITS") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
Some(TraceConfig { frame, want_bits })
})
.as_ref()
}
pub(crate) fn dump(
tag: &str,
frame_idx: usize,
start: usize,
end: usize,
channels: usize,
nb_ebands: usize,
band_e: &[CeltGlog],
want_bits: bool,
) {
crate::test_trace::trace_println!("celt_band_energy[{}].{}.end={}", frame_idx, tag, end);
for band in start..end {
for channel in 0..channels {
let idx = band + channel * nb_ebands;
if idx >= band_e.len() {
continue;
}
let value = band_e[idx];
crate::test_trace::trace_println!(
"celt_band_energy[{}].{}.band[{}].bandE[{}]={:.9}",
frame_idx,
tag,
band,
channel,
value
);
if want_bits {
crate::test_trace::trace_println!(
"celt_band_energy[{}].{}.band[{}].bandE_bits[{}]=0x{:08x}",
frame_idx,
tag,
band,
channel,
value.to_bits()
);
}
}
}
}
}
#[cfg(test)]
mod celt_loge_adjust_trace {
extern crate std;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
pub(crate) struct TraceConfig {
frame: Option<usize>,
band: Option<usize>,
want_bits: bool,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
Some(FRAME_INDEX.fetch_add(1, Ordering::Relaxed))
} else {
None
}
}
fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_LOGE_ADJUST") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_LOGE_ADJUST_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
let band = env::var("CELT_TRACE_LOGE_ADJUST_BAND")
.ok()
.and_then(|value| value.parse::<usize>().ok());
let want_bits = match env::var("CELT_TRACE_LOGE_ADJUST_BITS") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
Some(TraceConfig {
frame,
band,
want_bits,
})
})
.as_ref()
}
fn should_dump(frame_idx: usize, band: usize) -> bool {
config().map_or(false, |cfg| {
cfg.frame.map_or(true, |frame| frame == frame_idx)
&& cfg.band.map_or(true, |target_band| target_band == band)
})
}
pub(crate) fn dump_if_match(
frame_idx: usize,
band: usize,
channel: usize,
log_before: f32,
old: f32,
err: f32,
diff: f32,
apply: bool,
log_after: f32,
) {
if !should_dump(frame_idx, band) {
return;
}
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].log_before={log_before:.9e}"
);
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].old={old:.9e}"
);
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].err={err:.9e}"
);
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].diff={diff:.9e}"
);
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].apply={}",
apply as u8
);
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].log_after={log_after:.9e}"
);
let want_bits = config().map_or(false, |cfg| cfg.want_bits);
if want_bits {
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].log_before_bits=0x{:08x}",
log_before.to_bits()
);
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].old_bits=0x{:08x}",
old.to_bits()
);
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].err_bits=0x{:08x}",
err.to_bits()
);
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].diff_bits=0x{:08x}",
diff.to_bits()
);
crate::test_trace::trace_println!(
"celt_loge_adjust[{frame_idx}].band[{band}].c[{channel}].log_after_bits=0x{:08x}",
log_after.to_bits()
);
}
}
}
#[cfg(test)]
mod celt_prefilter_trace {
extern crate std;
use core::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
pub(crate) struct TraceConfig {
frame: Option<usize>,
want_bits: bool,
start: usize,
count: usize,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
static CURRENT_FRAME: AtomicIsize = AtomicIsize::new(-1);
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
let idx = FRAME_INDEX.fetch_add(1, Ordering::Relaxed);
CURRENT_FRAME.store(idx as isize, Ordering::Relaxed);
Some(idx)
} else {
CURRENT_FRAME.store(-1, Ordering::Relaxed);
None
}
}
pub(crate) fn should_dump(frame_idx: usize) -> bool {
config().map_or(false, |cfg| {
cfg.frame.map_or(true, |frame| frame == frame_idx)
})
}
pub(crate) fn current_frame_idx() -> Option<usize> {
let value = CURRENT_FRAME.load(Ordering::Relaxed);
if value >= 0 {
Some(value as usize)
} else {
None
}
}
pub(crate) fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_PREFILTER") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_PREFILTER_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
let want_bits = match env::var("CELT_TRACE_PREFILTER_BITS") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
let start = env::var("CELT_TRACE_PREFILTER_START")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(0);
let count = env::var("CELT_TRACE_PREFILTER_COUNT")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(64);
Some(TraceConfig {
frame,
want_bits,
start,
count,
})
})
.as_ref()
}
pub(crate) fn dump(tag: &str, frame_idx: usize, input: &[f32], channels: usize) {
let cfg = match config() {
Some(cfg) => cfg,
None => return,
};
if channels == 0 {
return;
}
let len = input.len() / channels;
let start = cfg.start.min(len);
let end = start.saturating_add(cfg.count).min(len);
crate::test_trace::trace_println!("celt_prefilter[{}].{}.len={}", frame_idx, tag, len);
for ch in 0..channels {
let base = ch * len;
for i in start..end {
let value = input[base + i];
crate::test_trace::trace_println!(
"celt_prefilter[{}].{}.ch[{}].sample[{}]={:.9}",
frame_idx,
tag,
ch,
i,
value
);
if cfg.want_bits {
crate::test_trace::trace_println!(
"celt_prefilter[{}].{}.ch[{}].sample_bits[{}]=0x{:08x}",
frame_idx,
tag,
ch,
i,
value.to_bits()
);
}
}
}
}
}
#[cfg(test)]
mod celt_pcm_input_trace {
extern crate std;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
pub(crate) struct TraceConfig {
frame: Option<usize>,
want_bits: bool,
start: usize,
count: usize,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
Some(FRAME_INDEX.fetch_add(1, Ordering::Relaxed))
} else {
None
}
}
pub(crate) fn dump(tag: &str, frame_idx: usize, pcm: &[f32], channels: usize, len: usize) {
let cfg = match config() {
Some(cfg) => cfg,
None => return,
};
if cfg.frame.map_or(false, |frame| frame != frame_idx) {
return;
}
if channels == 0 {
return;
}
let start = cfg.start.min(len);
let end = start.saturating_add(cfg.count).min(len);
crate::test_trace::trace_println!("celt_pcm[{}].{}.len={}", frame_idx, tag, len);
for ch in 0..channels {
for i in start..end {
let idx = i * channels + ch;
if idx >= pcm.len() {
continue;
}
let value = pcm[idx];
crate::test_trace::trace_println!(
"celt_pcm[{}].{}.ch[{}].sample[{}]={:.9}",
frame_idx,
tag,
ch,
i,
value
);
if cfg.want_bits {
crate::test_trace::trace_println!(
"celt_pcm[{}].{}.ch[{}].sample_bits[{}]=0x{:08x}",
frame_idx,
tag,
ch,
i,
value.to_bits()
);
}
}
}
}
fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_PCM") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_PCM_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
let want_bits = match env::var("CELT_TRACE_PCM_BITS") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
let start = env::var("CELT_TRACE_PCM_START")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(0);
let count = env::var("CELT_TRACE_PCM_COUNT")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(64);
Some(TraceConfig {
frame,
want_bits,
start,
count,
})
})
.as_ref()
}
}
#[cfg(test)]
mod celt_mdct_trace {
extern crate std;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
pub(crate) struct TraceConfig {
frame: Option<usize>,
want_bits: bool,
start: usize,
count: usize,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
Some(FRAME_INDEX.fetch_add(1, Ordering::Relaxed))
} else {
None
}
}
pub(crate) fn should_dump(frame_idx: usize) -> bool {
config().map_or(false, |cfg| {
cfg.frame.map_or(true, |frame| frame == frame_idx)
})
}
pub(crate) fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_MDCT") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_MDCT_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
let want_bits = match env::var("CELT_TRACE_MDCT_BITS") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
let start = env::var("CELT_TRACE_MDCT_START")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(0);
let count = env::var("CELT_TRACE_MDCT_COUNT")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(64);
Some(TraceConfig {
frame,
want_bits,
start,
count,
})
})
.as_ref()
}
pub(crate) fn dump(tag: &str, frame_idx: usize, freq: &[f32], channels: usize) {
let cfg = match config() {
Some(cfg) => cfg,
None => return,
};
if channels == 0 {
return;
}
let len = freq.len() / channels;
let start = cfg.start.min(len);
let end = start.saturating_add(cfg.count).min(len);
crate::test_trace::trace_println!("celt_mdct[{}].{}.len={}", frame_idx, tag, len);
for ch in 0..channels {
let base = ch * len;
for i in start..end {
let value = freq[base + i];
crate::test_trace::trace_println!(
"celt_mdct[{}].{}.ch[{}].idx[{}]={:.9}",
frame_idx,
tag,
ch,
i,
value
);
if cfg.want_bits {
crate::test_trace::trace_println!(
"celt_mdct[{}].{}.ch[{}].idx_bits[{}]=0x{:08x}",
frame_idx,
tag,
ch,
i,
value.to_bits()
);
}
}
}
}
}
#[cfg(test)]
mod celt_transient_trace {
extern crate std;
use core::cell::Cell;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::env;
use std::sync::OnceLock;
use std::thread_local;
pub(crate) struct TraceConfig {
frame: Option<usize>,
want_bits: bool,
}
static TRACE_CONFIG: OnceLock<Option<TraceConfig>> = OnceLock::new();
static FRAME_INDEX: AtomicUsize = AtomicUsize::new(0);
thread_local! { static CURRENT_FRAME: Cell<Option<usize>> = Cell::new(None); }
pub(crate) fn begin_frame() -> Option<usize> {
if config().is_some() {
let frame = FRAME_INDEX.fetch_add(1, Ordering::Relaxed);
CURRENT_FRAME.with(|current| current.set(Some(frame)));
Some(frame)
} else {
CURRENT_FRAME.with(|current| current.set(None));
None
}
}
pub(crate) fn current_frame_idx() -> Option<usize> {
CURRENT_FRAME.with(|current| current.get())
}
pub(crate) fn should_dump(frame_idx: usize) -> bool {
config().map_or(false, |cfg| {
cfg.frame.map_or(true, |frame| frame == frame_idx)
})
}
pub(crate) fn want_bits() -> bool {
config().map_or(false, |cfg| cfg.want_bits)
}
fn config() -> Option<&'static TraceConfig> {
TRACE_CONFIG
.get_or_init(|| {
let enabled = match env::var("CELT_TRACE_TRANSIENT") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
if !enabled {
return None;
}
let frame = env::var("CELT_TRACE_TRANSIENT_FRAME")
.ok()
.and_then(|value| value.parse::<usize>().ok());
let want_bits = match env::var("CELT_TRACE_TRANSIENT_BITS") {
Ok(value) => !value.is_empty() && value != "0",
Err(_) => false,
};
Some(TraceConfig { frame, want_bits })
})
.as_ref()
}
}
const MAX_CHANNELS: usize = 2;
const COMBFILTER_MAXPERIOD: usize = 1024;
const MAX_TF_BANDS: usize = 50;
const MAX_TF_BAND_SIZE: usize = 208;
const LEAK_BANDS: usize = 19;
const PREEMPHASIS_CLIP_LIMIT: CeltSig = 65_536.0;
const OPUS_BITRATE_MAX: OpusInt32 = -1;
const TRIM_ICDF: [u8; 11] = [126, 124, 119, 109, 87, 41, 19, 9, 4, 2, 0];
const SPREAD_ICDF: [u8; 4] = [25, 23, 2, 0];
const TAPSET_ICDF: [u8; 3] = [2, 1, 0];
const TO_OPUS_TABLE: [u8; 20] = [
0xE0, 0xE8, 0xF0, 0xF8, 0xC0, 0xC8, 0xD0, 0xD8, 0xA0, 0xA8, 0xB0, 0xB8, 0x00, 0x00, 0x00, 0x00,
0x80, 0x88, 0x90, 0x98,
];
const FROM_OPUS_TABLE: [u8; 16] = [
0x80, 0x88, 0x90, 0x98, 0x40, 0x48, 0x50, 0x58, 0x20, 0x28, 0x30, 0x38, 0x00, 0x08, 0x10, 0x18,
];
fn to_opus(value: u8) -> Option<u8> {
if value < 0xA0 {
let mapped = TO_OPUS_TABLE[(value >> 3) as usize];
if mapped != 0 {
return Some(mapped | (value & 0x7));
}
}
None
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CeltEncoderInitError {
InvalidChannelCount,
InvalidStreamChannels,
UnsupportedSampleRate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CeltEncoderCtlError {
InvalidArgument,
Unimplemented,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CeltEncodeError {
InsufficientPcm,
InvalidFrameSize,
MissingOutput,
}
#[allow(clippy::large_enum_variant)]
pub(crate) enum EncoderCtlRequest<'enc, 'req> {
SetComplexity(i32),
SetStartBand(i32),
SetEndBand(i32),
SetPrediction(i32),
SetPacketLossPerc(i32),
SetVbrConstraint(bool),
SetVbr(bool),
SetBitrate(OpusInt32),
SetChannels(usize),
SetLsbDepth(i32),
GetLsbDepth(&'req mut i32),
SetPhaseInversionDisabled(bool),
GetPhaseInversionDisabled(&'req mut bool),
ResetState,
SetInputClipping(bool),
SetSignalling(i32),
SetAnalysis(&'enc AnalysisInfo),
SetSilkInfo(&'enc SilkInfo),
GetMode(&'req mut Option<&'enc OpusCustomMode<'enc>>),
GetFinalRange(&'req mut OpusUint32),
SetLfe(bool),
SetEnergyMask(Option<&'enc [CeltGlog]>),
}
#[must_use]
pub(crate) fn opus_custom_encoder_get_size(mode: &OpusCustomMode<'_>, channels: usize) -> usize {
let in_mem = channels * mode.overlap;
let prefilter_mem = channels * COMBFILTER_MAXPERIOD;
let band_count = channels * mode.num_ebands;
in_mem * core::mem::size_of::<CeltSig>()
+ prefilter_mem * core::mem::size_of::<CeltSig>()
+ 4 * band_count * core::mem::size_of::<CeltGlog>()
+ {
#[cfg(feature = "fixed_point")]
{
(in_mem + prefilter_mem) * core::mem::size_of::<FixedCeltSig>()
+ 2 * band_count * core::mem::size_of::<FixedCeltGlog>()
}
#[cfg(not(feature = "fixed_point"))]
{
0
}
}
}
#[must_use]
pub(crate) fn celt_encoder_get_size(channels: usize) -> Option<usize> {
opus_custom_mode_find_static(48_000, 960)
.map(|mode| opus_custom_encoder_get_size(&mode, channels))
}
pub(crate) fn opus_custom_encoder_init_arch<'mode>(
alloc: &mut CeltEncoderAlloc,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
arch: i32,
rng_seed: OpusUint32,
) -> Result<OpusCustomEncoder<'mode>, CeltEncoderInitError> {
alloc.init_custom_encoder_with_arch(mode, channels, channels, arch, rng_seed)
}
pub(crate) fn opus_custom_encoder_init<'mode>(
alloc: &mut CeltEncoderAlloc,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
rng_seed: OpusUint32,
) -> Result<OpusCustomEncoder<'mode>, CeltEncoderInitError> {
alloc.init_custom_encoder(mode, channels, channels, rng_seed)
}
pub(crate) fn celt_encoder_init(
alloc: &mut CeltEncoderAlloc,
sampling_rate: OpusInt32,
channels: usize,
arch: i32,
rng_seed: OpusUint32,
) -> Result<OpusCustomEncoder<'static>, CeltEncoderInitError> {
let upsample = resampling_factor(sampling_rate);
if upsample == 0 {
return Err(CeltEncoderInitError::UnsupportedSampleRate);
}
let mode = opus_custom_mode_find_static_ref(48_000, 960).expect("static mode");
alloc.init_internal(mode, channels, channels, upsample, arch, rng_seed)
}
pub(crate) fn opus_custom_encoder_destroy(_encoder: OwnedCeltEncoder<'_>) {}
#[derive(Debug)]
pub(crate) struct OwnedCeltEncoder<'mode> {
encoder: OpusCustomEncoder<'mode>,
}
impl<'mode> OwnedCeltEncoder<'mode> {
#[must_use]
pub fn encoder(&mut self) -> &mut OpusCustomEncoder<'mode> {
&mut self.encoder
}
pub fn new(
mode: &'mode OpusCustomMode<'mode>,
sampling_rate: OpusInt32,
channels: usize,
rng_seed: OpusUint32,
) -> Result<Self, CeltEncoderInitError> {
opus_custom_encoder_create(mode, sampling_rate, channels, rng_seed)
}
}
impl<'mode> core::ops::Deref for OwnedCeltEncoder<'mode> {
type Target = OpusCustomEncoder<'mode>;
fn deref(&self) -> &Self::Target {
&self.encoder
}
}
impl<'mode> core::ops::DerefMut for OwnedCeltEncoder<'mode> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.encoder
}
}
pub(crate) fn opus_custom_encoder_create<'mode>(
mode: &'mode OpusCustomMode<'mode>,
sampling_rate: OpusInt32,
channels: usize,
rng_seed: OpusUint32,
) -> Result<OwnedCeltEncoder<'mode>, CeltEncoderInitError> {
let mut alloc = CeltEncoderAlloc::new(mode, channels);
let encoder = if sampling_rate == mode.sample_rate {
alloc.init_custom_encoder(mode, channels, channels, rng_seed)
} else {
alloc.init_encoder_for_rate(mode, channels, channels, sampling_rate, rng_seed)
}?;
Ok(OwnedCeltEncoder { encoder })
}
fn l1_metric(tmp: &[OpusVal16], n: usize, lm: i32, bias: OpusVal16) -> OpusVal32 {
assert!(n <= tmp.len());
let mut l1: OpusVal32 = 0.0;
for &value in &tmp[..n] {
l1 += value.abs() as OpusVal32;
}
let freq_bias = (lm as OpusVal32) * bias as OpusVal32;
l1 + freq_bias * l1
}
fn stereo_analysis(mode: &OpusCustomMode<'_>, x: &[CeltNorm], lm: usize, n0: usize) -> bool {
const EPSILON: f32 = 1.0e-15;
debug_assert!(
mode.num_ebands >= 14,
"stereo analysis expects at least 14 bands"
);
debug_assert!(
x.len() >= 2 * n0,
"stereo analysis requires two channel buffers"
);
let mut sum_lr = EPSILON;
let mut sum_ms = EPSILON;
for band in 0..13 {
let start = (mode.e_bands[band] as usize) << lm;
let end = (mode.e_bands[band + 1] as usize) << lm;
if end <= start || end > n0 {
continue;
}
for idx in start..end {
let left = x[idx];
let right = x[n0 + idx];
let mid = left + right;
let side = left - right;
sum_lr += left.abs() + right.abs();
sum_ms += mid.abs() + side.abs();
}
}
sum_ms *= FRAC_1_SQRT_2;
let mut thetas = 13i32;
if lm <= 1 {
thetas -= 8;
}
let base = i32::from(mode.e_bands[13]) << (lm + 1);
let lhs = (base + thetas) as f32 * sum_ms;
let rhs = base as f32 * sum_lr;
lhs > rhs
}
#[allow(clippy::too_many_arguments)]
fn tf_analysis(
mode: &OpusCustomMode<'_>,
len: usize,
is_transient: bool,
tf_res: &mut [i32],
lambda: i32,
x: &[CeltNorm],
n0: usize,
lm: usize,
tf_estimate: OpusVal16,
tf_chan: usize,
importance: &[i32],
) -> i32 {
debug_assert!(lm < TF_SELECT_TABLE.len());
debug_assert!(len <= tf_res.len());
debug_assert!(len <= importance.len());
debug_assert!(len < mode.e_bands.len());
if len == 0 {
return 0;
}
let bias = 0.04 * (0.5 - tf_estimate).max(-0.25);
let mut max_band = 0usize;
for band in 0..len {
let start = mode.e_bands[band] as usize;
let end = mode.e_bands[band + 1] as usize;
let width = end.saturating_sub(start);
max_band = max(max_band, width << lm);
}
debug_assert!(len <= MAX_TF_BANDS);
debug_assert!(max_band <= MAX_TF_BAND_SIZE);
let mut metric_storage = [0i32; MAX_TF_BANDS];
let mut path0_storage = [0i32; MAX_TF_BANDS];
let mut path1_storage = [0i32; MAX_TF_BANDS];
let mut tmp_storage = [0.0f32; MAX_TF_BAND_SIZE];
let mut tmp_alt_storage = [0.0f32; MAX_TF_BAND_SIZE];
let metric = &mut metric_storage[..len];
let path0 = &mut path0_storage[..len];
let path1 = &mut path1_storage[..len];
let tmp = &mut tmp_storage[..max_band.max(1)];
let tmp_alt = &mut tmp_alt_storage[..max_band.max(1)];
let lm_i32 = lm as i32;
for band in 0..len {
let start = mode.e_bands[band] as usize;
let end = mode.e_bands[band + 1] as usize;
let width = end.saturating_sub(start);
let n = width << lm;
if n == 0 {
continue;
}
let offset = tf_chan * n0 + (start << lm);
debug_assert!(offset + n <= x.len());
tmp[..n].copy_from_slice(&x[offset..offset + n]);
let narrow = width == 1;
let mut best_level = 0i32;
let mut best_l1 = l1_metric(&tmp[..n], n, if is_transient { lm_i32 } else { 0 }, bias);
if is_transient && !narrow {
tmp_alt[..n].copy_from_slice(&tmp[..n]);
let blocks = n >> lm;
if blocks > 0 {
haar1(&mut tmp_alt[..n], blocks, 1 << lm);
let l1 = l1_metric(&tmp_alt[..n], n, lm_i32 + 1, bias);
if l1 < best_l1 {
best_l1 = l1;
best_level = -1;
}
}
}
let extra = if is_transient || narrow { 0 } else { 1 };
for k in 0..(lm + extra) {
let blocks = n >> k;
if blocks == 0 {
break;
}
haar1(&mut tmp[..n], blocks, 1 << k);
let b = if is_transient {
lm_i32 - k as i32 - 1
} else {
k as i32 + 1
};
let l1 = l1_metric(&tmp[..n], n, b, bias);
if l1 < best_l1 {
best_l1 = l1;
best_level = k as i32 + 1;
}
}
let mut value = if is_transient {
2 * best_level
} else {
-2 * best_level
};
if narrow && (value == 0 || value == -2 * lm_i32) {
value -= 1;
}
metric[band] = value;
}
let table = &TF_SELECT_TABLE[lm];
let base_index = if is_transient { 4 } else { 0 };
let mut selcost = [0i32; 2];
for sel in 0..2 {
let idx0 = base_index + 2 * sel;
let idx1 = idx0 + 1;
let target0 = 2 * i32::from(table[idx0]);
let target1 = 2 * i32::from(table[idx1]);
let mut cost0 = importance[0] * (metric[0] - target0).abs();
let mut cost1 = importance[0] * (metric[0] - target1).abs();
if !is_transient {
cost1 += lambda;
}
for band in 1..len {
let from0 = cost0;
let from1 = cost1 + lambda;
let curr0;
if from0 < from1 {
curr0 = from0;
path0[band] = 0;
} else {
curr0 = from1;
path0[band] = 1;
}
let from0 = cost0 + lambda;
let from1 = cost1;
let curr1;
if from0 < from1 {
curr1 = from0;
path1[band] = 0;
} else {
curr1 = from1;
path1[band] = 1;
}
cost0 = curr0 + importance[band] * (metric[band] - target0).abs();
cost1 = curr1 + importance[band] * (metric[band] - target1).abs();
}
selcost[sel] = cost0.min(cost1);
}
let mut tf_select = 0i32;
if is_transient && selcost[1] < selcost[0] {
tf_select = 1;
}
let idx0 = base_index + 2 * tf_select as usize;
let idx1 = idx0 + 1;
let target0 = 2 * i32::from(table[idx0]);
let target1 = 2 * i32::from(table[idx1]);
let mut cost0 = importance[0] * (metric[0] - target0).abs();
let mut cost1 = importance[0] * (metric[0] - target1).abs();
if !is_transient {
cost1 += lambda;
}
for band in 1..len {
let from0 = cost0;
let from1 = cost1 + lambda;
let curr0;
if from0 < from1 {
curr0 = from0;
path0[band] = 0;
} else {
curr0 = from1;
path0[band] = 1;
}
let from0 = cost0 + lambda;
let from1 = cost1;
let curr1;
if from0 < from1 {
curr1 = from0;
path1[band] = 0;
} else {
curr1 = from1;
path1[band] = 1;
}
cost0 = curr0 + importance[band] * (metric[band] - target0).abs();
cost1 = curr1 + importance[band] * (metric[band] - target1).abs();
}
tf_res[len - 1] = if cost0 < cost1 { 0 } else { 1 };
if len >= 2 {
for band in (0..=(len - 2)).rev() {
let next = tf_res[band + 1];
tf_res[band] = if next == 1 {
path1[band + 1]
} else {
path0[band + 1]
};
}
}
tf_select
}
#[allow(clippy::too_many_arguments)]
fn alloc_trim_analysis(
mode: &OpusCustomMode<'_>,
x: &[CeltNorm],
band_log_e: &[CeltGlog],
end: usize,
lm: usize,
channels: usize,
n0: usize,
analysis: &AnalysisInfo,
stereo_saving: &mut OpusVal16,
tf_estimate: OpusVal16,
intensity: usize,
surround_trim: CeltGlog,
equiv_rate: OpusInt32,
_arch: i32,
) -> i32 {
debug_assert!(channels == 1 || channels == 2);
debug_assert!(
x.len() >= channels * n0,
"insufficient MDCT samples for alloc_trim_analysis"
);
debug_assert!(band_log_e.len() >= channels * mode.num_ebands);
let mut trim = 5.0f32;
if equiv_rate < 64_000 {
trim = 4.0;
} else if equiv_rate < 80_000 {
let frac = ((equiv_rate - 64_000) >> 10) as f32;
trim = 4.0 + (1.0 / 16.0) * frac;
}
if channels == 2 {
let mut sum = 0.0f32;
let limit = intensity.min(mode.num_ebands);
for band in 0..8.min(mode.num_ebands) {
let start = (mode.e_bands[band] as usize) << lm;
let end = (mode.e_bands[band + 1] as usize) << lm;
if end <= start || end > n0 {
continue;
}
let left = &x[start..end];
let right = &x[n0 + start..n0 + end];
let partial = celt_inner_prod(left, right);
sum += partial;
}
sum *= 1.0 / 8.0;
sum = sum.abs().min(1.0);
let mut min_xc = sum;
for band in 8..limit {
let start = (mode.e_bands[band] as usize) << lm;
let end = (mode.e_bands[band + 1] as usize) << lm;
if end <= start || end > n0 {
continue;
}
let left = &x[start..end];
let right = &x[n0 + start..n0 + end];
let partial = celt_inner_prod(left, right).abs().min(1.0);
if partial < min_xc {
min_xc = partial;
}
}
let log_xc = celt_log2(1.001 - sum * sum);
let alt = celt_log2(1.001 - min_xc * min_xc);
let mut log_xc2 = 0.5 * log_xc;
if alt > log_xc2 {
log_xc2 = alt;
}
let adjustment = (0.75 * log_xc).max(-4.0);
trim += adjustment;
let candidate = (-0.5 * log_xc2).min(*stereo_saving + 0.25);
*stereo_saving = candidate;
}
let nb_ebands = mode.num_ebands;
let mut diff = 0.0f32;
if end > 1 {
for ch in 0..channels {
let base = ch * nb_ebands;
for band in 0..(end - 1) {
let weight = (2 + 2 * band as i32 - end as i32) as f32;
diff += band_log_e[base + band] * weight;
}
}
diff /= (channels * (end - 1)) as f32;
}
let slope = ((diff + 1.0) / 6.0).clamp(-2.0, 2.0);
trim -= slope;
trim -= surround_trim;
trim -= 2.0 * tf_estimate;
if analysis.valid {
let tonal = 2.0 * (analysis.tonality_slope + 0.05);
trim -= tonal.clamp(-2.0, 2.0);
}
let mut trim_index = floorf(trim + 0.5) as i32;
trim_index = trim_index.clamp(0, 10);
trim_index
}
#[allow(clippy::too_many_arguments)]
fn compute_mdcts(
mode: &OpusCustomMode<'_>,
short_blocks: usize,
input: &[CeltSig],
output: &mut [CeltSig],
coded_channels: usize,
total_channels: usize,
lm: usize,
upsample: usize,
arch: i32,
) {
assert!(coded_channels > 0 && coded_channels <= total_channels);
assert!(upsample > 0);
assert!(lm <= mode.max_lm);
let overlap = mode.overlap;
let (block_count, shift) = if short_blocks != 0 {
(short_blocks, mode.max_lm)
} else {
(1, mode.max_lm - lm)
};
let transform_len = mode.mdct.effective_len(shift);
assert!(transform_len.is_multiple_of(2));
let frame_len = transform_len >> 1;
let channel_input_stride = block_count * frame_len + overlap;
let channel_output_stride = block_count * frame_len;
assert!(input.len() >= total_channels * channel_input_stride);
assert!(output.len() >= total_channels * channel_output_stride);
for channel in 0..total_channels {
let input_base = channel * channel_input_stride;
let output_base = channel * channel_output_stride;
for block in 0..block_count {
let input_offset = input_base + block * frame_len;
let output_offset = output_base + block;
let input_end = input_offset + overlap + frame_len;
let output_end = output_base + channel_output_stride;
#[cfg(test)]
mdct_input_trace::set_call(channel, block);
clt_mdct_forward(
&mode.mdct,
&input[input_offset..input_end],
&mut output[output_offset..output_end],
mode.window,
overlap,
shift,
block_count,
);
}
}
if total_channels == 2 && coded_channels == 1 {
let band_len = block_count * frame_len;
for i in 0..band_len {
output[i] = 0.5 * (output[i] + output[band_len + i]);
}
}
if upsample != 1 {
for channel in 0..coded_channels {
let base = channel * channel_output_stride;
let bound = channel_output_stride / upsample;
let (to_scale, to_zero) =
output[base..base + channel_output_stride].split_at_mut(bound);
for value in to_scale.iter_mut() {
*value *= upsample as CeltSig;
}
to_zero.fill(0.0);
}
}
let _ = arch;
}
#[cfg(feature = "fixed_point")]
#[allow(clippy::too_many_arguments)]
fn compute_mdcts_fixed(
mode: &OpusCustomMode<'_>,
fixed_mdct: &FixedMdctLookup,
fixed_window: &[FixedCeltCoef],
short_blocks: usize,
input: &[FixedCeltSig],
output: &mut [FixedCeltSig],
coded_channels: usize,
total_channels: usize,
lm: usize,
upsample: usize,
) {
assert!(coded_channels > 0 && coded_channels <= total_channels);
assert!(upsample > 0);
assert!(lm <= mode.max_lm);
let overlap = mode.overlap;
let (block_count, shift) = if short_blocks != 0 {
(short_blocks, mode.max_lm)
} else {
(1, mode.max_lm - lm)
};
let transform_len = fixed_mdct.effective_len(shift);
assert!(transform_len.is_multiple_of(2));
let frame_len = transform_len >> 1;
let channel_input_stride = block_count * frame_len + overlap;
let channel_output_stride = block_count * frame_len;
assert!(input.len() >= total_channels * channel_input_stride);
assert!(output.len() >= total_channels * channel_output_stride);
for channel in 0..total_channels {
let input_base = channel * channel_input_stride;
let output_base = channel * channel_output_stride;
for block in 0..block_count {
let input_offset = input_base + block * frame_len;
let output_offset = output_base + block;
let input_end = input_offset + overlap + frame_len;
let output_end = output_base + channel_output_stride;
clt_mdct_forward_fixed(
fixed_mdct,
&input[input_offset..input_end],
&mut output[output_offset..output_end],
fixed_window,
overlap,
shift,
block_count,
);
}
}
if total_channels == 2 && coded_channels == 1 {
let band_len = block_count * frame_len;
for i in 0..band_len {
let left = output[i];
let right = output[band_len + i];
output[i] = add32(shr32(left, 1), shr32(right, 1));
}
}
if upsample != 1 {
for channel in 0..coded_channels {
let base = channel * channel_output_stride;
let bound = channel_output_stride / upsample;
for idx in 0..bound {
output[base + idx] = output[base + idx].wrapping_mul(upsample as i32);
}
output[base + bound..base + channel_output_stride].fill(0);
}
}
}
fn ensure_pcm_capacity(pcmp: &[OpusRes], channels: usize, samples: usize) {
if samples == 0 {
return;
}
let Some(last_frame_index) = samples.checked_sub(1) else {
return;
};
let required = channels
.checked_mul(last_frame_index)
.and_then(|value| value.checked_add(1))
.expect("pcm length calculation overflowed");
assert!(
pcmp.len() >= required,
"PCM slice is shorter than the requested frame"
);
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn celt_preemphasis(
pcmp: &[OpusRes],
inp: &mut [CeltSig],
n: usize,
channels: usize,
upsample: usize,
coef: &[OpusVal16; 4],
mem: &mut CeltSig,
clip: bool,
) {
assert!(channels > 0, "channel count must be positive");
assert!(upsample > 0, "upsample factor must be positive");
assert!(
inp.len() >= n,
"output buffer too small for requested frame"
);
let coef0 = coef[0];
let mut m = *mem;
if coef[1] == 0.0 && upsample == 1 && !clip {
ensure_pcm_capacity(pcmp, channels, n);
for i in 0..n {
let x = pcmp[channels * i] * CELT_SIG_SCALE;
inp[i] = x - m;
m = coef0 * x;
}
*mem = m;
return;
}
let nu = n / upsample;
if upsample != 1 {
inp[..n].fill(0.0);
}
ensure_pcm_capacity(pcmp, channels, nu);
for i in 0..nu {
inp[i * upsample] = pcmp[channels * i] * CELT_SIG_SCALE;
}
if clip {
for i in 0..nu {
let index = i * upsample;
inp[index] = inp[index].clamp(-PREEMPHASIS_CLIP_LIMIT, PREEMPHASIS_CLIP_LIMIT);
}
}
if coef[1] == 0.0 {
for value in &mut inp[..n] {
let x = *value;
*value = x - m;
m = coef0 * x;
}
} else {
let coef1 = coef[1];
let coef2 = coef[2];
for value in &mut inp[..n] {
let x = *value;
let tmp = coef2 * x;
*value = tmp + m;
m = coef1 * *value - coef0 * tmp;
}
}
*mem = m;
}
#[cfg(feature = "fixed_point")]
pub(crate) fn celt_preemphasis_fixed(
pcmp: &[CeltSig],
inp: &mut [FixedCeltSig],
n: usize,
channels: usize,
upsample: usize,
coef: &[OpusVal16; 4],
mem: &mut FixedCeltSig,
clip: bool,
) {
assert!(channels > 0, "channel count must be positive");
assert!(upsample > 0, "upsample factor must be positive");
assert!(
inp.len() >= n,
"output buffer too small for requested frame"
);
let coef0 = qconst16(f64::from(coef[0]), 15);
let coef1 = qconst16(f64::from(coef[1]), 15);
let coef2 = qconst16(f64::from(coef[2]), SIG_SHIFT);
let mut m = *mem;
if coef[1] == 0.0 && upsample == 1 && !clip {
ensure_pcm_capacity(pcmp, channels, n);
for i in 0..n {
let x = float2sig(pcmp[channels * i]);
inp[i] = sub32(x, m);
m = mult16_32_q15(coef0, x);
}
*mem = m;
return;
}
let nu = n / upsample;
if upsample != 1 {
inp[..n].fill(0);
}
ensure_pcm_capacity(pcmp, channels, nu);
for i in 0..nu {
inp[i * upsample] = float2sig(pcmp[channels * i]);
}
if coef[1] != 0.0 {
for value in &mut inp[..n] {
let x = *value;
let tmp = shl32(mult16_32_q15(coef2, x), (15 - SIG_SHIFT) as u32);
*value = add32(tmp, m);
m = sub32(mult16_32_q15(coef1, *value), mult16_32_q15(coef0, tmp));
}
} else {
for value in &mut inp[..n] {
let x = *value;
*value = sub32(x, m);
m = mult16_32_q15(coef0, x);
}
}
*mem = m;
}
#[derive(Debug, Default)]
pub(crate) struct CeltEncoderAlloc {
in_mem: Vec<CeltSig>,
prefilter_mem: Vec<CeltSig>,
#[cfg(feature = "fixed_point")]
fixed_in_mem: Vec<FixedCeltSig>,
#[cfg(feature = "fixed_point")]
fixed_prefilter_mem: Vec<FixedCeltSig>,
old_band_e: Vec<CeltGlog>,
old_log_e: Vec<CeltGlog>,
old_log_e2: Vec<CeltGlog>,
energy_error: Vec<CeltGlog>,
#[cfg(feature = "fixed_point")]
fixed_old_band_e: Vec<FixedCeltGlog>,
#[cfg(feature = "fixed_point")]
fixed_energy_error: Vec<FixedCeltGlog>,
}
impl CeltEncoderAlloc {
pub(crate) fn new(mode: &OpusCustomMode<'_>, channels: usize) -> Self {
assert!(
channels > 0 && channels <= MAX_CHANNELS,
"unsupported channel layout"
);
let overlap = mode.overlap * channels;
let band_count = channels * mode.num_ebands;
Self {
in_mem: vec![0.0; overlap],
prefilter_mem: vec![0.0; channels * COMBFILTER_MAXPERIOD],
#[cfg(feature = "fixed_point")]
fixed_in_mem: vec![0; overlap],
#[cfg(feature = "fixed_point")]
fixed_prefilter_mem: vec![0; channels * COMBFILTER_MAXPERIOD],
old_band_e: vec![0.0; band_count],
old_log_e: vec![0.0; band_count],
old_log_e2: vec![0.0; band_count],
energy_error: vec![0.0; band_count],
#[cfg(feature = "fixed_point")]
fixed_old_band_e: vec![0; band_count],
#[cfg(feature = "fixed_point")]
fixed_energy_error: vec![0; band_count],
}
}
#[must_use]
pub(crate) fn size_in_bytes(&self) -> usize {
self.in_mem.len() * core::mem::size_of::<CeltSig>()
+ self.prefilter_mem.len() * core::mem::size_of::<CeltSig>()
+ (self.old_band_e.len()
+ self.old_log_e.len()
+ self.old_log_e2.len()
+ self.energy_error.len())
* core::mem::size_of::<CeltGlog>()
+ {
#[cfg(feature = "fixed_point")]
{
(self.fixed_in_mem.len()
+ self.fixed_prefilter_mem.len()
+ self.fixed_old_band_e.len()
+ self.fixed_energy_error.len())
* core::mem::size_of::<FixedCeltGlog>()
}
#[cfg(not(feature = "fixed_point"))]
{
0
}
}
}
pub(crate) fn as_encoder<'mode>(
&mut self,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
stream_channels: usize,
energy_mask: Option<&'mode [CeltGlog]>,
) -> OpusCustomEncoder<'mode> {
#[cfg(feature = "fixed_point")]
{
OpusCustomEncoder::new(
mode,
channels,
stream_channels,
energy_mask,
core::mem::take(&mut self.in_mem).into_boxed_slice(),
core::mem::take(&mut self.prefilter_mem).into_boxed_slice(),
core::mem::take(&mut self.fixed_in_mem).into_boxed_slice(),
core::mem::take(&mut self.fixed_prefilter_mem).into_boxed_slice(),
core::mem::take(&mut self.old_band_e).into_boxed_slice(),
core::mem::take(&mut self.old_log_e).into_boxed_slice(),
core::mem::take(&mut self.old_log_e2).into_boxed_slice(),
core::mem::take(&mut self.energy_error).into_boxed_slice(),
core::mem::take(&mut self.fixed_old_band_e).into_boxed_slice(),
core::mem::take(&mut self.fixed_energy_error).into_boxed_slice(),
)
}
#[cfg(not(feature = "fixed_point"))]
{
OpusCustomEncoder::new(
mode,
channels,
stream_channels,
energy_mask,
core::mem::take(&mut self.in_mem).into_boxed_slice(),
core::mem::take(&mut self.prefilter_mem).into_boxed_slice(),
core::mem::take(&mut self.old_band_e).into_boxed_slice(),
core::mem::take(&mut self.old_log_e).into_boxed_slice(),
core::mem::take(&mut self.old_log_e2).into_boxed_slice(),
core::mem::take(&mut self.energy_error).into_boxed_slice(),
)
}
}
pub(crate) fn reset(&mut self) {
self.in_mem.fill(0.0);
self.prefilter_mem.fill(0.0);
self.old_band_e.fill(0.0);
self.energy_error.fill(0.0);
self.old_log_e.fill(-28.0);
self.old_log_e2.fill(-28.0);
#[cfg(feature = "fixed_point")]
{
self.fixed_in_mem.fill(0);
self.fixed_prefilter_mem.fill(0);
self.fixed_old_band_e.fill(0);
self.fixed_energy_error.fill(0);
}
}
fn init_internal<'mode>(
&mut self,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
stream_channels: usize,
upsample: u32,
arch: i32,
rng_seed: OpusUint32,
) -> Result<OpusCustomEncoder<'mode>, CeltEncoderInitError> {
if channels == 0 || channels > MAX_CHANNELS {
return Err(CeltEncoderInitError::InvalidChannelCount);
}
if stream_channels == 0 || stream_channels > channels {
return Err(CeltEncoderInitError::InvalidStreamChannels);
}
self.reset();
let mut encoder = self.as_encoder(mode, channels, stream_channels, None);
encoder.reset_runtime_state();
encoder.upsample = upsample as i32;
encoder.start_band = 0;
encoder.end_band = mode.effective_ebands as i32;
encoder.signalling = 1;
encoder.arch = arch;
encoder.constrained_vbr = true;
encoder.clip = true;
encoder.bitrate = OPUS_BITRATE_MAX;
encoder.use_vbr = false;
encoder.force_intra = false;
encoder.complexity = 5;
encoder.lsb_depth = 24;
encoder.loss_rate = 0;
encoder.lfe = false;
encoder.disable_prefilter = false;
encoder.disable_inv = false;
encoder.rng = rng_seed;
Ok(encoder)
}
pub(crate) fn init_custom_encoder_with_arch<'mode>(
&mut self,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
stream_channels: usize,
arch: i32,
rng_seed: OpusUint32,
) -> Result<OpusCustomEncoder<'mode>, CeltEncoderInitError> {
self.init_internal(mode, channels, stream_channels, 1, arch, rng_seed)
}
pub(crate) fn init_custom_encoder<'mode>(
&mut self,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
stream_channels: usize,
rng_seed: OpusUint32,
) -> Result<OpusCustomEncoder<'mode>, CeltEncoderInitError> {
self.init_custom_encoder_with_arch(
mode,
channels,
stream_channels,
opus_select_arch(),
rng_seed,
)
}
pub(crate) fn init_encoder_for_rate<'mode>(
&mut self,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
stream_channels: usize,
sampling_rate: OpusInt32,
rng_seed: OpusUint32,
) -> Result<OpusCustomEncoder<'mode>, CeltEncoderInitError> {
let upsample = resampling_factor(sampling_rate);
if upsample == 0 {
return Err(CeltEncoderInitError::UnsupportedSampleRate);
}
self.init_internal(
mode,
channels,
stream_channels,
upsample,
opus_select_arch(),
rng_seed,
)
}
}
pub(crate) fn opus_custom_encoder_ctl<'enc, 'req>(
encoder: &mut OpusCustomEncoder<'enc>,
request: EncoderCtlRequest<'enc, 'req>,
) -> Result<(), CeltEncoderCtlError> {
match request {
EncoderCtlRequest::SetComplexity(value) => {
if !(0..=10).contains(&value) {
return Err(CeltEncoderCtlError::InvalidArgument);
}
encoder.complexity = value;
}
EncoderCtlRequest::SetStartBand(value) => {
let max = encoder.mode.num_ebands as i32;
if value < 0 || value >= max {
return Err(CeltEncoderCtlError::InvalidArgument);
}
encoder.start_band = value;
}
EncoderCtlRequest::SetEndBand(value) => {
let max = encoder.mode.num_ebands as i32;
if value < 1 || value > max {
return Err(CeltEncoderCtlError::InvalidArgument);
}
encoder.end_band = value;
}
EncoderCtlRequest::SetPrediction(value) => {
if !(0..=2).contains(&value) {
return Err(CeltEncoderCtlError::InvalidArgument);
}
encoder.disable_prefilter = value <= 1;
encoder.force_intra = value == 0;
}
EncoderCtlRequest::SetPacketLossPerc(value) => {
if !(0..=100).contains(&value) {
return Err(CeltEncoderCtlError::InvalidArgument);
}
encoder.loss_rate = value;
}
EncoderCtlRequest::SetVbrConstraint(value) => {
encoder.constrained_vbr = value;
}
EncoderCtlRequest::SetVbr(value) => {
encoder.use_vbr = value;
}
EncoderCtlRequest::SetBitrate(value) => {
if value <= 500 && value != OPUS_BITRATE_MAX {
return Err(CeltEncoderCtlError::InvalidArgument);
}
let capped = min(value, 260_000 * encoder.channels as OpusInt32);
encoder.bitrate = capped;
}
EncoderCtlRequest::SetChannels(value) => {
if value == 0 || value > encoder.channels {
return Err(CeltEncoderCtlError::InvalidArgument);
}
encoder.stream_channels = value;
}
EncoderCtlRequest::SetLsbDepth(value) => {
if !(8..=24).contains(&value) {
return Err(CeltEncoderCtlError::InvalidArgument);
}
encoder.lsb_depth = value;
}
EncoderCtlRequest::GetLsbDepth(out) => {
*out = encoder.lsb_depth;
}
EncoderCtlRequest::SetPhaseInversionDisabled(value) => {
encoder.disable_inv = value;
}
EncoderCtlRequest::GetPhaseInversionDisabled(out) => {
*out = encoder.disable_inv;
}
EncoderCtlRequest::ResetState => {
encoder.reset_runtime_state();
}
EncoderCtlRequest::SetInputClipping(value) => {
encoder.clip = value;
}
EncoderCtlRequest::SetSignalling(value) => {
encoder.signalling = value;
}
EncoderCtlRequest::SetAnalysis(info) => {
encoder.analysis = info.clone();
}
EncoderCtlRequest::SetSilkInfo(info) => {
encoder.silk_info = info.clone();
}
EncoderCtlRequest::GetMode(slot) => {
*slot = Some(encoder.mode);
}
EncoderCtlRequest::GetFinalRange(slot) => {
*slot = encoder.rng;
}
EncoderCtlRequest::SetLfe(value) => {
encoder.lfe = value;
}
EncoderCtlRequest::SetEnergyMask(mask) => {
encoder.energy_mask = mask;
}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn transient_analysis(
input: &[OpusVal32],
len: usize,
channels: usize,
tf_estimate: &mut OpusVal16,
tf_chan: &mut usize,
allow_weak_transients: bool,
weak_transient: &mut bool,
tone_freq: OpusVal16,
toneishness: OpusVal32,
) -> bool {
const INV_TABLE: [u8; 128] = [
255, 255, 156, 110, 86, 70, 59, 51, 45, 40, 37, 33, 31, 28, 26, 25, 23, 22, 21, 20, 19, 18,
17, 16, 16, 15, 15, 14, 13, 13, 12, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 9, 9,
8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2,
];
debug_assert!(channels * len <= input.len());
#[cfg(test)]
let trace_frame_idx = celt_transient_trace::current_frame_idx()
.filter(|&idx| celt_transient_trace::should_dump(idx));
#[cfg(test)]
let trace_bits = celt_transient_trace::want_bits();
let mut tmp = vec![0.0f32; len];
*weak_transient = false;
let mut forward_decay = 0.0625f32;
if allow_weak_transients {
forward_decay = 0.03125f32;
}
let len2 = len / 2;
let mut mask_metric = 0i32;
*tf_chan = 0;
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!("celt_transient[{frame_idx}].len={len}");
crate::test_trace::trace_println!("celt_transient[{frame_idx}].channels={channels}");
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].allow_weak_transients={}",
i32::from(allow_weak_transients)
);
}
for c in 0..channels {
let mut mem0 = 0.0f32;
let mut mem1 = 0.0f32;
for i in 0..len {
let x = input[c * len + i];
let y = mem0 + x;
let mem00 = mem0;
mem0 = mem0 - x + 0.5 * mem1;
mem1 = x - mem00;
tmp[i] = y;
}
for value in tmp.iter_mut().take(len.min(12)) {
*value = 0.0;
}
let mut mean = 0.0f32;
mem0 = 0.0;
for i in 0..len2 {
let x0 = tmp[2 * i];
let x1 = tmp[2 * i + 1];
let x2 = x0 * x0 + x1 * x1;
mean += x2;
mem0 = x2 + (1.0 - forward_decay) * mem0;
tmp[i] = forward_decay * mem0;
}
mem0 = 0.0;
let mut max_e = 0.0f32;
for i in (0..len2).rev() {
mem0 = tmp[i] + 0.875 * mem0;
let value = 0.125 * mem0;
tmp[i] = value;
if value > max_e {
max_e = value;
}
}
let frame_energy = celt_sqrt(mean * max_e * 0.5 * len2 as f32);
let norm = (len2 as f32) / (frame_energy + 1e-15f32);
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].channel[{c}].mean={:.9e}",
mean as f64
);
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].channel[{c}].max_e={:.9e}",
max_e as f64
);
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].channel[{c}].frame_energy={:.9e}",
frame_energy as f64
);
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].channel[{c}].norm={:.9e}",
norm as f64
);
}
let mut unmask = 0i32;
for i in (12..len2.saturating_sub(5)).step_by(4) {
debug_assert!(!tmp[i].is_nan());
debug_assert!(!norm.is_nan());
let product = 64.0f64 * f64::from(norm) * (f64::from(tmp[i]) + 1e-15f64);
let scaled = floor(product);
let clamped = scaled.max(0.0).min(127.0) as usize;
unmask += i32::from(INV_TABLE[clamped]);
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].channel[{c}].unmask_step i={i} tmp={:.9e} product={:.9e} scaled={:.9e} clamped={clamped} inv={}",
tmp[i] as f64,
product,
scaled,
INV_TABLE[clamped],
);
}
}
if len2 > 17 {
let denom = 6 * (len2 as i32 - 17);
if denom > 0 {
let value = (64 * unmask * 4) / denom;
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].channel[{c}].unmask_sum={unmask}"
);
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].channel[{c}].unmask_norm={value}"
);
}
if value > mask_metric {
mask_metric = value;
*tf_chan = c;
}
}
}
}
let mut is_transient = mask_metric > 200;
if toneishness > 0.98 && tone_freq < 0.026 {
is_transient = false;
}
if allow_weak_transients && is_transient && mask_metric < 600 {
is_transient = false;
*weak_transient = true;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!("celt_transient[{frame_idx}].mask_metric={mask_metric}");
}
let mut tf_max = celt_sqrt(27.0 * mask_metric as f32) - 42.0;
#[cfg(test)]
let tf_max_raw = tf_max;
if tf_max < 0.0 {
tf_max = 0.0;
}
let tf_max = tf_max.min(163.0);
let value = (0.0069 * tf_max - 0.139).max(0.0);
*tf_estimate = celt_sqrt(value);
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].tf_max_raw={:.9e}",
tf_max_raw as f64
);
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].tf_max={:.9e}",
tf_max as f64
);
crate::test_trace::trace_println!("celt_transient[{frame_idx}].value={:.9e}", value as f64);
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].tf_estimate={:.9e}",
*tf_estimate as f64
);
if trace_bits {
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].tf_max_raw_bits=0x{:08x}",
tf_max_raw.to_bits()
);
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].tf_max_bits=0x{:08x}",
tf_max.to_bits()
);
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].value_bits=0x{:08x}",
value.to_bits()
);
crate::test_trace::trace_println!(
"celt_transient[{frame_idx}].tf_estimate_bits=0x{:08x}",
(*tf_estimate).to_bits()
);
}
}
is_transient
}
fn patch_transient_decision(
new_e: &[CeltGlog],
old_e: &[CeltGlog],
nb_ebands: usize,
start: usize,
end: usize,
channels: usize,
) -> bool {
debug_assert!(new_e.len() >= channels * nb_ebands);
debug_assert!(old_e.len() >= channels * nb_ebands);
debug_assert!(start < end);
debug_assert!(end <= nb_ebands);
let mut spread_old = vec![0.0f32; nb_ebands];
if channels == 1 {
spread_old[start] = old_e[start];
for i in (start + 1)..end {
let prev = spread_old[i - 1] - 1.0;
spread_old[i] = prev.max(old_e[i]);
}
} else {
spread_old[start] = old_e[start].max(old_e[start + nb_ebands]);
for i in (start + 1)..end {
let prev = spread_old[i - 1] - 1.0;
let pair = old_e[i].max(old_e[i + nb_ebands]);
spread_old[i] = prev.max(pair);
}
}
if end >= 2 {
for i in (start..=(end - 2)).rev() {
let next = spread_old[i + 1] - 1.0;
if next > spread_old[i] {
spread_old[i] = next;
}
}
}
let start_i = start.max(2);
let mut mean_diff = 0.0f32;
for c in 0..channels {
let base = c * nb_ebands;
for i in start_i..(end.saturating_sub(1)) {
let x1 = new_e[base + i].max(0.0);
let x2 = spread_old[i].max(0.0);
let diff = (x1 - x2).max(0.0);
mean_diff += diff;
}
}
let denom = (channels * (end.saturating_sub(1).saturating_sub(start_i))) as f32;
if denom > 0.0 {
mean_diff /= denom;
}
mean_diff > 1.0
}
#[allow(clippy::needless_range_loop)]
#[allow(clippy::too_many_arguments)]
fn dynalloc_analysis(
band_log_e: &[CeltGlog],
band_log_e2: &[CeltGlog],
old_band_e: &[CeltGlog],
nb_ebands: usize,
start: usize,
end: usize,
channels: usize,
offsets: &mut [i32],
lsb_depth: i32,
log_n: &[i16],
is_transient: bool,
vbr: bool,
constrained_vbr: bool,
e_bands: &[i16],
lm: i32,
effective_bytes: i32,
tot_boost: &mut i32,
lfe: bool,
surround_dynalloc: &mut [CeltGlog],
analysis: &AnalysisInfo,
importance: &mut [i32],
spread_weight: &mut [i32],
tone_freq: OpusVal16,
toneishness: OpusVal32,
) -> CeltGlog {
debug_assert!(channels <= MAX_CHANNELS);
debug_assert!(band_log_e.len() >= channels * nb_ebands);
debug_assert!(band_log_e2.len() >= channels * nb_ebands);
debug_assert!(old_band_e.len() >= channels * nb_ebands);
debug_assert!(offsets.len() >= nb_ebands);
debug_assert!(importance.len() >= nb_ebands);
debug_assert!(spread_weight.len() >= nb_ebands);
debug_assert!(log_n.len() >= end);
debug_assert!(e_bands.len() > end);
debug_assert!(surround_dynalloc.len() >= end);
offsets.iter_mut().for_each(|value| *value = 0);
importance.iter_mut().for_each(|value| *value = 0);
spread_weight.iter_mut().for_each(|value| *value = 0);
let mut follower = vec![0.0f32; channels * nb_ebands];
let mut noise_floor = vec![0.0f32; nb_ebands];
let mut band_log_e3 = vec![0.0f32; nb_ebands];
let mut max_depth = -31.9f32;
#[cfg(test)]
let mut max_band = 0usize;
#[cfg(test)]
let mut max_channel = 0usize;
#[cfg(test)]
let mut max_band_log_e = 0.0f32;
#[cfg(test)]
let mut max_noise_floor = 0.0f32;
#[cfg(test)]
let mut max_depth_val = max_depth;
let depth_shift = (9 - lsb_depth) as f32;
for i in 0..end {
let log_n_val = f32::from(log_n[i]);
let mean = E_MEANS
.get(i)
.copied()
.unwrap_or_else(|| *E_MEANS.last().expect("non-empty e_means"));
let index = (i + 5) as f32;
noise_floor[i] = 0.0625 * log_n_val + 0.5 + depth_shift - mean + 0.0062 * index * index;
}
for c in 0..channels {
let base = c * nb_ebands;
for i in 0..end {
let depth = band_log_e[base + i] - noise_floor[i];
if depth > max_depth {
max_depth = depth;
#[cfg(test)]
{
max_band = i;
max_channel = c;
max_band_log_e = band_log_e[base + i];
max_noise_floor = noise_floor[i];
max_depth_val = depth;
}
}
}
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!("celt_dynalloc_max[{frame_idx}].band={max_band}");
crate::test_trace::trace_println!(
"celt_dynalloc_max[{frame_idx}].channel={max_channel}"
);
crate::test_trace::trace_println!(
"celt_dynalloc_max[{frame_idx}].band_log_e={:.9e}",
max_band_log_e as f64
);
crate::test_trace::trace_println!(
"celt_dynalloc_max[{frame_idx}].band_log_e_bits=0x{:08x}",
max_band_log_e.to_bits()
);
crate::test_trace::trace_println!(
"celt_dynalloc_max[{frame_idx}].noise_floor={:.9e}",
max_noise_floor as f64
);
crate::test_trace::trace_println!(
"celt_dynalloc_max[{frame_idx}].noise_floor_bits=0x{:08x}",
max_noise_floor.to_bits()
);
crate::test_trace::trace_println!(
"celt_dynalloc_max[{frame_idx}].depth={:.9e}",
max_depth_val as f64
);
crate::test_trace::trace_println!(
"celt_dynalloc_max[{frame_idx}].depth_bits=0x{:08x}",
max_depth_val.to_bits()
);
}
}
let mut mask = vec![0.0f32; nb_ebands];
let mut sig = vec![0.0f32; nb_ebands];
for i in 0..end {
let mut value = band_log_e[i] - noise_floor[i];
if channels == 2 {
let other = band_log_e[nb_ebands + i] - noise_floor[i];
if other > value {
value = other;
}
}
mask[i] = value;
sig[i] = value;
}
for i in 1..end {
let candidate = mask[i - 1] - 2.0;
if candidate > mask[i] {
mask[i] = candidate;
}
}
if end >= 2 {
for i in (0..=end - 2).rev() {
let candidate = mask[i + 1] - 3.0;
if candidate > mask[i] {
mask[i] = candidate;
}
}
}
let base_threshold = (max_depth - 12.0).max(0.0);
for i in 0..end {
let clamp = base_threshold.max(mask[i]);
let smr = sig[i] - clamp;
let rounded = floorf(smr + 0.5);
let mut shift = -(rounded as i32);
shift = shift.clamp(0, 5);
spread_weight[i] = 32 >> shift;
}
let mut total_boost_bits = 0i32;
if effective_bytes >= (30 + 5 * lm) && !lfe {
let mut last = 0usize;
for c in 0..channels {
let base = c * nb_ebands;
band_log_e3[..end].copy_from_slice(&band_log_e2[base..base + end]);
if lm == 0 {
for i in 0..end.min(8) {
let idx = base + i;
let current = band_log_e2[idx];
let previous = old_band_e[idx];
band_log_e3[i] = current.max(previous);
}
}
let follower_slice = &mut follower[base..base + nb_ebands];
if end > 0 {
follower_slice[0] = band_log_e3[0];
}
for i in 1..end {
if band_log_e3[i] > band_log_e3[i - 1] + 0.5 {
last = i;
}
let candidate = follower_slice[i - 1] + 1.5;
follower_slice[i] = band_log_e3[i].min(candidate);
}
let mut idx = last;
while idx > 0 {
let prev = idx - 1;
let candidate = follower_slice[idx] + 2.0;
let min_val = candidate.min(band_log_e3[prev]);
if min_val < follower_slice[prev] {
follower_slice[prev] = min_val;
}
idx -= 1;
}
if end >= 3 {
let median_start = median_of_3(&band_log_e3[..3]) - 1.0;
follower_slice[0] = follower_slice[0].max(median_start);
if end > 1 {
follower_slice[1] = follower_slice[1].max(median_start);
}
let median_end = median_of_3(&band_log_e3[end - 3..end]) - 1.0;
if end >= 2 {
follower_slice[end - 2] = follower_slice[end - 2].max(median_end);
}
follower_slice[end - 1] = follower_slice[end - 1].max(median_end);
}
if end > 4 {
for i in 2..end - 2 {
let median = median_of_5(&band_log_e3[i - 2..i + 3]) - 1.0;
if median > follower_slice[i] {
follower_slice[i] = median;
}
}
}
for i in 0..end {
if noise_floor[i] > follower_slice[i] {
follower_slice[i] = noise_floor[i];
}
}
}
if channels == 2 {
for i in start..end {
let left_idx = i;
let right_idx = nb_ebands + i;
let updated_right = follower[right_idx].max(follower[left_idx] - 4.0);
follower[right_idx] = updated_right;
let updated_left = follower[left_idx].max(updated_right - 4.0);
follower[left_idx] = updated_left;
let left_depth = (band_log_e[left_idx] - follower[left_idx]).max(0.0);
let right_depth = (band_log_e[right_idx] - follower[right_idx]).max(0.0);
follower[left_idx] = 0.5 * (left_depth + right_depth);
}
} else {
for i in start..end {
follower[i] = (band_log_e[i] - follower[i]).max(0.0);
}
}
for i in start..end {
let surround = surround_dynalloc[i];
if surround > follower[i] {
follower[i] = surround;
}
}
for i in start..end {
let capped = follower[i].min(4.0);
let weight = 13.0 * celt_exp2(capped);
importance[i] = floorf(weight + 0.5) as i32;
}
if ((!vbr) || constrained_vbr) && !is_transient {
for value in &mut follower[start..end] {
*value *= 0.5;
}
}
for i in start..end {
if i < 8 {
follower[i] *= 2.0;
}
if i >= 12 {
follower[i] *= 0.5;
}
}
if toneishness > 0.98 {
let freq_bin = floorf(tone_freq * (120.0 / core::f32::consts::PI) + 0.5) as i32;
for i in start..end {
let band_low = i32::from(e_bands[i]);
let band_high = i32::from(e_bands[i + 1]);
if freq_bin >= band_low && freq_bin <= band_high {
follower[i] += 2.0;
}
if freq_bin >= band_low - 1 && freq_bin <= band_high + 1 {
follower[i] += 1.0;
}
if freq_bin >= band_low - 2 && freq_bin <= band_high + 2 {
follower[i] += 1.0;
}
if freq_bin >= band_low - 3 && freq_bin <= band_high + 3 {
follower[i] += 0.5;
}
}
}
if analysis.valid {
let leak_len = end.min(LEAK_BANDS).min(analysis.leak_boost.len());
for i in start..leak_len {
follower[i] += f32::from(analysis.leak_boost[i]) / 64.0;
}
}
for i in start..end {
let follower_val = follower[i].min(4.0);
let band_width = i32::from(e_bands[i + 1]) - i32::from(e_bands[i]);
let width = (channels as i32 * band_width) << lm;
let (boost, boost_bits) = if width < 6 {
let boost = follower_val as i32;
let bits = (boost * width) << BITRES;
(boost, bits)
} else if width > 48 {
let boost = (follower_val * 8.0) as i32;
let bits = ((boost * width) << BITRES) / 8;
(boost, bits)
} else {
let boost = (follower_val * width as f32 / 6.0) as i32;
let bits = (boost * 6) << BITRES;
(boost, bits)
};
if ((!vbr) || (constrained_vbr && !is_transient))
&& (((total_boost_bits + boost_bits) >> BITRES) >> 3) > (2 * effective_bytes / 3)
{
let cap = (2 * effective_bytes / 3) << (BITRES + 3);
offsets[i] = cap - total_boost_bits;
total_boost_bits = cap;
break;
}
offsets[i] = boost;
total_boost_bits += boost_bits;
}
} else {
for value in &mut importance[start..end] {
*value = 13;
}
}
*tot_boost = total_boost_bits;
max_depth
}
#[allow(clippy::too_many_arguments)]
fn run_prefilter(
encoder: &mut OpusCustomEncoder<'_>,
input: &mut [CeltSig],
channels: usize,
n: usize,
prefilter_tapset: i32,
pitch: &mut i32,
gain: &mut OpusVal16,
qgain: &mut i32,
enabled: bool,
tf_estimate: OpusVal16,
nb_available_bytes: i32,
analysis: &AnalysisInfo,
mut tone_freq: OpusVal16,
toneishness: OpusVal32,
) -> bool {
assert!(channels > 0, "run_prefilter requires at least one channel");
assert!(n > 0, "run_prefilter expects a positive frame size");
#[cfg(test)]
let trace_frame_idx = celt_prefilter_trace::current_frame_idx()
.filter(|&idx| celt_prefilter_trace::should_dump(idx));
#[cfg(test)]
crate::celt::pitch::remove_doubling_trace_set_frame(trace_frame_idx);
let mode = encoder.mode;
let overlap = mode.overlap;
let stride = overlap + n;
let history_len = COMBFILTER_MAXPERIOD;
assert!(
input.len() >= channels * stride,
"time buffer must expose channels * (n + overlap) samples",
);
assert!(
encoder.prefilter_mem.len() >= channels * history_len,
"prefilter history must expose channels * COMBFILTER_MAXPERIOD samples",
);
let mut pre = vec![0.0; channels * (n + history_len)];
for ch in 0..channels {
let pre_offset = ch * (n + history_len);
let pre_slice = &mut pre[pre_offset..pre_offset + history_len + n];
let history = &encoder.prefilter_mem[ch * history_len..(ch + 1) * history_len];
pre_slice[..history_len].copy_from_slice(history);
let input_offset = ch * stride;
let input_slice = &input[input_offset + overlap..input_offset + overlap + n];
pre_slice[history_len..history_len + n].copy_from_slice(input_slice);
}
let mut channel_views: [&[f32]; MAX_CHANNELS] = [&[]; MAX_CHANNELS];
for ch in 0..channels {
let start = ch * (n + history_len);
let end = start + history_len + n;
channel_views[ch] = &pre[start..end];
}
let mut pitch_index = COMBFILTER_MINPERIOD as i32;
let mut gain1 = 0.0;
if enabled {
let downsample_len = history_len + n;
let mut pitch_buf = vec![0.0; downsample_len >> 1];
pitch_downsample(
&channel_views[..channels],
&mut pitch_buf,
downsample_len,
encoder.arch,
);
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
if std::env::var("CELT_TRACE_PITCH_BUF")
.map(|value| !value.is_empty() && value != "0")
.unwrap_or(false)
{
let start = std::env::var("CELT_TRACE_PITCH_BUF_START")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(0);
let count = std::env::var("CELT_TRACE_PITCH_BUF_COUNT")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(64);
let want_bits = std::env::var("CELT_TRACE_PITCH_BUF_BITS")
.map(|value| !value.is_empty() && value != "0")
.unwrap_or(false);
let end = start.saturating_add(count).min(pitch_buf.len());
for idx in start..end {
crate::test_trace::trace_println!(
"celt_pitch_buf[{frame_idx}].idx[{idx}]={:.9}",
pitch_buf[idx]
);
if want_bits {
crate::test_trace::trace_println!(
"celt_pitch_buf[{frame_idx}].idx_bits[{idx}]=0x{:08x}",
pitch_buf[idx].to_bits()
);
}
}
}
}
let search_span = history_len - 3 * COMBFILTER_MINPERIOD;
if search_span > 0 {
let offset = history_len >> 1;
if offset < pitch_buf.len() {
let result = pitch_search(
&pitch_buf[offset..],
&pitch_buf,
n,
search_span,
encoder.arch,
);
pitch_index = history_len as i32 - result;
}
}
gain1 = remove_doubling(
&pitch_buf,
history_len,
COMBFILTER_MINPERIOD,
n,
&mut pitch_index,
encoder.prefilter_period,
encoder.prefilter_gain,
encoder.arch,
);
let max_period = (history_len - 2) as i32;
if pitch_index > max_period {
pitch_index = max_period;
}
gain1 *= 0.7;
if toneishness > 0.99 {
while tone_freq >= 0.39 {
tone_freq *= 0.5;
}
if tone_freq > 0.006_148 {
let candidate = floorf(0.5 + 2.0 * core::f32::consts::PI / tone_freq) as i32;
pitch_index = candidate.min(max_period);
} else {
pitch_index = COMBFILTER_MINPERIOD as i32;
}
gain1 = 0.75;
}
if encoder.loss_rate > 2 {
gain1 *= 0.5;
}
if encoder.loss_rate > 4 {
gain1 *= 0.5;
}
if encoder.loss_rate > 8 {
gain1 = 0.0;
}
}
if analysis.valid {
gain1 *= analysis.max_pitch_ratio;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].gain1_pre={:.9}",
gain1
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pitch_index_pre={}",
pitch_index
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].prefilter_period_pre={}",
encoder.prefilter_period
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].prefilter_gain_pre={:.9}",
encoder.prefilter_gain
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].analysis_max_pitch_ratio={:.9}",
analysis.max_pitch_ratio
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].tf_estimate={:.9}",
tf_estimate
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].toneishness={:.9}",
toneishness
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].tone_freq={:.9}",
tone_freq
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].nb_available_bytes={}",
nb_available_bytes
);
}
let mut pf_threshold: f32 = 0.2;
if (pitch_index - encoder.prefilter_period).abs() * 10 > pitch_index {
pf_threshold += 0.2;
if tf_estimate > 0.98 {
gain1 = 0.0;
}
}
if nb_available_bytes < 25 {
pf_threshold += 0.1;
}
if nb_available_bytes < 35 {
pf_threshold += 0.1;
}
if encoder.prefilter_gain > 0.4 {
pf_threshold -= 0.1;
}
if encoder.prefilter_gain > 0.55 {
pf_threshold -= 0.1;
}
pf_threshold = pf_threshold.max(0.2);
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pf_threshold={:.9}",
pf_threshold
);
}
let mut pf_on = false;
let mut qg_local = 0;
if gain1 < pf_threshold {
gain1 = 0.0;
} else {
if (gain1 - encoder.prefilter_gain).abs() < 0.1 {
gain1 = encoder.prefilter_gain;
}
let mut quant = floorf(0.5 + gain1 * 32.0 / 3.0) as i32 - 1;
quant = quant.clamp(0, 7);
gain1 = 0.093_75 * (quant + 1) as f32;
qg_local = quant;
pf_on = true;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].gain1_quant={:.9}",
gain1
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pitch_index_quant={}",
pitch_index
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].qg_local={}",
qg_local
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pf_on_pre_cancel={}",
pf_on as i32
);
}
let mut before = [0.0f32; MAX_CHANNELS];
let mut after = [0.0f32; MAX_CHANNELS];
let mut cancel_pitch = false;
let prev_tapset = encoder.prefilter_tapset.max(0) as usize;
let new_tapset = prefilter_tapset.max(0) as usize;
let offset = mode.short_mdct_size.saturating_sub(overlap).min(n);
encoder.prefilter_period = encoder.prefilter_period.max(COMBFILTER_MINPERIOD as i32);
for ch in 0..channels {
let input_offset = ch * stride;
let (head, tail) = input[input_offset..input_offset + stride].split_at_mut(overlap);
head.copy_from_slice(&encoder.in_mem[ch * overlap..(ch + 1) * overlap]);
let mut sum_before = 0.0;
for sample in tail.iter().take(n) {
sum_before += sample.abs();
}
before[ch] = sum_before;
let pre_offset = ch * (n + history_len);
let pre_channel = &pre[pre_offset..pre_offset + history_len + n];
if offset > 0 {
let (first, rest) = tail.split_at_mut(offset);
comb_filter(
first,
pre_channel,
history_len,
offset,
encoder.prefilter_period,
encoder.prefilter_period,
-encoder.prefilter_gain,
-encoder.prefilter_gain,
prev_tapset,
prev_tapset,
&[],
0,
encoder.arch,
);
comb_filter(
rest,
pre_channel,
history_len + offset,
n - offset,
encoder.prefilter_period,
pitch_index,
-encoder.prefilter_gain,
-gain1,
prev_tapset,
new_tapset,
mode.window,
overlap,
encoder.arch,
);
} else {
comb_filter(
tail,
pre_channel,
history_len,
n,
encoder.prefilter_period,
pitch_index,
-encoder.prefilter_gain,
-gain1,
prev_tapset,
new_tapset,
mode.window,
overlap,
encoder.arch,
);
}
let mut sum_after = 0.0;
for sample in tail.iter().take(n) {
sum_after += sample.abs();
}
after[ch] = sum_after;
}
if channels == 2 {
let thresh0 = 0.25 * gain1 * before[0] + 0.01 * before[1];
let thresh1 = 0.25 * gain1 * before[1] + 0.01 * before[0];
if (after[0] - before[0]) > thresh0 || (after[1] - before[1]) > thresh1 {
cancel_pitch = true;
}
if (before[0] - after[0]) < thresh0 && (before[1] - after[1]) < thresh1 {
cancel_pitch = true;
}
} else if after[0] > before[0] {
cancel_pitch = true;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
for ch in 0..channels {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].before.ch[{ch}]={:.9}",
before[ch]
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].after.ch[{ch}]={:.9}",
after[ch]
);
}
if channels == 2 {
let thresh0 = 0.25 * gain1 * before[0] + 0.01 * before[1];
let thresh1 = 0.25 * gain1 * before[1] + 0.01 * before[0];
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].thresh0={:.9}",
thresh0
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].thresh1={:.9}",
thresh1
);
} else {
let thresh0 = 0.25 * gain1 * before[0];
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].thresh0={:.9}",
thresh0
);
}
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].cancel_pitch={}",
cancel_pitch as i32
);
}
if cancel_pitch {
for ch in 0..channels {
let input_offset = ch * stride;
let channel = &mut input[input_offset..input_offset + stride];
let pre_offset = ch * (n + history_len);
let pre_channel = &pre[pre_offset..pre_offset + history_len + n];
channel[overlap..overlap + n]
.copy_from_slice(&pre_channel[history_len..history_len + n]);
if overlap > 0 && offset < n {
let span = overlap.min(n - offset);
let start = overlap + offset;
let end = start + span;
comb_filter(
&mut channel[start..end],
pre_channel,
history_len + offset,
span,
encoder.prefilter_period,
pitch_index,
-encoder.prefilter_gain,
0.0,
prev_tapset,
new_tapset,
mode.window,
span,
encoder.arch,
);
}
}
gain1 = 0.0;
qg_local = 0;
pf_on = false;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].gain1_final={:.9}",
gain1
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].qg_local_final={}",
qg_local
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pf_on_final={}",
pf_on as i32
);
}
for ch in 0..channels {
let input_offset = ch * stride;
let channel = &input[input_offset + n..input_offset + n + overlap];
encoder.in_mem[ch * overlap..(ch + 1) * overlap].copy_from_slice(channel);
let pre_offset = ch * (n + history_len);
let pre_channel = &pre[pre_offset..pre_offset + history_len + n];
let mem = &mut encoder.prefilter_mem[ch * history_len..(ch + 1) * history_len];
if n > history_len {
mem.copy_from_slice(&pre_channel[n..n + history_len]);
} else {
let shift = history_len - n;
mem.copy_within(n..history_len, 0);
mem[shift..].copy_from_slice(&pre_channel[history_len..history_len + n]);
}
}
*gain = gain1;
*pitch = pitch_index;
*qgain = qg_local;
pf_on
}
#[cfg(feature = "fixed_point")]
fn q15_to_float(value: FixedOpusVal16) -> f32 {
value as f32 / 32_768.0
}
#[cfg(feature = "fixed_point")]
#[allow(clippy::too_many_arguments)]
fn run_prefilter_fixed(
encoder: &mut OpusCustomEncoder<'_>,
input: &mut [CeltSig],
input_fixed: &mut [FixedCeltSig],
channels: usize,
n: usize,
prefilter_tapset: i32,
pitch: &mut i32,
gain: &mut OpusVal16,
gain_fixed: &mut FixedOpusVal16,
qgain: &mut i32,
enabled: bool,
tf_estimate: OpusVal16,
nb_available_bytes: i32,
analysis: &AnalysisInfo,
tone_freq: OpusVal16,
toneishness: OpusVal32,
) -> bool {
assert!(channels > 0, "run_prefilter requires at least one channel");
assert!(n > 0, "run_prefilter expects a positive frame size");
#[cfg(test)]
let trace_frame_idx = celt_prefilter_trace::current_frame_idx()
.filter(|&idx| celt_prefilter_trace::should_dump(idx));
#[cfg(test)]
crate::celt::pitch::remove_doubling_trace_set_frame(trace_frame_idx);
let mode = encoder.mode;
let overlap = mode.overlap;
let stride = overlap + n;
let history_len = COMBFILTER_MAXPERIOD;
assert!(
input_fixed.len() >= channels * stride,
"time buffer must expose channels * (n + overlap) samples",
);
assert!(
encoder.fixed_prefilter_mem.len() >= channels * history_len,
"prefilter history must expose channels * COMBFILTER_MAXPERIOD samples",
);
assert!(
encoder.fixed_in_mem.len() >= channels * overlap,
"overlap history must expose channels * overlap samples",
);
let mut pre = vec![0; channels * (n + history_len)];
for ch in 0..channels {
let pre_offset = ch * (n + history_len);
let pre_slice = &mut pre[pre_offset..pre_offset + history_len + n];
let history = &encoder.fixed_prefilter_mem[ch * history_len..(ch + 1) * history_len];
pre_slice[..history_len].copy_from_slice(history);
let input_offset = ch * stride;
let input_slice = &input_fixed[input_offset + overlap..input_offset + overlap + n];
pre_slice[history_len..history_len + n].copy_from_slice(input_slice);
}
let mut channel_views: [&[FixedCeltSig]; MAX_CHANNELS] = [&[]; MAX_CHANNELS];
for ch in 0..channels {
let start = ch * (n + history_len);
let end = start + history_len + n;
channel_views[ch] = &pre[start..end];
}
let mut pitch_index = COMBFILTER_MINPERIOD as i32;
let mut gain1: FixedOpusVal16;
if enabled {
let downsample_len = history_len + n;
let mut pitch_buf = vec![0i16; downsample_len >> 1];
pitch_downsample_fixed(
&channel_views[..channels],
&mut pitch_buf,
downsample_len,
encoder.arch,
);
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
if std::env::var("CELT_TRACE_PITCH_BUF")
.map(|value| !value.is_empty() && value != "0")
.unwrap_or(false)
{
let start = std::env::var("CELT_TRACE_PITCH_BUF_START")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(0);
let count = std::env::var("CELT_TRACE_PITCH_BUF_COUNT")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(64);
let want_bits = std::env::var("CELT_TRACE_PITCH_BUF_BITS")
.map(|value| !value.is_empty() && value != "0")
.unwrap_or(false);
let end = start.saturating_add(count).min(pitch_buf.len());
for idx in start..end {
let value = pitch_buf[idx] as f32;
crate::test_trace::trace_println!(
"celt_pitch_buf[{frame_idx}].idx[{idx}]={:.9}",
value
);
if want_bits {
crate::test_trace::trace_println!(
"celt_pitch_buf[{frame_idx}].idx_bits[{idx}]=0x{:08x}",
pitch_buf[idx] as u16 as u32
);
}
}
}
}
let search_span = history_len - 3 * COMBFILTER_MINPERIOD;
if search_span > 0 {
let offset = history_len >> 1;
if offset < pitch_buf.len() {
let result = pitch_search_fixed(
&pitch_buf[offset..],
&pitch_buf,
n,
search_span,
encoder.arch,
);
pitch_index = history_len as i32 - result;
}
}
gain1 = remove_doubling_fixed(
&pitch_buf,
history_len,
COMBFILTER_MINPERIOD,
n,
&mut pitch_index,
encoder.prefilter_period,
encoder.fixed_prefilter_gain,
encoder.arch,
);
let max_period = (history_len - 2) as i32;
if pitch_index > max_period {
pitch_index = max_period;
}
gain1 = mult16_16_q15(qconst16(0.7, 15), gain1);
let toneishness_fixed = qconst32(f64::from(toneishness), 29);
if toneishness_fixed > qconst32(0.99, 29) {
let mut tone_freq_fixed = qconst16(f64::from(tone_freq), 13);
while tone_freq_fixed >= qconst16(0.39, 13) {
tone_freq_fixed >>= 1;
}
if tone_freq_fixed > qconst16(0.006_148, 13) && tone_freq_fixed > 0 {
let candidate = 51472 / tone_freq_fixed as i32;
pitch_index = candidate.min(max_period);
} else {
pitch_index = COMBFILTER_MINPERIOD as i32;
}
gain1 = qconst16(0.75, 15);
}
if encoder.loss_rate > 2 {
gain1 >>= 1;
}
if encoder.loss_rate > 4 {
gain1 >>= 1;
}
if encoder.loss_rate > 8 {
gain1 = 0;
}
} else {
gain1 = 0;
pitch_index = COMBFILTER_MINPERIOD as i32;
}
if analysis.valid {
let scaled = (gain1 as f32) * analysis.max_pitch_ratio;
let clamped = scaled.max(i16::MIN as f32).min(i16::MAX as f32) as i32;
gain1 = clamped as FixedOpusVal16;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].gain1_pre={:.9}",
q15_to_float(gain1)
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pitch_index_pre={}",
pitch_index
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].prefilter_period_pre={}",
encoder.prefilter_period
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].prefilter_gain_pre={:.9}",
q15_to_float(encoder.fixed_prefilter_gain)
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].analysis_max_pitch_ratio={:.9}",
analysis.max_pitch_ratio
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].tf_estimate={:.9}",
tf_estimate
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].toneishness={:.9}",
toneishness
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].tone_freq={:.9}",
tone_freq
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].nb_available_bytes={}",
nb_available_bytes
);
}
let mut pf_threshold = qconst16(0.2, 15);
let tf_estimate_fixed = qconst16(f64::from(tf_estimate), 14);
if (pitch_index - encoder.prefilter_period).abs() * 10 > pitch_index {
pf_threshold = pf_threshold.wrapping_add(qconst16(0.2, 15));
if tf_estimate_fixed > qconst16(0.98, 14) {
gain1 = 0;
}
}
if nb_available_bytes < 25 {
pf_threshold = pf_threshold.wrapping_add(qconst16(0.1, 15));
}
if nb_available_bytes < 35 {
pf_threshold = pf_threshold.wrapping_add(qconst16(0.1, 15));
}
if encoder.fixed_prefilter_gain > qconst16(0.4, 15) {
pf_threshold = pf_threshold.wrapping_sub(qconst16(0.1, 15));
}
if encoder.fixed_prefilter_gain > qconst16(0.55, 15) {
pf_threshold = pf_threshold.wrapping_sub(qconst16(0.1, 15));
}
pf_threshold = pf_threshold.max(qconst16(0.2, 15));
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pf_threshold={:.9}",
q15_to_float(pf_threshold)
);
}
let mut pf_on = false;
let mut qg_local = 0i32;
if gain1 < pf_threshold {
gain1 = 0;
} else {
let diff = (gain1 as i32 - encoder.fixed_prefilter_gain as i32).abs();
if diff < qconst16(0.1, 15) as i32 {
gain1 = encoder.fixed_prefilter_gain;
}
let mut quant = ((gain1 as i32 + 1536) >> 10) / 3 - 1;
quant = quant.clamp(0, 7);
gain1 = (qconst16(0.09375, 15) as i32 * (quant + 1)) as FixedOpusVal16;
qg_local = quant;
pf_on = true;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].gain1_quant={:.9}",
q15_to_float(gain1)
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pitch_index_quant={}",
pitch_index
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].qg_local={}",
qg_local
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pf_on_pre_cancel={}",
if pf_on { 1 } else { 0 }
);
}
let mut before = [0i32; MAX_CHANNELS];
let mut after = [0i32; MAX_CHANNELS];
let mut cancel_pitch = false;
let prev_tapset = encoder.prefilter_tapset.max(0) as usize;
let new_tapset = prefilter_tapset.max(0) as usize;
let offset = mode.short_mdct_size.saturating_sub(overlap).min(n);
encoder.prefilter_period = encoder.prefilter_period.max(COMBFILTER_MINPERIOD as i32);
for ch in 0..channels {
let input_offset = ch * stride;
let (head, tail) = input_fixed[input_offset..input_offset + stride].split_at_mut(overlap);
head.copy_from_slice(&encoder.fixed_in_mem[ch * overlap..(ch + 1) * overlap]);
let mut sum_before = 0i32;
for &sample in tail.iter().take(n) {
sum_before = sum_before.wrapping_add(abs32(shr32(sample, SIG_SHIFT)));
}
before[ch] = sum_before;
let pre_offset = ch * (n + history_len);
let pre_channel = &pre[pre_offset..pre_offset + history_len + n];
let g0 = encoder.fixed_prefilter_gain.wrapping_neg();
if offset > 0 {
let (first, rest) = tail.split_at_mut(offset);
comb_filter_fixed(
first,
pre_channel,
history_len,
offset,
encoder.prefilter_period,
encoder.prefilter_period,
g0,
g0,
prev_tapset,
prev_tapset,
&[],
0,
encoder.arch,
);
comb_filter_fixed(
rest,
pre_channel,
history_len + offset,
n - offset,
encoder.prefilter_period,
pitch_index,
g0,
gain1.wrapping_neg(),
prev_tapset,
new_tapset,
&encoder.fixed_window,
overlap,
encoder.arch,
);
} else {
comb_filter_fixed(
tail,
pre_channel,
history_len,
n,
encoder.prefilter_period,
pitch_index,
g0,
gain1.wrapping_neg(),
prev_tapset,
new_tapset,
&encoder.fixed_window,
overlap,
encoder.arch,
);
}
let mut sum_after = 0i32;
for &sample in tail.iter().take(n) {
sum_after = sum_after.wrapping_add(abs32(shr32(sample, SIG_SHIFT)));
}
after[ch] = sum_after;
}
if channels == 2 {
let thresh0 = add32(
mult16_32_q15(mult16_16_q15(qconst16(0.25, 15), gain1), before[0]),
mult16_32_q15(qconst16(0.01, 15), before[1]),
);
let thresh1 = add32(
mult16_32_q15(mult16_16_q15(qconst16(0.25, 15), gain1), before[1]),
mult16_32_q15(qconst16(0.01, 15), before[0]),
);
if after[0].wrapping_sub(before[0]) > thresh0 || after[1].wrapping_sub(before[1]) > thresh1
{
cancel_pitch = true;
}
if before[0].wrapping_sub(after[0]) < thresh0 && before[1].wrapping_sub(after[1]) < thresh1
{
cancel_pitch = true;
}
} else if after[0] > before[0] {
cancel_pitch = true;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
for ch in 0..channels {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].before.ch[{ch}]={}",
before[ch]
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].after.ch[{ch}]={}",
after[ch]
);
}
if channels == 2 {
let thresh0 = add32(
mult16_32_q15(mult16_16_q15(qconst16(0.25, 15), gain1), before[0]),
mult16_32_q15(qconst16(0.01, 15), before[1]),
);
let thresh1 = add32(
mult16_32_q15(mult16_16_q15(qconst16(0.25, 15), gain1), before[1]),
mult16_32_q15(qconst16(0.01, 15), before[0]),
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].thresh0={}",
thresh0
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].thresh1={}",
thresh1
);
} else {
let thresh0 = mult16_32_q15(mult16_16_q15(qconst16(0.25, 15), gain1), before[0]);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].thresh0={}",
thresh0
);
}
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].cancel_pitch={}",
if cancel_pitch { 1 } else { 0 }
);
}
if cancel_pitch {
for ch in 0..channels {
let input_offset = ch * stride;
let channel = &mut input_fixed[input_offset..input_offset + stride];
let pre_offset = ch * (n + history_len);
let pre_channel = &pre[pre_offset..pre_offset + history_len + n];
channel[overlap..overlap + n]
.copy_from_slice(&pre_channel[history_len..history_len + n]);
if overlap > 0 && offset < n {
let span = overlap.min(n - offset);
let start = overlap + offset;
let end = start + span;
comb_filter_fixed(
&mut channel[start..end],
pre_channel,
history_len + offset,
span,
encoder.prefilter_period,
pitch_index,
encoder.fixed_prefilter_gain.wrapping_neg(),
0,
prev_tapset,
new_tapset,
&encoder.fixed_window,
span,
encoder.arch,
);
}
}
gain1 = 0;
qg_local = 0;
pf_on = false;
}
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].gain1_final={:.9}",
q15_to_float(gain1)
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].qg_local_final={}",
qg_local
);
crate::test_trace::trace_println!(
"celt_prefilter_debug[{frame_idx}].pf_on_final={}",
if pf_on { 1 } else { 0 }
);
}
for ch in 0..channels {
let input_offset = ch * stride;
let channel = &input_fixed[input_offset + n..input_offset + n + overlap];
encoder.fixed_in_mem[ch * overlap..(ch + 1) * overlap].copy_from_slice(channel);
let pre_offset = ch * (n + history_len);
let pre_channel = &pre[pre_offset..pre_offset + history_len + n];
let mem = &mut encoder.fixed_prefilter_mem[ch * history_len..(ch + 1) * history_len];
if n > history_len {
mem.copy_from_slice(&pre_channel[n..n + history_len]);
} else {
let shift = history_len - n;
mem.copy_within(n..history_len, 0);
mem[shift..].copy_from_slice(&pre_channel[history_len..history_len + n]);
}
}
fill_float_sig(input, input_fixed);
fill_float_sig(&mut encoder.in_mem, &encoder.fixed_in_mem);
fill_float_sig(&mut encoder.prefilter_mem, &encoder.fixed_prefilter_mem);
*gain_fixed = gain1;
*gain = q15_to_float(gain1);
*pitch = pitch_index;
*qgain = qg_local;
pf_on
}
fn tf_encode(
start: usize,
end: usize,
is_transient: bool,
tf_res: &mut [i32],
lm: usize,
mut tf_select: i32,
enc: &mut EcEnc<'_>,
) {
debug_assert!(start <= tf_res.len());
debug_assert!(end <= tf_res.len());
debug_assert!(lm < TF_SELECT_TABLE.len());
let mut budget = enc.ctx().storage * 8;
let mut tell = ec_tell(enc.ctx()) as OpusUint32;
let mut logp = if is_transient { 2u32 } else { 4u32 };
let mut curr = 0;
let mut tf_changed = 0;
let reserve_select = lm > 0 && tell + logp < budget;
if reserve_select {
budget -= 1;
}
for slot in start..end {
if tell + logp <= budget {
let symbol = OpusInt32::from(tf_res[slot] ^ curr);
enc.enc_bit_logp(symbol, logp);
tell = ec_tell(enc.ctx()) as OpusUint32;
curr = tf_res[slot];
tf_changed |= curr;
} else {
tf_res[slot] = curr;
}
logp = if is_transient { 4u32 } else { 5u32 };
}
let base = 4 * usize::from(is_transient);
if reserve_select
&& TF_SELECT_TABLE[lm][base + tf_changed as usize]
!= TF_SELECT_TABLE[lm][base + 2 + tf_changed as usize]
{
enc.enc_bit_logp(tf_select, 1);
} else {
tf_select = 0;
}
debug_assert!((0..=1).contains(&tf_select));
for slot in start..end {
debug_assert!((0..=1).contains(&tf_res[slot]));
let offset = base + 2 * tf_select as usize + tf_res[slot] as usize;
tf_res[slot] = i32::from(TF_SELECT_TABLE[lm][offset]);
}
}
#[allow(clippy::too_many_arguments)]
fn compute_vbr(
mode: &OpusCustomMode<'_>,
analysis: &AnalysisInfo,
base_target: OpusInt32,
lm: i32,
bitrate: OpusInt32,
last_coded_bands: i32,
channels: usize,
intensity: i32,
constrained_vbr: bool,
stereo_saving: OpusVal16,
tot_boost: OpusInt32,
tf_estimate: OpusVal16,
pitch_change: bool,
max_depth: CeltGlog,
lfe: bool,
has_surround_mask: bool,
surround_masking: CeltGlog,
temporal_vbr: CeltGlog,
) -> OpusInt32 {
use crate::celt::entcode::BITRES;
let bitres = BITRES as i32;
let nb_ebands = mode.num_ebands;
let e_bands = mode.e_bands;
let mut coded_bands = if last_coded_bands > 0 {
last_coded_bands as usize
} else {
nb_ebands
};
coded_bands = coded_bands.min(nb_ebands);
let mut coded_bins = i32::from(e_bands[coded_bands]) << lm;
if channels == 2 {
let stereo_index = intensity.clamp(0, coded_bands as i32) as usize;
coded_bins += i32::from(e_bands[stereo_index]) << lm;
}
let mut target = base_target;
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].base_target={base_target}"
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].coded_bins={coded_bins}"
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].coded_bands={coded_bands}"
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].intensity={intensity}"
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].tot_boost={tot_boost}"
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].pitch_change={}",
i32::from(pitch_change)
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].analysis_valid={}",
if analysis.valid { 1 } else { 0 }
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].activity={:.9e}",
analysis.activity as f64
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].activity_bits=0x{:08x}",
analysis.activity.to_bits()
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].tonality={:.9e}",
analysis.tonality as f64
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].tonality_bits=0x{:08x}",
analysis.tonality.to_bits()
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].tf_estimate={:.9e}",
tf_estimate as f64
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].tf_estimate_bits=0x{:08x}",
tf_estimate.to_bits()
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].stereo_saving={:.9e}",
stereo_saving as f64
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].stereo_saving_bits=0x{:08x}",
stereo_saving.to_bits()
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].max_depth={:.9e}",
max_depth as f64
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].max_depth_bits=0x{:08x}",
max_depth.to_bits()
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].surround_masking={:.9e}",
surround_masking as f64
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].surround_masking_bits=0x{:08x}",
surround_masking.to_bits()
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].temporal_vbr={:.9e}",
temporal_vbr as f64
);
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].temporal_vbr_bits=0x{:08x}",
temporal_vbr.to_bits()
);
}
}
if analysis.valid && analysis.activity < 0.4 {
let coded = (i64::from(coded_bins) << bitres) as f32;
let reduction = (coded * (0.4 - analysis.activity)) as i32;
target -= reduction;
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].after_activity={target}"
);
}
}
if channels == 2 && coded_bins > 0 {
let stereo_bands = intensity.clamp(0, coded_bands as i32) as usize;
let stereo_dof = (i32::from(e_bands[stereo_bands]) << lm) - stereo_bands as i32;
if stereo_dof > 0 {
let max_frac = 0.8f32 * stereo_dof as f32 / coded_bins as f32;
let capped_saving = stereo_saving.min(1.0);
let term1 = (max_frac * target as f32) as i32;
let raw = capped_saving - 0.1f32;
let term2 = (raw * (i64::from(stereo_dof) << bitres) as f32) as i32;
target -= term1.min(term2);
}
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].after_stereo={target}"
);
}
}
target += tot_boost - (19 << lm);
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!("celt_vbr_compute[{frame_idx}].after_boost={target}");
}
}
let tf_calibration = 0.044f32;
let tf_adjust = (tf_estimate - tf_calibration) * target as f32;
target += tf_adjust as i32;
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!("celt_vbr_compute[{frame_idx}].after_tf={target}");
}
}
if analysis.valid && !lfe {
let tonal = (analysis.tonality - 0.15f32).max(0.0) - 0.12f32;
if tonal != 0.0 {
let coded = (i64::from(coded_bins) << bitres) as f32;
let mut tonal_target = target + (1.2f32 * coded * tonal) as i32;
if pitch_change {
tonal_target += (0.8f32 * coded) as i32;
}
target = tonal_target;
}
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].after_tonality={target}"
);
}
}
if has_surround_mask && !lfe {
let surround_delta = (surround_masking * (i64::from(coded_bins) << bitres) as f32) as i32;
let surround_target = target + surround_delta;
target = max(target / 4, surround_target);
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].after_surround={target}"
);
}
}
if nb_ebands >= 2 {
let bins = i32::from(e_bands[nb_ebands - 2]) << lm;
let floor_depth = ((i64::from(channels as i32 * bins) << bitres) as f32 * max_depth) as i32;
let floor_depth = max(floor_depth, target >> 2);
target = min(target, floor_depth);
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!("celt_vbr_compute[{frame_idx}].after_floor={target}");
}
}
if (!has_surround_mask || lfe) && constrained_vbr {
let delta = (target - base_target) as f32;
target = base_target + (0.67f32 * delta) as i32;
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].after_constrained={target}"
);
}
}
if !has_surround_mask && tf_estimate < 0.2f32 {
let clamp = (96_000 - bitrate).clamp(0, 32_000);
let amount = 0.0000031f32 * clamp as f32;
let tvbr_factor = temporal_vbr * amount;
target += (tvbr_factor * target as f32) as i32;
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_vbr_compute[{frame_idx}].after_temporal={target}"
);
}
}
let doubled = base_target.saturating_mul(2);
target = min(doubled, target);
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!("celt_vbr_compute[{frame_idx}].after_cap={target}");
}
}
target
}
fn resolve_lm(mode: &OpusCustomMode<'_>, frame_size: usize) -> Option<usize> {
let mut n = mode.short_mdct_size;
for lm in 0..=mode.max_lm {
if n == frame_size {
return Some(lm);
}
n <<= 1;
}
None
}
fn mdct_shift_for_frame(mode: &OpusCustomMode<'_>, frame_size: usize) -> Option<usize> {
let target = frame_size.checked_mul(2)?;
(0..=mode.mdct.max_shift()).find(|&shift| mode.mdct.effective_len(shift) == target)
}
fn prepare_time_domain(
encoder: &mut OpusCustomEncoder<'_>,
pcm: &[CeltSig],
frame_size: usize,
) -> Result<Vec<CeltSig>, CeltEncodeError> {
let channels = encoder.channels;
if pcm.len() < channels * frame_size {
return Err(CeltEncodeError::InsufficientPcm);
}
let overlap = encoder.mode.overlap;
let stride = frame_size + overlap;
let mut buffer = vec![0.0f32; channels * stride];
for ch in 0..channels {
let input_stride = channels;
let dst_offset = ch * stride;
let prev = &encoder.in_mem[ch * overlap..(ch + 1) * overlap];
buffer[dst_offset..dst_offset + overlap].copy_from_slice(prev);
let mut mem = encoder.preemph_mem_encoder[ch];
for i in 0..frame_size {
let raw = pcm[i * input_stride + ch];
let emphasised = raw - mem;
buffer[dst_offset + overlap + i] = emphasised;
mem = encoder.mode.pre_emphasis[0] * raw;
}
encoder.preemph_mem_encoder[ch] = mem;
if overlap > 0 {
let available = frame_size.min(overlap);
let copy_start = dst_offset + overlap + frame_size - available;
let dst_start = dst_offset + frame_size;
let tail = buffer[copy_start..copy_start + available].to_vec();
buffer[dst_start..dst_start + available].copy_from_slice(&tail);
if available < overlap {
buffer[dst_start + available..dst_start + overlap].fill(0.0);
}
}
}
Ok(buffer)
}
#[allow(clippy::too_many_arguments)]
fn compute_mdct_spectrum(
mode: &OpusCustomMode<'_>,
short_blocks: bool,
time: &[CeltSig],
freq: &mut [CeltSig],
encoded_channels: usize,
total_channels: usize,
frame_size: usize,
shift: usize,
) {
let overlap = mode.overlap;
let stride = frame_size + overlap;
let blocks = if short_blocks { 1usize << shift } else { 1 };
for ch in 0..encoded_channels {
let src_index = ch * stride;
let input = &time[src_index..src_index + overlap + frame_size];
let output = &mut freq[ch * frame_size..(ch + 1) * frame_size];
clt_mdct_forward(
&mode.mdct,
input,
output,
mode.window,
overlap,
shift,
blocks,
);
}
if total_channels == 2 && encoded_channels == 1 {
for i in 0..(frame_size / 2) {
let left = freq[i];
let right = freq[frame_size / 2 + i];
freq[i] = 0.5 * (left + right);
}
}
}
fn update_overlap_history(
encoder: &mut OpusCustomEncoder<'_>,
time: &[CeltSig],
frame_size: usize,
) {
let channels = encoder.channels;
let overlap = encoder.mode.overlap;
let stride = frame_size + overlap;
for ch in 0..channels {
let src = &time[ch * stride + frame_size..ch * stride + frame_size + overlap];
encoder.in_mem[ch * overlap..(ch + 1) * overlap].copy_from_slice(src);
}
}
fn encode_internal(
encoder: &mut OpusCustomEncoder<'_>,
pcm: &[CeltSig],
frame_size: usize,
_enc: &mut EcEnc<'_>,
) -> Result<(), CeltEncodeError> {
let mode = encoder.mode;
let Some(lm) = resolve_lm(mode, frame_size) else {
return Err(CeltEncodeError::InvalidFrameSize);
};
let Some(shift) = mdct_shift_for_frame(mode, frame_size) else {
return Err(CeltEncodeError::InvalidFrameSize);
};
let time = prepare_time_domain(encoder, pcm, frame_size)?;
update_overlap_history(encoder, &time, frame_size);
let mut freq = vec![0.0f32; encoder.stream_channels * frame_size];
compute_mdct_spectrum(
mode,
false,
&time,
&mut freq,
encoder.stream_channels,
encoder.channels,
frame_size,
shift,
);
let end_band = encoder.end_band.clamp(0, mode.num_ebands as i32) as usize;
let mut band_e = vec![0.0f32; encoder.stream_channels * mode.num_ebands];
compute_band_energies(
mode,
&freq,
&mut band_e,
end_band,
encoder.stream_channels,
lm,
encoder.arch,
);
let mut band_log = vec![0.0f32; encoder.stream_channels * mode.num_ebands];
#[cfg(feature = "fixed_point")]
{
let mut fixed_freq = vec![0; encoder.stream_channels * frame_size];
let mut band_e_fixed = vec![0; encoder.stream_channels * mode.num_ebands];
let mut band_log_fixed = vec![0; encoder.stream_channels * mode.num_ebands];
fill_fixed_sig(&mut fixed_freq, &freq);
compute_band_energies_fixed(
mode,
&fixed_freq,
&mut band_e_fixed,
end_band,
encoder.stream_channels,
lm,
);
amp2_log2_fixed(
mode,
end_band,
end_band,
&band_e_fixed,
&mut band_log_fixed,
encoder.stream_channels,
);
sync_loge_from_fixed(&mut band_log, &band_log_fixed);
}
#[cfg(not(feature = "fixed_point"))]
{
amp2_log2(
mode,
end_band,
end_band,
&band_e,
&mut band_log,
encoder.stream_channels,
);
}
let stride = mode.num_ebands;
for (dst, src) in encoder
.old_band_e
.chunks_mut(stride)
.zip(band_e.chunks(stride))
.take(encoder.stream_channels)
{
dst[..end_band].copy_from_slice(&src[..end_band]);
}
for (dst, src) in encoder
.old_log_e
.chunks_mut(stride)
.zip(band_log.chunks(stride))
.take(encoder.stream_channels)
{
dst[..end_band].copy_from_slice(&src[..end_band]);
}
for chunk in encoder
.old_log_e2
.chunks_mut(stride)
.take(encoder.stream_channels)
{
for slot in &mut chunk[..end_band] {
*slot = (*slot * 0.75) + 0.25 * -28.0;
}
}
encoder.last_coded_bands = end_band as i32;
encoder.tapset_decision = 0;
encoder.consec_transient = 0;
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn celt_encode_with_ec_inner<'a>(
encoder: &mut OpusCustomEncoder<'_>,
pcm: &[CeltSig],
enc: &mut EcEnc<'a>,
use_external: bool,
header_bytes: usize,
mut nb_compressed_bytes: usize,
frame_size_internal: usize,
upsample: usize,
lm: usize,
m: usize,
n: usize,
start: usize,
end: i32,
hybrid: bool,
) -> Result<usize, CeltEncodeError> {
let mode = encoder.mode;
let nb_ebands = mode.num_ebands;
let overlap = mode.overlap;
let cc = encoder.channels;
let c = encoder.stream_channels;
#[cfg(test)]
let trace_frame_idx = celt_alloc_trace::begin_frame();
#[cfg(test)]
let trace_band_frame_idx = celt_band_energy_trace::begin_frame();
#[cfg(test)]
let trace_band_should_dump = trace_band_frame_idx.map_or(false, |frame_idx| {
celt_band_energy_trace::should_dump(frame_idx)
});
#[cfg(test)]
let trace_band_want_bits = trace_band_should_dump && celt_band_energy_trace::want_bits();
#[cfg(test)]
let trace_prefilter_frame_idx = celt_prefilter_trace::begin_frame();
#[cfg(test)]
let trace_prefilter_should_dump = trace_prefilter_frame_idx.map_or(false, |frame_idx| {
celt_prefilter_trace::should_dump(frame_idx)
});
#[cfg(test)]
let trace_mdct_frame_idx = celt_mdct_trace::begin_frame();
#[cfg(test)]
let trace_mdct_should_dump =
trace_mdct_frame_idx.map_or(false, |frame_idx| celt_mdct_trace::should_dump(frame_idx));
#[cfg(test)]
mdct_input_trace::set_frame(trace_mdct_frame_idx.unwrap_or(usize::MAX));
#[cfg(test)]
let _trace_transient_frame_idx = celt_transient_trace::begin_frame();
#[cfg(test)]
let trace_pcm_frame_idx = celt_pcm_input_trace::begin_frame();
#[cfg(test)]
let trace_ctrl_frame_idx = celt_ctrl_trace::begin_frame();
#[cfg(test)]
let trace_vbr_frame_idx = celt_vbr_budget_trace::begin_frame();
#[cfg(test)]
let trace_rc_frame_idx = celt_rc_trace::begin_frame();
let mut tell0_frac = 1u32;
let mut tell = 1i32;
let mut nb_filled_bytes = 0i32;
if use_external {
tell0_frac = ec_tell_frac(enc.ctx());
tell = ec_tell(enc.ctx());
nb_filled_bytes = (tell + 4) >> 3;
}
let mut vbr_rate = 0i32;
let effective_bytes: i32;
if encoder.use_vbr && encoder.bitrate != OPUS_BITRATE_MAX {
let den = (mode.sample_rate >> BITRES) as i32;
vbr_rate = (encoder.bitrate * frame_size_internal as i32 + (den >> 1)) / den;
if encoder.signalling != 0 && !use_external {
vbr_rate -= 8 << BITRES;
}
effective_bytes = vbr_rate >> (3 + BITRES);
} else {
let mut tmp = encoder.bitrate.saturating_mul(frame_size_internal as i32);
if tell > 1 {
tmp = tmp.saturating_add(tell.saturating_mul(mode.sample_rate));
}
if encoder.bitrate != OPUS_BITRATE_MAX {
let extra = if encoder.signalling != 0 && !use_external {
1
} else {
0
};
let target = (tmp + 4 * mode.sample_rate) / (8 * mode.sample_rate) - extra;
nb_compressed_bytes = max(2, min(nb_compressed_bytes as i32, target)) as usize;
enc.enc_shrink(nb_compressed_bytes as OpusUint32);
}
effective_bytes = nb_compressed_bytes as i32 - nb_filled_bytes;
}
let mut nb_available_bytes = nb_compressed_bytes as i32 - nb_filled_bytes;
let shift = 3i32 - lm as i32;
let base_rate = nb_compressed_bytes as i32 * 8 * 50;
let mut equiv_rate = if shift >= 0 {
base_rate << shift
} else {
base_rate >> (-shift)
};
let lfe_adjust = (40 * c as i32 + 20) * ((400 >> lm) - 50);
equiv_rate -= lfe_adjust;
if encoder.bitrate != OPUS_BITRATE_MAX {
equiv_rate = min(equiv_rate, encoder.bitrate - lfe_adjust);
}
#[cfg(test)]
let mut vbr_min_bytes = -1;
#[cfg(test)]
let mut vbr_max_allowed = nb_available_bytes;
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
celt_vbr_budget_trace::dump_if_match(
frame_idx,
"pre_cvbr",
use_external,
encoder.constrained_vbr,
vbr_rate,
encoder.vbr_reservoir,
encoder.vbr_offset,
encoder.vbr_drift,
nb_compressed_bytes,
nb_available_bytes,
nb_filled_bytes,
vbr_min_bytes,
vbr_max_allowed,
-1,
-1,
0,
-1,
tell,
ec_tell_frac(enc.ctx()) as i32,
);
}
if vbr_rate > 0 && encoder.constrained_vbr {
let vbr_bound = vbr_rate;
let min_bytes = if tell == 1 { 2 } else { 0 };
let max_allowed = min(
max(
min_bytes,
(vbr_rate + vbr_bound - encoder.vbr_reservoir) >> (BITRES + 3),
),
nb_available_bytes,
);
#[cfg(test)]
{
vbr_min_bytes = min_bytes;
vbr_max_allowed = max_allowed;
}
if max_allowed < nb_available_bytes {
nb_compressed_bytes = (nb_filled_bytes + max_allowed) as usize;
nb_available_bytes = max_allowed;
enc.enc_shrink(nb_compressed_bytes as OpusUint32);
}
}
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
celt_vbr_budget_trace::dump_if_match(
frame_idx,
"post_cvbr",
use_external,
encoder.constrained_vbr,
vbr_rate,
encoder.vbr_reservoir,
encoder.vbr_offset,
encoder.vbr_drift,
nb_compressed_bytes,
nb_available_bytes,
nb_filled_bytes,
vbr_min_bytes,
vbr_max_allowed,
-1,
-1,
0,
-1,
tell,
ec_tell_frac(enc.ctx()) as i32,
);
}
let mut total_bits = nb_compressed_bytes as i32 * 8;
let eff_end = min(end as usize, mode.effective_ebands);
let sample_span = c * (n.saturating_sub(overlap)) / upsample;
let overlap_span = c * overlap / upsample;
let mut sample_max = encoder
.overlap_max
.max(celt_maxabs_res(&pcm[..sample_span]));
encoder.overlap_max = celt_maxabs_res(&pcm[sample_span..sample_span + overlap_span]);
sample_max = sample_max.max(encoder.overlap_max);
#[cfg(feature = "fixed_point")]
let mut silence = sample_max == 0.0;
#[cfg(not(feature = "fixed_point"))]
let mut silence = sample_max <= (1.0 / ((1u32 << encoder.lsb_depth) as f32));
if tell == 1 {
enc.enc_bit_logp(silence as i32, 15);
} else {
silence = false;
}
if silence {
if vbr_rate > 0 {
nb_compressed_bytes = min(nb_compressed_bytes, (nb_filled_bytes + 2) as usize);
total_bits = nb_compressed_bytes as i32 * 8;
nb_available_bytes = 2;
enc.enc_shrink(nb_compressed_bytes as OpusUint32);
}
let consumed = ec_tell(enc.ctx());
enc.ctx_mut().nbits_total += total_bits - consumed;
tell = total_bits;
}
let mut input = vec![0.0f32; cc * (n + overlap)];
#[cfg(feature = "fixed_point")]
let mut input_fixed = vec![0; cc * (n + overlap)];
#[cfg(test)]
if let Some(frame_idx) = trace_pcm_frame_idx {
celt_pcm_input_trace::dump("preemph_in", frame_idx, pcm, cc, frame_size_internal);
}
for ch in 0..cc {
let input_offset = ch * (n + overlap);
let prefilter_offset = (ch + 1) * COMBFILTER_MAXPERIOD - overlap;
let input_slice = &mut input[input_offset + overlap..input_offset + overlap + n];
#[cfg(feature = "fixed_point")]
let input_fixed_slice =
&mut input_fixed[input_offset + overlap..input_offset + overlap + n];
let channel_pcm = &pcm[ch..];
let need_clip = encoder.clip && sample_max > PREEMPHASIS_CLIP_LIMIT;
celt_preemphasis(
channel_pcm,
input_slice,
n,
cc,
upsample,
&mode.pre_emphasis,
&mut encoder.preemph_mem_encoder[ch],
need_clip,
);
#[cfg(feature = "fixed_point")]
{
celt_preemphasis_fixed(
channel_pcm,
input_fixed_slice,
n,
cc,
upsample,
&mode.pre_emphasis,
&mut encoder.fixed_preemph_mem_encoder[ch],
need_clip,
);
}
#[cfg(not(feature = "fixed_point"))]
{
input[input_offset..input_offset + overlap].copy_from_slice(
&encoder.prefilter_mem[prefilter_offset..prefilter_offset + overlap],
);
}
#[cfg(feature = "fixed_point")]
{
let overlap_src =
&encoder.fixed_prefilter_mem[prefilter_offset..prefilter_offset + overlap];
let overlap_dst = &mut input_fixed[input_offset..input_offset + overlap];
overlap_dst.copy_from_slice(overlap_src);
for (dst, &value) in input[input_offset..input_offset + overlap]
.iter_mut()
.zip(overlap_src.iter())
{
*dst = fixed_sig_to_float(value);
}
}
}
#[cfg(test)]
if trace_prefilter_should_dump {
celt_prefilter_trace::dump("pre", trace_prefilter_frame_idx.unwrap(), &input, cc);
}
let mut toneishness = 0.0f32;
let tone_freq = tone_detect(&input, cc, n + overlap, &mut toneishness, mode.sample_rate);
let mut tf_estimate = 0.0f32;
let mut tf_chan = 0usize;
let mut weak_transient = false;
let mut is_transient = false;
let mut short_blocks = 0usize;
if encoder.complexity >= 1 && !encoder.lfe {
let allow_weak = hybrid && effective_bytes < 15 && encoder.silk_info.signal_type != 2;
is_transient = transient_analysis(
&input,
n + overlap,
cc,
&mut tf_estimate,
&mut tf_chan,
allow_weak,
&mut weak_transient,
tone_freq,
toneishness,
);
}
let mut pitch_index = COMBFILTER_MINPERIOD as i32;
let mut gain1 = 0.0;
#[cfg(feature = "fixed_point")]
let mut gain1_fixed = 0;
let mut qg = 0;
let mut pitch_change = false;
let prefilter_tapset = encoder.tapset_decision;
#[cfg(test)]
let prefilter_period_old = encoder.prefilter_period;
#[cfg(test)]
let prefilter_gain_old = encoder.prefilter_gain;
#[cfg(test)]
let prefilter_tapset_old = encoder.prefilter_tapset;
let enabled = ((encoder.lfe && nb_available_bytes > 3) || nb_available_bytes > 12 * c as i32)
&& !hybrid
&& !silence
&& tell + 16 <= total_bits
&& !encoder.disable_prefilter
&& encoder.complexity >= 5;
let analysis = encoder.analysis.clone();
#[cfg(not(feature = "fixed_point"))]
let pf_on = run_prefilter(
encoder,
&mut input,
cc,
n,
prefilter_tapset,
&mut pitch_index,
&mut gain1,
&mut qg,
enabled,
tf_estimate,
nb_available_bytes,
&analysis,
tone_freq,
toneishness,
);
#[cfg(feature = "fixed_point")]
let pf_on = run_prefilter_fixed(
encoder,
&mut input,
&mut input_fixed,
cc,
n,
prefilter_tapset,
&mut pitch_index,
&mut gain1,
&mut gain1_fixed,
&mut qg,
enabled,
tf_estimate,
nb_available_bytes,
&analysis,
tone_freq,
toneishness,
);
#[cfg(test)]
if trace_prefilter_should_dump {
let frame_idx = trace_prefilter_frame_idx.unwrap();
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].enabled={}",
if enabled { 1 } else { 0 }
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].pf_on={}",
if pf_on { 1 } else { 0 }
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].pitch_index={pitch_index}"
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].gain1={:.9e}",
gain1 as f64
);
crate::test_trace::trace_println!("celt_prefilter_param[{frame_idx}].qg={qg}");
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].prefilter_period_old={prefilter_period_old}"
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].prefilter_gain_old={:.9e}",
prefilter_gain_old as f64
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].prefilter_tapset_old={prefilter_tapset_old}"
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].prefilter_tapset={prefilter_tapset}"
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].tf_estimate={:.9e}",
tf_estimate as f64
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].tone_freq={:.9e}",
tone_freq as f64
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].toneishness={:.9e}",
toneishness as f64
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].nb_available_bytes={nb_available_bytes}"
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].analysis_valid={}",
if analysis.valid { 1 } else { 0 }
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].max_pitch_ratio={:.9e}",
analysis.max_pitch_ratio as f64
);
crate::test_trace::trace_println!(
"celt_prefilter_param[{frame_idx}].loss_rate={}",
encoder.loss_rate
);
}
#[cfg(test)]
if trace_prefilter_should_dump {
celt_prefilter_trace::dump("post", trace_prefilter_frame_idx.unwrap(), &input, cc);
}
if (gain1 > 0.4 || encoder.prefilter_gain > 0.4)
&& (!encoder.analysis.valid || encoder.analysis.tonality > 0.3)
&& (pitch_index > (1.26 * encoder.prefilter_period as f32) as i32
|| pitch_index < (0.79 * encoder.prefilter_period as f32) as i32)
{
pitch_change = true;
}
if !pf_on {
if !hybrid && tell + 16 <= total_bits {
enc.enc_bit_logp(0, 1);
}
} else {
enc.enc_bit_logp(1, 1);
pitch_index += 1;
let octave = ec_ilog(pitch_index as u32) - 5;
enc.enc_uint(octave as u32, 6);
enc.enc_bits((pitch_index - (16 << octave)) as u32, (4 + octave) as u32);
pitch_index -= 1;
enc.enc_bits(qg as u32, 3);
enc.enc_icdf(prefilter_tapset.max(0) as usize, &TAPSET_ICDF, 2);
}
let mut transient_got_disabled = false;
if lm > 0 && ec_tell(enc.ctx()) + 3 <= total_bits {
if is_transient {
short_blocks = m;
}
} else {
is_transient = false;
transient_got_disabled = true;
}
let mut freq = vec![0.0f32; cc * n];
let mut band_e = vec![0.0f32; nb_ebands * c];
let mut band_log_e = vec![0.0f32; nb_ebands * c];
let mut band_log_e2 = vec![0.0f32; nb_ebands * c];
#[cfg(feature = "fixed_point")]
let mut fixed_freq = vec![0; cc * n];
#[cfg(feature = "fixed_point")]
let mut band_e_fixed = vec![0; nb_ebands * c];
#[cfg(feature = "fixed_point")]
let mut band_log_e_fixed = vec![0; nb_ebands * c];
#[cfg(feature = "fixed_point")]
let mut band_log_e2_fixed = vec![0; nb_ebands * c];
let second_mdct = short_blocks != 0 && encoder.complexity >= 8;
if second_mdct {
#[cfg(test)]
mdct_input_trace::set_tag(1);
compute_mdcts(
mode,
0,
&input,
&mut freq,
c,
cc,
lm,
upsample,
encoder.arch,
);
#[cfg(test)]
if trace_mdct_should_dump {
celt_mdct_trace::dump("mdct2", trace_mdct_frame_idx.unwrap(), &freq, c);
}
compute_band_energies(
mode,
&freq[..c * n],
&mut band_e,
eff_end,
c,
lm,
encoder.arch,
);
#[cfg(test)]
if trace_band_should_dump {
celt_band_energy_trace::dump(
"mdct2",
trace_band_frame_idx.unwrap(),
0,
eff_end,
c,
nb_ebands,
&band_e,
trace_band_want_bits,
);
}
#[cfg(feature = "fixed_point")]
{
compute_mdcts_fixed(
mode,
&encoder.fixed_mdct,
&encoder.fixed_window,
0,
&input_fixed,
&mut fixed_freq,
c,
cc,
lm,
upsample,
);
compute_band_energies_fixed(mode, &fixed_freq, &mut band_e_fixed, eff_end, c, lm);
amp2_log2_fixed(
mode,
eff_end,
end as usize,
&band_e_fixed,
&mut band_log_e2_fixed,
c,
);
let offset = lm_offset_fixed(lm);
for channel in 0..c {
let base = channel * nb_ebands;
for band in 0..end as usize {
band_log_e2_fixed[base + band] = add32(band_log_e2_fixed[base + band], offset);
}
}
sync_loge_from_fixed(&mut band_log_e2, &band_log_e2_fixed);
}
#[cfg(not(feature = "fixed_point"))]
{
amp2_log2(mode, eff_end, end as usize, &band_e, &mut band_log_e2, c);
for channel in 0..c {
let base = channel * nb_ebands;
for band in 0..end as usize {
band_log_e2[base + band] += 0.5 * lm as f32;
}
}
}
}
#[cfg(test)]
mdct_input_trace::set_tag(0);
compute_mdcts(
mode,
short_blocks,
&input,
&mut freq,
c,
cc,
lm,
upsample,
encoder.arch,
);
#[cfg(test)]
if trace_mdct_should_dump {
celt_mdct_trace::dump("main", trace_mdct_frame_idx.unwrap(), &freq, c);
}
debug_assert!(
!freq[0].is_nan() && (c == 1 || !freq[n].is_nan()),
"MDCT should not produce NaN coefficients",
);
if cc == 2 && c == 1 {
tf_chan = 0;
}
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
if celt_vbr_budget_trace::should_dump(frame_idx) {
let band = 0usize;
let band_start = (mode.e_bands[band] as usize) << lm;
let band_end = (mode.e_bands[band + 1] as usize) << lm;
for channel in 0..c {
let base = channel * n;
let mut sum = 1e-27_f32;
for (bin_offset, idx) in (base + band_start..base + band_end).enumerate() {
let value = freq[idx] as f32;
let sq = value * value;
sum += sq;
crate::test_trace::trace_println!(
"celt_band0_bin[{frame_idx}].ch[{channel}].bin[{bin_offset}].x={:.9e}",
value as f64
);
crate::test_trace::trace_println!(
"celt_band0_bin[{frame_idx}].ch[{channel}].bin[{bin_offset}].x_bits=0x{:08x}",
value.to_bits()
);
crate::test_trace::trace_println!(
"celt_band0_bin[{frame_idx}].ch[{channel}].bin[{bin_offset}].sq={:.9e}",
sq as f64
);
crate::test_trace::trace_println!(
"celt_band0_bin[{frame_idx}].ch[{channel}].bin[{bin_offset}].sq_bits=0x{:08x}",
sq.to_bits()
);
}
crate::test_trace::trace_println!(
"celt_band0_bin[{frame_idx}].ch[{channel}].sum={:.9e}",
sum as f64
);
crate::test_trace::trace_println!(
"celt_band0_bin[{frame_idx}].ch[{channel}].sum_bits=0x{:08x}",
sum.to_bits()
);
let amp = celt_sqrt(sum) as f32;
crate::test_trace::trace_println!(
"celt_band0_bin[{frame_idx}].ch[{channel}].sqrt={:.9e}",
amp as f64
);
crate::test_trace::trace_println!(
"celt_band0_bin[{frame_idx}].ch[{channel}].sqrt_bits=0x{:08x}",
amp.to_bits()
);
}
}
}
compute_band_energies(
mode,
&freq[..c * n],
&mut band_e,
eff_end,
c,
lm,
encoder.arch,
);
#[cfg(test)]
if trace_band_should_dump {
celt_band_energy_trace::dump(
"main",
trace_band_frame_idx.unwrap(),
0,
eff_end,
c,
nb_ebands,
&band_e,
trace_band_want_bits,
);
}
if encoder.lfe {
for band in 2..end as usize {
band_e[band] = band_e[band].min(1e-4 * band_e[0]).max(1e-15);
}
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
let band = 0usize;
crate::test_trace::trace_println!("celt_band_log_e[{frame_idx}].band={band}");
for channel in 0..c {
let idx = channel * nb_ebands + band;
let value = band_e[idx] as f32;
crate::test_trace::trace_println!(
"celt_band_log_e[{frame_idx}].bandE.ch[{channel}]={:.9e}",
value as f64
);
crate::test_trace::trace_println!(
"celt_band_log_e[{frame_idx}].bandE_bits.ch[{channel}]=0x{:08x}",
value.to_bits()
);
}
}
}
#[cfg(feature = "fixed_point")]
{
compute_mdcts_fixed(
mode,
&encoder.fixed_mdct,
&encoder.fixed_window,
short_blocks,
&input_fixed,
&mut fixed_freq,
c,
cc,
lm,
upsample,
);
compute_band_energies_fixed(mode, &fixed_freq, &mut band_e_fixed, eff_end, c, lm);
if encoder.lfe {
let limit = mult16_32_q15(qconst16(1e-4, 15), band_e_fixed[0]);
let min_val = FixedCeltEner::from(FIXED_EPSILON);
for band in 2..end as usize {
let idx = band;
let clamped = if band_e_fixed[idx] > limit {
limit
} else {
band_e_fixed[idx]
};
band_e_fixed[idx] = clamped.max(min_val);
}
}
amp2_log2_fixed(
mode,
eff_end,
end as usize,
&band_e_fixed,
&mut band_log_e_fixed,
c,
);
sync_loge_from_fixed(&mut band_log_e, &band_log_e_fixed);
}
#[cfg(not(feature = "fixed_point"))]
{
amp2_log2(mode, eff_end, end as usize, &band_e, &mut band_log_e, c);
}
#[cfg(test)]
if let Some(frame_idx) = celt_vbr_budget_trace::current_frame_idx() {
if celt_vbr_budget_trace::should_dump(frame_idx) {
let band = 0usize;
for channel in 0..c {
let idx = channel * nb_ebands + band;
let value = band_log_e[idx] as f32;
crate::test_trace::trace_println!(
"celt_band_log_e[{frame_idx}].bandLogE.ch[{channel}]={:.9e}",
value as f64
);
crate::test_trace::trace_println!(
"celt_band_log_e[{frame_idx}].bandLogE_bits.ch[{channel}]=0x{:08x}",
value.to_bits()
);
}
}
}
let mut surround_dynalloc = vec![0.0f32; nb_ebands * c];
let mut surround_masking = 0.0f32;
let mut temporal_vbr = 0.0f32;
let mut surround_trim = 0.0f32;
if !hybrid && encoder.energy_mask.is_some() && !encoder.lfe {
let mask_end = max(2, encoder.last_coded_bands) as usize;
let mut mask_avg = 0.0f32;
let mut diff = 0.0f32;
let mut count = 0.0f32;
if let Some(mask) = encoder.energy_mask {
for channel in 0..c {
let base = channel * nb_ebands;
for band in 0..mask_end {
let mut value = mask[base + band].clamp(-2.0, 0.25);
if value > 0.0 {
value *= 0.5;
}
let width = (mode.e_bands[band + 1] - mode.e_bands[band]) as f32;
mask_avg += value * width;
count += width;
diff += value * (1 + 2 * band as i32 - mask_end as i32) as f32;
}
}
debug_assert!(count > 0.0);
mask_avg = mask_avg / count + 0.2;
diff = diff * 6.0
/ (c as f32 * (mask_end as f32 - 1.0) * (mask_end as f32 + 1.0) * mask_end as f32);
diff *= 0.5;
diff = diff.clamp(-0.031, 0.031);
let mut midband = 0usize;
while midband + 1 < nb_ebands && mode.e_bands[midband + 1] < mode.e_bands[mask_end] / 2
{
midband += 1;
}
let mut count_dynalloc = 0;
for band in 0..mask_end {
let lin = mask_avg + diff * (band as i32 - midband as i32) as f32;
let mut unmask = if c == 2 {
mask[band].max(mask[nb_ebands + band])
} else {
mask[band]
};
unmask = unmask.min(0.0);
unmask -= lin;
if unmask > 0.25 {
surround_dynalloc[band] = unmask - 0.25;
count_dynalloc += 1;
}
}
if count_dynalloc >= 3 {
mask_avg += 0.25;
if mask_avg > 0.0 {
mask_avg = 0.0;
diff = 0.0;
surround_dynalloc[..mask_end].fill(0.0);
} else {
for band in 0..mask_end {
surround_dynalloc[band] = (surround_dynalloc[band] - 0.25).max(0.0);
}
}
}
mask_avg += 0.2;
surround_trim = 64.0 * diff;
surround_masking = mask_avg;
}
}
if !encoder.lfe {
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_temporal_vbr[{frame_idx}].spec_avg_pre={:.9e}",
encoder.spec_avg as f64
);
}
}
let mut follow = -10.0f32;
let mut frame_avg = 0.0f32;
let offset = if short_blocks != 0 {
0.5 * lm as f32
} else {
0.0
};
for band in start..end as usize {
let mut candidate = band_log_e[band] - offset;
#[cfg(test)]
let left_val = band_log_e[band];
#[cfg(test)]
let mut right_val = 0.0f32;
if c == 2 {
let right = band_log_e[nb_ebands + band] - offset;
#[cfg(test)]
{
right_val = band_log_e[nb_ebands + band];
}
candidate = candidate.max(right);
}
follow = (follow - 1.0).max(candidate);
frame_avg += follow;
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
if celt_vbr_budget_trace::should_dump(frame_idx) {
if c == 2 {
crate::test_trace::trace_println!(
"celt_temporal_vbr[{frame_idx}].band[{band}].loge_l={:.9e}",
left_val as f64
);
crate::test_trace::trace_println!(
"celt_temporal_vbr[{frame_idx}].band[{band}].loge_r={:.9e}",
right_val as f64
);
} else {
crate::test_trace::trace_println!(
"celt_temporal_vbr[{frame_idx}].band[{band}].loge={:.9e}",
left_val as f64
);
}
crate::test_trace::trace_println!(
"celt_temporal_vbr[{frame_idx}].band[{band}].candidate={:.9e}",
candidate as f64
);
crate::test_trace::trace_println!(
"celt_temporal_vbr[{frame_idx}].band[{band}].follow={:.9e}",
follow as f64
);
}
}
}
if end as usize > start {
frame_avg /= (end as usize - start) as f32;
}
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_temporal_vbr[{frame_idx}].frame_avg={:.9e}",
frame_avg as f64
);
}
}
temporal_vbr = frame_avg - encoder.spec_avg;
temporal_vbr = temporal_vbr.clamp(-1.5, 3.0);
encoder.spec_avg += 0.02 * temporal_vbr;
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_temporal_vbr[{frame_idx}].temporal_vbr={:.9e}",
temporal_vbr as f64
);
crate::test_trace::trace_println!(
"celt_temporal_vbr[{frame_idx}].spec_avg_post={:.9e}",
encoder.spec_avg as f64
);
}
}
}
if !second_mdct {
band_log_e2.copy_from_slice(&band_log_e);
#[cfg(feature = "fixed_point")]
band_log_e2_fixed.copy_from_slice(&band_log_e_fixed);
}
if lm > 0
&& ec_tell(enc.ctx()) + 3 <= total_bits
&& !is_transient
&& encoder.complexity >= 5
&& !encoder.lfe
&& !hybrid
{
if patch_transient_decision(
&band_log_e,
&encoder.old_band_e,
nb_ebands,
start,
end as usize,
c,
) {
is_transient = true;
short_blocks = m;
compute_mdcts(
mode,
short_blocks,
&input,
&mut freq,
c,
cc,
lm,
upsample,
encoder.arch,
);
compute_band_energies(
mode,
&freq[..c * n],
&mut band_e,
eff_end,
c,
lm,
encoder.arch,
);
#[cfg(feature = "fixed_point")]
{
compute_mdcts_fixed(
mode,
&encoder.fixed_mdct,
&encoder.fixed_window,
short_blocks,
&input_fixed,
&mut fixed_freq,
c,
cc,
lm,
upsample,
);
compute_band_energies_fixed(mode, &fixed_freq, &mut band_e_fixed, eff_end, c, lm);
amp2_log2_fixed(
mode,
eff_end,
end as usize,
&band_e_fixed,
&mut band_log_e_fixed,
c,
);
sync_loge_from_fixed(&mut band_log_e, &band_log_e_fixed);
let offset = lm_offset_fixed(lm);
for channel in 0..c {
let base = channel * nb_ebands;
for band in 0..end as usize {
band_log_e2_fixed[base + band] =
add32(band_log_e2_fixed[base + band], offset);
}
}
sync_loge_from_fixed(&mut band_log_e2, &band_log_e2_fixed);
}
#[cfg(not(feature = "fixed_point"))]
{
amp2_log2(mode, eff_end, end as usize, &band_e, &mut band_log_e, c);
for channel in 0..c {
let base = channel * nb_ebands;
for band in 0..end as usize {
band_log_e2[base + band] += 0.5 * lm as f32;
}
}
}
tf_estimate = 0.2;
}
}
if lm > 0 && ec_tell(enc.ctx()) + 3 <= total_bits {
enc.enc_bit_logp(is_transient as i32, 3);
}
let mut x = vec![0.0f32; c * n];
#[cfg(feature = "fixed_point")]
{
let mut x_fixed = vec![0i16; c * n];
normalise_bands_fixed(
mode,
&fixed_freq,
&mut x_fixed,
&band_e_fixed,
eff_end,
c,
m,
);
fill_float_norm(&mut x, &x_fixed);
}
#[cfg(not(feature = "fixed_point"))]
{
normalise_bands(mode, &freq[..c * n], &mut x, &band_e, eff_end, c, m);
}
let enable_tf_analysis = effective_bytes >= 15 * c as i32
&& !hybrid
&& encoder.complexity >= 2
&& !encoder.lfe
&& toneishness < 0.98;
let mut offsets = vec![0i32; nb_ebands];
let mut importance = vec![0i32; nb_ebands];
let mut spread_weight = vec![0i32; nb_ebands];
let mut tot_boost = 0i32;
let max_depth = dynalloc_analysis(
&band_log_e,
&band_log_e2,
&encoder.old_band_e,
nb_ebands,
start,
end as usize,
c,
&mut offsets,
encoder.lsb_depth,
mode.log_n,
is_transient,
encoder.use_vbr,
encoder.constrained_vbr,
mode.e_bands,
lm as i32,
effective_bytes,
&mut tot_boost,
encoder.lfe,
&mut surround_dynalloc,
&encoder.analysis,
&mut importance,
&mut spread_weight,
tone_freq,
toneishness,
);
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_dynalloc_summary[{frame_idx}].tot_boost={tot_boost}"
);
crate::test_trace::trace_println!(
"celt_dynalloc_summary[{frame_idx}].max_depth={:.9e}",
max_depth as f64
);
crate::test_trace::trace_println!(
"celt_dynalloc_summary[{frame_idx}].max_depth_bits=0x{:08x}",
max_depth.to_bits()
);
for band in start..end as usize {
crate::test_trace::trace_println!(
"celt_dynalloc_summary[{frame_idx}].offsets[{band}]={}",
offsets[band]
);
}
}
}
let mut tf_res = vec![0i32; nb_ebands];
let tf_select = if enable_tf_analysis {
let lambda = max(80, 20480 / effective_bytes + 2);
let tf_select = tf_analysis(
mode,
eff_end,
is_transient,
&mut tf_res,
lambda,
&x,
n,
lm,
tf_estimate,
tf_chan,
&importance,
);
for band in eff_end..end as usize {
tf_res[band] = tf_res[eff_end - 1];
}
tf_select
} else if hybrid && weak_transient {
for band in 0..end as usize {
tf_res[band] = 1;
}
0
} else if hybrid && effective_bytes < 15 && encoder.silk_info.signal_type != 2 {
for band in 0..end as usize {
tf_res[band] = 0;
}
is_transient as i32
} else {
for band in 0..end as usize {
tf_res[band] = is_transient as i32;
}
0
};
#[cfg(not(feature = "fixed_point"))]
let mut error = vec![0.0f32; c * nb_ebands];
#[cfg(feature = "fixed_point")]
let mut error_fixed = vec![0; c * nb_ebands];
#[cfg(test)]
let trace_loge_frame_idx = celt_loge_adjust_trace::begin_frame();
#[cfg(feature = "fixed_point")]
{
sync_loge_to_fixed(&mut encoder.fixed_old_band_e, &encoder.old_band_e);
sync_loge_to_fixed(&mut encoder.fixed_energy_error, &encoder.energy_error);
encoder.fixed_delayed_intra = glog_to_fixed(encoder.delayed_intra);
let diff_limit = glog_to_fixed(2.0);
let quarter = qconst16(0.25, 15);
for channel in 0..c {
let base = channel * nb_ebands;
for band in start..end as usize {
let idx = base + band;
let log_before = band_log_e_fixed[idx];
let old = encoder.fixed_old_band_e[idx];
let err = encoder.fixed_energy_error[idx];
let diff = abs32(sub32(log_before, old));
let apply = diff < diff_limit;
if apply {
band_log_e_fixed[idx] = sub32(log_before, mult16_32_q15(quarter, err));
}
#[cfg(test)]
if let Some(frame_idx) = trace_loge_frame_idx {
let log_before_f = glog_from_fixed(log_before);
let old_f = glog_from_fixed(old);
let err_f = glog_from_fixed(err);
let diff_f = glog_from_fixed(diff);
let log_after_f = glog_from_fixed(band_log_e_fixed[idx]);
celt_loge_adjust_trace::dump_if_match(
frame_idx,
band,
channel,
log_before_f,
old_f,
err_f,
diff_f,
apply,
log_after_f,
);
}
}
}
sync_loge_from_fixed(&mut band_log_e, &band_log_e_fixed);
}
#[cfg(not(feature = "fixed_point"))]
{
for channel in 0..c {
let base = channel * nb_ebands;
for band in start..end as usize {
let idx = base + band;
let log_before = band_log_e[idx];
let old = encoder.old_band_e[idx];
let err = encoder.energy_error[idx];
let diff = (log_before - old).abs();
let apply = diff < 2.0;
if apply {
band_log_e[idx] = log_before - 0.25 * err;
}
#[cfg(test)]
if let Some(frame_idx) = trace_loge_frame_idx {
celt_loge_adjust_trace::dump_if_match(
frame_idx,
band,
channel,
log_before,
old,
err,
diff,
apply,
band_log_e[idx],
);
}
}
}
}
#[cfg(feature = "fixed_point")]
{
quant_coarse_energy_fixed(
mode,
start,
end as usize,
eff_end,
&band_log_e_fixed,
&mut encoder.fixed_old_band_e,
total_bits as u32,
&mut error_fixed,
enc,
c,
lm,
nb_available_bytes,
encoder.force_intra,
&mut encoder.fixed_delayed_intra,
encoder.complexity >= 4,
encoder.loss_rate,
encoder.lfe,
);
encoder.delayed_intra = glog_from_fixed(encoder.fixed_delayed_intra);
sync_loge_from_fixed(&mut encoder.old_band_e, &encoder.fixed_old_band_e);
}
#[cfg(not(feature = "fixed_point"))]
{
quant_coarse_energy(
mode,
start,
end as usize,
eff_end,
&band_log_e,
&mut encoder.old_band_e,
total_bits as u32,
&mut error,
enc,
c,
lm,
nb_available_bytes,
encoder.force_intra,
&mut encoder.delayed_intra,
encoder.complexity >= 4,
encoder.loss_rate,
encoder.lfe,
);
}
tf_encode(
start,
end as usize,
is_transient,
&mut tf_res,
lm,
tf_select,
enc,
);
if ec_tell(enc.ctx()) + 4 <= total_bits {
if encoder.lfe {
encoder.tapset_decision = 0;
encoder.spread_decision = SPREAD_NORMAL;
} else if hybrid {
encoder.spread_decision = if encoder.complexity == 0 {
SPREAD_NONE
} else if is_transient {
SPREAD_NORMAL
} else {
SPREAD_AGGRESSIVE
};
} else if short_blocks != 0 || encoder.complexity < 3 || nb_available_bytes < 10 * c as i32
{
encoder.spread_decision = if encoder.complexity == 0 {
SPREAD_NONE
} else {
SPREAD_NORMAL
};
} else {
encoder.spread_decision = spreading_decision(
mode,
&x,
&mut encoder.tonal_average,
encoder.spread_decision,
&mut encoder.hf_average,
&mut encoder.tapset_decision,
pf_on && short_blocks == 0,
eff_end,
c,
m,
&spread_weight,
);
}
enc.enc_icdf(encoder.spread_decision as usize, &SPREAD_ICDF, 5);
} else {
encoder.spread_decision = SPREAD_NORMAL;
}
if encoder.lfe && !offsets.is_empty() {
offsets[0] = min(8, effective_bytes / 3);
}
let mut cap = vec![0i32; nb_ebands];
init_caps(mode, &mut cap, lm, c);
let mut dynalloc_logp = 6i32;
total_bits <<= BITRES;
let mut total_boost = 0i32;
let mut tell_frac = ec_tell_frac(enc.ctx()) as i32;
#[cfg(test)]
let tell_frac_pre_dynalloc = tell_frac;
for band in start..end as usize {
let width = (c as i32 * (mode.e_bands[band + 1] - mode.e_bands[band]) as i32) << lm;
let quanta = min(width << BITRES, max((6 << BITRES) as i32, width));
let mut dynalloc_loop_logp = dynalloc_logp;
let mut boost = 0i32;
let mut j = 0i32;
while tell_frac + (dynalloc_loop_logp << BITRES) < total_bits - total_boost
&& boost < cap[band]
{
let flag = (j < offsets[band]) as i32;
enc.enc_bit_logp(flag, dynalloc_loop_logp as u32);
tell_frac = ec_tell_frac(enc.ctx()) as i32;
if flag == 0 {
break;
}
boost += quanta;
total_boost += quanta;
dynalloc_loop_logp = 1;
j += 1;
}
if j > 0 {
dynalloc_logp = max(2, dynalloc_logp - 1);
}
offsets[band] = boost;
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_dynalloc_bits[{frame_idx}].band[{band}].boost={boost}"
);
crate::test_trace::trace_println!(
"celt_dynalloc_bits[{frame_idx}].band[{band}].tell_frac={tell_frac}"
);
crate::test_trace::trace_println!(
"celt_dynalloc_bits[{frame_idx}].band[{band}].total_boost={total_boost}"
);
}
}
}
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_dynalloc_bits[{frame_idx}].tell_frac_pre={tell_frac_pre_dynalloc}"
);
crate::test_trace::trace_println!(
"celt_dynalloc_bits[{frame_idx}].tell_frac_post={tell_frac}"
);
crate::test_trace::trace_println!(
"celt_dynalloc_bits[{frame_idx}].total_boost={total_boost}"
);
}
}
let mut dual_stereo = 0;
if c == 2 {
if lm != 0 {
dual_stereo = stereo_analysis(mode, &x, lm, n) as i32;
}
static INTENSITY_THRESHOLDS: [OpusVal16; 21] = [
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 16.0, 24.0, 36.0, 44.0, 50.0, 56.0, 62.0, 67.0,
72.0, 79.0, 88.0, 106.0, 134.0,
];
static INTENSITY_HYSTERESIS: [OpusVal16; 21] = [
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 4.0,
5.0, 6.0, 8.0, 8.0,
];
let intensity = hysteresis_decision(
(equiv_rate / 1000) as f32,
&INTENSITY_THRESHOLDS,
&INTENSITY_HYSTERESIS,
encoder.intensity as usize,
);
encoder.intensity = intensity.min(end as usize).max(start) as i32;
}
let mut alloc_trim = 5;
if tell_frac + ((6 << BITRES) as i32) <= total_bits - total_boost {
if start > 0 || encoder.lfe {
encoder.stereo_saving = 0.0;
alloc_trim = 5;
} else {
alloc_trim = alloc_trim_analysis(
mode,
&x,
&band_log_e,
end as usize,
lm,
c,
n,
&encoder.analysis,
&mut encoder.stereo_saving,
tf_estimate,
encoder.intensity.max(0) as usize,
surround_trim,
equiv_rate,
encoder.arch,
);
}
enc.enc_icdf(alloc_trim as usize, &TRIM_ICDF, 7);
tell_frac = ec_tell_frac(enc.ctx()) as i32;
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
if celt_vbr_budget_trace::should_dump(frame_idx) {
crate::test_trace::trace_println!(
"celt_alloc_trim[{frame_idx}].tell_frac_post={tell_frac}"
);
crate::test_trace::trace_println!(
"celt_alloc_trim[{frame_idx}].total_boost={total_boost}"
);
}
}
}
if vbr_rate > 0 {
let lm_diff = mode.max_lm as i32 - lm as i32;
let lm_shift = lm_diff.max(0) as u32;
let mut base_target = if !hybrid {
vbr_rate - ((40 * c as i32 + 20) << BITRES)
} else {
max(0, vbr_rate - ((9 * c as i32 + 4) << BITRES))
};
if encoder.constrained_vbr {
base_target += encoder.vbr_offset >> lm_shift;
}
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
celt_vbr_budget_trace::dump_inputs_if_match(
frame_idx,
tell_frac,
tot_boost,
tf_estimate,
encoder.stereo_saving,
encoder.intensity,
encoder.last_coded_bands,
i32::from(pitch_change),
max_depth,
surround_masking,
temporal_vbr,
);
}
let mut target = if !hybrid {
compute_vbr(
mode,
&encoder.analysis,
base_target,
lm as i32,
equiv_rate,
encoder.last_coded_bands,
c,
encoder.intensity,
encoder.constrained_vbr,
encoder.stereo_saving,
tot_boost,
tf_estimate,
pitch_change,
max_depth,
encoder.lfe,
encoder.energy_mask.is_some(),
surround_masking,
temporal_vbr,
)
} else {
let mut target = base_target;
let frame_shift = 3u32.saturating_sub(lm as u32);
if encoder.silk_info.offset < 100 {
target += (12 << BITRES) >> frame_shift;
}
if encoder.silk_info.offset > 100 {
target -= (18 << BITRES) >> frame_shift;
}
target += ((tf_estimate - 0.25) * (50 << BITRES) as f32) as i32;
if tf_estimate > 0.7 {
target = max(target, 50 << BITRES);
}
target
};
target += tell_frac;
let mut min_allowed =
((tell_frac + total_boost + (1 << (BITRES + 3)) - 1) >> (BITRES + 3)) + 2;
if hybrid {
min_allowed = max(
min_allowed,
(tell0_frac as i32 + (37 << BITRES) + total_boost + (1 << (BITRES + 3)) - 1)
>> (BITRES + 3),
);
}
nb_available_bytes = (target + (1 << (BITRES + 2))) >> (BITRES + 3);
nb_available_bytes = max(min_allowed, nb_available_bytes);
nb_available_bytes = min(nb_compressed_bytes as i32, nb_available_bytes);
let mut delta = target - vbr_rate;
target = nb_available_bytes << (BITRES + 3);
if silence {
nb_available_bytes = 2;
target = (2 * 8) << BITRES;
delta = 0;
}
let alpha = if encoder.vbr_count < 970 {
encoder.vbr_count += 1;
celt_rcp((encoder.vbr_count + 20) as f32)
} else {
0.001
};
if encoder.constrained_vbr {
encoder.vbr_reservoir += target - vbr_rate;
let drift_scale = 1i32 << lm_shift;
encoder.vbr_drift += (alpha
* ((delta * drift_scale) - encoder.vbr_offset - encoder.vbr_drift) as f32)
as i32;
encoder.vbr_offset = -encoder.vbr_drift;
}
if encoder.constrained_vbr && encoder.vbr_reservoir < 0 {
let adjust = (-encoder.vbr_reservoir) / (8 << BITRES);
if !silence {
nb_available_bytes += adjust;
}
encoder.vbr_reservoir = 0;
}
nb_compressed_bytes = min(nb_compressed_bytes, nb_available_bytes as usize);
enc.enc_shrink(nb_compressed_bytes as OpusUint32);
#[cfg(test)]
if let Some(frame_idx) = trace_vbr_frame_idx {
celt_vbr_budget_trace::dump_if_match(
frame_idx,
"post_target",
use_external,
encoder.constrained_vbr,
vbr_rate,
encoder.vbr_reservoir,
encoder.vbr_offset,
encoder.vbr_drift,
nb_compressed_bytes,
nb_available_bytes,
nb_filled_bytes,
vbr_min_bytes,
vbr_max_allowed,
base_target,
target,
delta,
min_allowed,
tell,
ec_tell_frac(enc.ctx()) as i32,
);
}
}
let mut fine_quant = vec![0i32; nb_ebands];
let mut pulses = vec![0i32; nb_ebands];
let mut fine_priority = vec![0i32; nb_ebands];
let tell_frac = ec_tell_frac(enc.ctx());
let mut bits = ((nb_compressed_bytes as i32 * 8) << BITRES) - tell_frac as i32 - 1;
let anti_collapse_rsv = if is_transient && lm >= 2 && bits >= ((lm as i32 + 2) << BITRES) {
1 << BITRES
} else {
0
};
bits -= anti_collapse_rsv;
let mut signal_bandwidth = end as i32 - 1;
if encoder.analysis.valid {
let min_bandwidth = if equiv_rate < 32_000 * c as i32 {
13
} else if equiv_rate < 48_000 * c as i32 {
16
} else if equiv_rate < 60_000 * c as i32 {
18
} else if equiv_rate < 80_000 * c as i32 {
19
} else {
20
};
signal_bandwidth = max(encoder.analysis.bandwidth, min_bandwidth);
}
if encoder.lfe {
signal_bandwidth = 1;
}
#[cfg(test)]
if let Some(frame_idx) = trace_ctrl_frame_idx {
celt_ctrl_trace::dump_if_match(
frame_idx,
use_external,
header_bytes,
nb_compressed_bytes,
nb_filled_bytes,
nb_available_bytes,
effective_bytes,
vbr_rate,
equiv_rate,
total_bits,
tell0_frac,
tell,
tell_frac,
tf_estimate,
tf_chan,
is_transient,
short_blocks,
encoder.spread_decision,
encoder.intensity,
alloc_trim,
signal_bandwidth,
start,
end as usize,
bits,
);
}
let mut balance = 0;
let coded_bands = clt_compute_allocation(
mode,
start,
end as usize,
&offsets,
&cap,
alloc_trim,
&mut encoder.intensity,
&mut dual_stereo,
bits,
&mut balance,
&mut pulses,
&mut fine_quant,
&mut fine_priority,
c as i32,
lm as i32,
Some(enc),
None,
encoder.last_coded_bands,
signal_bandwidth,
);
#[cfg(test)]
if let Some(frame_idx) = trace_frame_idx {
celt_alloc_trace::dump_if_match(
frame_idx,
use_external,
header_bytes,
nb_compressed_bytes,
tell0_frac,
tell,
nb_filled_bytes,
tell_frac,
start,
end as usize,
bits,
coded_bands,
balance,
encoder.intensity,
dual_stereo,
&pulses,
&fine_quant,
&fine_priority,
);
}
if encoder.last_coded_bands != 0 {
encoder.last_coded_bands = min(
encoder.last_coded_bands + 1,
max(encoder.last_coded_bands - 1, coded_bands),
);
} else {
encoder.last_coded_bands = coded_bands;
}
#[cfg(test)]
if let Some(frame_idx) = trace_rc_frame_idx {
celt_rc_trace::dump_if_match(frame_idx, "pre_quant_fine_energy", enc.ctx());
}
#[cfg(feature = "fixed_point")]
{
quant_fine_energy_fixed(
mode,
start,
end as usize,
&mut encoder.fixed_old_band_e,
&mut error_fixed,
&fine_quant,
enc,
c,
);
sync_loge_from_fixed(&mut encoder.old_band_e, &encoder.fixed_old_band_e);
}
#[cfg(not(feature = "fixed_point"))]
{
quant_fine_energy(
mode,
start,
end as usize,
&mut encoder.old_band_e,
&mut error,
&fine_quant,
enc,
c,
);
}
#[cfg(test)]
if let Some(frame_idx) = trace_rc_frame_idx {
celt_rc_trace::dump_if_match(frame_idx, "pre_quant_all_bands", enc.ctx());
}
let mut collapse_masks = vec![0u8; c * nb_ebands];
let total_available = (nb_compressed_bytes as i32 * (8 << BITRES)) - anti_collapse_rsv;
let (x0, x1) = if c == 2 {
let (left, right) = x.split_at_mut(n);
(left, Some(right))
} else {
(&mut x[..], None)
};
{
let mut coder = BandCodingState::Encoder(enc);
quant_all_bands(
true,
mode,
start,
end as usize,
x0,
x1,
&mut collapse_masks,
&band_e,
&pulses,
short_blocks != 0,
encoder.spread_decision,
dual_stereo != 0,
encoder.intensity.max(0) as usize,
&tf_res,
total_available,
balance,
&mut coder,
lm as i32,
coded_bands.max(0) as usize,
&mut encoder.rng,
encoder.complexity,
encoder.arch,
encoder.disable_inv,
);
}
#[cfg(test)]
if let Some(frame_idx) = trace_rc_frame_idx {
celt_rc_trace::dump_if_match(frame_idx, "post_quant", enc.ctx());
}
if anti_collapse_rsv > 0 {
let anti_collapse_on = encoder.consec_transient < 2;
enc.enc_bits(anti_collapse_on as u32, 1);
}
#[cfg(test)]
if let Some(frame_idx) = trace_rc_frame_idx {
celt_rc_trace::dump_if_match(frame_idx, "post_anticollapse", enc.ctx());
}
let remaining_bits = nb_compressed_bytes as i32 * 8 - ec_tell(enc.ctx());
#[cfg(feature = "fixed_point")]
{
quant_energy_finalise_fixed(
mode,
start,
end as usize,
&mut encoder.fixed_old_band_e,
&mut error_fixed,
&fine_quant,
&fine_priority,
remaining_bits,
enc,
c,
);
sync_loge_from_fixed(&mut encoder.old_band_e, &encoder.fixed_old_band_e);
}
#[cfg(not(feature = "fixed_point"))]
{
quant_energy_finalise(
mode,
start,
end as usize,
&mut encoder.old_band_e,
&mut error,
&fine_quant,
&fine_priority,
remaining_bits,
enc,
c,
);
}
#[cfg(test)]
if let Some(frame_idx) = trace_rc_frame_idx {
celt_rc_trace::dump_if_match(frame_idx, "post_fine_energy", enc.ctx());
}
#[cfg(feature = "fixed_point")]
{
encoder.fixed_energy_error.fill(0);
let clamp = glog_to_fixed(0.5);
for channel in 0..c {
let base = channel * nb_ebands;
for band in start..end as usize {
let idx = base + band;
let err = error_fixed[idx];
let clamped = err.clamp(-clamp, clamp);
encoder.fixed_energy_error[idx] = clamped;
}
}
sync_loge_from_fixed(&mut encoder.energy_error, &encoder.fixed_energy_error);
}
#[cfg(not(feature = "fixed_point"))]
{
encoder.energy_error.fill(0.0);
for channel in 0..c {
let base = channel * nb_ebands;
for band in start..end as usize {
let idx = base + band;
encoder.energy_error[idx] = error[idx].clamp(-0.5, 0.5);
}
}
}
if silence {
#[cfg(feature = "fixed_point")]
{
let reset = glog_to_fixed(-28.0);
for value in encoder.fixed_old_band_e.iter_mut().take(c * nb_ebands) {
*value = reset;
}
sync_loge_from_fixed(&mut encoder.old_band_e, &encoder.fixed_old_band_e);
}
#[cfg(not(feature = "fixed_point"))]
{
for value in encoder.old_band_e.iter_mut().take(c * nb_ebands) {
*value = -28.0;
}
}
}
encoder.prefilter_period = pitch_index;
encoder.prefilter_gain = gain1;
encoder.prefilter_tapset = prefilter_tapset;
#[cfg(feature = "fixed_point")]
{
encoder.fixed_prefilter_gain = gain1_fixed;
}
if cc == 2 && c == 1 {
#[cfg(feature = "fixed_point")]
{
let (left, right) = encoder.fixed_old_band_e.split_at_mut(nb_ebands);
right[..nb_ebands].copy_from_slice(&left[..nb_ebands]);
sync_loge_from_fixed(&mut encoder.old_band_e, &encoder.fixed_old_band_e);
}
#[cfg(not(feature = "fixed_point"))]
{
let (left, right) = encoder.old_band_e.split_at_mut(nb_ebands);
right[..nb_ebands].copy_from_slice(&left[..nb_ebands]);
}
}
if !is_transient {
let span = cc * nb_ebands;
encoder.old_log_e2[..span].copy_from_slice(&encoder.old_log_e[..span]);
encoder.old_log_e[..span].copy_from_slice(&encoder.old_band_e[..span]);
} else {
for idx in 0..cc * nb_ebands {
encoder.old_log_e[idx] = encoder.old_log_e[idx].min(encoder.old_band_e[idx]);
}
}
for channel in 0..cc {
let base = channel * nb_ebands;
for band in 0..start {
encoder.old_band_e[base + band] = 0.0;
encoder.old_log_e[base + band] = -28.0;
encoder.old_log_e2[base + band] = -28.0;
}
for band in end as usize..nb_ebands {
encoder.old_band_e[base + band] = 0.0;
encoder.old_log_e[base + band] = -28.0;
encoder.old_log_e2[base + band] = -28.0;
}
}
if is_transient || transient_got_disabled {
encoder.consec_transient += 1;
} else {
encoder.consec_transient = 0;
}
encoder.rng = enc.ctx().rng;
enc.enc_done();
if enc.ctx().error != 0 {
return Err(CeltEncodeError::MissingOutput);
}
Ok(nb_compressed_bytes + header_bytes)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn celt_encode_with_ec(
encoder: &mut OpusCustomEncoder<'_>,
pcm: Option<&[CeltSig]>,
frame_size: usize,
compressed: Option<&mut [u8]>,
range_encoder: Option<&mut EcEnc<'_>>,
) -> Result<usize, CeltEncodeError> {
let pcm = pcm.ok_or(CeltEncodeError::InsufficientPcm)?;
let mode = encoder.mode;
let cc = encoder.channels;
let c = encoder.stream_channels;
debug_assert!(cc > 0 && cc <= MAX_CHANNELS);
debug_assert!(c > 0 && c <= cc);
let start = encoder.start_band.max(0) as usize;
let mut end = encoder.end_band;
let hybrid = start != 0;
let upsample = encoder.upsample.max(1) as usize;
let frame_size_internal = frame_size
.checked_mul(upsample)
.ok_or(CeltEncodeError::InvalidFrameSize)?;
let mut lm = None;
let mut current_size = mode.short_mdct_size;
for cand in 0..=mode.max_lm {
if current_size == frame_size_internal {
lm = Some(cand);
break;
}
current_size <<= 1;
}
let lm = lm.ok_or(CeltEncodeError::InvalidFrameSize)?;
let m = 1usize << lm;
let n = m * mode.short_mdct_size;
let sample_count = frame_size_internal / upsample;
if pcm.len() < cc * sample_count {
return Err(CeltEncodeError::InsufficientPcm);
}
let use_external = range_encoder.is_some();
let mut output_buf = compressed;
let mut nb_compressed_bytes = if let Some(enc) = range_encoder.as_ref() {
enc.ctx().storage as usize
} else {
output_buf.as_ref().map(|buf| buf.len()).unwrap_or(0)
};
if nb_compressed_bytes < 2 {
return Err(CeltEncodeError::MissingOutput);
}
let mut header_bytes = 0usize;
if !use_external && encoder.signalling != 0 {
let buf = output_buf.take().ok_or(CeltEncodeError::MissingOutput)?;
let tmp = ((mode.effective_ebands as i32 - end) >> 1).max(0);
end = (mode.effective_ebands as i32 - tmp).max(1);
encoder.end_band = end;
let mut header = ((tmp as u8) << 5) | ((lm as u8) << 3) | (((c == 2) as u8) << 2);
if mode.sample_rate == 48_000 && mode.short_mdct_size == 120 {
header = to_opus(header).ok_or(CeltEncodeError::InvalidFrameSize)?;
}
let (slot, rest) = buf.split_at_mut(1);
slot[0] = header;
output_buf = Some(rest);
header_bytes = 1;
nb_compressed_bytes = nb_compressed_bytes.saturating_sub(1);
}
nb_compressed_bytes = nb_compressed_bytes.min(1275);
if let Some(enc) = range_encoder {
return celt_encode_with_ec_inner(
encoder,
pcm,
enc,
use_external,
header_bytes,
nb_compressed_bytes,
frame_size_internal,
upsample,
lm,
m,
n,
start,
end,
hybrid,
);
}
let buf = output_buf.ok_or(CeltEncodeError::MissingOutput)?;
let mut local_enc = EcEnc::new(buf);
celt_encode_with_ec_inner(
encoder,
pcm,
&mut local_enc,
use_external,
header_bytes,
nb_compressed_bytes,
frame_size_internal,
upsample,
lm,
m,
n,
start,
end,
hybrid,
)
}
fn required_pcm_samples(channels: usize, frame_size: usize) -> Result<usize, CeltEncodeError> {
channels
.checked_mul(frame_size)
.ok_or(CeltEncodeError::InsufficientPcm)
}
fn celt_maxabs_res(samples: &[OpusRes]) -> OpusRes {
celt_maxabs16(samples)
}
pub(crate) fn convert_i16_to_celt_sig(pcm: &[OpusInt16], required: usize) -> Vec<CeltSig> {
let scale = 1.0 / CELT_SIG_SCALE;
pcm.iter()
.take(required)
.map(|&sample| (sample as CeltSig) * scale)
.collect()
}
fn convert_i24_to_celt_sig(pcm: &[OpusInt32], required: usize) -> Vec<CeltSig> {
let scale = 1.0 / (CELT_SIG_SCALE * 256.0);
pcm.iter()
.take(required)
.map(|&sample| (sample as CeltSig) * scale)
.collect()
}
fn convert_f32_to_celt_sig(pcm: &[f32], required: usize) -> Vec<CeltSig> {
pcm.iter().take(required).copied().collect()
}
fn encode_with_converted_pcm(
encoder: &mut OpusCustomEncoder<'_>,
pcm: &[CeltSig],
frame_size: usize,
compressed: &mut [u8],
nb_compressed_bytes: usize,
) -> Result<usize, CeltEncodeError> {
if nb_compressed_bytes > compressed.len() {
return Err(CeltEncodeError::MissingOutput);
}
if nb_compressed_bytes < 2 {
return Err(CeltEncodeError::MissingOutput);
}
celt_encode_with_ec(
encoder,
Some(pcm),
frame_size,
Some(&mut compressed[..nb_compressed_bytes]),
None,
)
}
pub(crate) fn opus_custom_encode(
encoder: &mut OpusCustomEncoder<'_>,
pcm: &[OpusInt16],
frame_size: usize,
compressed: &mut [u8],
nb_compressed_bytes: usize,
) -> Result<usize, CeltEncodeError> {
let required = required_pcm_samples(encoder.channels, frame_size)?;
if pcm.len() < required {
return Err(CeltEncodeError::InsufficientPcm);
}
let converted = convert_i16_to_celt_sig(pcm, required);
encode_with_converted_pcm(
encoder,
&converted,
frame_size,
compressed,
nb_compressed_bytes,
)
}
pub(crate) fn opus_custom_encode24(
encoder: &mut OpusCustomEncoder<'_>,
pcm: &[OpusInt32],
frame_size: usize,
compressed: &mut [u8],
nb_compressed_bytes: usize,
) -> Result<usize, CeltEncodeError> {
let required = required_pcm_samples(encoder.channels, frame_size)?;
if pcm.len() < required {
return Err(CeltEncodeError::InsufficientPcm);
}
let converted = convert_i24_to_celt_sig(pcm, required);
encode_with_converted_pcm(
encoder,
&converted,
frame_size,
compressed,
nb_compressed_bytes,
)
}
pub(crate) fn opus_custom_encode_float(
encoder: &mut OpusCustomEncoder<'_>,
pcm: &[f32],
frame_size: usize,
compressed: &mut [u8],
nb_compressed_bytes: usize,
) -> Result<usize, CeltEncodeError> {
let required = required_pcm_samples(encoder.channels, frame_size)?;
if pcm.len() < required {
return Err(CeltEncodeError::InsufficientPcm);
}
let converted = convert_f32_to_celt_sig(pcm, required);
encode_with_converted_pcm(
encoder,
&converted,
frame_size,
compressed,
nb_compressed_bytes,
)
}
fn median_of_5(values: &[CeltGlog]) -> CeltGlog {
debug_assert!(values.len() >= 5);
let (mut t0, mut t1) = if values[0] > values[1] {
(values[1], values[0])
} else {
(values[0], values[1])
};
let t2 = values[2];
let (mut t3, mut t4) = if values[3] > values[4] {
(values[4], values[3])
} else {
(values[3], values[4])
};
if t0 > t3 {
core::mem::swap(&mut t0, &mut t3);
core::mem::swap(&mut t1, &mut t4);
}
if t2 > t1 {
if t1 < t3 { t2.min(t3) } else { t4.min(t1) }
} else if t2 < t3 {
t1.min(t3)
} else {
t2.min(t4)
}
}
pub(crate) fn tone_lpc(x: &[OpusVal16], delay: usize, lpc: &mut [OpusVal32; 2]) -> bool {
let len = x.len();
assert!(len > 2 * delay, "tone_lpc requires len > 2 * delay");
let mut r00 = 0.0f32;
let mut r01 = 0.0f32;
let mut r02 = 0.0f32;
let limit = len - 2 * delay;
for i in 0..limit {
let xi = x[i];
r00 += xi * xi;
r01 += xi * x[i + delay];
r02 += xi * x[i + 2 * delay];
}
let mut edges = 0.0f32;
let tail2_base = len - 2 * delay;
for i in 0..delay {
let tail = x[tail2_base + i];
let head = x[i];
edges += tail * tail - head * head;
}
let mut r11 = r00 + edges;
edges = 0.0;
let tail1_base = len - delay;
for i in 0..delay {
let tail = x[tail1_base + i];
let head = x[i + delay];
edges += tail * tail - head * head;
}
let r22 = r11 + edges;
edges = 0.0;
for i in 0..delay {
let head0 = x[i];
let head1 = x[i + delay];
let tail0 = x[tail2_base + i];
let tail1 = x[tail1_base + i];
edges += tail0 * tail1 - head0 * head1;
}
let mut r12 = r01 + edges;
let r00_total = r00 + r22;
let r01_total = r01 + r12;
let r11_total = 2.0 * r11;
let r02_total = 2.0 * r02;
let r12_total = r12 + r01;
r00 = r00_total;
r01 = r01_total;
r11 = r11_total;
r02 = r02_total;
r12 = r12_total;
let den = (r00 * r11) - (r01 * r01);
if den < 0.001 * (r00 * r11) {
return true;
}
let num1 = (r02 * r11) - (r01 * r12);
if num1 >= den {
lpc[1] = 1.0;
} else if num1 <= -den {
lpc[1] = -1.0;
} else {
lpc[1] = frac_div32_q29(num1, den);
}
let num0 = (r00 * r12) - (r02 * r01);
if 0.5 * num0 >= den {
lpc[0] = 1.999_999;
} else if 0.5 * num0 <= -den {
lpc[0] = -1.999_999;
} else {
lpc[0] = frac_div32_q29(num0, den);
}
false
}
pub(crate) fn tone_detect(
input: &[CeltSig],
channels: usize,
n: usize,
toneishness: &mut OpusVal32,
fs: OpusInt32,
) -> OpusVal16 {
debug_assert!(channels == 1 || channels == 2);
debug_assert!(n > 0);
debug_assert!(input.len() >= channels * n);
let mut workspace = vec![0.0f32; n];
if channels == 2 {
let stride = n;
for i in 0..n {
workspace[i] = input[i] + input[stride + i];
}
} else {
workspace.copy_from_slice(&input[..n]);
}
normalize_tone_input(&mut workspace);
let mut lpc = [0.0f32; 2];
let mut delay = 1usize;
let mut fail = tone_lpc(&workspace, delay, &mut lpc);
let mut max_delay = fs.max(0) as usize / 3000;
if max_delay == 0 {
max_delay = 1;
}
while delay <= max_delay && (fail || (lpc[0] > 1.0 && lpc[1] < 0.0)) {
delay *= 2;
if 2 * delay >= n {
fail = true;
break;
}
fail = tone_lpc(&workspace, delay, &mut lpc);
}
if !fail && (lpc[0] * lpc[0] + 3.999_999 * lpc[1]) < 0.0 {
*toneishness = -lpc[1];
let angle = {
#[cfg(feature = "fixed_point")]
{
acos_approx(0.5 * lpc[0])
}
#[cfg(not(feature = "fixed_point"))]
{
acosf(0.5 * lpc[0])
}
};
(angle / delay as OpusVal32) as OpusVal16
} else {
*toneishness = 0.0;
-1.0
}
}
#[cfg(feature = "fixed_point")]
pub(crate) fn normalize_tone_input(x: &mut [OpusVal16]) {
if x.is_empty() {
return;
}
let mut ac0: OpusInt32 = x.len() as OpusInt32;
for &sample in x.iter() {
let sample16: OpusInt32 = sample as i16 as OpusInt32;
ac0 = ac0.wrapping_add((sample16 * sample16) >> 10);
}
let shift = 5 - ((28 - celt_ilog2(ac0)) >> 1);
if shift > 0 {
let bias = 1 << (shift - 1);
for sample in x.iter_mut() {
let value: OpusInt32 = (*sample) as OpusInt32;
let scaled = (value + bias) >> shift;
*sample = scaled as OpusVal16;
}
}
}
#[cfg(not(feature = "fixed_point"))]
pub(crate) fn normalize_tone_input(_x: &mut [OpusVal16]) {}
#[cfg(feature = "fixed_point")]
pub(crate) fn acos_approx(mut x: OpusVal32) -> OpusVal32 {
let flip = x < 0.0;
if flip {
x = -x;
}
let x_q29: i32 = (x.clamp(0.0, 1.0) * (1u32 << 29) as f32) as i32;
let x14: i32 = x_q29 >> 15; let mut tmp: i32 = ((762 * x14) >> 14) - 3_308;
tmp = ((tmp * x14) >> 14) + 25_726;
let radicand: i32 = max(0, (1 << 30) - (x_q29 << 1)); tmp = (tmp * celt_sqrt_fixed(radicand)) >> 16;
let tmp_q14 = if flip { 25_736 - tmp } else { tmp };
tmp_q14 as f32 / 16_384.0
}
#[cfg(not(feature = "fixed_point"))]
pub(crate) fn acos_approx(x: OpusVal32) -> OpusVal32 {
acosf(x.clamp(-1.0, 1.0))
}
fn median_of_3(values: &[CeltGlog]) -> CeltGlog {
debug_assert!(values.len() >= 3);
let (t0, t1) = if values[0] > values[1] {
(values[1], values[0])
} else {
(values[0], values[1])
};
let t2 = values[2];
if t1 < t2 {
t1
} else if t0 < t2 {
t2
} else {
t0
}
}
#[cfg(test)]
mod tests {
extern crate std;
#[cfg(feature = "fixed_point")]
use super::celt_preemphasis_fixed;
use super::{
COMBFILTER_MAXPERIOD, CeltEncodeError, CeltEncoderAlloc, CeltEncoderInitError, CeltSig,
EncoderCtlRequest, MAX_CHANNELS, OPUS_BITRATE_MAX, OpusCustomEncoder, OpusCustomMode,
OpusUint32, PREEMPHASIS_CLIP_LIMIT, celt_encoder_init, celt_preemphasis,
convert_f32_to_celt_sig, convert_i16_to_celt_sig, convert_i24_to_celt_sig,
opus_custom_encode, opus_custom_encoder_destroy, opus_custom_encoder_init,
opus_custom_encoder_init_arch,
};
use super::{
CeltEncoderCtlError, alloc_trim_analysis, compute_mdcts, compute_vbr, dynalloc_analysis,
l1_metric, median_of_3, median_of_5, opus_custom_encoder_ctl, patch_transient_decision,
stereo_analysis, tf_analysis, tf_encode, tone_detect, tone_lpc, transient_analysis,
};
#[cfg(not(feature = "fixed_point"))]
use super::{acos_approx, normalize_tone_input};
use crate::celt::OpusVal16;
use crate::celt::celt::TF_SELECT_TABLE;
use crate::celt::cpu_support::opus_select_arch;
use crate::celt::entenc::EcEnc;
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_arch::DB_SHIFT;
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_arch::float2sig;
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_ops::qconst32;
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_ops::{mult16_32_q15, qconst16, sub32};
use crate::celt::float_cast::CELT_SIG_SCALE;
use crate::celt::math::celt_log2;
use crate::celt::modes::{
compute_preemphasis, opus_custom_mode_create, opus_custom_mode_find_static,
};
#[cfg(feature = "fixed_point")]
use crate::celt::types::FixedCeltSig;
use crate::celt::types::{AnalysisInfo, OpusCustomDecoder, OpusRes};
use crate::celt::vq::SPREAD_NORMAL;
use alloc::format;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::f32::consts::{FRAC_1_SQRT_2, PI};
use libm::floorf;
use libm::sinf;
use std::env;
const EPSILON: f32 = 1e-6;
fn assert_slice_close(actual: &[CeltSig], expected: &[CeltSig]) {
assert_eq!(actual.len(), expected.len());
for (index, (a, b)) in actual.iter().zip(expected).enumerate() {
assert!(
(a - b).abs() < EPSILON,
"mismatch at index {index}: {a} vs {b}"
);
}
}
#[test]
fn l1_metric_matches_reference_bias() {
let tmp: [OpusVal16; 4] = [1.0, -2.0, 0.5, -0.25];
let unbiased = l1_metric(&tmp, tmp.len(), 0, 0.125);
assert!((unbiased - 3.75).abs() < EPSILON);
let biased = l1_metric(&tmp, tmp.len(), 2, 0.5);
assert!((biased - 7.5).abs() < EPSILON);
}
#[test]
fn tf_analysis_prefers_frequency_resolution_for_flat_spectrum() {
let mode = opus_custom_mode_find_static(48_000, 960).expect("static mode");
let lm = 0;
let len = 4;
let n0 = (mode.e_bands[len] as usize) << lm;
let x = vec![0.0; n0];
let mut tf_res = vec![1; len];
let importance = vec![1; len];
let tf_select = tf_analysis(
&mode,
len,
false,
&mut tf_res,
100,
&x,
n0,
lm,
0.0,
0,
&importance,
);
assert_eq!(tf_select, 0);
assert!(tf_res.iter().take(len).all(|&value| value == 0));
}
#[test]
fn tf_analysis_enables_tf_select_for_transient_pattern() {
let mode = opus_custom_mode_find_static(48_000, 960).expect("static mode");
let lm = 1;
let len = 9;
let n0 = (mode.e_bands[len] as usize) << lm;
let mut x = vec![0.0; n0];
let pattern = [-9.119_444, -7.347_349, 9.822_017, -6.768_198];
let start = (mode.e_bands[8] as usize) << lm;
x[start..start + pattern.len()].copy_from_slice(&pattern);
let mut tf_res = vec![0; len];
let importance = vec![1; len];
let tf_select = tf_analysis(
&mode,
len,
true,
&mut tf_res,
80,
&x,
n0,
lm,
0.0,
0,
&importance,
);
assert_eq!(tf_select, 1);
assert_eq!(tf_res[8], 1);
}
#[test]
fn compute_mdcts_matches_manual_mdct() {
let mode = opus_custom_mode_find_static(48_000, 960).expect("static mode");
let short_blocks = 0;
let lm = 0;
let upsample = 1;
let total_channels = 2;
let coded_channels = 2;
let block_count = 1;
let shift = mode.max_lm - lm;
let transform_len = mode.mdct.effective_len(shift);
let frame_len = transform_len >> 1;
let overlap = mode.overlap;
let channel_input_stride = block_count * frame_len + overlap;
let channel_output_stride = block_count * frame_len;
let mut input = vec![0.0; total_channels * channel_input_stride];
for (index, sample) in input.iter_mut().enumerate() {
*sample = index as f32;
}
let mut expected = vec![0.0; total_channels * channel_output_stride];
for channel in 0..total_channels {
let input_offset = channel * channel_input_stride;
let output_offset = channel * channel_output_stride;
crate::celt::mdct::clt_mdct_forward(
&mode.mdct,
&input[input_offset..input_offset + overlap + frame_len],
&mut expected[output_offset..output_offset + channel_output_stride],
mode.window,
overlap,
shift,
block_count,
);
}
let mut output = vec![0.0; total_channels * channel_output_stride];
compute_mdcts(
&mode,
short_blocks,
&input,
&mut output,
coded_channels,
total_channels,
lm,
upsample,
0,
);
assert_slice_close(&output, &expected);
}
#[test]
fn compute_mdcts_downmixes_stereo() {
let mode = opus_custom_mode_find_static(48_000, 960).expect("static mode");
let short_blocks = 0;
let lm = 0;
let upsample = 1;
let total_channels = 2;
let block_count = 1;
let shift = mode.max_lm - lm;
let transform_len = mode.mdct.effective_len(shift);
let frame_len = transform_len >> 1;
let overlap = mode.overlap;
let channel_input_stride = block_count * frame_len + overlap;
let channel_output_stride = block_count * frame_len;
let mut input = vec![0.0; total_channels * channel_input_stride];
for (index, sample) in input.iter_mut().enumerate() {
*sample = (index as f32) / 5.0;
}
let mut stereo = vec![0.0; total_channels * channel_output_stride];
compute_mdcts(
&mode,
short_blocks,
&input,
&mut stereo,
total_channels,
total_channels,
lm,
upsample,
0,
);
let mut mono = vec![0.0; total_channels * channel_output_stride];
compute_mdcts(
&mode,
short_blocks,
&input,
&mut mono,
1,
total_channels,
lm,
upsample,
0,
);
for i in 0..block_count * frame_len {
let expected = 0.5 * (stereo[i] + stereo[block_count * frame_len + i]);
assert!((mono[i] - expected).abs() < EPSILON);
}
}
#[test]
fn compute_mdcts_scales_for_upsampling() {
let mode = opus_custom_mode_find_static(48_000, 960).expect("static mode");
let short_blocks = 0;
let lm = 0;
let total_channels = 1;
let block_count = 1;
let shift = mode.max_lm - lm;
let transform_len = mode.mdct.effective_len(shift);
let frame_len = transform_len >> 1;
let overlap = mode.overlap;
let channel_input_stride = block_count * frame_len + overlap;
let channel_output_stride = block_count * frame_len;
let mut input = vec![0.0; total_channels * channel_input_stride];
for (index, sample) in input.iter_mut().enumerate() {
*sample = (index as f32) / 7.0;
}
let mut baseline = vec![0.0; total_channels * channel_output_stride];
compute_mdcts(
&mode,
short_blocks,
&input,
&mut baseline,
total_channels,
total_channels,
lm,
1,
0,
);
let upsample = 2;
let mut scaled = vec![0.0; total_channels * channel_output_stride];
compute_mdcts(
&mode,
short_blocks,
&input,
&mut scaled,
total_channels,
total_channels,
lm,
upsample,
0,
);
let bound = block_count * frame_len / upsample;
for i in 0..bound {
assert!((scaled[i] - baseline[i] * upsample as f32).abs() < EPSILON);
}
for value in &scaled[bound..block_count * frame_len] {
assert!(value.abs() < EPSILON);
}
}
#[test]
fn tf_encode_applies_select_when_budget_allows() {
let mut buffer = [0u8; 16];
let mut enc = EcEnc::new(&mut buffer);
let mut tf_res = [0, 1, 1, 0];
tf_encode(0, tf_res.len(), false, &mut tf_res, 1, 1, &mut enc);
let expected = [
i32::from(TF_SELECT_TABLE[1][2]),
i32::from(TF_SELECT_TABLE[1][3]),
i32::from(TF_SELECT_TABLE[1][3]),
i32::from(TF_SELECT_TABLE[1][2]),
];
assert_eq!(tf_res, expected);
}
#[test]
fn tf_encode_clamps_to_previous_when_budget_is_exhausted() {
let mut buffer = [0u8; 0];
let mut enc = EcEnc::new(&mut buffer);
let mut tf_res = [1, 0];
tf_encode(0, tf_res.len(), false, &mut tf_res, 0, 1, &mut enc);
assert_eq!(tf_res, [0, 0]);
}
#[test]
fn tf_encode_accepts_empty_band_ranges() {
let mut buffer = [0u8; 16];
let mut enc = EcEnc::new(&mut buffer);
let mut tf_res = [0, 1, 0];
let expected = tf_res;
tf_encode(2, 1, false, &mut tf_res, 1, 1, &mut enc);
assert_eq!(tf_res, expected);
}
#[test]
fn celt_preemphasis_fast_path_matches_reference() {
let coef = compute_preemphasis(48_000);
let pcm: [OpusRes; 4] = [0.0, 0.25, -0.5, 1.0];
let n = pcm.len();
let mut output = vec![0.0; n];
let mut expected = vec![0.0; n];
let mut state = 0.0;
let mut expected_state = state;
for i in 0..n {
let x = pcm[i] * CELT_SIG_SCALE;
expected[i] = x - expected_state;
expected_state = coef[0] * x;
}
celt_preemphasis(&pcm, &mut output, n, 1, 1, &coef, &mut state, false);
assert_slice_close(&output, &expected);
assert!((state - expected_state).abs() < EPSILON);
}
#[test]
fn celt_preemphasis_handles_upsampling_and_clipping() {
let coef = compute_preemphasis(48_000);
let n = 6;
let upsample = 2;
let channels = 2;
let pcm: [OpusRes; 6] = [1.0, 3.5, -2.0, -4.0, 0.25, -0.75];
let pcmp = &pcm[1..];
let mut output = vec![42.0; n];
let mut expected = vec![0.0; n];
let mut state = 123.0;
let mut expected_state = state;
let nu = n / upsample;
expected.fill(0.0);
for i in 0..nu {
let sample = pcmp[channels * i];
expected[i * upsample] = sample * CELT_SIG_SCALE;
}
for i in 0..nu {
let index = i * upsample;
expected[index] =
expected[index].clamp(-PREEMPHASIS_CLIP_LIMIT, PREEMPHASIS_CLIP_LIMIT);
}
for value in &mut expected[..n] {
let x = *value;
*value = x - expected_state;
expected_state = coef[0] * x;
}
celt_preemphasis(
pcmp,
&mut output,
n,
channels,
upsample,
&coef,
&mut state,
true,
);
assert_slice_close(&output, &expected);
assert!((state - expected_state).abs() < EPSILON);
}
#[test]
fn celt_preemphasis_three_tap_path_matches_reference() {
let coef = compute_preemphasis(16_000);
let pcm: [OpusRes; 5] = [0.5, -0.25, 0.75, -0.5, 0.0];
let n = pcm.len();
let mut output = vec![0.0; n];
let mut expected = vec![0.0; n];
let mut state = -321.0;
let mut expected_state = state;
for i in 0..n {
expected[i] = pcm[i] * CELT_SIG_SCALE;
}
for value in &mut expected {
let x = *value;
let tmp = coef[2] * x;
*value = tmp + expected_state;
expected_state = coef[1] * *value - coef[0] * tmp;
}
celt_preemphasis(&pcm, &mut output, n, 1, 1, &coef, &mut state, false);
assert_slice_close(&output, &expected);
assert!((state - expected_state).abs() < EPSILON);
}
#[cfg(feature = "fixed_point")]
fn pcm_from_i16(values: &[i16]) -> Vec<f32> {
values
.iter()
.map(|&value| value as f32 / CELT_SIG_SCALE)
.collect()
}
#[cfg(feature = "fixed_point")]
fn preemphasis_reference_fixed(
pcmp: &[CeltSig],
inp: &mut [FixedCeltSig],
n: usize,
channels: usize,
upsample: usize,
coef: &[OpusVal16; 4],
mem: &mut FixedCeltSig,
clip: bool,
) {
let coef0 = qconst16(f64::from(coef[0]), 15);
let mut m = *mem;
if coef[1] == 0.0 && upsample == 1 && !clip {
for i in 0..n {
let x = float2sig(pcmp[channels * i]);
inp[i] = sub32(x, m);
m = mult16_32_q15(coef0, x);
}
*mem = m;
return;
}
let nu = n / upsample;
if upsample != 1 {
inp[..n].fill(0);
}
for i in 0..nu {
inp[i * upsample] = float2sig(pcmp[channels * i]);
}
for value in &mut inp[..n] {
let x = *value;
*value = sub32(x, m);
m = mult16_32_q15(coef0, x);
}
*mem = m;
}
#[cfg(feature = "fixed_point")]
#[test]
fn celt_preemphasis_fixed_fast_path_matches_reference() {
let pcm_values: [i16; 8] = [1000, -2000, 1500, -500, 700, -900, 300, -100];
let pcm = pcm_from_i16(&pcm_values);
let n = pcm_values.len();
let mut output = vec![0; n];
let mut expected = vec![0; n];
let coef: [OpusVal16; 4] = [0.85, 0.0, 0.0, 0.0];
let mut state: FixedCeltSig = 1234;
let mut expected_state = state;
celt_preemphasis_fixed(&pcm, &mut output, n, 1, 1, &coef, &mut state, false);
preemphasis_reference_fixed(
&pcm,
&mut expected,
n,
1,
1,
&coef,
&mut expected_state,
false,
);
assert_eq!(output, expected);
assert_eq!(state, expected_state);
}
#[cfg(feature = "fixed_point")]
#[test]
fn celt_preemphasis_fixed_upsample_matches_reference() {
let pcm_values: [i16; 5] = [1200, -800, 600, -400, 200];
let pcm = pcm_from_i16(&pcm_values);
let n = 10;
let mut output = vec![0; n];
let mut expected = vec![0; n];
let coef: [OpusVal16; 4] = [0.6, 0.0, 0.0, 0.0];
let mut state: FixedCeltSig = -2222;
let mut expected_state = state;
celt_preemphasis_fixed(&pcm, &mut output, n, 1, 2, &coef, &mut state, true);
preemphasis_reference_fixed(
&pcm,
&mut expected,
n,
1,
2,
&coef,
&mut expected_state,
true,
);
assert_eq!(output, expected);
assert_eq!(state, expected_state);
}
fn get_lsb_depth(encoder: &mut OpusCustomEncoder<'_>) -> i32 {
let mut value = 0;
opus_custom_encoder_ctl(encoder, EncoderCtlRequest::GetLsbDepth(&mut value)).unwrap();
value
}
fn get_phase_disabled(encoder: &mut OpusCustomEncoder<'_>) -> bool {
let mut value = false;
opus_custom_encoder_ctl(
encoder,
EncoderCtlRequest::GetPhaseInversionDisabled(&mut value),
)
.unwrap();
value
}
fn get_final_range(encoder: &mut OpusCustomEncoder<'_>) -> OpusUint32 {
let mut value = 0;
opus_custom_encoder_ctl(encoder, EncoderCtlRequest::GetFinalRange(&mut value)).unwrap();
value
}
fn assert_mode_matches(encoder: &mut OpusCustomEncoder<'_>, expected: *const OpusCustomMode) {
let mut slot = None;
opus_custom_encoder_ctl(encoder, EncoderCtlRequest::GetMode(&mut slot)).unwrap();
let mode_ref = slot.expect("mode");
assert_eq!(mode_ref as *const OpusCustomMode, expected);
}
#[test]
fn transient_analysis_outputs_valid_metrics() {
let len = 64;
let mut input = vec![0.0f32; len];
input[0] = 10.0;
let mut tf_estimate = 0.0f32;
let mut tf_chan = 0usize;
let mut weak = false;
let _detected = transient_analysis(
&input,
len,
1,
&mut tf_estimate,
&mut tf_chan,
false,
&mut weak,
0.1,
0.0,
);
assert!(tf_estimate >= 0.0);
assert_eq!(tf_chan, 0);
assert!(!weak);
}
#[test]
fn transient_analysis_rejects_flat_signal() {
let len = 64;
let input = vec![0.5f32; len];
let mut tf_estimate = 0.0f32;
let mut tf_chan = 0usize;
let mut weak = false;
let detected = transient_analysis(
&input,
len,
1,
&mut tf_estimate,
&mut tf_chan,
true,
&mut weak,
0.0,
1.0,
);
assert!(!detected);
assert!(!weak);
assert_eq!(tf_chan, 0);
assert!(tf_estimate >= 0.0);
}
#[test]
fn patch_transient_decision_returns_boolean() {
let nb_ebands = 5;
let start = 0;
let end = nb_ebands;
let channels = 1;
let mut new_e = vec![0.0f32; nb_ebands];
let mut old_e = vec![0.0f32; nb_ebands];
old_e.fill(-2.0);
new_e.fill(-2.0);
new_e[2] = 2.0;
let increase = patch_transient_decision(&new_e, &old_e, nb_ebands, start, end, channels);
let baseline = patch_transient_decision(&old_e, &old_e, nb_ebands, start, end, channels);
assert!(u8::from(increase) >= u8::from(baseline));
}
#[test]
fn dynalloc_analysis_defaults_when_disabled() {
let nb_ebands = 4;
let channels = 1;
let start = 0;
let end = nb_ebands;
let band_log_e = vec![0.5f32; channels * nb_ebands];
let band_log_e2 = band_log_e.clone();
let old_band_e = vec![-28.0f32; channels * nb_ebands];
let log_n = vec![10i16; nb_ebands];
let e_bands = [0i16, 1, 2, 3, 4];
let mut offsets = vec![1i32; nb_ebands];
let mut importance = vec![0i32; nb_ebands];
let mut spread_weight = vec![0i32; nb_ebands];
let mut surround_dynalloc = vec![0.0f32; nb_ebands];
let mut tot_boost = -1;
let max_depth = dynalloc_analysis(
&band_log_e,
&band_log_e2,
&old_band_e,
nb_ebands,
start,
end,
channels,
&mut offsets,
8,
&log_n,
false,
true,
false,
&e_bands,
0,
10,
&mut tot_boost,
false,
&mut surround_dynalloc,
&AnalysisInfo::default(),
&mut importance,
&mut spread_weight,
0.0,
0.0,
);
assert!(max_depth > 0.0);
assert_eq!(tot_boost, 0);
assert!(offsets.iter().all(|&value| value == 0));
assert_eq!(&importance[start..end], &[13, 13, 13, 13]);
}
#[test]
fn dynalloc_analysis_accounts_for_surround_boost() {
let nb_ebands = 4;
let channels = 1;
let start = 0;
let end = nb_ebands;
let band_log_e = vec![6.0f32; channels * nb_ebands];
let band_log_e2 = band_log_e.clone();
let old_band_e = vec![5.0f32; channels * nb_ebands];
let log_n = vec![8i16; nb_ebands];
let e_bands = [0i16, 1, 2, 3, 4];
let mut offsets = vec![0i32; nb_ebands];
let mut importance = vec![0i32; nb_ebands];
let mut spread_weight = vec![0i32; nb_ebands];
let mut surround_dynalloc = vec![0.0f32; nb_ebands];
surround_dynalloc[0] = 3.0;
let mut tot_boost = 0;
let toneishness = 0.0f32;
let max_depth = dynalloc_analysis(
&band_log_e,
&band_log_e2,
&old_band_e,
nb_ebands,
start,
end,
channels,
&mut offsets,
6,
&log_n,
false,
true,
false,
&e_bands,
0,
60,
&mut tot_boost,
false,
&mut surround_dynalloc,
&AnalysisInfo::default(),
&mut importance,
&mut spread_weight,
0.05,
toneishness,
);
assert!(max_depth > 0.0);
assert!(importance[0] > 13);
assert_eq!(offsets[0], 4);
assert_eq!(tot_boost, 32);
}
#[test]
fn stereo_analysis_matches_manual_decision() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let lm = 0usize;
let n0 = mode.short_mdct_size << lm;
let mut x = vec![0.0f32; 2 * n0];
for i in 0..n0 {
let sample = ((i % 7) as f32 - 3.0) * 0.125;
x[i] = sample;
x[n0 + i] = 0.6 * sample;
}
let result = stereo_analysis(&mode, &x, lm, n0);
let mut sum_lr = 1.0e-15f32;
let mut sum_ms = 1.0e-15f32;
for band in 0..13 {
let start = (mode.e_bands[band] as usize) << lm;
let end = (mode.e_bands[band + 1] as usize) << lm;
for idx in start..end {
let left = x[idx];
let right = x[n0 + idx];
let mid = left + right;
let side = left - right;
sum_lr += left.abs() + right.abs();
sum_ms += mid.abs() + side.abs();
}
}
sum_ms *= FRAC_1_SQRT_2;
let mut thetas = 13i32;
if lm <= 1 {
thetas -= 8;
}
let base = i32::from(mode.e_bands[13]) << (lm + 1);
let expected = (base + thetas) as f32 * sum_ms > base as f32 * sum_lr;
assert_eq!(result, expected);
}
#[test]
fn alloc_trim_analysis_matches_reference_flow() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let lm = 0usize;
let n0 = mode.short_mdct_size << lm;
let channels = 2;
let mut x = vec![0.0f32; channels * n0];
for i in 0..n0 {
let sample = (0.005 * i as f32).sin();
x[i] = sample;
x[n0 + i] = 0.5 * sample + 0.1;
}
let nb_ebands = mode.num_ebands;
let mut band_log_e = vec![0.0f32; channels * nb_ebands];
for c in 0..channels {
for b in 0..nb_ebands {
band_log_e[c * nb_ebands + b] = 0.1 * (b as f32 + c as f32);
}
}
let mut analysis = AnalysisInfo::default();
analysis.valid = true;
analysis.tonality_slope = 0.075;
let mut stereo_saving = 0.0f32;
let tf_estimate = 0.35;
let surround_trim = 0.2;
let end = nb_ebands.min(15);
let intensity = end;
let equiv_rate = 72_000;
let trim_index = alloc_trim_analysis(
&mode,
&x,
&band_log_e,
end,
lm,
channels,
n0,
&analysis,
&mut stereo_saving,
tf_estimate,
intensity,
surround_trim,
equiv_rate,
0,
);
let mut expected_trim = if equiv_rate < 64_000 {
4.0
} else if equiv_rate < 80_000 {
4.0 + ((equiv_rate - 64_000) >> 10) as f32 / 16.0
} else {
5.0
};
let mut sum = 0.0f32;
for band in 0..8.min(mode.num_ebands) {
let start = (mode.e_bands[band] as usize) << lm;
let end = (mode.e_bands[band + 1] as usize) << lm;
for idx in start..end {
sum += x[idx] * x[n0 + idx];
}
}
sum *= 1.0 / 8.0;
sum = sum.abs().min(1.0);
let mut min_xc = sum;
for band in 8..intensity.min(mode.num_ebands) {
let start = (mode.e_bands[band] as usize) << lm;
let end = (mode.e_bands[band + 1] as usize) << lm;
for idx in start..end {
let partial = (x[idx] * x[n0 + idx]).abs().min(1.0);
if partial < min_xc {
min_xc = partial;
}
}
}
let log_xc = celt_log2(1.001 - sum * sum);
let alt = celt_log2(1.001 - min_xc * min_xc);
let half_log = 0.5 * log_xc;
let log_xc2 = if alt > half_log { alt } else { half_log };
expected_trim += (0.75 * log_xc).max(-4.0);
let expected_stereo = (-0.5 * log_xc2).min(0.25);
let mut diff = 0.0f32;
if end > 1 {
for c in 0..channels {
let base = c * nb_ebands;
for band in 0..(end - 1) {
let weight = (2 + 2 * band as i32 - end as i32) as f32;
diff += band_log_e[base + band] * weight;
}
}
diff /= (channels * (end - 1)) as f32;
}
expected_trim -= ((diff + 1.0) / 6.0).clamp(-2.0, 2.0);
expected_trim -= surround_trim;
expected_trim -= 2.0 * tf_estimate;
if analysis.valid {
let tonal = 2.0 * (analysis.tonality_slope + 0.05);
expected_trim -= tonal.clamp(-2.0, 2.0);
}
let mut expected_index = floorf(expected_trim + 0.5) as i32;
expected_index = expected_index.clamp(0, 10);
assert_eq!(trim_index, expected_index);
assert!(
(stereo_saving - expected_stereo).abs() < 1e-6,
"stereo_saving={} expected={}",
stereo_saving,
expected_stereo
);
}
#[test]
fn compute_vbr_penalises_quiet_analysis() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut analysis = AnalysisInfo::default();
analysis.valid = true;
analysis.activity = 0.0;
let base_target = 12_000;
let target = compute_vbr(
&mode,
&analysis,
base_target,
0,
64_000,
0,
1,
mode.effective_ebands as i32,
false,
0.0,
0,
0.0,
false,
10.0,
false,
false,
0.0,
0.0,
);
assert!(target < base_target);
}
#[test]
fn compute_vbr_caps_to_twice_base() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut analysis = AnalysisInfo::default();
analysis.valid = true;
analysis.tonality = 1.0;
analysis.activity = 0.5;
let base_target = 10_000;
let target = compute_vbr(
&mode,
&analysis,
base_target,
0,
64_000,
0,
2,
mode.effective_ebands as i32,
false,
1.0,
2_000,
1.0,
true,
10.0,
false,
false,
0.0,
0.5,
);
assert!(target <= base_target * 2);
}
#[test]
fn allocation_matches_reference_layout() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let alloc = CeltEncoderAlloc::new(&mode, 2);
assert_eq!(alloc.in_mem.len(), mode.overlap * 2);
assert_eq!(alloc.prefilter_mem.len(), 2 * COMBFILTER_MAXPERIOD);
assert_eq!(alloc.old_band_e.len(), 2 * mode.num_ebands);
assert_eq!(alloc.old_log_e.len(), 2 * mode.num_ebands);
assert_eq!(alloc.old_log_e2.len(), 2 * mode.num_ebands);
assert_eq!(alloc.energy_error.len(), 2 * mode.num_ebands);
#[cfg(feature = "fixed_point")]
{
assert_eq!(alloc.fixed_in_mem.len(), mode.overlap * 2);
assert_eq!(alloc.fixed_prefilter_mem.len(), 2 * COMBFILTER_MAXPERIOD);
assert_eq!(alloc.fixed_old_band_e.len(), 2 * mode.num_ebands);
assert_eq!(alloc.fixed_energy_error.len(), 2 * mode.num_ebands);
}
let bytes = alloc.size_in_bytes();
assert_eq!(bytes, super::opus_custom_encoder_get_size(&mode, 2));
}
#[test]
fn reset_initialises_energy_histories() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
alloc.old_log_e.fill(0.5);
alloc.old_log_e2.fill(0.25);
alloc.old_band_e.fill(1.0);
alloc.energy_error.fill(1.0);
#[cfg(feature = "fixed_point")]
{
alloc.fixed_in_mem.fill(7);
alloc.fixed_prefilter_mem.fill(9);
alloc.fixed_old_band_e.fill(7);
alloc.fixed_energy_error.fill(9);
}
alloc.prefilter_mem.fill(1.0);
alloc.in_mem.fill(1.0);
alloc.reset();
assert!(alloc.in_mem.iter().all(|&v| v == 0.0));
assert!(alloc.prefilter_mem.iter().all(|&v| v == 0.0));
assert!(alloc.old_band_e.iter().all(|&v| v == 0.0));
assert!(alloc.energy_error.iter().all(|&v| v == 0.0));
assert!(alloc.old_log_e.iter().all(|&v| (v + 28.0).abs() < 1e-6));
assert!(alloc.old_log_e2.iter().all(|&v| (v + 28.0).abs() < 1e-6));
#[cfg(feature = "fixed_point")]
{
assert!(alloc.fixed_in_mem.iter().all(|&v| v == 0));
assert!(alloc.fixed_prefilter_mem.iter().all(|&v| v == 0));
assert!(alloc.fixed_old_band_e.iter().all(|&v| v == 0));
assert!(alloc.fixed_energy_error.iter().all(|&v| v == 0));
}
}
#[test]
fn init_custom_encoder_sets_reference_defaults() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 2);
let encoder = alloc
.init_custom_encoder(&mode, 2, 2, 0xDEADBEEF)
.expect("encoder");
assert_eq!(encoder.channels, 2);
assert_eq!(encoder.stream_channels, 2);
assert_eq!(encoder.upsample, 1);
assert_eq!(encoder.start_band, 0);
assert_eq!(encoder.end_band, mode.effective_ebands as i32);
assert_eq!(encoder.signalling, 1);
assert_eq!(encoder.arch, opus_select_arch());
assert!(encoder.constrained_vbr);
assert!(encoder.clip);
assert_eq!(encoder.bitrate, OPUS_BITRATE_MAX);
assert!(!encoder.use_vbr);
assert_eq!(encoder.complexity, 5);
assert_eq!(encoder.lsb_depth, 24);
assert_eq!(encoder.spread_decision, SPREAD_NORMAL);
assert!((encoder.delayed_intra - 1.0).abs() < 1e-6);
#[cfg(feature = "fixed_point")]
{
assert_eq!(encoder.fixed_delayed_intra, qconst32(1.0, DB_SHIFT));
}
assert_eq!(encoder.tonal_average, 256);
assert_eq!(encoder.hf_average, 0);
assert_eq!(encoder.tapset_decision, 0);
assert_eq!(encoder.rng, 0xDEADBEEF);
assert!(encoder.old_log_e.iter().all(|&v| (v + 28.0).abs() < 1e-6));
assert!(encoder.old_log_e2.iter().all(|&v| (v + 28.0).abs() < 1e-6));
}
#[test]
fn opus_custom_encoder_init_arch_honours_requested_architecture() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
let encoder =
opus_custom_encoder_init_arch(&mut alloc, &mode, 1, 11, 1234).expect("encoder");
assert_eq!(encoder.arch, 11);
assert_eq!(encoder.stream_channels, 1);
}
#[test]
fn opus_custom_encoder_init_defaults_stream_channels() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 2);
let encoder = opus_custom_encoder_init(&mut alloc, &mode, 2, 77).expect("encoder");
assert_eq!(encoder.channels, 2);
assert_eq!(encoder.stream_channels, 2);
assert_eq!(encoder.arch, opus_select_arch());
}
#[test]
fn celt_encoder_init_sets_resampling_factor() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
let encoder = celt_encoder_init(&mut alloc, 24_000, 1, 3, 0).expect("encoder");
assert_eq!(encoder.upsample, 2);
assert_eq!(encoder.arch, 3);
}
#[test]
fn celt_encoder_init_rejects_invalid_rate() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
let err = celt_encoder_init(&mut alloc, 44_100, 1, 0, 0).unwrap_err();
assert_eq!(err, CeltEncoderInitError::UnsupportedSampleRate);
}
#[test]
fn opus_custom_encoder_destroy_drops_allocation() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let encoder = opus_custom_encoder_create(&mode, 48_000, 1, 0).expect("encoder");
opus_custom_encoder_destroy(encoder);
}
#[test]
fn init_encoder_for_rate_rejects_unsupported_sampling_rate() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
let err = alloc
.init_encoder_for_rate(&mode, 1, 1, 44_100, 0)
.unwrap_err();
assert_eq!(err, CeltEncoderInitError::UnsupportedSampleRate);
}
#[test]
fn channel_validation_matches_reference_limits() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
let err = alloc.init_custom_encoder(&mode, 0, 0, 0).unwrap_err();
assert_eq!(err, CeltEncoderInitError::InvalidChannelCount);
let err = alloc
.init_custom_encoder(&mode, MAX_CHANNELS + 1, 1, 0)
.unwrap_err();
assert_eq!(err, CeltEncoderInitError::InvalidChannelCount);
let err = alloc.init_custom_encoder(&mode, 1, 0, 0).unwrap_err();
assert_eq!(err, CeltEncoderInitError::InvalidStreamChannels);
let err = alloc.init_custom_encoder(&mode, 2, 3, 0).unwrap_err();
assert_eq!(err, CeltEncoderInitError::InvalidStreamChannels);
}
#[test]
fn ctl_updates_encoder_state() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 2);
let mut encoder = alloc
.init_custom_encoder(&mode, 2, 2, 1234)
.expect("encoder");
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetComplexity(7)).unwrap();
assert_eq!(encoder.complexity, 7);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetStartBand(1)).unwrap();
assert_eq!(encoder.start_band, 1);
opus_custom_encoder_ctl(
&mut encoder,
EncoderCtlRequest::SetEndBand(mode.num_ebands as i32),
)
.unwrap();
assert_eq!(encoder.end_band, mode.num_ebands as i32);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetPrediction(2)).unwrap();
assert!(!encoder.disable_prefilter);
assert!(!encoder.force_intra);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetPacketLossPerc(25)).unwrap();
assert_eq!(encoder.loss_rate, 25);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetVbrConstraint(false)).unwrap();
assert!(!encoder.constrained_vbr);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetVbr(true)).unwrap();
assert!(encoder.use_vbr);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetBitrate(700_000)).unwrap();
assert_eq!(encoder.bitrate, 260_000 * 2);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetChannels(1)).unwrap();
assert_eq!(encoder.stream_channels, 1);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetLsbDepth(16)).unwrap();
assert_eq!(encoder.lsb_depth, 16);
opus_custom_encoder_ctl(
&mut encoder,
EncoderCtlRequest::SetPhaseInversionDisabled(true),
)
.unwrap();
assert!(encoder.disable_inv);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetInputClipping(false)).unwrap();
assert!(!encoder.clip);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetSignalling(0)).unwrap();
assert_eq!(encoder.signalling, 0);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetLfe(true)).unwrap();
assert!(encoder.lfe);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetEnergyMask(None)).unwrap();
assert!(encoder.energy_mask.is_none());
let lsb_depth = get_lsb_depth(&mut encoder);
assert_eq!(lsb_depth, encoder.lsb_depth);
let phase_disabled = get_phase_disabled(&mut encoder);
assert!(phase_disabled);
let final_range = get_final_range(&mut encoder);
assert_eq!(final_range, encoder.rng);
encoder.rng = 42;
encoder.old_log_e.fill(0.0);
encoder.energy_mask = Some(&[]);
let expected_mode_ptr = encoder.mode as *const OpusCustomMode;
assert_mode_matches(&mut encoder, expected_mode_ptr);
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::ResetState).unwrap();
assert_eq!(encoder.rng, 0);
assert!(encoder.old_log_e.iter().all(|&v| (v + 28.0).abs() < 1e-6));
assert!(encoder.energy_mask.is_none());
}
#[test]
fn ctl_validates_arguments() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
let mut encoder = alloc
.init_custom_encoder(&mode, 1, 1, 9876)
.expect("encoder");
let err = opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetComplexity(11))
.unwrap_err();
assert_eq!(err, CeltEncoderCtlError::InvalidArgument);
let err =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetStartBand(-1)).unwrap_err();
assert_eq!(err, CeltEncoderCtlError::InvalidArgument);
let err =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetEndBand(0)).unwrap_err();
assert_eq!(err, CeltEncoderCtlError::InvalidArgument);
let err =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetPrediction(3)).unwrap_err();
assert_eq!(err, CeltEncoderCtlError::InvalidArgument);
let err = opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetPacketLossPerc(101))
.unwrap_err();
assert_eq!(err, CeltEncoderCtlError::InvalidArgument);
let err =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetBitrate(400)).unwrap_err();
assert_eq!(err, CeltEncoderCtlError::InvalidArgument);
let err =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetChannels(2)).unwrap_err();
assert_eq!(err, CeltEncoderCtlError::InvalidArgument);
let err =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetLsbDepth(6)).unwrap_err();
assert_eq!(err, CeltEncoderCtlError::InvalidArgument);
}
#[test]
fn opus_custom_encode_errors_on_short_pcm() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
let mut encoder = alloc
.init_custom_encoder(&mode, 1, 1, 123)
.expect("encoder");
let pcm = vec![0i16; 100];
let mut compressed = vec![0u8; 16];
let limit = compressed.len();
let err = opus_custom_encode(&mut encoder, &pcm, 960, &mut compressed, limit).unwrap_err();
assert_eq!(err, CeltEncodeError::InsufficientPcm);
}
#[test]
fn opus_custom_encode_errors_when_output_too_small() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
let mut encoder = alloc
.init_custom_encoder(&mode, 1, 1, 234)
.expect("encoder");
let pcm = vec![0i16; 960];
let mut compressed = vec![0u8; 8];
let err = opus_custom_encode(&mut encoder, &pcm, 960, &mut compressed, 16).unwrap_err();
assert_eq!(err, CeltEncodeError::MissingOutput);
}
#[test]
fn opus_custom_encode_errors_when_nb_compressed_bytes_below_minimum() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let mut alloc = CeltEncoderAlloc::new(&mode, 1);
let mut encoder = alloc
.init_custom_encoder(&mode, 1, 1, 345)
.expect("encoder");
let pcm = vec![0i16; 960];
let mut compressed = vec![0u8; 16];
let err = opus_custom_encode(&mut encoder, &pcm, 960, &mut compressed, 1).unwrap_err();
assert_eq!(err, CeltEncodeError::MissingOutput);
}
#[test]
fn convert_i16_to_celt_sig_scales_to_opus_res() {
let input = [0i16, -32_768, 32_767, 1_234];
let converted = convert_i16_to_celt_sig(&input, input.len());
let scale = 1.0 / CELT_SIG_SCALE;
for (value, &sample) in converted.iter().zip(&input) {
let expected = sample as f32 * scale;
assert!((value - expected).abs() < 1e-6);
}
}
#[test]
fn convert_i24_to_celt_sig_scales_to_opus_res() {
let input = [0i32, 100_000, -150_000, 255, -255, 8_388_607, -8_388_608];
let converted = convert_i24_to_celt_sig(&input, input.len());
let scale = 1.0 / (CELT_SIG_SCALE * 256.0);
for (value, &sample) in converted.iter().zip(&input) {
let expected = sample as f32 * scale;
assert!((value - expected).abs() < 1e-6);
}
}
#[test]
fn convert_f32_to_celt_sig_preserves_samples() {
let input = [0.0f32, 0.5 / CELT_SIG_SCALE, -1.5];
let converted = convert_f32_to_celt_sig(&input, input.len());
assert_eq!(converted, input.to_vec());
}
#[test]
fn median_of_5_matches_sorted_middle() {
let samples = [
[1.0f32, 5.0, 3.0, 2.0, 4.0],
[9.0, -1.0, 2.0, 2.0, 8.0],
[12.0, 12.0, 11.0, 13.0, 12.5],
];
for data in samples {
let mut sorted = data;
sorted.sort_by(|a, b| a.partial_cmp(b).expect("no NaN"));
let expected = sorted[2];
assert_eq!(median_of_5(&data), expected);
}
}
#[test]
fn median_of_3_selects_middle_value() {
let samples = [[1.0f32, 3.0, 2.0], [5.0, -2.0, 4.0], [7.5, 7.5, 7.0]];
for data in samples {
let mut sorted = data;
sorted.sort_by(|a, b| a.partial_cmp(b).expect("no NaN"));
let expected = sorted[1];
assert_eq!(median_of_3(&data), expected);
}
}
#[test]
fn tone_lpc_recovers_sinusoid_predictor() {
let len = 240;
let delay = 1;
let mut samples = vec![0.0f32; len];
let omega = 2.0 * PI * 0.1;
for (n, slot) in samples.iter_mut().enumerate() {
*slot = (omega * n as f32).sin();
}
let mut lpc = [0.0f32; 2];
let failed = tone_lpc(&samples, delay, &mut lpc);
assert!(
!failed,
"tone_lpc should succeed for a well-conditioned tone"
);
let expected_cos = 2.0 * omega.cos();
assert!((lpc[0] - expected_cos).abs() < 1e-3);
assert!((lpc[1] + 1.0).abs() < 1e-3);
}
#[test]
fn tone_detect_identifies_sinusoid() {
let fs = 48_000;
let n = 960;
let target_hz = 440.0;
let omega = 2.0 * PI * target_hz / fs as f32;
let mut input = vec![0.0f32; n];
for (i, sample) in input.iter_mut().enumerate() {
*sample = sinf(omega * i as f32);
}
let mut toneishness = 0.0f32;
let freq = tone_detect(&input, 1, n, &mut toneishness, fs);
assert!(freq > 0.0, "freq {freq} omega {omega}");
assert!(freq < 0.1, "freq {freq} omega {omega}");
assert!(toneishness > 0.8);
}
#[test]
fn tone_detect_rejects_silence() {
let n = 240;
let input = vec![0.0f32; n];
let mut toneishness = 1.0f32;
let freq = tone_detect(&input, 1, n, &mut toneishness, 48_000);
assert_eq!(freq, -1.0);
assert_eq!(toneishness, 0.0);
}
#[cfg(not(feature = "fixed_point"))]
#[test]
fn normalize_tone_input_is_noop_for_float_build() {
let mut data = [0.125f32, -0.5, 1.25, -1.75];
let original = data;
normalize_tone_input(&mut data);
assert_eq!(data, original);
}
#[cfg(not(feature = "fixed_point"))]
#[test]
#[cfg_attr(miri, ignore = "libm relies on inline assembly under Miri")]
fn acos_approx_matches_libm_for_float_build() {
let samples = [-1.0f32, -0.5, 0.0, 0.3, 0.75, 1.0];
for &value in &samples {
let expected = libm::acosf(value);
let approx = acos_approx(value);
assert!(
(approx - expected).abs() < 1e-6,
"approximation should match libm for value {value}"
);
}
}
use super::super::celt_decoder::{
CeltDecodeError, opus_custom_decode, opus_custom_decode_float, opus_custom_decode24,
opus_custom_decoder_create,
};
use super::{opus_custom_encode_float, opus_custom_encode24, opus_custom_encoder_create};
use crate::opus_decoder::{
OpusDecodeError, OpusDecoder, opus_decode, opus_decode_float, opus_decode24,
opus_decoder_create,
};
use crate::opus_encoder::{
OpusEncoder, OpusEncoderCtlRequest, opus_encode, opus_encode_float, opus_encode24,
opus_encoder_create, opus_encoder_ctl,
};
use core::f64::consts::PI as PI_F64;
const MAX_PACKET: usize = 1500;
const OPUS_APPLICATION_RESTRICTED_LOWDELAY: i32 = 2051;
const SINE_SWEEP_AMPLITUDE: f64 = 0.5;
const FULL_SINE_SWEEP_DURATION_S: f64 = 60.0;
struct FastRand {
rz: u32,
rw: u32,
}
impl FastRand {
fn new(seed: u32) -> Self {
Self { rz: seed, rw: seed }
}
fn next(&mut self) -> u32 {
self.rz = 36969u32
.wrapping_mul(self.rz & 65535)
.wrapping_add(self.rz >> 16);
self.rw = 18000u32
.wrapping_mul(self.rw & 65535)
.wrapping_add(self.rw >> 16);
(self.rz << 16).wrapping_add(self.rw)
}
fn rand_sample<T: Copy>(&mut self, arr: &[T]) -> T {
arr[self.next() as usize % arr.len()]
}
}
fn generate_sine_sweep_i16(
amplitude: f64,
sample_rate: usize,
channels: usize,
duration_seconds: f64,
) -> Vec<i16> {
let num_samples = (duration_seconds * sample_rate as f64 + 0.5) as usize;
let start_freq = 100.0;
let end_freq = sample_rate as f64 / 2.0;
let max_sample_value = i16::MAX as f64;
let mut output = vec![0i16; num_samples * channels];
let b = ((end_freq + start_freq) / start_freq).ln() / duration_seconds;
let a = start_freq / b;
for i in 0..num_samples {
let t = i as f64 / sample_rate as f64;
let sample = amplitude * (2.0 * PI_F64 * a * (b * t).exp() - b * t - 1.0).sin();
let sample_i16 = (sample * max_sample_value + 0.5).floor() as i16;
for ch in 0..channels {
output[i * channels + ch] = sample_i16;
}
}
output
}
fn generate_sine_sweep_i32(
amplitude: f64,
sample_rate: usize,
channels: usize,
duration_seconds: f64,
bit_depth: u32,
) -> Vec<i32> {
let num_samples = (duration_seconds * sample_rate as f64 + 0.5) as usize;
let start_freq = 100.0;
let end_freq = sample_rate as f64 / 2.0;
let max_sample_value = ((1u64 << (bit_depth - 1)) - 1) as f64;
let mut output = vec![0i32; num_samples * channels];
let b = ((end_freq + start_freq) / start_freq).ln() / duration_seconds;
let a = start_freq / b;
for i in 0..num_samples {
let t = i as f64 / sample_rate as f64;
let sample = amplitude * (2.0 * PI_F64 * a * (b * t).exp() - b * t - 1.0).sin();
let sample_i32 = (sample * max_sample_value + 0.5).floor() as i32;
for ch in 0..channels {
output[i * channels + ch] = sample_i32;
}
}
output
}
fn generate_sine_sweep_f32(
amplitude: f64,
sample_rate: usize,
channels: usize,
duration_seconds: f64,
) -> Vec<f32> {
let num_samples = (duration_seconds * sample_rate as f64 + 0.5) as usize;
let start_freq = 100.0;
let end_freq = sample_rate as f64 / 2.0;
let mut output = vec![0.0f32; num_samples * channels];
let b = ((end_freq + start_freq) / start_freq).ln() / duration_seconds;
let a = start_freq / b;
for i in 0..num_samples {
let t = i as f64 / sample_rate as f64;
let sample = amplitude * (2.0 * PI_F64 * a * (b * t).exp() - b * t - 1.0).sin();
let sample_f32 = sample as f32;
for ch in 0..channels {
output[i * channels + ch] = sample_f32;
}
}
output
}
fn seed_from_env() -> u32 {
env::var("SEED")
.ok()
.and_then(|value| value.parse::<u32>().ok())
.unwrap_or(0xC0DEC0DE)
}
fn no_fuzz() -> bool {
if env::var_os("TEST_OPUS_FUZZ").is_some() {
return false;
}
env::var_os("TEST_OPUS_NOFUZZ").is_some() || cfg!(debug_assertions)
}
fn custom_fuzz_settings() -> (usize, usize, f64) {
if env::var_os("TEST_OPUS_FUZZ").is_some() {
return (5, 40, FULL_SINE_SWEEP_DURATION_S);
}
if no_fuzz() {
return (1, 4, 0.5);
}
(3, 12, 2.0)
}
fn rand_f64(rng: &mut FastRand) -> f64 {
rng.next() as f64 / (u32::MAX as f64 + 1.0)
}
struct TestCustomParams {
sample_rate: i32,
num_channels: usize,
frame_size: usize,
float_encode: bool,
float_decode: bool,
custom_encode: bool,
custom_decode: bool,
encoder_bit_depth: i32,
decoder_bit_depth: i32,
}
fn run_custom_test<'a>(
params: &TestCustomParams,
duration_seconds: f64,
mut custom_encoder: Option<&mut OpusCustomEncoder<'a>>,
mut opus_encoder: Option<&mut OpusEncoder<'a>>,
mut custom_decoder: Option<&mut OpusCustomDecoder<'a>>,
mut opus_decoder: Option<&mut OpusDecoder<'a>>,
mut custom_decoder_corrupt: Option<&mut OpusCustomDecoder<'a>>,
mut opus_decoder_corrupt: Option<&mut OpusDecoder<'a>>,
rng: &mut FastRand,
) -> Result<(), String> {
let sample_rate = params.sample_rate as usize;
let channels = params.num_channels;
let frame_size = params.frame_size;
let min_duration = frame_size as f64 / sample_rate as f64;
let duration = duration_seconds.max(min_duration);
let mut input_samples = (duration * sample_rate as f64 + 0.5) as usize;
if input_samples < frame_size {
input_samples = frame_size;
}
let input_len = input_samples * channels;
let input_f32 = if params.float_encode {
Some(generate_sine_sweep_f32(
SINE_SWEEP_AMPLITUDE,
sample_rate,
channels,
duration,
))
} else {
None
};
let input_i32 = if !params.float_encode && params.encoder_bit_depth == 24 {
Some(generate_sine_sweep_i32(
SINE_SWEEP_AMPLITUDE,
sample_rate,
channels,
duration,
24,
))
} else {
None
};
let input_i16 = if !params.float_encode && params.encoder_bit_depth != 24 {
Some(generate_sine_sweep_i16(
SINE_SWEEP_AMPLITUDE,
sample_rate,
channels,
duration,
))
} else {
None
};
let mut output_f32 = if params.float_decode {
Some(vec![0.0f32; input_len])
} else {
None
};
let mut output_i32 = if !params.float_decode && params.decoder_bit_depth == 24 {
Some(vec![0i32; input_len])
} else {
None
};
let mut output_i16 = if !params.float_decode && params.decoder_bit_depth != 24 {
Some(vec![0i16; input_len])
} else {
None
};
let mut packet = vec![0u8; MAX_PACKET + 257];
let mut packet_corrupt = vec![0u8; MAX_PACKET + 257];
let mut scratch = vec![0i16; frame_size * channels];
let mut samp_count = 0usize;
while samp_count + frame_size <= input_samples {
let offset = samp_count * channels;
let end = offset + frame_size * channels;
let len = if params.custom_encode {
let enc = match custom_encoder.as_mut() {
Some(enc) => enc,
None => return Err(String::from("missing custom encoder")),
};
if params.float_encode {
let input = input_f32.as_ref().ok_or("missing float input")?;
opus_custom_encode_float(
enc,
&input[offset..end],
frame_size,
&mut packet,
MAX_PACKET,
)
.map_err(|err| format!("opus_custom_encode_float failed: {err:?}"))?
} else if params.encoder_bit_depth == 24 {
let input = input_i32.as_ref().ok_or("missing int24 input")?;
opus_custom_encode24(
enc,
&input[offset..end],
frame_size,
&mut packet,
MAX_PACKET,
)
.map_err(|err| format!("opus_custom_encode24 failed: {err:?}"))?
} else {
let input = input_i16.as_ref().ok_or("missing int16 input")?;
opus_custom_encode(
enc,
&input[offset..end],
frame_size,
&mut packet,
MAX_PACKET,
)
.map_err(|err| format!("opus_custom_encode failed: {err:?}"))?
}
} else {
let enc = match opus_encoder.as_mut() {
Some(enc) => enc,
None => return Err(String::from("missing opus encoder")),
};
let packet_slice = &mut packet[..MAX_PACKET];
if params.float_encode {
let input = input_f32.as_ref().ok_or("missing float input")?;
opus_encode_float(enc, &input[offset..end], frame_size, packet_slice)
.map_err(|err| format!("opus_encode_float failed: {err:?}"))?
} else if params.encoder_bit_depth == 24 {
let input = input_i32.as_ref().ok_or("missing int24 input")?;
opus_encode24(enc, &input[offset..end], frame_size, packet_slice)
.map_err(|err| format!("opus_encode24 failed: {err:?}"))?
} else {
let input = input_i16.as_ref().ok_or("missing int16 input")?;
opus_encode(enc, &input[offset..end], frame_size, packet_slice)
.map_err(|err| format!("opus_encode failed: {err:?}"))?
}
};
if len == 0 {
return Err(String::from("encoder returned 0 bytes"));
}
if len > MAX_PACKET {
return Err(format!("encoded length {len} exceeds max packet size"));
}
packet_corrupt.copy_from_slice(&packet);
for error_pos in 0..5usize {
if error_pos < len && rng.next() % 5 == 0 {
packet_corrupt[error_pos] = (rng.next() & 0xFF) as u8;
}
}
let ber_1 = (1.0 - 100.0 * (1e-10 + rand_f64(rng)).ln()) as i32;
let mut len2 = (1.0 - (len as f64) * (1e-10 + rand_f64(rng)).ln()) as i32;
if len2 < 0 {
len2 = 0;
}
let len2 = len.min(len2 as usize);
let mut error_pos = 0i32;
loop {
let increment = (-(ber_1 as f64) * (1e-10 + rand_f64(rng)).ln()) as i32;
error_pos += increment;
if error_pos >= (len2 * 8) as i32 {
break;
}
let byte = (error_pos / 8) as usize;
let bit = (error_pos & 7) as u8;
if byte < packet_corrupt.len() {
packet_corrupt[byte] ^= 1u8 << bit;
}
}
let corrupt_slice = &packet_corrupt[..len2];
if params.custom_decode {
let dec = match custom_decoder_corrupt.as_mut() {
Some(dec) => dec,
None => return Err(String::from("missing custom decoder (corrupt)")),
};
let result = opus_custom_decode(dec, Some(corrupt_slice), &mut scratch, frame_size);
if !matches!(
result,
Ok(_)
| Err(CeltDecodeError::BadArgument
| CeltDecodeError::InvalidPacket
| CeltDecodeError::PacketLoss,)
) {
return Err(format!("opus_custom_decode corrupt failed: {result:?}"));
}
} else {
let dec = match opus_decoder_corrupt.as_mut() {
Some(dec) => dec,
None => return Err(String::from("missing opus decoder (corrupt)")),
};
let result = opus_decode(
dec,
Some(corrupt_slice),
len2,
&mut scratch,
frame_size,
false,
);
if !matches!(
result,
Ok(_)
| Err(OpusDecodeError::BadArgument
| OpusDecodeError::InvalidPacket
| OpusDecodeError::BufferTooSmall,)
) {
return Err(format!("opus_decode corrupt failed: {result:?}"));
}
}
let packet_slice = &packet[..len];
let samples_decoded = if params.float_decode {
let output = output_f32.as_mut().ok_or("missing float output")?;
let out_slice = &mut output[offset..end];
if params.custom_decode {
let dec = match custom_decoder.as_mut() {
Some(dec) => dec,
None => return Err(String::from("missing custom decoder")),
};
opus_custom_decode_float(dec, Some(packet_slice), out_slice, frame_size)
.map_err(|err| format!("opus_custom_decode_float failed: {err:?}"))?
} else {
let dec = match opus_decoder.as_mut() {
Some(dec) => dec,
None => return Err(String::from("missing opus decoder")),
};
opus_decode_float(dec, Some(packet_slice), len, out_slice, frame_size, false)
.map_err(|err| format!("opus_decode_float failed: {err:?}"))?
}
} else if params.decoder_bit_depth == 24 {
let output = output_i32.as_mut().ok_or("missing int24 output")?;
let out_slice = &mut output[offset..end];
if params.custom_decode {
let dec = match custom_decoder.as_mut() {
Some(dec) => dec,
None => return Err(String::from("missing custom decoder")),
};
opus_custom_decode24(dec, Some(packet_slice), out_slice, frame_size)
.map_err(|err| format!("opus_custom_decode24 failed: {err:?}"))?
} else {
let dec = match opus_decoder.as_mut() {
Some(dec) => dec,
None => return Err(String::from("missing opus decoder")),
};
opus_decode24(dec, Some(packet_slice), len, out_slice, frame_size, false)
.map_err(|err| format!("opus_decode24 failed: {err:?}"))?
}
} else {
let output = output_i16.as_mut().ok_or("missing int16 output")?;
let out_slice = &mut output[offset..end];
if params.custom_decode {
let dec = match custom_decoder.as_mut() {
Some(dec) => dec,
None => return Err(String::from("missing custom decoder")),
};
opus_custom_decode(dec, Some(packet_slice), out_slice, frame_size)
.map_err(|err| format!("opus_custom_decode failed: {err:?}"))?
} else {
let dec = match opus_decoder.as_mut() {
Some(dec) => dec,
None => return Err(String::from("missing opus decoder")),
};
opus_decode(dec, Some(packet_slice), len, out_slice, frame_size, false)
.map_err(|err| format!("opus_decode failed: {err:?}"))?
}
};
if samples_decoded != frame_size {
return Err(format!(
"decode returned {samples_decoded} samples (expected {frame_size})"
));
}
samp_count += frame_size;
}
Ok(())
}
#[cfg(feature = "custom_modes")]
#[test]
fn opus_custom_encoder_decoder_creation_various_configs() {
let sample_rates = [8000, 12000, 16000, 24000, 48000];
let channels_opts = [1, 2];
let frame_sizes_ms_x2 = [5, 10, 20, 40];
let mut rng = FastRand::new(12345);
let mut success_count = 0;
for &sample_rate in &sample_rates {
for &num_channels in &channels_opts {
for &frame_size_ms_x2 in &frame_sizes_ms_x2 {
let frame_size = frame_size_ms_x2 * sample_rate / 2000;
if (sample_rate == 8000 || sample_rate == 12000) && frame_size_ms_x2 == 5 {
continue;
}
let mode_result = opus_custom_mode_create(sample_rate as i32, frame_size);
let owned_mode = match mode_result {
Ok(m) => m,
Err(_) => continue, };
let mode = owned_mode.mode();
let encoder_result = opus_custom_encoder_create(
&mode,
sample_rate as i32,
num_channels,
rng.next(),
);
let mut encoder = match encoder_result {
Ok(enc) => enc,
Err(_) => continue,
};
let decoder_result = opus_custom_decoder_create(&mode, num_channels);
let _decoder = match decoder_result {
Ok(dec) => dec,
Err(_) => continue,
};
let bitrates = [12000, 24000, 48000, 96000];
let bitrate = rng.rand_sample(&bitrates);
let _ = opus_custom_encoder_ctl(
&mut encoder,
EncoderCtlRequest::SetBitrate(bitrate),
);
let _ = opus_custom_encoder_ctl(
&mut encoder,
EncoderCtlRequest::SetComplexity(rng.next() as i32 % 11),
);
success_count += 1;
}
}
}
assert!(
success_count > 0,
"No configurations were successfully tested"
);
}
#[cfg(feature = "custom_modes")]
#[test]
fn opus_custom_encode_processes_pcm_without_panic() {
let owned_mode = opus_custom_mode_create(48000, 960).expect("mode");
let mode = owned_mode.mode();
let mut encoder = opus_custom_encoder_create(&mode, 48000, 1, 12345).expect("encoder");
let input = generate_sine_sweep_i16(0.5, 48000, 1, 0.02);
let mut packet = vec![0u8; 1500];
let packet_cap = packet.len();
let result = opus_custom_encode(&mut encoder, &input, 960, &mut packet, packet_cap);
assert!(result.is_ok(), "Encode should not fail");
}
#[cfg(feature = "custom_modes")]
#[test]
fn opus_custom_decoder_handles_plc() {
let owned_mode = opus_custom_mode_create(48000, 960).expect("mode");
let mode = owned_mode.mode();
let mut decoder = opus_custom_decoder_create(&mode, 1).expect("decoder");
let mut output = vec![0i16; 960];
let _result = opus_custom_decode(&mut decoder, None, &mut output, 960);
}
#[cfg(feature = "custom_modes")]
#[test]
fn opus_custom_encoder_ctl_coverage() {
let owned_mode = opus_custom_mode_create(48000, 960).expect("mode");
let mode = owned_mode.mode();
let mut encoder = opus_custom_encoder_create(&mode, 48000, 2, 11111).expect("encoder");
for &bitrate in &[6000, 12000, 24000, 48000, 96000, 510000] {
let result =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetBitrate(bitrate));
assert!(result.is_ok(), "SetBitrate({}) failed", bitrate);
}
for &vbr in &[false, true] {
let result = opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetVbr(vbr));
assert!(result.is_ok(), "SetVbr({}) failed", vbr);
}
for &constraint in &[false, true] {
let result = opus_custom_encoder_ctl(
&mut encoder,
EncoderCtlRequest::SetVbrConstraint(constraint),
);
assert!(result.is_ok(), "SetVbrConstraint({}) failed", constraint);
}
for complexity in 0..=10 {
let result =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetComplexity(complexity));
assert!(result.is_ok(), "SetComplexity({}) failed", complexity);
}
for &loss in &[0, 1, 2, 5, 10] {
let result =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetPacketLossPerc(loss));
assert!(result.is_ok(), "SetPacketLossPerc({}) failed", loss);
}
for &depth in &[8, 16, 24] {
let result =
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetLsbDepth(depth));
assert!(result.is_ok(), "SetLsbDepth({}) failed", depth);
}
}
#[cfg(feature = "custom_modes")]
#[test]
fn opus_custom_float_encode_processes_without_panic() {
let owned_mode = opus_custom_mode_create(48000, 960).expect("mode");
let mode = owned_mode.mode();
let mut encoder = opus_custom_encoder_create(&mode, 48000, 1, 22222).expect("encoder");
let mut input = vec![0.0f32; 960];
for (i, sample) in input.iter_mut().enumerate() {
let t = i as f32 / 48000.0;
*sample = 0.5 * (2.0 * core::f32::consts::PI * 440.0 * t).sin();
}
let mut packet = vec![0u8; 1500];
let packet_cap = packet.len();
let result = opus_custom_encode_float(&mut encoder, &input, 960, &mut packet, packet_cap);
assert!(result.is_ok(), "Float encode should not fail");
}
#[cfg(feature = "custom_modes")]
#[test]
fn opus_custom_24bit_encode_processes_without_panic() {
let owned_mode = opus_custom_mode_create(48000, 960).expect("mode");
let mode = owned_mode.mode();
let mut encoder = opus_custom_encoder_create(&mode, 48000, 1, 33333).expect("encoder");
let mut input = vec![0i32; 960];
for (i, sample) in input.iter_mut().enumerate() {
let t = i as f64 / 48000.0;
let value = 0.5 * (2.0 * PI_F64 * 440.0 * t).sin();
*sample = (value * 8_388_607.0) as i32; }
let mut packet = vec![0u8; 1500];
let packet_cap = packet.len();
let result = opus_custom_encode24(&mut encoder, &input, 960, &mut packet, packet_cap);
assert!(result.is_ok(), "24-bit encode should not fail");
}
#[cfg(feature = "custom_modes")]
#[test]
fn opus_custom_float_decode_handles_packets() {
let owned_mode = opus_custom_mode_create(48000, 960).expect("mode");
let mode = owned_mode.mode();
let mut decoder = opus_custom_decoder_create(&mode, 1).expect("decoder");
let mut output = vec![0.0f32; 960];
let _result = opus_custom_decode_float(&mut decoder, None, &mut output, 960);
}
#[cfg(feature = "custom_modes")]
#[test]
fn opus_custom_24bit_decode_handles_packets() {
let owned_mode = opus_custom_mode_create(48000, 960).expect("mode");
let mode = owned_mode.mode();
let mut decoder = opus_custom_decoder_create(&mode, 1).expect("decoder");
let mut output = vec![0i32; 960];
let _result = opus_custom_decode24(&mut decoder, None, &mut output, 960);
}
#[cfg(feature = "custom_modes")]
#[test]
#[cfg_attr(miri, ignore = "custom fuzz test is too slow under Miri")]
fn opus_custom_encode_decode_roundtrip() {
let seed = seed_from_env();
let (num_encoders_to_fuzz, num_setting_changes, duration_seconds) = custom_fuzz_settings();
let mut rng = FastRand::new(seed);
let sampling_rates = [8000, 12000, 16000, 24000, 48000];
let channels = [1usize, 2];
let bitrates = [
6000,
12000,
16000,
24000,
32000,
48000,
64000,
96000,
510000,
OPUS_BITRATE_MAX,
];
let use_vbr = [false, true, true];
let vbr_constraints = [false, true, true];
let complexities = [0i32, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let packet_loss_perc = [0i32, 1, 2, 5];
let lsb_depths = [8i32, 24];
let frame_sizes_ms_x2 = [5usize, 10, 20, 40];
#[cfg(not(feature = "fixed_point"))]
let use_float_encode = [false, true];
#[cfg(not(feature = "fixed_point"))]
let use_float_decode = [false, true];
let use_custom_encode = [false, true];
let use_custom_decode = [false, true];
let encoder_bit_depths = [16i32, 24];
let decoder_bit_depths = [16i32, 24];
for _ in 0..num_encoders_to_fuzz {
let sample_rate = rng.rand_sample(&sampling_rates);
let mut params = TestCustomParams {
sample_rate,
num_channels: 1,
frame_size: 0,
float_encode: false,
float_decode: false,
custom_encode: true,
custom_decode: true,
encoder_bit_depth: 16,
decoder_bit_depth: 16,
};
if sample_rate == 48_000 {
params.custom_encode = rng.rand_sample(&use_custom_encode);
params.custom_decode = rng.rand_sample(&use_custom_decode);
if !(params.custom_encode || params.custom_decode) {
continue;
}
}
params.num_channels = rng.rand_sample(&channels);
let frame_size_ms_x2 = rng.rand_sample(&frame_sizes_ms_x2);
params.frame_size = frame_size_ms_x2 * sample_rate as usize / 2000;
if (sample_rate == 8000 || sample_rate == 12000) && frame_size_ms_x2 == 5 {
continue;
}
let owned_mode = if params.custom_encode || params.custom_decode {
match opus_custom_mode_create(sample_rate, params.frame_size) {
Ok(mode) => Some(mode),
Err(_) => continue,
}
} else {
None
};
let mode_ref = owned_mode.as_ref().map(|mode| mode.mode());
let mut custom_encoder = if params.custom_encode {
let mode = mode_ref.as_ref().expect("custom mode");
Some(
opus_custom_encoder_create(
mode,
sample_rate as i32,
params.num_channels,
rng.next(),
)
.unwrap_or_else(|err| panic!("custom encoder create failed: {err:?}")),
)
} else {
None
};
let mut opus_encoder = if !params.custom_encode {
Some(
opus_encoder_create(
sample_rate as i32,
params.num_channels as i32,
OPUS_APPLICATION_RESTRICTED_LOWDELAY,
)
.unwrap_or_else(|err| panic!("opus encoder create failed: {err:?}")),
)
} else {
None
};
let mut custom_decoder = if params.custom_decode {
let mode = mode_ref.as_ref().expect("custom mode");
Some(
opus_custom_decoder_create(mode, params.num_channels)
.unwrap_or_else(|err| panic!("custom decoder create failed: {err:?}")),
)
} else {
None
};
let mut custom_decoder_copy = if params.custom_decode {
let mode = mode_ref.as_ref().expect("custom mode");
Some(
opus_custom_decoder_create(mode, params.num_channels)
.unwrap_or_else(|err| panic!("custom decoder copy failed: {err:?}")),
)
} else {
None
};
let mut opus_decoder = if !params.custom_decode {
Some(
opus_decoder_create(sample_rate as i32, params.num_channels as i32)
.unwrap_or_else(|err| panic!("opus decoder create failed: {err:?}")),
)
} else {
None
};
let mut opus_decoder_copy = if !params.custom_decode {
Some(
opus_decoder_create(sample_rate as i32, params.num_channels as i32)
.unwrap_or_else(|err| panic!("opus decoder copy failed: {err:?}")),
)
} else {
None
};
for _ in 0..num_setting_changes {
let bitrate = rng.rand_sample(&bitrates);
let vbr = rng.rand_sample(&use_vbr);
let vbr_constraint = rng.rand_sample(&vbr_constraints);
let complexity = rng.rand_sample(&complexities);
let pkt_loss = rng.rand_sample(&packet_loss_perc);
let lsb_depth = rng.rand_sample(&lsb_depths);
#[cfg(not(feature = "fixed_point"))]
{
params.float_encode = rng.rand_sample(&use_float_encode);
params.float_decode = rng.rand_sample(&use_float_decode);
}
#[cfg(feature = "fixed_point")]
{
params.float_encode = false;
params.float_decode = false;
}
params.encoder_bit_depth = rng.rand_sample(&encoder_bit_depths);
params.decoder_bit_depth = rng.rand_sample(&decoder_bit_depths);
let context = format!(
"test_opus_custom: {} kHz, {} ch, float_encode: {}, float_decode: {}, \
encoder_bit_depth: {}, decoder_bit_depth: {}, custom_encode: {}, custom_decode: {}, \
{} bps, vbr: {}, vbr constraint: {}, complexity: {}, pkt loss: {}%, lsb depth: {}, ({}/2) ms",
sample_rate / 1000,
params.num_channels,
params.float_encode as i32,
params.float_decode as i32,
params.encoder_bit_depth,
params.decoder_bit_depth,
params.custom_encode as i32,
params.custom_decode as i32,
bitrate,
vbr as i32,
vbr_constraint as i32,
complexity,
pkt_loss,
lsb_depth,
frame_size_ms_x2
);
if params.custom_encode {
let enc = custom_encoder.as_mut().expect("custom encoder");
opus_custom_encoder_ctl(enc, EncoderCtlRequest::SetBitrate(bitrate))
.unwrap_or_else(|err| panic!("{context}: set bitrate failed: {err:?}"));
opus_custom_encoder_ctl(enc, EncoderCtlRequest::SetVbr(vbr))
.unwrap_or_else(|err| panic!("{context}: set vbr failed: {err:?}"));
opus_custom_encoder_ctl(
enc,
EncoderCtlRequest::SetVbrConstraint(vbr_constraint),
)
.unwrap_or_else(|err| panic!("{context}: set vbr constraint failed: {err:?}"));
opus_custom_encoder_ctl(enc, EncoderCtlRequest::SetComplexity(complexity))
.unwrap_or_else(|err| panic!("{context}: set complexity failed: {err:?}"));
opus_custom_encoder_ctl(enc, EncoderCtlRequest::SetPacketLossPerc(pkt_loss))
.unwrap_or_else(|err| panic!("{context}: set packet loss failed: {err:?}"));
opus_custom_encoder_ctl(enc, EncoderCtlRequest::SetLsbDepth(lsb_depth))
.unwrap_or_else(|err| panic!("{context}: set lsb depth failed: {err:?}"));
} else {
let enc = opus_encoder.as_mut().expect("opus encoder");
opus_encoder_ctl(enc, OpusEncoderCtlRequest::SetBitrate(bitrate))
.unwrap_or_else(|err| panic!("{context}: set bitrate failed: {err:?}"));
opus_encoder_ctl(enc, OpusEncoderCtlRequest::SetVbr(vbr))
.unwrap_or_else(|err| panic!("{context}: set vbr failed: {err:?}"));
opus_encoder_ctl(enc, OpusEncoderCtlRequest::SetVbrConstraint(vbr_constraint))
.unwrap_or_else(|err| {
panic!("{context}: set vbr constraint failed: {err:?}")
});
opus_encoder_ctl(enc, OpusEncoderCtlRequest::SetComplexity(complexity))
.unwrap_or_else(|err| panic!("{context}: set complexity failed: {err:?}"));
opus_encoder_ctl(enc, OpusEncoderCtlRequest::SetPacketLossPerc(pkt_loss))
.unwrap_or_else(|err| panic!("{context}: set packet loss failed: {err:?}"));
opus_encoder_ctl(enc, OpusEncoderCtlRequest::SetLsbDepth(lsb_depth))
.unwrap_or_else(|err| panic!("{context}: set lsb depth failed: {err:?}"));
}
if let Err(err) = run_custom_test(
¶ms,
duration_seconds,
custom_encoder.as_mut().map(|enc| &mut **enc),
opus_encoder.as_mut(),
custom_decoder.as_mut().map(|dec| &mut **dec),
opus_decoder.as_mut(),
custom_decoder_copy.as_mut().map(|dec| &mut **dec),
opus_decoder_copy.as_mut(),
&mut rng,
) {
panic!("{context} failed: {err} (seed {seed})");
}
}
}
}
#[test]
fn celt_alloc_trace_output() {
let path = match std::env::var("CELT_TRACE_PCM") {
Ok(value) => value,
Err(_) => return,
};
let frames = std::env::var("CELT_TRACE_FRAMES")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(64);
let mut file = std::fs::File::open(&path).expect("open CELT_TRACE_PCM");
let mut encoder =
crate::opus_encoder::opus_encoder_create(48_000, 2, 2049).expect("encoder init");
crate::opus_encoder::opus_encoder_ctl(
&mut encoder,
crate::opus_encoder::OpusEncoderCtlRequest::SetBitrate(64_000),
)
.expect("set bitrate");
let channels = 2usize;
let mut input_bytes = vec![0u8; 960 * channels * 2];
let mut input_pcm = vec![0i16; 960 * channels];
let mut packet = vec![0u8; 3 * 1276];
for _frame_idx in 0..frames {
match std::io::Read::read_exact(&mut file, &mut input_bytes) {
Ok(()) => {}
Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => break,
Err(err) => panic!("read CELT_TRACE_PCM failed: {err}"),
}
for (sample, chunk) in input_pcm.iter_mut().zip(input_bytes.chunks_exact(2)) {
*sample = i16::from_le_bytes([chunk[0], chunk[1]]);
}
let _ = crate::opus_encoder::opus_encode(&mut encoder, &input_pcm, 960, &mut packet)
.expect("opus_encode");
}
}
}