#![allow(dead_code)]
use alloc::vec;
use alloc::vec::Vec;
#[cfg(test)]
extern crate std;
use crate::celt::BandCodingState;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::bands::anti_collapse;
use crate::celt::bands::celt_lcg_rand;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::bands::denormalise_bands;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::bands::quant_all_bands;
#[cfg(feature = "fixed_point")]
use crate::celt::bands::{anti_collapse_fixed, quant_all_bands_decode_fixed};
#[cfg(feature = "fixed_point")]
use crate::celt::bands::{denormalise_bands_fixed, denormalise_bands_fixed_native};
#[cfg(any(not(feature = "fixed_point"), test))]
use crate::celt::celt::comb_filter;
#[cfg(feature = "fixed_point")]
use crate::celt::celt::comb_filter_fixed;
#[cfg(feature = "fixed_point")]
use crate::celt::celt::comb_filter_fixed_in_place;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::celt::comb_filter_in_place;
use crate::celt::celt::{COMBFILTER_MINPERIOD, TF_SELECT_TABLE, init_caps, resampling_factor};
use crate::celt::cpu_support::{OPUS_ARCHMASK, opus_select_arch};
use crate::celt::entcode::{self, BITRES};
use crate::celt::entdec::EcDec;
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_arch::{
DB_SHIFT, Q15_ONE, SIG_SHIFT, int16tosig, res2float as fixed_res_to_float,
sig2res as fixed_sig_to_res, sig2word16,
};
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_ops::{
add32_ovflw, mult16_16_q15, mult16_32_q15, pshr32, qconst16, qconst16_clamped, qconst32,
shl32 as shl32_fixed,
};
use crate::celt::float_cast::CELT_SIG_SCALE;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::float_cast::float2int;
use crate::celt::float_cast::float2int16;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::lpc::{celt_autocorr, celt_iir, celt_lpc};
#[cfg(feature = "fixed_point")]
use crate::celt::lpc::{celt_autocorr_fixed, celt_fir_fixed, celt_iir_fixed, celt_lpc_fixed};
#[cfg(feature = "fixed_point")]
use crate::celt::math::celt_zlog2;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::math::{celt_sqrt, frac_div32};
#[cfg(feature = "fixed_point")]
use crate::celt::math_fixed::{
celt_maxabs16, celt_sqrt as celt_sqrt_fixed, frac_div32 as frac_div32_fixed,
};
#[cfg(not(feature = "fixed_point"))]
use crate::celt::mdct::clt_mdct_backward;
#[cfg(feature = "fixed_point")]
use crate::celt::mdct_fixed::{FixedMdctLookup, clt_mdct_backward_fixed};
use crate::celt::modes::{opus_custom_mode_find_static, opus_custom_mode_find_static_ref};
#[cfg(not(feature = "fixed_point"))]
use crate::celt::pitch::{pitch_downsample, pitch_search};
#[cfg(feature = "fixed_point")]
use crate::celt::pitch::{pitch_downsample_fixed, pitch_search_fixed};
#[cfg(not(feature = "fixed_point"))]
use crate::celt::quant_bands::unquant_energy_finalise;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::quant_bands::{unquant_coarse_energy, unquant_fine_energy};
#[cfg(feature = "fixed_point")]
use crate::celt::quant_bands::{
unquant_coarse_energy_fixed, unquant_energy_finalise_fixed, unquant_fine_energy_fixed,
};
use crate::celt::rate::clt_compute_allocation_with_scratch;
#[cfg(any(feature = "fixed_point", feature = "deep_plc"))]
use crate::celt::types::OpusInt16;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::types::OpusVal32;
use crate::celt::types::{
CeltGlog, CeltNorm, CeltSig, OpusCustomDecoder, OpusCustomMode, OpusInt32, OpusRes, OpusUint32,
OpusVal16,
};
#[cfg(feature = "fixed_point")]
use crate::celt::types::{
FixedCeltCoef, FixedCeltGlog, FixedCeltNorm, FixedCeltSig, FixedOpusVal16,
};
use crate::celt::vq::SPREAD_NORMAL;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::vq::renormalise_vector;
#[cfg(feature = "fixed_point")]
use crate::celt::vq::renormalise_vector_fixed;
#[cfg(feature = "deep_plc")]
use crate::celt::{
LpcNetPlcState, PLC_FRAME_SIZE, PLC_UPDATE_SAMPLES, PREEMPHASIS, SINC_FILTER, SINC_ORDER,
update_plc_state,
};
#[cfg(not(feature = "fixed_point"))]
use core::cmp::Ordering;
use core::cmp::{max, min};
#[cfg(feature = "deep_plc")]
type PlcHandle<'a> = Option<&'a mut LpcNetPlcState>;
#[cfg(not(feature = "deep_plc"))]
type PlcHandle<'a> = ();
#[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")]
#[inline]
fn lpc_from_fixed(value: FixedOpusVal16) -> OpusVal16 {
value as f32 / (1 << 12) as f32
}
#[cfg(feature = "fixed_point")]
#[inline]
fn lpc_to_fixed(value: OpusVal16) -> FixedOpusVal16 {
qconst16(value as f64, 12)
}
#[cfg(feature = "fixed_point")]
fn sync_from_fixed_primary_to_float_cache(decoder: &mut OpusCustomDecoder<'_>) {
debug_assert_eq!(decoder.decode_mem_fixed.len(), decoder.decode_mem.len());
debug_assert_eq!(decoder.lpc_fixed.len(), decoder.lpc.len());
debug_assert_eq!(decoder.old_ebands_fixed.len(), decoder.old_ebands.len());
debug_assert_eq!(decoder.old_log_e_fixed.len(), decoder.old_log_e.len());
debug_assert_eq!(decoder.old_log_e2_fixed.len(), decoder.old_log_e2.len());
debug_assert_eq!(
decoder.background_log_e_fixed.len(),
decoder.background_log_e.len()
);
for (dst, &src) in decoder
.decode_mem
.iter_mut()
.zip(decoder.decode_mem_fixed.iter())
{
*dst = fixed_sig_to_float(src);
}
for (dst, &src) in decoder.lpc.iter_mut().zip(decoder.lpc_fixed.iter()) {
*dst = lpc_from_fixed(src);
}
for (dst, &src) in decoder
.preemph_mem_decoder
.iter_mut()
.zip(decoder.fixed_preemph_mem_decoder.iter())
{
*dst = fixed_sig_to_float(src);
}
sync_loge_from_fixed(&mut decoder.old_ebands, &decoder.old_ebands_fixed);
sync_loge_from_fixed(&mut decoder.old_log_e, &decoder.old_log_e_fixed);
sync_loge_from_fixed(&mut decoder.old_log_e2, &decoder.old_log_e2_fixed);
sync_loge_from_fixed(
&mut decoder.background_log_e,
&decoder.background_log_e_fixed,
);
}
#[cfg(feature = "fixed_point")]
fn sync_from_float_cache_to_fixed_primary(decoder: &mut OpusCustomDecoder<'_>) {
debug_assert_eq!(decoder.decode_mem_fixed.len(), decoder.decode_mem.len());
debug_assert_eq!(decoder.lpc_fixed.len(), decoder.lpc.len());
debug_assert_eq!(decoder.old_ebands_fixed.len(), decoder.old_ebands.len());
debug_assert_eq!(decoder.old_log_e_fixed.len(), decoder.old_log_e.len());
debug_assert_eq!(decoder.old_log_e2_fixed.len(), decoder.old_log_e2.len());
debug_assert_eq!(
decoder.background_log_e_fixed.len(),
decoder.background_log_e.len()
);
for (dst, &src) in decoder
.decode_mem_fixed
.iter_mut()
.zip(decoder.decode_mem.iter())
{
*dst = celt_sig_to_fixed(src);
}
for (dst, &src) in decoder.lpc_fixed.iter_mut().zip(decoder.lpc.iter()) {
*dst = lpc_to_fixed(src);
}
for (dst, &src) in decoder
.fixed_preemph_mem_decoder
.iter_mut()
.zip(decoder.preemph_mem_decoder.iter())
{
*dst = celt_sig_to_fixed(src);
}
sync_loge_to_fixed(&mut decoder.old_ebands_fixed, &decoder.old_ebands);
sync_loge_to_fixed(&mut decoder.old_log_e_fixed, &decoder.old_log_e);
sync_loge_to_fixed(&mut decoder.old_log_e2_fixed, &decoder.old_log_e2);
sync_loge_to_fixed(
&mut decoder.background_log_e_fixed,
&decoder.background_log_e,
);
}
#[cfg(feature = "fixed_point")]
fn sync_fixed_output_window_to_float_cache(
decoder: &mut OpusCustomDecoder<'_>,
channels: usize,
output_start: usize,
output_len: usize,
) {
let stride = DECODE_BUFFER_SIZE + decoder.overlap;
for channel in 0..channels {
let base = channel
.checked_mul(stride)
.expect("channel stride multiplication overflow");
for i in 0..output_len {
let idx = base + output_start + i;
decoder.decode_mem[idx] = fixed_sig_to_float(decoder.decode_mem_fixed[idx]);
}
}
}
const LPC_ORDER: usize = 24;
pub(crate) const DECODE_BUFFER_SIZE: usize = 2048;
const MAX_PERIOD: i32 = 1024;
const PLC_PITCH_LAG_MAX: i32 = 720;
const PLC_PITCH_LAG_MIN: i32 = 100;
const SIG_SAT: CeltSig = 536_870_911.0;
#[cfg(feature = "fixed_point")]
const FIXED_SIG_SAT: FixedCeltSig = 536_870_911;
#[cfg(feature = "fixed_point")]
type FixedSynthesisCtx<'a> = (&'a FixedMdctLookup, &'a [FixedCeltCoef], usize);
#[cfg(not(feature = "fixed_point"))]
type FixedSynthesisCtx<'a> = ();
#[cfg(feature = "fixed_point")]
fn fixed_sig_to_float(value: FixedCeltSig) -> f32 {
value as f32 / (1u32 << SIG_SHIFT) as f32
}
#[cfg(feature = "fixed_point")]
#[inline]
fn celt_sig_to_fixed(value: CeltSig) -> FixedCeltSig {
libm::rintf(value * (1u32 << SIG_SHIFT) as f32).clamp(i32::MIN as f32, i32::MAX as f32)
as FixedCeltSig
}
#[cfg(feature = "fixed_point")]
#[inline]
fn fixed_sig_to_word16(value: CeltSig) -> OpusInt16 {
sig2word16(celt_sig_to_fixed(value))
}
#[cfg(feature = "fixed_point")]
#[inline]
fn q15_to_float(value: FixedOpusVal16) -> f32 {
value as f32 / (1u32 << 15) as f32
}
#[cfg(not(feature = "fixed_point"))]
#[inline]
fn decoder_noise_renormalise_runtime(x: &mut [OpusVal16], n: usize, gain: f32, arch: i32) {
renormalise_vector(x, n, gain, arch);
}
#[cfg(feature = "fixed_point")]
#[inline]
fn fixed_norm_to_float(value: FixedOpusVal16) -> OpusVal16 {
f32::from(value) * (1.0 / 32_768.0)
}
#[cfg(feature = "fixed_point")]
#[inline]
fn fixed_norm_slice_to_float(dst: &mut [OpusVal16], src: &[FixedCeltNorm]) {
debug_assert_eq!(dst.len(), src.len());
for (out, &value) in dst.iter_mut().zip(src.iter()) {
*out = fixed_norm_to_float(value);
}
}
#[cfg(feature = "fixed_point")]
#[inline]
fn float_norm_slice_to_fixed(dst: &mut [FixedCeltNorm], src: &[OpusVal16]) {
debug_assert_eq!(dst.len(), src.len());
for (out, &value) in dst.iter_mut().zip(src.iter()) {
*out = float2int16(value);
}
}
#[cfg(feature = "fixed_point")]
#[inline]
fn gain_to_q31(gain: f32) -> i32 {
qconst32(f64::from(gain), 31)
}
#[cfg(feature = "fixed_point")]
#[inline]
fn raw_norm_float_to_i16(value: OpusVal16) -> FixedOpusVal16 {
libm::rintf(value).clamp(f32::from(i16::MIN), f32::from(i16::MAX)) as FixedOpusVal16
}
#[cfg(feature = "fixed_point")]
fn decoder_noise_renormalise_runtime(x: &mut [OpusVal16], n: usize, gain: f32, arch: i32) {
assert!(x.len() >= n, "input vector shorter than band size");
let mut fixed_x: Vec<FixedOpusVal16> = x
.iter()
.take(n)
.map(|&sample| raw_norm_float_to_i16(sample))
.collect();
renormalise_vector_fixed(&mut fixed_x, n, gain_to_q31(gain), arch);
for (dst, &sample) in x.iter_mut().take(n).zip(fixed_x.iter()) {
*dst = fixed_norm_to_float(sample);
}
}
#[cfg(feature = "fixed_point")]
fn plc_decay_terms_fixed(exc_sig: &[CeltSig], exc_length: usize) -> (i32, i32, FixedOpusVal16) {
debug_assert!(!exc_sig.is_empty(), "excitation history cannot be empty");
debug_assert!(
exc_length <= exc_sig.len(),
"excitation length exceeds history"
);
let exc_i16: Vec<OpusInt16> = exc_sig
.iter()
.map(|&sample| fixed_sig_to_word16(sample))
.collect();
plc_decay_terms_fixed_native(&exc_i16, exc_length)
}
#[cfg(feature = "fixed_point")]
#[inline]
fn plc_ratio_from_energies_fixed(s1: i32, s2: i32) -> FixedOpusVal16 {
celt_sqrt_fixed(frac_div32_fixed(
(s1 >> 1).wrapping_add(1),
s2.wrapping_add(1),
)) as FixedOpusVal16
}
#[cfg(feature = "fixed_point")]
fn plc_ratio_terms_fixed(old_sig: &[CeltSig], new_sig: &[CeltSig]) -> (i32, i32, FixedOpusVal16) {
debug_assert_eq!(
old_sig.len(),
new_sig.len(),
"energy windows must have equal length"
);
let old_fixed: Vec<FixedCeltSig> = old_sig
.iter()
.map(|&sample| celt_sig_to_fixed(sample))
.collect();
let new_fixed: Vec<FixedCeltSig> = new_sig
.iter()
.map(|&sample| celt_sig_to_fixed(sample))
.collect();
plc_ratio_terms_fixed_native(&old_fixed, &new_fixed)
}
#[cfg(feature = "fixed_point")]
fn plc_decay_terms_fixed_native(
exc: &[FixedOpusVal16],
exc_length: usize,
) -> (i32, i32, FixedOpusVal16) {
debug_assert!(!exc.is_empty(), "excitation history cannot be empty");
debug_assert!(exc_length <= exc.len(), "excitation length exceeds history");
let history_len = exc.len();
let start = history_len.saturating_sub(exc_length);
let maxabs = celt_maxabs16(&exc[start..]);
let shift = max(0, 2 * celt_zlog2(maxabs) - 20) as u32;
let decay_length = exc_length >> 1;
let mut e1 = 1i32;
let mut e2 = 1i32;
for i in 0..decay_length {
let a = i32::from(exc[history_len - decay_length + i]);
let b = i32::from(exc[history_len - 2 * decay_length + i]);
e1 = e1.wrapping_add((a.wrapping_mul(a)) >> shift);
e2 = e2.wrapping_add((b.wrapping_mul(b)) >> shift);
}
e1 = min(e1, e2);
let decay = celt_sqrt_fixed(frac_div32_fixed(e1 >> 1, e2)) as FixedOpusVal16;
(e1, e2, decay)
}
#[cfg(feature = "fixed_point")]
fn plc_ratio_terms_fixed_native(
old_sig: &[FixedCeltSig],
new_sig: &[FixedCeltSig],
) -> (i32, i32, FixedOpusVal16) {
debug_assert_eq!(
old_sig.len(),
new_sig.len(),
"energy windows must have equal length"
);
let mut s1 = 0i32;
let mut s2 = 0i32;
for (&old_sample, &new_sample) in old_sig.iter().zip(new_sig.iter()) {
let old = i32::from(sig2word16(old_sample));
let new = i32::from(sig2word16(new_sample));
s1 = s1.wrapping_add((old.wrapping_mul(old)) >> 10);
s2 = s2.wrapping_add((new.wrapping_mul(new)) >> 10);
}
let ratio = plc_ratio_from_energies_fixed(s1, s2);
(s1, s2, ratio)
}
#[cfg(feature = "fixed_point")]
#[inline]
fn postfilter_gain_to_fixed(value: OpusVal16) -> FixedOpusVal16 {
crate::celt::fixed_ops::qconst16_clamped(f64::from(value), 15)
}
#[cfg(not(feature = "fixed_point"))]
fn apply_inverse_mdct(
mode: &OpusCustomMode<'_>,
freq: &[CeltSig],
output: &mut [CeltSig],
bands: usize,
nb: usize,
shift: usize,
) {
if bands == 0 {
return;
}
let stride = bands;
assert!(freq.len() >= nb.saturating_mul(stride));
assert!(output.len() >= nb.saturating_mul(stride));
let mut temp = vec![0.0f32; nb];
for band in 0..bands {
for (idx, sample) in temp.iter_mut().enumerate() {
let src_index = band + idx * stride;
*sample = freq.get(src_index).copied().unwrap_or_default();
}
let start = band * nb;
clt_mdct_backward(
&mode.mdct,
&temp,
&mut output[start..],
mode.window,
mode.overlap,
shift,
1,
);
}
}
#[cfg(feature = "fixed_point")]
#[allow(clippy::too_many_arguments)]
fn apply_inverse_mdct_fixed(
fixed_mdct: &FixedMdctLookup,
fixed_window: &[FixedCeltCoef],
overlap: usize,
freq: &[FixedCeltSig],
output: &mut [CeltSig],
bands: usize,
nb: usize,
shift: usize,
) {
let mut fixed_output = vec![0; output.len()];
apply_inverse_mdct_fixed_native(
fixed_mdct,
fixed_window,
overlap,
freq,
&mut fixed_output,
bands,
nb,
shift,
);
for (dst, &src) in output.iter_mut().zip(fixed_output.iter()) {
*dst = fixed_sig_to_float(src);
}
}
#[cfg(feature = "fixed_point")]
#[allow(clippy::too_many_arguments)]
fn apply_inverse_mdct_fixed_native(
fixed_mdct: &FixedMdctLookup,
fixed_window: &[FixedCeltCoef],
overlap: usize,
freq: &[FixedCeltSig],
output: &mut [FixedCeltSig],
bands: usize,
nb: usize,
shift: usize,
) {
if bands == 0 {
return;
}
let stride = bands;
assert!(freq.len() >= nb.saturating_mul(stride));
assert!(output.len() >= nb.saturating_mul(stride));
let mut temp = vec![0; nb];
let mdct = fixed_mdct;
let window = fixed_window;
for band in 0..bands {
for (idx, slot) in temp.iter_mut().enumerate() {
let src_index = band + idx * stride;
*slot = freq.get(src_index).copied().unwrap_or_default();
}
let start = band * nb;
clt_mdct_backward_fixed(mdct, &temp, &mut output[start..], window, overlap, shift, 1);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn celt_synthesis(
mode: &OpusCustomMode<'_>,
x: &[CeltNorm],
out_syn: &mut [&mut [CeltSig]],
old_band_e: &[CeltGlog],
start: usize,
eff_end: usize,
coded_channels: usize,
output_channels: usize,
is_transient: bool,
lm: usize,
downsample: usize,
silence: bool,
fixed_ctx: FixedSynthesisCtx<'_>,
) {
assert!(output_channels <= out_syn.len());
assert!(coded_channels <= 2);
assert!(output_channels <= 2);
assert!(lm <= mode.max_lm);
assert!(eff_end <= mode.num_ebands);
assert!(downsample > 0);
#[cfg(not(feature = "fixed_point"))]
#[allow(clippy::let_unit_value)]
let _ = fixed_ctx;
let nb_ebands = mode.num_ebands;
let n = mode.short_mdct_size << lm;
let m = 1 << lm;
assert!(x.len() >= coded_channels * n);
assert!(old_band_e.len() >= coded_channels * nb_ebands);
for channel in out_syn.iter_mut().take(output_channels) {
assert!(channel.len() >= n);
}
let (bands, nb, shift) = if is_transient {
(m, mode.short_mdct_size, mode.max_lm)
} else {
(1, mode.short_mdct_size << lm, mode.max_lm - lm)
};
#[cfg(feature = "fixed_point")]
let (fixed_mdct, fixed_window, overlap) = fixed_ctx;
#[cfg(not(feature = "fixed_point"))]
let mut freq = vec![0.0f32; n];
#[cfg(feature = "fixed_point")]
let mut freq = vec![0; n];
match (output_channels, coded_channels) {
(2, 1) => {
let (left, right) = out_syn.split_at_mut(1);
let left_out = &mut *left[0];
let right_out = &mut *right[0];
#[cfg(not(feature = "fixed_point"))]
{
denormalise_bands(
mode,
&x[..n],
&mut freq,
&old_band_e[..nb_ebands],
start,
eff_end,
m,
downsample,
silence,
);
let freq_copy = freq.clone();
apply_inverse_mdct(mode, &freq_copy, left_out, bands, nb, shift);
apply_inverse_mdct(mode, &freq, right_out, bands, nb, shift);
}
#[cfg(feature = "fixed_point")]
{
denormalise_bands_fixed(
mode,
&x[..n],
&mut freq,
&old_band_e[..nb_ebands],
start,
eff_end,
m,
downsample,
silence,
);
let freq_copy = freq.clone();
apply_inverse_mdct_fixed(
fixed_mdct,
fixed_window,
overlap,
&freq_copy,
left_out,
bands,
nb,
shift,
);
apply_inverse_mdct_fixed(
fixed_mdct,
fixed_window,
overlap,
&freq,
right_out,
bands,
nb,
shift,
);
}
}
(1, 2) => {
let out = &mut *out_syn[0];
#[cfg(not(feature = "fixed_point"))]
{
denormalise_bands(
mode,
&x[..n],
&mut freq,
&old_band_e[..nb_ebands],
start,
eff_end,
m,
downsample,
silence,
);
let mut freq_other = vec![0.0f32; n];
denormalise_bands(
mode,
&x[n..n * 2],
&mut freq_other,
&old_band_e[nb_ebands..nb_ebands * 2],
start,
eff_end,
m,
downsample,
silence,
);
for (lhs, rhs) in freq.iter_mut().zip(freq_other.iter()) {
*lhs = 0.5 * (*lhs + *rhs);
}
apply_inverse_mdct(mode, &freq, out, bands, nb, shift);
}
#[cfg(feature = "fixed_point")]
{
denormalise_bands_fixed(
mode,
&x[..n],
&mut freq,
&old_band_e[..nb_ebands],
start,
eff_end,
m,
downsample,
silence,
);
let mut freq_other = vec![0; n];
denormalise_bands_fixed(
mode,
&x[n..n * 2],
&mut freq_other,
&old_band_e[nb_ebands..nb_ebands * 2],
start,
eff_end,
m,
downsample,
silence,
);
for (lhs, rhs) in freq.iter_mut().zip(freq_other.iter()) {
*lhs = pshr32(add32_ovflw(*lhs, *rhs), 1);
}
apply_inverse_mdct_fixed(
fixed_mdct,
fixed_window,
overlap,
&freq,
out,
bands,
nb,
shift,
);
}
}
_ => {
for channel in 0..output_channels {
let spectrum = &x[channel * n..(channel + 1) * n];
let energy = &old_band_e[channel * nb_ebands..(channel + 1) * nb_ebands];
#[cfg(not(feature = "fixed_point"))]
{
denormalise_bands(
mode, spectrum, &mut freq, energy, start, eff_end, m, downsample, silence,
);
let output = &mut *out_syn[channel];
apply_inverse_mdct(mode, &freq, output, bands, nb, shift);
}
#[cfg(feature = "fixed_point")]
{
denormalise_bands_fixed(
mode, spectrum, &mut freq, energy, start, eff_end, m, downsample, silence,
);
let output = &mut *out_syn[channel];
apply_inverse_mdct_fixed(
fixed_mdct,
fixed_window,
overlap,
&freq,
output,
bands,
nb,
shift,
);
}
}
}
}
for channel in out_syn.iter_mut().take(output_channels) {
for sample in (*channel).iter_mut().take(n) {
*sample = sample.clamp(-SIG_SAT, SIG_SAT);
}
}
}
#[cfg(feature = "fixed_point")]
#[allow(clippy::too_many_arguments)]
fn celt_synthesis_fixed_native(
mode: &OpusCustomMode<'_>,
x: &[FixedCeltNorm],
out_syn: &mut [&mut [FixedCeltSig]],
old_band_e: &[FixedCeltGlog],
start: usize,
eff_end: usize,
coded_channels: usize,
output_channels: usize,
is_transient: bool,
lm: usize,
downsample: usize,
silence: bool,
fixed_mdct: &FixedMdctLookup,
fixed_window: &[FixedCeltCoef],
overlap: usize,
) {
assert!(output_channels <= out_syn.len());
assert!(coded_channels <= 2);
assert!(output_channels <= 2);
assert!(lm <= mode.max_lm);
assert!(eff_end <= mode.num_ebands);
assert!(downsample > 0);
let nb_ebands = mode.num_ebands;
let n = mode.short_mdct_size << lm;
let m = 1 << lm;
assert!(x.len() >= coded_channels * n);
assert!(old_band_e.len() >= coded_channels * nb_ebands);
for channel in out_syn.iter_mut().take(output_channels) {
assert!(channel.len() >= n);
}
let (bands, nb, shift) = if is_transient {
(m, mode.short_mdct_size, mode.max_lm)
} else {
(1, mode.short_mdct_size << lm, mode.max_lm - lm)
};
let mut freq = vec![0; n];
match (output_channels, coded_channels) {
(2, 1) => {
let (left, right) = out_syn.split_at_mut(1);
let left_out = &mut *left[0];
let right_out = &mut *right[0];
denormalise_bands_fixed_native(
mode,
&x[..n],
&mut freq,
&old_band_e[..nb_ebands],
start,
eff_end,
m,
downsample,
silence,
);
let freq_copy = freq.clone();
apply_inverse_mdct_fixed_native(
fixed_mdct,
fixed_window,
overlap,
&freq_copy,
left_out,
bands,
nb,
shift,
);
apply_inverse_mdct_fixed_native(
fixed_mdct,
fixed_window,
overlap,
&freq,
right_out,
bands,
nb,
shift,
);
}
(1, 2) => {
let out = &mut *out_syn[0];
denormalise_bands_fixed_native(
mode,
&x[..n],
&mut freq,
&old_band_e[..nb_ebands],
start,
eff_end,
m,
downsample,
silence,
);
let mut freq_other = vec![0; n];
denormalise_bands_fixed_native(
mode,
&x[n..n * 2],
&mut freq_other,
&old_band_e[nb_ebands..nb_ebands * 2],
start,
eff_end,
m,
downsample,
silence,
);
for (lhs, rhs) in freq.iter_mut().zip(freq_other.iter()) {
*lhs = pshr32(add32_ovflw(*lhs, *rhs), 1);
}
apply_inverse_mdct_fixed_native(
fixed_mdct,
fixed_window,
overlap,
&freq,
out,
bands,
nb,
shift,
);
}
_ => {
for channel in 0..output_channels {
let spectrum = &x[channel * n..(channel + 1) * n];
let energy = &old_band_e[channel * nb_ebands..(channel + 1) * nb_ebands];
denormalise_bands_fixed_native(
mode, spectrum, &mut freq, energy, start, eff_end, m, downsample, silence,
);
let output = &mut *out_syn[channel];
apply_inverse_mdct_fixed_native(
fixed_mdct,
fixed_window,
overlap,
&freq,
output,
bands,
nb,
shift,
);
}
}
}
for channel in out_syn.iter_mut().take(output_channels) {
for sample in (*channel).iter_mut().take(n) {
*sample = (*sample).clamp(-FIXED_SIG_SAT, FIXED_SIG_SAT);
}
}
}
fn celt_plc_pitch_search(decode_mem: &[&[CeltSig]], channels: usize, arch: i32) -> i32 {
if channels == 0 {
return PLC_PITCH_LAG_MAX;
}
let mut channel_views = Vec::with_capacity(channels);
for (idx, channel) in decode_mem.iter().take(channels).enumerate() {
debug_assert!(
channel.len() >= DECODE_BUFFER_SIZE,
"channel {idx} must expose at least DECODE_BUFFER_SIZE samples",
);
let end = DECODE_BUFFER_SIZE.min(channel.len());
channel_views.push(&channel[..end]);
}
if channel_views.is_empty() {
return PLC_PITCH_LAG_MAX;
}
let offset = (PLC_PITCH_LAG_MAX >> 1) as usize;
let max_pitch = (PLC_PITCH_LAG_MAX - PLC_PITCH_LAG_MIN) as usize;
let target_len = DECODE_BUFFER_SIZE.saturating_sub(PLC_PITCH_LAG_MAX as usize);
if target_len == 0 {
return PLC_PITCH_LAG_MAX;
}
#[cfg(not(feature = "fixed_point"))]
{
let mut lp_pitch_buf = vec![0.0f32; DECODE_BUFFER_SIZE >> 1];
pitch_downsample(&channel_views, &mut lp_pitch_buf, DECODE_BUFFER_SIZE, arch);
if offset >= lp_pitch_buf.len() {
return PLC_PITCH_LAG_MAX;
}
let pitch_index = pitch_search(
&lp_pitch_buf[offset..],
&lp_pitch_buf,
target_len,
max_pitch,
arch,
);
PLC_PITCH_LAG_MAX - pitch_index
}
#[cfg(feature = "fixed_point")]
{
let mut fixed_channels = Vec::with_capacity(channel_views.len());
for channel in &channel_views {
let mut fixed = Vec::with_capacity(DECODE_BUFFER_SIZE);
for &sample in *channel {
fixed.push(celt_sig_to_fixed(sample));
}
fixed_channels.push(fixed);
}
let fixed_views: Vec<&[FixedCeltSig]> = fixed_channels.iter().map(Vec::as_slice).collect();
let mut lp_pitch_buf = vec![0i16; DECODE_BUFFER_SIZE >> 1];
pitch_downsample_fixed(&fixed_views, &mut lp_pitch_buf, DECODE_BUFFER_SIZE, arch);
if offset >= lp_pitch_buf.len() {
return PLC_PITCH_LAG_MAX;
}
let pitch_index = pitch_search_fixed(
&lp_pitch_buf[offset..],
&lp_pitch_buf,
target_len,
max_pitch,
arch,
);
PLC_PITCH_LAG_MAX - pitch_index
}
}
#[cfg(feature = "fixed_point")]
fn celt_plc_pitch_search_fixed(decode_mem: &[&[FixedCeltSig]], channels: usize, arch: i32) -> i32 {
if channels == 0 {
return PLC_PITCH_LAG_MAX;
}
let mut channel_views = Vec::with_capacity(channels);
for (idx, channel) in decode_mem.iter().take(channels).enumerate() {
debug_assert!(
channel.len() >= DECODE_BUFFER_SIZE,
"channel {idx} must expose at least DECODE_BUFFER_SIZE samples",
);
let end = DECODE_BUFFER_SIZE.min(channel.len());
channel_views.push(&channel[..end]);
}
if channel_views.is_empty() {
return PLC_PITCH_LAG_MAX;
}
let offset = (PLC_PITCH_LAG_MAX >> 1) as usize;
let max_pitch = (PLC_PITCH_LAG_MAX - PLC_PITCH_LAG_MIN) as usize;
let target_len = DECODE_BUFFER_SIZE.saturating_sub(PLC_PITCH_LAG_MAX as usize);
if target_len == 0 {
return PLC_PITCH_LAG_MAX;
}
let mut lp_pitch_buf = vec![0i16; DECODE_BUFFER_SIZE >> 1];
pitch_downsample_fixed(&channel_views, &mut lp_pitch_buf, DECODE_BUFFER_SIZE, arch);
if offset >= lp_pitch_buf.len() {
return PLC_PITCH_LAG_MAX;
}
let pitch_index = pitch_search_fixed(
&lp_pitch_buf[offset..],
&lp_pitch_buf,
target_len,
max_pitch,
arch,
);
PLC_PITCH_LAG_MAX - pitch_index
}
#[cfg(feature = "fixed_point")]
fn plc_bandwidth_expand_for_iir(lpc: &mut [FixedOpusVal16]) {
loop {
let mut sum = i32::from(qconst16(1.0, SIG_SHIFT));
for &coeff in lpc.iter() {
sum = sum.wrapping_add(i32::from(coeff).abs());
}
if sum < 65_535 {
break;
}
let mut tmp = Q15_ONE;
for coeff in lpc.iter_mut() {
tmp = mult16_16_q15(qconst16(0.99, 15), tmp);
*coeff = mult16_16_q15(*coeff, tmp);
}
}
}
#[cfg(feature = "fixed_point")]
fn celt_decode_lost_pitch_fixed(
decoder: &mut OpusCustomDecoder<'_>,
n: usize,
overlap: usize,
pitch_index: i32,
fade: FixedOpusVal16,
arch: i32,
) {
let channels = decoder.channels;
let stride = DECODE_BUFFER_SIZE + overlap;
let max_period = MAX_PERIOD as usize;
debug_assert!(pitch_index > 0);
let loss_duration = decoder.loss_duration;
let pitch_index_usize = pitch_index as usize;
let exc_length = min(2 * pitch_index_usize, max_period);
let start_index = DECODE_BUFFER_SIZE - n;
let extrapolation_len = n + overlap;
let fixed_window = decoder.fixed_window.clone();
let (decode_mem_fixed, lpc_fixed) = (&mut decoder.decode_mem_fixed, &mut decoder.lpc_fixed);
for ch in 0..channels {
let channel_base = ch * stride;
let lpc_base = ch * LPC_ORDER;
let channel_mem = &mut decode_mem_fixed[channel_base..channel_base + stride];
let mut exc = vec![0i16; max_period + LPC_ORDER];
for (i, slot) in exc.iter_mut().enumerate() {
let src = DECODE_BUFFER_SIZE - max_period - LPC_ORDER + i;
*slot = sig2word16(channel_mem[src]);
}
if loss_duration == 0 {
let mut ac = [0i32; LPC_ORDER + 1];
let input = &exc[LPC_ORDER..LPC_ORDER + max_period];
let window = if overlap == 0 {
None
} else {
Some(&fixed_window[..overlap])
};
celt_autocorr_fixed(input, &mut ac, window, overlap, LPC_ORDER, arch);
ac[0] = ac[0].wrapping_add(ac[0] >> 13);
for (i, entry) in ac.iter_mut().enumerate().skip(1) {
*entry = entry.wrapping_sub(mult16_32_q15((2 * i * i) as i16, *entry));
}
let lpc = &mut lpc_fixed[lpc_base..lpc_base + LPC_ORDER];
#[cfg(test)]
if ch == 0 {
crate::test_trace::trace_println!("rust exc_first8={:?}", &input[..8]);
for (idx, value) in ac.iter().enumerate() {
crate::test_trace::trace_println!("rust ac[{idx}]={value}");
}
}
celt_lpc_fixed(lpc, &ac);
#[cfg(test)]
if ch == 0 {
crate::test_trace::trace_println!("rust lpc_after_solver={:?}", &lpc[..]);
}
plc_bandwidth_expand_for_iir(lpc);
}
let lpc_coeffs = &lpc_fixed[lpc_base..lpc_base + LPC_ORDER];
let fir_start = max_period - exc_length;
let mut fir_tmp = vec![0i16; exc_length];
#[cfg(test)]
if ch == 0 {
let mut h = 2166136261u32;
for &v in &exc {
let x = v as u16;
h = (h ^ u32::from(x & 0xFF)).wrapping_mul(16777619);
h = (h ^ u32::from(x >> 8)).wrapping_mul(16777619);
}
crate::test_trace::trace_println!("rust raw_exc_hash=0x{h:08x}");
crate::test_trace::trace_println!(
"rust fir_hist_first24={:?}",
&exc[fir_start..fir_start + LPC_ORDER]
);
crate::test_trace::trace_println!(
"rust fir_cur_first16={:?}",
&exc[fir_start + LPC_ORDER..fir_start + LPC_ORDER + 16]
);
}
celt_fir_fixed(
&exc[fir_start..fir_start + LPC_ORDER + exc_length],
lpc_coeffs,
&mut fir_tmp,
);
exc[LPC_ORDER + fir_start..LPC_ORDER + fir_start + exc_length].copy_from_slice(&fir_tmp);
#[cfg(test)]
if ch == 0 {
let mut h = 2166136261u32;
for &v in &exc {
let x = v as u16;
h = (h ^ u32::from(x & 0xFF)).wrapping_mul(16777619);
h = (h ^ u32::from(x >> 8)).wrapping_mul(16777619);
}
crate::test_trace::trace_println!("rust postfir_exc_hash=0x{h:08x}");
for i in 0..16 {
crate::test_trace::trace_println!(
"rust postfir[{i}]={}",
exc[LPC_ORDER + fir_start + i]
);
}
}
let (_, _, decay) =
plc_decay_terms_fixed_native(&exc[LPC_ORDER..LPC_ORDER + max_period], exc_length);
let move_len = DECODE_BUFFER_SIZE - n;
channel_mem.copy_within(n..n + move_len, 0);
let extrapolation_offset = max_period - pitch_index_usize;
#[cfg(test)]
if ch == 0 {
crate::test_trace::trace_println!(
"rust extrap_src_with_ord={:?}",
&exc[LPC_ORDER + extrapolation_offset..LPC_ORDER + extrapolation_offset + 16]
);
crate::test_trace::trace_println!(
"rust extrap_src_raw={:?}",
&exc[extrapolation_offset..extrapolation_offset + 16]
);
}
let reference_base = DECODE_BUFFER_SIZE - max_period - n + extrapolation_offset;
let mut attenuation = mult16_16_q15(fade, decay);
let mut s1 = 0i32;
let mut j = 0usize;
for i in 0..extrapolation_len {
if j >= pitch_index_usize {
j -= pitch_index_usize;
attenuation = mult16_16_q15(attenuation, decay);
}
let sample = mult16_16_q15(attenuation, exc[LPC_ORDER + extrapolation_offset + j]);
channel_mem[start_index + i] = int16tosig(sample);
let reference = i32::from(sig2word16(channel_mem[reference_base + j]));
s1 = s1.wrapping_add((reference.wrapping_mul(reference)) >> 10);
j += 1;
}
#[cfg(test)]
if ch == 0 {
let mut h = 2166136261u32;
for &v in &channel_mem[start_index..start_index + extrapolation_len] {
let x = v as u32;
for b in [x as u8, (x >> 8) as u8, (x >> 16) as u8, (x >> 24) as u8] {
h = (h ^ u32::from(b)).wrapping_mul(16777619);
}
}
crate::test_trace::trace_println!("rust preiir_hash=0x{h:08x} s1={s1} decay={decay}");
for i in 0..16 {
crate::test_trace::trace_println!(
"rust preiir[{i}]={}",
sig2word16(channel_mem[start_index + i])
);
}
}
let mut lpc_mem = vec![0i16; LPC_ORDER];
for (idx, mem) in lpc_mem.iter_mut().enumerate() {
*mem = sig2word16(channel_mem[start_index - 1 - idx]);
}
#[cfg(test)]
if ch == 0 {
let mut h = 2166136261u32;
for &v in &lpc_mem {
let x = v as u16;
h = (h ^ u32::from(x & 0xFF)).wrapping_mul(16777619);
h = (h ^ u32::from(x >> 8)).wrapping_mul(16777619);
}
crate::test_trace::trace_println!("rust lpc_mem_hash=0x{h:08x}");
}
let input = channel_mem[start_index..start_index + extrapolation_len].to_vec();
let mut filtered = vec![0i32; extrapolation_len];
celt_iir_fixed(&input, lpc_coeffs, &mut filtered, &mut lpc_mem);
for (dst, &sample) in channel_mem[start_index..start_index + extrapolation_len]
.iter_mut()
.zip(filtered.iter())
{
*dst = sample.clamp(-FIXED_SIG_SAT, FIXED_SIG_SAT);
}
#[cfg(test)]
if ch == 0 {
let mut h = 2166136261u32;
for &v in &channel_mem[start_index..start_index + extrapolation_len] {
let x = v as u32;
for b in [x as u8, (x >> 8) as u8, (x >> 16) as u8, (x >> 24) as u8] {
h = (h ^ u32::from(b)).wrapping_mul(16777619);
}
}
let mut hm = 2166136261u32;
for &v in &lpc_mem {
let x = v as u16;
hm = (hm ^ u32::from(x & 0xFF)).wrapping_mul(16777619);
hm = (hm ^ u32::from(x >> 8)).wrapping_mul(16777619);
}
crate::test_trace::trace_println!(
"rust postiir_hash=0x{h:08x} lpc_mem_after=0x{hm:08x}"
);
for i in 0..16 {
crate::test_trace::trace_println!(
"rust postiir[{i}]={}",
sig2word16(channel_mem[start_index + i])
);
}
}
let mut s2 = 0i32;
for &sample in &channel_mem[start_index..start_index + extrapolation_len] {
let word = i32::from(sig2word16(sample));
s2 = s2.wrapping_add((word.wrapping_mul(word)) >> 10);
}
#[cfg(test)]
if ch == 0 {
crate::test_trace::trace_println!("rust s2={s2}");
}
if s1 <= (s2 >> 2) {
for value in &mut channel_mem[start_index..start_index + extrapolation_len] {
*value = 0;
}
} else if s1 < s2 {
let ratio = plc_ratio_from_energies_fixed(s1, s2);
for i in 0..overlap {
let gain = Q15_ONE
.wrapping_sub(mult16_16_q15(fixed_window[i], Q15_ONE.wrapping_sub(ratio)));
channel_mem[start_index + i] = mult16_32_q15(gain, channel_mem[start_index + i]);
}
for i in overlap..extrapolation_len {
channel_mem[start_index + i] = mult16_32_q15(ratio, channel_mem[start_index + i]);
}
}
}
decoder.prefilter_and_fold = true;
sync_from_fixed_primary_to_float_cache(decoder);
}
fn prefilter_and_fold(decoder: &mut OpusCustomDecoder<'_>, n: usize) {
let channels = decoder.channels;
if channels == 0 {
return;
}
let overlap = decoder.overlap;
if overlap == 0 {
return;
}
debug_assert!(n <= DECODE_BUFFER_SIZE, "prefilter span exceeds history");
let stride = DECODE_BUFFER_SIZE + overlap;
debug_assert_eq!(decoder.decode_mem.len(), stride * channels);
let start = DECODE_BUFFER_SIZE
.checked_sub(n)
.expect("prefilter span exceeds decode buffer");
debug_assert!(
start + overlap <= stride,
"decode buffer lacks overlap tail"
);
debug_assert!(decoder.postfilter_tapset_old >= 0);
debug_assert!(decoder.postfilter_tapset >= 0);
let tapset0 = decoder.postfilter_tapset_old.max(0) as usize;
let tapset1 = decoder.postfilter_tapset.max(0) as usize;
#[cfg(not(feature = "fixed_point"))]
{
let mut etmp = vec![OpusVal32::default(); overlap];
let window = decoder.mode.window;
debug_assert!(window.len() >= overlap);
for channel in 0..channels {
let offset = channel * stride;
let channel_mem = &mut decoder.decode_mem[offset..offset + stride];
comb_filter(
&mut etmp,
channel_mem,
start,
overlap,
decoder.postfilter_period_old,
decoder.postfilter_period,
-decoder.postfilter_gain_old,
-decoder.postfilter_gain,
tapset0,
tapset1,
&[],
0,
decoder.arch,
);
for i in 0..(overlap / 2) {
let forward = window[i] * etmp[overlap - 1 - i];
let reverse = window[overlap - 1 - i] * etmp[i];
channel_mem[start + i] = forward + reverse;
}
}
}
#[cfg(feature = "fixed_point")]
{
debug_assert!(decoder.fixed_window.len() >= overlap);
let g0 = postfilter_gain_to_fixed(decoder.postfilter_gain_old).wrapping_neg();
let g1 = postfilter_gain_to_fixed(decoder.postfilter_gain).wrapping_neg();
for channel in 0..channels {
let offset = channel * stride;
let channel_mem_fixed = &mut decoder.decode_mem_fixed[offset..offset + stride];
let mut fixed_etmp = vec![0; overlap];
comb_filter_fixed(
&mut fixed_etmp,
channel_mem_fixed,
start,
overlap,
decoder.postfilter_period_old,
decoder.postfilter_period,
g0,
g1,
tapset0,
tapset1,
&decoder.fixed_window[..0],
0,
decoder.arch,
);
for i in 0..(overlap / 2) {
let forward = mult16_32_q15(decoder.fixed_window[i], fixed_etmp[overlap - 1 - i]);
let reverse = mult16_32_q15(decoder.fixed_window[overlap - 1 - i], fixed_etmp[i]);
let folded = add32_ovflw(forward, reverse);
let idx = start + i;
channel_mem_fixed[idx] = folded;
decoder.decode_mem[offset + idx] = fixed_sig_to_float(folded);
}
}
}
}
pub(crate) fn celt_decode_lost(
decoder: &mut OpusCustomDecoder<'_>,
n: usize,
lm: usize,
lpcnet: PlcHandle<'_>,
) {
#[cfg(feature = "fixed_point")]
sync_from_fixed_primary_to_float_cache(decoder);
celt_decode_lost_impl(decoder, n, lm, lpcnet);
}
fn celt_decode_lost_impl(
decoder: &mut OpusCustomDecoder<'_>,
n: usize,
lm: usize,
lpcnet: PlcHandle<'_>,
) {
#[cfg(feature = "deep_plc")]
let mut lpcnet = lpcnet;
#[cfg(not(feature = "deep_plc"))]
let _ = lpcnet;
let channels = decoder.channels;
if channels == 0 || n == 0 {
let increment = ((1usize) << lm) as i32;
decoder.loss_duration = min(10_000, decoder.loss_duration + increment);
return;
}
assert!(
channels <= MAX_CHANNELS,
"decoder only supports mono or stereo"
);
assert!(n <= DECODE_BUFFER_SIZE, "frame size exceeds decode history");
let mode = decoder.mode;
let overlap = mode.overlap;
let stride = DECODE_BUFFER_SIZE + overlap;
assert!(decoder.decode_mem.len() >= stride * channels);
let nb_ebands = mode.num_ebands;
assert!(mode.e_bands.len() > nb_ebands);
let raw_start = max(decoder.start_band, 0);
let start_band = min(raw_start as usize, nb_ebands);
let raw_end = max(decoder.end_band, raw_start);
let end_band = min(raw_end as usize, nb_ebands);
let eff_end = max(start_band, min(end_band, mode.effective_ebands));
let loss_duration = decoder.loss_duration;
#[cfg(feature = "deep_plc")]
let noise_based = if let Some(lpcnet) = lpcnet.as_ref() {
start_band != 0 || (lpcnet.fec_fill_pos == 0 && (decoder.skip_plc || loss_duration >= 80))
} else {
loss_duration >= 40 || start_band != 0 || decoder.skip_plc
};
#[cfg(not(feature = "deep_plc"))]
let noise_based = loss_duration >= 40 || start_band != 0 || decoder.skip_plc;
let arch = decoder.arch;
if noise_based {
let move_len = DECODE_BUFFER_SIZE
.checked_sub(n)
.expect("frame size larger than decode buffer")
+ overlap;
for channel_slice in decoder.decode_mem.chunks_mut(stride).take(channels) {
channel_slice.copy_within(n..n + move_len, 0);
}
if decoder.prefilter_and_fold {
prefilter_and_fold(decoder, n);
}
let decay = if loss_duration == 0 { 1.5_f32 } else { 0.5_f32 };
for ch in 0..channels {
let base = ch * nb_ebands;
for band in start_band..end_band {
let idx = base + band;
let background = decoder.background_log_e[idx];
let current = decoder.old_ebands[idx];
decoder.old_ebands[idx] = background.max(current - decay);
}
}
let mut seed = decoder.rng;
let mut spectrum = vec![0.0f32; channels * n];
for ch in 0..channels {
for band in start_band..eff_end {
let band_start = (mode.e_bands[band] as usize) << lm;
let width = mode.e_bands[band + 1] - mode.e_bands[band];
debug_assert!(width >= 0);
let band_width = ((width as usize) << lm).min(n.saturating_sub(band_start));
if band_width == 0 {
continue;
}
let offset = ch * n + band_start;
let slice = &mut spectrum[offset..offset + band_width];
for sample in slice.iter_mut() {
seed = celt_lcg_rand(seed);
*sample = ((seed as i32) >> 20) as f32;
}
decoder_noise_renormalise_runtime(slice, band_width, 1.0, arch);
}
}
decoder.rng = seed;
let start = DECODE_BUFFER_SIZE - n;
let downsample = max(decoder.downsample, 1) as usize;
#[cfg(feature = "fixed_point")]
let fixed_ctx = (
&decoder.fixed_mdct,
decoder.fixed_window.as_slice(),
decoder.overlap,
);
#[cfg(not(feature = "fixed_point"))]
let fixed_ctx = ();
{
let (decode_mem, old_ebands) = (&mut decoder.decode_mem, &decoder.old_ebands);
let mut outputs = Vec::with_capacity(channels);
for channel_slice in decode_mem.chunks_mut(stride).take(channels) {
outputs.push(&mut channel_slice[start..]);
}
celt_synthesis(
mode,
&spectrum,
&mut outputs,
old_ebands,
start_band,
eff_end,
channels,
channels,
false,
lm,
downsample,
false,
fixed_ctx,
);
}
decoder.prefilter_and_fold = false;
decoder.skip_plc = true;
#[cfg(feature = "fixed_point")]
sync_from_float_cache_to_fixed_primary(decoder);
} else {
#[cfg(feature = "fixed_point")]
{
let pitch_index = if loss_duration == 0 {
#[cfg(feature = "deep_plc")]
if let Some(lpcnet) = lpcnet.as_deref_mut() {
if lpcnet.loaded {
let mut views = Vec::with_capacity(channels);
for channel_slice in decoder.decode_mem.chunks(stride).take(channels) {
views.push(channel_slice);
}
update_plc_state(lpcnet, &views, &mut decoder.plc_preemphasis_mem);
}
}
let mut fixed_views = Vec::with_capacity(channels);
for channel_slice in decoder.decode_mem_fixed.chunks(stride).take(channels) {
fixed_views.push(channel_slice);
}
let search = celt_plc_pitch_search_fixed(&fixed_views, channels, arch);
decoder.last_pitch_index = search;
search
} else {
decoder.last_pitch_index
};
let fade = if loss_duration == 0 {
Q15_ONE
} else {
qconst16(0.8, 15)
};
celt_decode_lost_pitch_fixed(decoder, n, overlap, pitch_index, fade, arch);
}
#[cfg(not(feature = "fixed_point"))]
{
let pitch_index = if loss_duration == 0 {
let mut views = Vec::with_capacity(channels);
for channel_slice in decoder.decode_mem.chunks(stride).take(channels) {
views.push(channel_slice);
}
#[cfg(feature = "deep_plc")]
if let Some(lpcnet) = lpcnet.as_deref_mut() {
if lpcnet.loaded {
update_plc_state(lpcnet, &views, &mut decoder.plc_preemphasis_mem);
}
}
let search = celt_plc_pitch_search(&views, channels, arch);
decoder.last_pitch_index = search;
search
} else {
decoder.last_pitch_index
};
let fade = if loss_duration == 0 { 1.0f32 } else { 0.8_f32 };
let max_period = MAX_PERIOD as usize;
let pitch_index = pitch_index.clamp(PLC_PITCH_LAG_MIN, PLC_PITCH_LAG_MAX);
let pitch_index_usize = min(pitch_index as usize, max_period);
let exc_length = min(2 * pitch_index_usize, max_period);
let mut exc = vec![0.0f32; max_period + LPC_ORDER];
let mut fir_tmp = vec![0.0f32; exc_length];
let (decode_mem, lpc) = (&mut decoder.decode_mem, &mut decoder.lpc);
for (ch, channel_slice) in decode_mem.chunks_mut(stride).take(channels).enumerate() {
for (i, slot) in exc.iter_mut().enumerate() {
let src = DECODE_BUFFER_SIZE + overlap - max_period - LPC_ORDER + i;
*slot = channel_slice[src];
}
if loss_duration == 0 {
let mut ac = [0.0f32; LPC_ORDER + 1];
let input = &exc[LPC_ORDER..LPC_ORDER + max_period];
let window = if overlap == 0 {
None
} else {
Some(&mode.window[..overlap])
};
celt_autocorr(input, &mut ac, window, overlap, LPC_ORDER, arch);
ac[0] *= 1.0001;
for (i, entry) in ac.iter_mut().enumerate().skip(1) {
let factor = 0.008_f32 * 0.008_f32 * (i * i) as f32;
*entry -= *entry * factor;
}
let base_idx = ch * LPC_ORDER;
let lpc_slice = &mut lpc[base_idx..base_idx + LPC_ORDER];
celt_lpc(lpc_slice, &ac);
}
let base_idx = ch * LPC_ORDER;
let lpc_coeffs = &lpc[base_idx..base_idx + LPC_ORDER];
let start = max_period - exc_length;
for (idx, value) in fir_tmp.iter_mut().enumerate() {
let mut acc = exc[LPC_ORDER + start + idx];
for (tap, coeff) in lpc_coeffs.iter().enumerate() {
let hist_index = LPC_ORDER + start + idx - 1 - tap;
acc += coeff * exc[hist_index];
}
*value = acc;
}
for (idx, value) in fir_tmp.iter().enumerate() {
exc[LPC_ORDER + start + idx] = *value;
}
let mut e1 = 1.0f32;
let mut e2 = 1.0f32;
let decay_length = exc_length / 2;
if decay_length > 0 {
let exc_slice = &exc[LPC_ORDER..];
for i in 0..decay_length {
let a = exc_slice[max_period - decay_length + i];
e1 += a * a;
let b = exc_slice[max_period - 2 * decay_length + i];
e2 += b * b;
}
}
e1 = e1.min(e2);
let decay = celt_sqrt(frac_div32(0.5 * e1, e2));
let move_len = DECODE_BUFFER_SIZE
.checked_sub(n)
.expect("frame size larger than decode buffer");
channel_slice.copy_within(n..n + move_len, 0);
let extrapolation_offset = max_period - pitch_index_usize;
let extrapolation_len = n + overlap;
let mut attenuation = fade * decay;
let mut j = 0usize;
let start_index = DECODE_BUFFER_SIZE - n;
let reference_base = DECODE_BUFFER_SIZE - max_period - n + extrapolation_offset;
let mut s1 = 0.0f32;
for i in 0..extrapolation_len {
if j >= pitch_index_usize {
j -= pitch_index_usize;
attenuation *= decay;
}
let sample = attenuation * exc[LPC_ORDER + extrapolation_offset + j];
channel_slice[start_index + i] = sample;
let reference = channel_slice[reference_base + j];
s1 += reference * reference;
j += 1;
}
let mut lpc_mem = vec![0.0f32; LPC_ORDER];
for (idx, mem) in lpc_mem.iter_mut().enumerate() {
*mem = channel_slice[start_index - 1 - idx];
}
let mut filtered =
channel_slice[start_index..start_index + extrapolation_len].to_vec();
let input = filtered.clone();
celt_iir(&input, lpc_coeffs, &mut filtered, &mut lpc_mem);
channel_slice[start_index..start_index + extrapolation_len]
.copy_from_slice(&filtered);
let mut s2 = 0.0f32;
for sample in &filtered {
s2 += sample * sample;
}
let threshold = 0.2 * s2;
if matches!(s1.partial_cmp(&threshold), Some(Ordering::Greater)) {
if matches!(s1.partial_cmp(&s2), Some(Ordering::Less)) {
let ratio = celt_sqrt(frac_div32(0.5 * s1 + 1.0, s2 + 1.0));
for i in 0..overlap {
let gain = 1.0 - mode.window[i] * (1.0 - ratio);
channel_slice[start_index + i] *= gain;
}
for i in overlap..extrapolation_len {
channel_slice[start_index + i] *= ratio;
}
}
} else {
for value in &mut channel_slice[start_index..start_index + extrapolation_len] {
*value = 0.0;
}
}
}
decoder.prefilter_and_fold = true;
}
#[cfg(feature = "deep_plc")]
if let Some(lpcnet) = lpcnet.as_deref_mut() {
if lpcnet.loaded && (decoder.complexity >= 5 || lpcnet.fec_fill_pos > 0) {
let start_index = DECODE_BUFFER_SIZE - n;
let mut buf_copy = vec![0.0f32; channels * overlap];
for (ch, channel_slice) in
decoder.decode_mem.chunks(stride).take(channels).enumerate()
{
let src = &channel_slice[start_index..start_index + overlap];
let base = ch * overlap;
buf_copy[base..base + overlap].copy_from_slice(src);
}
let samples_needed16k = (n + SINC_ORDER + overlap) / 3;
if loss_duration == 0 {
decoder.plc_fill = 0;
}
debug_assert!(decoder.plc_fill >= 0);
while (decoder.plc_fill as usize) < samples_needed16k {
let fill = decoder.plc_fill as usize;
let end = fill + PLC_FRAME_SIZE;
debug_assert!(end <= decoder.plc_pcm.len());
lpcnet.lpcnet_plc_conceal(&mut decoder.plc_pcm[fill..end]);
decoder.plc_fill += PLC_FRAME_SIZE as i32;
}
let plc_pcm = &mut decoder.plc_pcm;
let plc_fill = &mut decoder.plc_fill;
let plc_preemphasis_mem = &mut decoder.plc_preemphasis_mem;
let decode_mem = &mut decoder.decode_mem;
let (first, rest) = decode_mem.split_at_mut(stride);
for i in 0..(n + overlap) / 3 {
let mut sum = 0.0f32;
for j in 0..17 {
sum += 3.0 * f32::from(plc_pcm[i + j]) * SINC_FILTER[3 * j];
}
first[start_index + 3 * i] = sum;
let mut sum = 0.0f32;
for j in 0..16 {
sum += 3.0 * f32::from(plc_pcm[i + j + 1]) * SINC_FILTER[3 * j + 2];
}
first[start_index + 3 * i + 1] = sum;
let mut sum = 0.0f32;
for j in 0..16 {
sum += 3.0 * f32::from(plc_pcm[i + j + 1]) * SINC_FILTER[3 * j + 1];
}
first[start_index + 3 * i + 2] = sum;
}
let shift = n / 3;
let fill = (*plc_fill).max(0) as usize;
debug_assert!(fill >= shift);
if fill > shift {
plc_pcm.copy_within(shift..fill, 0);
}
*plc_fill -= shift as i32;
for i in 0..n {
let tmp = first[start_index + i];
first[start_index + i] -= PREEMPHASIS * *plc_preemphasis_mem;
*plc_preemphasis_mem = tmp;
}
let mut overlap_mem = *plc_preemphasis_mem;
for i in 0..overlap {
let tmp = first[DECODE_BUFFER_SIZE + i];
first[DECODE_BUFFER_SIZE + i] -= PREEMPHASIS * overlap_mem;
overlap_mem = tmp;
}
if channels == 2 {
let second = &mut rest[..stride];
second.copy_from_slice(first);
}
if loss_duration == 0 {
for (ch, channel_slice) in
decode_mem.chunks_mut(stride).take(channels).enumerate()
{
let base = ch * overlap;
for i in 0..overlap {
channel_slice[start_index + i] = (1.0 - mode.window[i])
* buf_copy[base + i]
+ mode.window[i] * channel_slice[start_index + i];
}
}
}
}
}
decoder.prefilter_and_fold = true;
}
let increment = ((1usize) << lm) as i32;
decoder.loss_duration = min(10_000, decoder.loss_duration + increment);
}
const MAX_CHANNELS: usize = 2;
pub(crate) fn canonical_mode() -> Option<&'static OpusCustomMode<'static>> {
opus_custom_mode_find_static_ref(48_000, 960)
}
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 POSTFILTER_GAIN_SCALE: OpusVal16 = 0.09375;
const FROM_OPUS_TABLE: [u8; 16] = [
0x80, 0x88, 0x90, 0x98, 0x40, 0x48, 0x50, 0x58, 0x20, 0x28, 0x30, 0x38, 0x00, 0x08, 0x10, 0x18,
];
fn from_opus(value: u8) -> Option<u8> {
if value < 0x80 {
None
} else {
let idx = ((value >> 3) as usize).saturating_sub(16);
FROM_OPUS_TABLE
.get(idx)
.map(|mapped| mapped | (value & 0x7))
}
}
const VERY_SMALL: CeltSig = 1.0e-30;
const INV_CELT_SIG_SCALE: f32 = 1.0 / CELT_SIG_SCALE;
#[inline]
fn multiply_coef(coef: OpusVal16, value: CeltSig) -> CeltSig {
coef * value
}
#[inline]
fn sig_to_res(value: CeltSig) -> OpusRes {
value * INV_CELT_SIG_SCALE
}
#[inline]
fn add_res(lhs: OpusRes, rhs: OpusRes) -> OpusRes {
lhs + rhs
}
#[inline]
fn preprocess_sample(sample: CeltSig, mem: CeltSig) -> CeltSig {
sample + mem + VERY_SMALL
}
#[inline]
fn shl32(value: CeltSig, _shift: i32) -> CeltSig {
value
}
#[inline]
fn sub_celt(lhs: CeltSig, rhs: CeltSig) -> CeltSig {
lhs - rhs
}
#[cfg(feature = "fixed_point")]
#[inline]
fn deemphasis_coef0_q15(coef: &[OpusVal16]) -> FixedOpusVal16 {
qconst16_clamped(f64::from(coef[0]), 15)
}
#[cfg(feature = "fixed_point")]
#[inline]
fn deemphasis_coef1_q15(coef: &[OpusVal16]) -> FixedOpusVal16 {
qconst16_clamped(f64::from(coef[1]), 15)
}
#[cfg(feature = "fixed_point")]
#[inline]
fn deemphasis_coef3_q13(coef: &[OpusVal16]) -> FixedOpusVal16 {
qconst16_clamped(f64::from(coef[3]), 13)
}
#[cfg(feature = "fixed_point")]
#[inline]
fn preprocess_sample_fixed(sample: FixedCeltSig, mem: FixedCeltSig) -> FixedCeltSig {
(sample + mem).clamp(-FIXED_SIG_SAT, FIXED_SIG_SAT)
}
#[cfg(feature = "fixed_point")]
fn deemphasis_fixed(
input: &[&[FixedCeltSig]],
pcm: &mut [OpusRes],
n: usize,
channels: usize,
downsample: usize,
coef: &[OpusVal16],
mem: &mut [FixedCeltSig],
accum: bool,
) {
if n == 0 || channels == 0 {
return;
}
debug_assert!(downsample > 0, "downsample factor must be non-zero");
debug_assert!(
input.len() >= channels,
"input must expose one slice per channel"
);
debug_assert!(
mem.len() >= channels,
"memory buffer must expose one value per channel"
);
debug_assert!(
!coef.is_empty(),
"pre-emphasis coefficients must not be empty"
);
let expected_samples = if downsample > 1 { n / downsample } else { n };
debug_assert!(
pcm.len() >= expected_samples * channels,
"PCM buffer too small for deemphasis output",
);
let coef0 = deemphasis_coef0_q15(coef);
if downsample == 1 && channels == 2 && !accum {
let left = input[0];
let right = input[1];
let mut mem_left = mem[0];
let mut mem_right = mem[1];
for j in 0..n {
let tmp_left = preprocess_sample_fixed(left[j], mem_left);
let tmp_right = preprocess_sample_fixed(right[j], mem_right);
mem_left = mult16_32_q15(coef0, tmp_left);
mem_right = mult16_32_q15(coef0, tmp_right);
pcm[2 * j] = fixed_res_to_float(fixed_sig_to_res(tmp_left));
pcm[2 * j + 1] = fixed_res_to_float(fixed_sig_to_res(tmp_right));
}
mem[0] = mem_left;
mem[1] = mem_right;
return;
}
let mut scratch = vec![0; n];
let nd = n / downsample;
for channel in 0..channels {
let samples = input[channel];
let mut m = mem[channel];
let mut apply_downsampling = false;
if coef.len() > 3 && coef[1] != 0.0 {
let coef1 = deemphasis_coef1_q15(coef);
let coef3 = deemphasis_coef3_q13(coef);
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample_fixed(sample, m);
m = mult16_32_q15(coef0, tmp) - mult16_32_q15(coef1, sample);
scratch[j] = shl32_fixed(mult16_32_q15(coef3, tmp), 2);
}
apply_downsampling = true;
} else if downsample > 1 {
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample_fixed(sample, m);
m = mult16_32_q15(coef0, tmp);
scratch[j] = tmp;
}
apply_downsampling = true;
} else if accum {
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample_fixed(sample, m);
m = mult16_32_q15(coef0, tmp);
let idx = j * channels + channel;
pcm[idx] += fixed_res_to_float(fixed_sig_to_res(tmp));
}
} else {
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample_fixed(sample, m);
m = mult16_32_q15(coef0, tmp);
let idx = j * channels + channel;
pcm[idx] = fixed_res_to_float(fixed_sig_to_res(tmp));
}
}
mem[channel] = m;
if apply_downsampling {
if accum {
for j in 0..nd {
let idx = j * channels + channel;
pcm[idx] += fixed_res_to_float(fixed_sig_to_res(scratch[j * downsample]));
}
} else {
for j in 0..nd {
let idx = j * channels + channel;
pcm[idx] = fixed_res_to_float(fixed_sig_to_res(scratch[j * downsample]));
}
}
}
}
}
#[cfg(feature = "fixed_point")]
fn deemphasis_fixed_to_int16(
input: &[&[FixedCeltSig]],
pcm: &mut [i16],
n: usize,
channels: usize,
downsample: usize,
coef: &[OpusVal16],
mem: &mut [FixedCeltSig],
) {
if n == 0 || channels == 0 {
return;
}
debug_assert!(downsample > 0, "downsample factor must be non-zero");
debug_assert!(
input.len() >= channels,
"input must expose one slice per channel"
);
debug_assert!(
mem.len() >= channels,
"memory buffer must expose one value per channel"
);
let expected_samples = if downsample > 1 { n / downsample } else { n };
debug_assert!(
pcm.len() >= expected_samples * channels,
"PCM buffer too small for deemphasis output",
);
let coef0 = deemphasis_coef0_q15(coef);
if downsample == 1 && channels == 2 {
let left = input[0];
let right = input[1];
let mut mem_left = mem[0];
let mut mem_right = mem[1];
for j in 0..n {
let tmp_left = preprocess_sample_fixed(left[j], mem_left);
let tmp_right = preprocess_sample_fixed(right[j], mem_right);
mem_left = mult16_32_q15(coef0, tmp_left);
mem_right = mult16_32_q15(coef0, tmp_right);
pcm[2 * j] = sig2word16(tmp_left);
pcm[2 * j + 1] = sig2word16(tmp_right);
}
mem[0] = mem_left;
mem[1] = mem_right;
return;
}
let mut scratch = vec![0; n];
let nd = n / downsample;
for channel in 0..channels {
let samples = input[channel];
let mut m = mem[channel];
if coef.len() > 3 && coef[1] != 0.0 {
let coef1 = deemphasis_coef1_q15(coef);
let coef3 = deemphasis_coef3_q13(coef);
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample_fixed(sample, m);
m = mult16_32_q15(coef0, tmp) - mult16_32_q15(coef1, sample);
scratch[j] = shl32_fixed(mult16_32_q15(coef3, tmp), 2);
}
for j in 0..nd {
let idx = j * channels + channel;
pcm[idx] = sig2word16(scratch[j * downsample]);
}
} else if downsample > 1 {
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample_fixed(sample, m);
m = mult16_32_q15(coef0, tmp);
scratch[j] = tmp;
}
for j in 0..nd {
let idx = j * channels + channel;
pcm[idx] = sig2word16(scratch[j * downsample]);
}
} else {
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample_fixed(sample, m);
m = mult16_32_q15(coef0, tmp);
let idx = j * channels + channel;
pcm[idx] = sig2word16(tmp);
}
}
mem[channel] = m;
}
}
fn deemphasis_stereo_simple(
input: &[&[CeltSig]],
pcm: &mut [OpusRes],
n: usize,
coef0: OpusVal16,
mem: &mut [CeltSig],
) {
debug_assert!(input.len() >= 2, "stereo deemphasis requires two channels");
debug_assert!(
pcm.len() >= 2 * n,
"PCM buffer must hold interleaved stereo samples"
);
debug_assert!(
mem.len() >= 2,
"pre-emphasis memory must expose two channels"
);
let left = input[0];
let right = input[1];
debug_assert!(left.len() >= n, "left channel does not expose N samples");
debug_assert!(right.len() >= n, "right channel does not expose N samples");
let mut mem_left = mem[0];
let mut mem_right = mem[1];
for (j, (&left_sample, &right_sample)) in left.iter().zip(right.iter()).take(n).enumerate() {
let tmp_left = preprocess_sample(left_sample, mem_left);
let tmp_right = preprocess_sample(right_sample, mem_right);
mem_left = multiply_coef(coef0, tmp_left);
mem_right = multiply_coef(coef0, tmp_right);
pcm[2 * j] = sig_to_res(tmp_left);
pcm[2 * j + 1] = sig_to_res(tmp_right);
}
mem[0] = mem_left;
mem[1] = mem_right;
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn deemphasis(
input: &[&[CeltSig]],
pcm: &mut [OpusRes],
n: usize,
channels: usize,
downsample: usize,
coef: &[OpusVal16],
mem: &mut [CeltSig],
accum: bool,
) {
if n == 0 || channels == 0 {
return;
}
debug_assert!(downsample > 0, "downsample factor must be non-zero");
debug_assert!(
input.len() >= channels,
"input must expose one slice per channel"
);
debug_assert!(
mem.len() >= channels,
"memory buffer must expose one value per channel"
);
debug_assert!(
!coef.is_empty(),
"pre-emphasis coefficients must not be empty"
);
let expected_samples = if downsample > 1 { n / downsample } else { n };
debug_assert!(
pcm.len() >= expected_samples * channels,
"PCM buffer too small for deemphasis output",
);
if downsample == 1 && channels == 2 && !accum {
deemphasis_stereo_simple(input, pcm, n, coef[0], mem);
return;
}
let mut scratch = vec![CeltSig::default(); n];
let coef0 = coef[0];
let nd = n / downsample;
for channel in 0..channels {
let samples = input[channel];
debug_assert!(
samples.len() >= n,
"channel {} does not expose N samples",
channel
);
let mut m = mem[channel];
let mut apply_downsampling = false;
if coef.len() > 3 && coef[1] != 0.0 {
let coef1 = coef[1];
let coef3 = coef[3];
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample(sample, m);
m = sub_celt(multiply_coef(coef0, tmp), multiply_coef(coef1, sample));
scratch[j] = shl32(multiply_coef(coef3, tmp), 2);
}
apply_downsampling = true;
} else if downsample > 1 {
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample(sample, m);
m = multiply_coef(coef0, tmp);
scratch[j] = tmp;
}
apply_downsampling = true;
} else if accum {
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample(sample, m);
m = multiply_coef(coef0, tmp);
let idx = j * channels + channel;
let converted = sig_to_res(tmp);
pcm[idx] = add_res(pcm[idx], converted);
}
} else {
for (j, &sample) in samples.iter().take(n).enumerate() {
let tmp = preprocess_sample(sample, m);
m = multiply_coef(coef0, tmp);
let idx = j * channels + channel;
pcm[idx] = sig_to_res(tmp);
}
}
mem[channel] = m;
if apply_downsampling {
for j in 0..nd {
let idx = j * channels + channel;
let converted = sig_to_res(scratch[j * downsample]);
if accum {
pcm[idx] = add_res(pcm[idx], converted);
} else {
pcm[idx] = converted;
}
}
}
}
}
#[repr(C)]
struct DecoderLayoutStub {
mode: *const (),
overlap: i32,
channels: i32,
stream_channels: i32,
downsample: i32,
start_band: i32,
end_band: i32,
signalling: i32,
disable_inv: i32,
complexity: i32,
arch: i32,
rng: OpusUint32,
error: i32,
last_pitch_index: i32,
loss_duration: i32,
skip_plc: i32,
postfilter_period: i32,
postfilter_period_old: i32,
postfilter_gain: OpusVal16,
postfilter_gain_old: OpusVal16,
postfilter_tapset: i32,
postfilter_tapset_old: i32,
prefilter_and_fold: i32,
preemph_mem_decoder: [CeltSig; 2],
#[cfg(feature = "deep_plc")]
plc_pcm: [OpusInt16; PLC_UPDATE_SAMPLES],
#[cfg(feature = "deep_plc")]
plc_fill: OpusInt32,
#[cfg(feature = "deep_plc")]
plc_preemphasis_mem: f32,
decode_mem_head: [CeltSig; 1],
}
const DECODER_PREFIX_SIZE: usize = core::mem::size_of::<DecoderLayoutStub>();
#[must_use]
pub(crate) fn opus_custom_decoder_get_size(
mode: &OpusCustomMode<'_>,
channels: usize,
) -> Option<usize> {
if channels == 0 || channels > MAX_CHANNELS {
return None;
}
let decode_mem = channels * (DECODE_BUFFER_SIZE + mode.overlap);
let lpc = channels * LPC_ORDER;
let band_history = 2 * mode.num_ebands;
let size = DECODER_PREFIX_SIZE
+ (decode_mem - 1) * core::mem::size_of::<CeltSig>()
+ lpc * core::mem::size_of::<OpusVal16>()
+ 4 * band_history * core::mem::size_of::<CeltGlog>()
+ {
#[cfg(feature = "fixed_point")]
{
decode_mem * core::mem::size_of::<FixedCeltSig>()
+ lpc * core::mem::size_of::<FixedOpusVal16>()
+ 4 * band_history * core::mem::size_of::<FixedCeltGlog>()
}
#[cfg(not(feature = "fixed_point"))]
{
0
}
};
Some(size)
}
#[must_use]
pub(crate) fn celt_decoder_get_size(channels: usize) -> Option<usize> {
opus_custom_mode_find_static(48_000, 960)
.and_then(|mode| opus_custom_decoder_get_size(&mode, channels))
}
#[derive(Debug)]
pub(crate) struct OwnedCeltDecoder<'mode> {
decoder: OpusCustomDecoder<'mode>,
}
impl<'mode> OwnedCeltDecoder<'mode> {
#[must_use]
pub fn decoder(&mut self) -> &mut OpusCustomDecoder<'mode> {
&mut self.decoder
}
pub fn new(
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
) -> Result<Self, CeltDecoderInitError> {
opus_custom_decoder_create(mode, channels)
}
}
impl<'mode> AsRef<OwnedCeltDecoder<'mode>> for OwnedCeltDecoder<'mode> {
fn as_ref(&self) -> &OwnedCeltDecoder<'mode> {
self
}
}
impl<'mode> AsMut<OwnedCeltDecoder<'mode>> for OwnedCeltDecoder<'mode> {
fn as_mut(&mut self) -> &mut OwnedCeltDecoder<'mode> {
self
}
}
impl<'mode> core::ops::Deref for OwnedCeltDecoder<'mode> {
type Target = OpusCustomDecoder<'mode>;
fn deref(&self) -> &Self::Target {
&self.decoder
}
}
impl<'mode> core::ops::DerefMut for OwnedCeltDecoder<'mode> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.decoder
}
}
impl<'mode> AsRef<OpusCustomDecoder<'mode>> for OwnedCeltDecoder<'mode> {
fn as_ref(&self) -> &OpusCustomDecoder<'mode> {
&self.decoder
}
}
impl<'mode> AsMut<OpusCustomDecoder<'mode>> for OwnedCeltDecoder<'mode> {
fn as_mut(&mut self) -> &mut OpusCustomDecoder<'mode> {
&mut self.decoder
}
}
pub(crate) fn celt_decoder_init(
alloc: &mut CeltDecoderAlloc,
sampling_rate: OpusInt32,
channels: usize,
) -> Result<OpusCustomDecoder<'static>, CeltDecoderInitError> {
let mode = canonical_mode().ok_or(CeltDecoderInitError::CanonicalModeUnavailable)?;
let mut decoder = alloc.init_decoder(mode, channels, channels)?;
let factor = resampling_factor(sampling_rate);
if factor == 0 {
return Err(CeltDecoderInitError::UnsupportedSampleRate);
}
decoder.downsample = factor as i32;
Ok(decoder)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CeltDecodeError {
BadArgument,
PacketLoss,
InvalidPacket,
}
#[derive(Debug)]
pub(crate) enum RangeDecoderHandle<'a> {
Owned(EcDec<'a>),
External(&'a mut EcDec<'a>),
}
impl<'a> RangeDecoderHandle<'a> {
fn new(packet: &'a [u8], external: Option<&'a mut EcDec<'a>>) -> Self {
match external {
Some(dec) => Self::External(dec),
None => Self::Owned(EcDec::new(packet)),
}
}
fn decoder(&mut self) -> &mut EcDec<'a> {
match self {
Self::Owned(decoder) => decoder,
Self::External(dec) => dec,
}
}
}
pub(crate) fn validate_celt_decoder(decoder: &OpusCustomDecoder<'_>) {
debug_assert_eq!(
decoder.overlap, decoder.mode.overlap,
"decoder overlap must match the mode configuration",
);
let mode_band_limit = decoder.mode.num_ebands as i32;
let standard_limit = 21;
let custom_limit = 25;
let end_limit = if mode_band_limit <= standard_limit {
mode_band_limit
} else {
mode_band_limit.min(custom_limit)
};
debug_assert!(
decoder.end_band <= end_limit,
"end band {} exceeds supported limit {}",
decoder.end_band,
end_limit
);
debug_assert!(
decoder.channels == 1 || decoder.channels == 2,
"decoder must be mono or stereo",
);
debug_assert!(
decoder.stream_channels == 1 || decoder.stream_channels == 2,
"stream must be mono or stereo",
);
debug_assert!(
decoder.downsample > 0,
"downsample factor must be strictly positive",
);
debug_assert!(
decoder.start_band >= 0,
"decoder start band must be non-negative",
);
debug_assert!(
decoder.start_band < decoder.end_band,
"start band must precede end band",
);
debug_assert!(
decoder.arch >= 0 && decoder.arch <= OPUS_ARCHMASK,
"architecture selection out of range",
);
debug_assert!(
decoder.last_pitch_index <= PLC_PITCH_LAG_MAX,
"last pitch index exceeds maximum lag",
);
debug_assert!(
decoder.last_pitch_index >= PLC_PITCH_LAG_MIN || decoder.last_pitch_index == 0,
"last pitch index below minimum lag",
);
debug_assert!(
decoder.postfilter_period < MAX_PERIOD,
"postfilter period must remain below MAX_PERIOD",
);
debug_assert!(
decoder.postfilter_period >= COMBFILTER_MINPERIOD as i32 || decoder.postfilter_period == 0,
"postfilter period must be zero or above the comb-filter minimum",
);
debug_assert!(
decoder.postfilter_period_old < MAX_PERIOD,
"previous postfilter period must remain below MAX_PERIOD",
);
debug_assert!(
decoder.postfilter_period_old >= COMBFILTER_MINPERIOD as i32
|| decoder.postfilter_period_old == 0,
"previous postfilter period must be zero or above the comb-filter minimum",
);
debug_assert!(
(0..=2).contains(&decoder.postfilter_tapset),
"postfilter tapset must be in the inclusive range [0, 2]",
);
debug_assert!(
(0..=2).contains(&decoder.postfilter_tapset_old),
"previous postfilter tapset must be in the inclusive range [0, 2]",
);
let stride = DECODE_BUFFER_SIZE + decoder.overlap;
debug_assert_eq!(
decoder.decode_mem.len(),
stride * decoder.channels,
"decode history must match channel-stride layout",
);
debug_assert_eq!(
decoder.lpc.len(),
LPC_ORDER * decoder.channels,
"LPC history size must match channel count",
);
let band_count = 2 * decoder.mode.num_ebands;
debug_assert_eq!(decoder.old_ebands.len(), band_count);
debug_assert_eq!(decoder.old_log_e.len(), band_count);
debug_assert_eq!(decoder.old_log_e2.len(), band_count);
debug_assert_eq!(decoder.background_log_e.len(), band_count);
#[cfg(feature = "fixed_point")]
{
debug_assert_eq!(decoder.decode_mem_fixed.len(), decoder.decode_mem.len());
debug_assert_eq!(decoder.lpc_fixed.len(), decoder.lpc.len());
debug_assert_eq!(decoder.old_ebands_fixed.len(), band_count);
debug_assert_eq!(decoder.old_log_e_fixed.len(), band_count);
debug_assert_eq!(decoder.old_log_e2_fixed.len(), band_count);
debug_assert_eq!(
decoder.background_log_e_fixed.len(),
decoder.background_log_e.len()
);
}
}
#[derive(Debug)]
pub(crate) struct FramePreparation<'a> {
pub range_decoder: Option<RangeDecoderHandle<'a>>,
pub spread_decision: OpusInt32,
pub is_transient: bool,
pub short_blocks: OpusInt32,
pub intra_ener: bool,
pub silence: bool,
pub alloc_trim: OpusInt32,
pub anti_collapse_rsv: OpusInt32,
pub intensity: OpusInt32,
pub dual_stereo: OpusInt32,
pub balance: OpusInt32,
pub coded_bands: OpusInt32,
pub postfilter_pitch: OpusInt32,
pub postfilter_gain: OpusVal16,
pub postfilter_tapset: OpusInt32,
pub total_bits: OpusInt32,
pub tell: OpusInt32,
pub bits: OpusInt32,
pub start: usize,
pub end: usize,
pub eff_end: usize,
pub lm: usize,
pub m: usize,
pub n: usize,
pub c: usize,
pub cc: usize,
pub packet_loss: bool,
}
impl<'a> FramePreparation<'a> {
#[allow(clippy::too_many_arguments)]
fn new_packet_loss(
start: usize,
end: usize,
eff_end: usize,
lm: usize,
m: usize,
n: usize,
c: usize,
cc: usize,
) -> Self {
Self {
range_decoder: None,
spread_decision: 0,
is_transient: false,
short_blocks: 0,
intra_ener: false,
silence: false,
alloc_trim: 0,
anti_collapse_rsv: 0,
intensity: 0,
dual_stereo: 0,
balance: 0,
coded_bands: 0,
postfilter_pitch: 0,
postfilter_gain: 0.0,
postfilter_tapset: 0,
total_bits: 0,
tell: 0,
bits: 0,
start,
end,
eff_end,
lm,
m,
n,
c,
cc,
packet_loss: true,
}
}
}
fn tf_decode(
start: usize,
end: usize,
is_transient: bool,
tf_res: &mut [OpusInt32],
lm: usize,
dec: &mut EcDec<'_>,
) {
let mut budget = dec.ctx().storage * 8;
let mut tell = entcode::ec_tell(dec.ctx()) as u32;
let mut logp: u32 = if is_transient { 2 } else { 4 };
let tf_select_rsv = lm > 0 && tell + logp < budget;
if tf_select_rsv {
budget -= 1;
}
let mut curr = 0;
let mut tf_changed = 0;
for slot in tf_res.iter_mut().take(end).skip(start) {
if tell + logp <= budget {
let bit = dec.dec_bit_logp(logp);
curr ^= bit;
tell = entcode::ec_tell(dec.ctx()) as u32;
tf_changed |= curr;
}
*slot = curr;
logp = if is_transient { 4 } else { 5 };
}
let mut tf_select = 0;
if tf_select_rsv {
let base = 4 * usize::from(is_transient);
if TF_SELECT_TABLE[lm][base + tf_changed as usize]
!= TF_SELECT_TABLE[lm][base + 2 + tf_changed as usize]
{
tf_select = dec.dec_bit_logp(1) as OpusInt32;
}
}
let base = 4 * usize::from(is_transient);
for slot in tf_res.iter_mut().take(end).skip(start) {
let idx = base + 2 * tf_select as usize + *slot as usize;
*slot = OpusInt32::from(TF_SELECT_TABLE[lm][idx]);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn prepare_frame<'mode, 'pkt>(
decoder: &mut OpusCustomDecoder<'mode>,
packet: &'pkt [u8],
frame_size: usize,
range_decoder: Option<&'pkt mut EcDec<'pkt>>,
) -> Result<FramePreparation<'pkt>, CeltDecodeError>
where
'mode: 'pkt,
{
let cc = decoder.channels;
let mut c = decoder.stream_channels;
if cc == 0 || c == 0 || cc > MAX_CHANNELS {
return Err(CeltDecodeError::BadArgument);
}
if frame_size == 0 {
return Err(CeltDecodeError::BadArgument);
}
let mode = decoder.mode;
let nb_ebands = mode.num_ebands;
let start = decoder.start_band as usize;
let mut end = decoder.end_band as usize;
let downsample = decoder.downsample as usize;
let mut scaled_frame = frame_size
.checked_mul(downsample)
.ok_or(CeltDecodeError::BadArgument)?;
let mut packet = packet;
let mut header_lm: Option<usize> = None;
if decoder.signalling != 0 && !packet.is_empty() {
let mut data0 = packet[0];
if mode.sample_rate == 48_000 && mode.short_mdct_size == 120 {
data0 = from_opus(data0).ok_or(CeltDecodeError::InvalidPacket)?;
}
end = max(
1,
mode.effective_ebands
.saturating_sub(2 * (data0 as usize >> 5)),
);
decoder.end_band = end as i32;
c = 1 + ((data0 >> 2) & 0x1) as usize;
header_lm = Some(((data0 >> 3) & 0x3) as usize);
if (packet[0] & 0x03) == 0x03 {
packet = packet.get(1..).ok_or(CeltDecodeError::InvalidPacket)?;
if packet.is_empty() {
return Err(CeltDecodeError::InvalidPacket);
}
if (packet[0] & 0x40) != 0 {
packet = packet.get(1..).ok_or(CeltDecodeError::InvalidPacket)?;
if packet.is_empty() {
return Err(CeltDecodeError::InvalidPacket);
}
let mut len = packet.len() as i32;
let mut padding = 0i32;
loop {
if packet.is_empty() {
return Err(CeltDecodeError::InvalidPacket);
}
let p = packet[0];
packet = &packet[1..];
len -= 1;
let tmp = if p == 255 { 254 } else { p as i32 };
len -= tmp;
padding += tmp;
if p != 255 {
break;
}
}
padding -= 1;
if len <= 0 || padding < 0 {
return Err(CeltDecodeError::InvalidPacket);
}
packet = packet
.get(..len as usize)
.ok_or(CeltDecodeError::InvalidPacket)?;
}
} else {
packet = packet.get(1..).ok_or(CeltDecodeError::InvalidPacket)?;
}
let lm = header_lm.unwrap_or(0);
if lm > mode.max_lm {
return Err(CeltDecodeError::InvalidPacket);
}
let required = mode.short_mdct_size << lm;
if scaled_frame < required {
return Err(CeltDecodeError::BadArgument);
}
scaled_frame = required;
}
if scaled_frame > (mode.short_mdct_size << mode.max_lm) {
return Err(CeltDecodeError::BadArgument);
}
let lm = if let Some(lm) = header_lm {
lm
} else {
(0..=mode.max_lm)
.find(|&cand| mode.short_mdct_size << cand == scaled_frame)
.ok_or(CeltDecodeError::BadArgument)?
};
let m = 1 << lm;
let n = m * mode.short_mdct_size;
if c == 0 || c > MAX_CHANNELS {
return Err(CeltDecodeError::BadArgument);
}
if packet.len() > 1275 {
return Err(CeltDecodeError::BadArgument);
}
let eff_end = min(end, mode.effective_ebands);
if packet.len() <= 1 {
return Ok(FramePreparation::new_packet_loss(
start, end, eff_end, lm, m, n, c, cc,
));
}
if decoder.loss_duration == 0 {
decoder.skip_plc = false;
}
let mut range_decoder = RangeDecoderHandle::new(packet, range_decoder);
let is_owned = matches!(&range_decoder, RangeDecoderHandle::Owned(_));
let dec = range_decoder.decoder();
if is_owned {
debug_assert_eq!(
dec.ctx().storage as usize,
packet.len(),
"range decoder storage must match packet length",
);
}
if c == 1 {
for band in 0..nb_ebands {
let idx = band;
let paired = nb_ebands + band;
decoder.old_ebands[idx] = decoder.old_ebands[idx].max(decoder.old_ebands[paired]);
}
}
let len_bits = (packet.len() * 8) as OpusInt32;
let mut tell = entcode::ec_tell(dec.ctx());
let mut silence = false;
if tell >= len_bits {
silence = true;
} else if tell == 1 {
silence = dec.dec_bit_logp(15) != 0;
}
if silence {
let consumed = entcode::ec_tell(dec.ctx());
dec.ctx_mut().nbits_total += len_bits - consumed;
tell = len_bits;
} else {
tell = entcode::ec_tell(dec.ctx());
}
let mut postfilter_gain = 0.0;
let mut postfilter_pitch = 0;
let mut postfilter_tapset = 0;
if start == 0 && tell + 16 <= len_bits {
if dec.dec_bit_logp(1) != 0 {
let octave = dec.dec_uint(6) as OpusInt32;
let low_bits = dec.dec_bits((4 + octave) as u32) as OpusInt32;
postfilter_pitch = ((16 << octave) + low_bits) - 1;
let qg = dec.dec_bits(3) as OpusInt32;
if entcode::ec_tell(dec.ctx()) + 2 <= len_bits {
postfilter_tapset = dec.dec_icdf(&TAPSET_ICDF, 2);
}
postfilter_gain = POSTFILTER_GAIN_SCALE * ((qg + 1) as OpusVal16);
}
tell = entcode::ec_tell(dec.ctx());
}
let mut is_transient = false;
if lm > 0 && tell + 3 <= len_bits {
is_transient = dec.dec_bit_logp(3) != 0;
tell = entcode::ec_tell(dec.ctx());
}
let short_blocks = if is_transient { m as OpusInt32 } else { 0 };
let mut intra_ener = false;
if tell + 3 <= len_bits {
intra_ener = dec.dec_bit_logp(3) != 0;
}
if !intra_ener && decoder.loss_duration != 0 {
let missing = min(10, decoder.loss_duration >> (lm as u32));
let safety = match lm {
0 => 1.5,
1 => 0.5,
_ => 0.0,
};
for ch in 0..2 {
for band in start..end {
let idx = ch * nb_ebands + band;
let mut e0 = decoder.old_ebands[idx];
let e1 = decoder.old_log_e[idx];
let e2 = decoder.old_log_e2[idx];
if e0 < e1.max(e2) {
let mut slope = (e1 - e0).max(0.5 * (e2 - e0));
slope = slope.min(2.0);
let reduction = (((missing + 1) as f32) * slope).max(0.0);
e0 -= reduction;
decoder.old_ebands[idx] = e0.max(-20.0);
} else {
decoder.old_ebands[idx] = decoder.old_ebands[idx].min(e1.min(e2));
}
decoder.old_ebands[idx] -= safety;
}
}
}
#[cfg(feature = "fixed_point")]
{
sync_loge_to_fixed(&mut decoder.old_ebands_fixed, &decoder.old_ebands);
unquant_coarse_energy_fixed(
mode,
start,
end,
&mut decoder.old_ebands_fixed,
intra_ener,
dec,
c,
lm,
);
sync_loge_from_fixed(&mut decoder.old_ebands, &decoder.old_ebands_fixed);
}
#[cfg(not(feature = "fixed_point"))]
{
unquant_coarse_energy(
mode,
start,
end,
&mut decoder.old_ebands,
intra_ener,
dec,
c,
lm,
);
}
let decode_tf_res = &mut decoder.decode_tf_res;
decode_tf_res.fill(0);
tf_decode(start, end, is_transient, decode_tf_res, lm, dec);
tell = entcode::ec_tell(dec.ctx());
let mut spread_decision = SPREAD_NORMAL;
if tell + 4 <= len_bits {
spread_decision = dec.dec_icdf(&SPREAD_ICDF, 5);
}
let decode_cap = &mut decoder.decode_cap;
decode_cap.fill(0);
init_caps(mode, decode_cap, lm, c);
let decode_offsets = &mut decoder.decode_offsets;
decode_offsets.fill(0);
let mut dynalloc_logp = 6;
let total_bits = len_bits << BITRES;
let mut dynalloc_total_bits = total_bits;
let mut tell_frac = entcode::ec_tell_frac(dec.ctx()) as OpusInt32;
for band in start..end {
let band_width = i32::from(mode.e_bands[band + 1] - mode.e_bands[band]);
let width = (c as OpusInt32 * band_width) << lm;
let six_bits = (6 << BITRES) as OpusInt32;
let quanta = min(width << BITRES, max(six_bits, width));
let mut dynalloc_loop_logp = dynalloc_logp;
let mut boost = 0;
while tell_frac + ((dynalloc_loop_logp as OpusInt32) << BITRES) < dynalloc_total_bits
&& boost < decode_cap[band]
{
let flag = dec.dec_bit_logp(dynalloc_loop_logp as u32);
tell_frac = entcode::ec_tell_frac(dec.ctx()) as OpusInt32;
if flag == 0 {
break;
}
boost += quanta;
dynalloc_total_bits -= quanta;
dynalloc_loop_logp = 1;
}
decode_offsets[band] = boost;
if boost > 0 {
dynalloc_logp = max(2, dynalloc_logp - 1);
}
}
let decode_fine_quant = &mut decoder.decode_fine_quant;
decode_fine_quant.fill(0);
let alloc_trim = if tell_frac + ((6 << BITRES) as OpusInt32) <= dynalloc_total_bits {
dec.dec_icdf(&TRIM_ICDF, 7)
} else {
5
};
#[cfg(feature = "fixed_point")]
let frame_total_bits = total_bits;
#[cfg(not(feature = "fixed_point"))]
let frame_total_bits = dynalloc_total_bits;
let mut bits = ((len_bits << BITRES) - entcode::ec_tell_frac(dec.ctx()) as OpusInt32) - 1;
let anti_collapse_rsv = if is_transient && lm >= 2 && bits >= ((lm as OpusInt32 + 2) << BITRES)
{
(1 << BITRES) as OpusInt32
} else {
0
};
bits -= anti_collapse_rsv;
let decode_pulses = &mut decoder.decode_pulses;
let decode_fine_priority = &mut decoder.decode_fine_priority;
decode_pulses.fill(0);
decode_fine_priority.fill(0);
let mut intensity = 0;
let mut dual_stereo = 0;
let mut balance = 0;
let decode_alloc_bits1 = &mut decoder.decode_alloc_bits1;
let decode_alloc_bits2 = &mut decoder.decode_alloc_bits2;
let decode_alloc_thresh = &mut decoder.decode_alloc_thresh;
let decode_alloc_trim_offset = &mut decoder.decode_alloc_trim_offset;
let coded_bands = clt_compute_allocation_with_scratch(
mode,
start,
end,
decode_offsets,
decode_cap,
alloc_trim,
&mut intensity,
&mut dual_stereo,
bits,
&mut balance,
decode_pulses,
decode_fine_quant,
decode_fine_priority,
c as OpusInt32,
lm as OpusInt32,
None,
Some(dec),
0,
0,
decode_alloc_bits1,
decode_alloc_bits2,
decode_alloc_thresh,
decode_alloc_trim_offset,
);
#[cfg(feature = "fixed_point")]
{
unquant_fine_energy_fixed(
mode,
start,
end,
&mut decoder.old_ebands_fixed,
decode_fine_quant,
dec,
c,
);
sync_loge_from_fixed(&mut decoder.old_ebands, &decoder.old_ebands_fixed);
}
#[cfg(not(feature = "fixed_point"))]
{
unquant_fine_energy(
mode,
start,
end,
&mut decoder.old_ebands,
decode_fine_quant,
dec,
c,
);
}
let tell = entcode::ec_tell(dec.ctx());
Ok(FramePreparation {
range_decoder: Some(range_decoder),
spread_decision,
is_transient,
short_blocks,
intra_ener,
silence,
alloc_trim,
anti_collapse_rsv,
intensity,
dual_stereo,
balance,
coded_bands,
postfilter_pitch,
postfilter_gain,
postfilter_tapset,
total_bits: frame_total_bits,
tell,
bits,
start,
end,
eff_end,
lm,
m,
n,
c,
cc,
packet_loss: false,
})
}
fn res_to_int24(sample: OpusRes) -> i32 {
#[cfg(feature = "fixed_point")]
{
crate::celt::res2int24(crate::celt::float2res(sample))
}
#[cfg(not(feature = "fixed_point"))]
{
let scale = CELT_SIG_SCALE * 256.0;
let scaled = (sample * scale).clamp(-8_388_608.0, 8_388_607.0);
float2int(scaled)
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn celt_decode_with_ec_dred<'mode, 'pkt>(
decoder: &mut OpusCustomDecoder<'mode>,
packet: Option<&'pkt [u8]>,
pcm: &mut [OpusRes],
frame_size: usize,
range_decoder: Option<&'pkt mut EcDec<'pkt>>,
accum: bool,
plc: PlcHandle<'_>,
) -> Result<usize, CeltDecodeError>
where
'mode: 'pkt,
{
#[cfg(feature = "fixed_point")]
sync_from_fixed_primary_to_float_cache(decoder);
let result =
celt_decode_with_ec_dred_impl(decoder, packet, pcm, frame_size, range_decoder, accum, plc);
#[cfg(feature = "fixed_point")]
if matches!(packet, Some(data) if data.len() > 1) {
sync_loge_to_fixed(&mut decoder.old_ebands_fixed, &decoder.old_ebands);
sync_loge_to_fixed(&mut decoder.old_log_e_fixed, &decoder.old_log_e);
sync_loge_to_fixed(&mut decoder.old_log_e2_fixed, &decoder.old_log_e2);
sync_loge_to_fixed(
&mut decoder.background_log_e_fixed,
&decoder.background_log_e,
);
}
result
}
#[allow(clippy::too_many_arguments)]
fn celt_decode_with_ec_dred_impl<'mode, 'pkt>(
decoder: &mut OpusCustomDecoder<'mode>,
packet: Option<&'pkt [u8]>,
pcm: &mut [OpusRes],
frame_size: usize,
range_decoder: Option<&'pkt mut EcDec<'pkt>>,
accum: bool,
plc: PlcHandle<'_>,
) -> Result<usize, CeltDecodeError>
where
'mode: 'pkt,
{
validate_celt_decoder(decoder);
let data = packet.unwrap_or(&[]);
let mut frame = prepare_frame(decoder, data, frame_size, range_decoder)?;
let mode = decoder.mode;
let nb_ebands = mode.num_ebands;
let overlap = mode.overlap;
let stride = DECODE_BUFFER_SIZE + overlap;
let downsample = decoder.downsample.max(1) as usize;
let cc = frame.cc;
if cc == 0 {
return Err(CeltDecodeError::BadArgument);
}
let n = frame.n;
let output_samples = n / downsample;
let required_pcm = output_samples
.checked_mul(cc)
.ok_or(CeltDecodeError::BadArgument)?;
if pcm.len() < required_pcm {
return Err(CeltDecodeError::BadArgument);
}
if frame.packet_loss {
celt_decode_lost(decoder, n, frame.lm, plc);
let start = DECODE_BUFFER_SIZE
.checked_sub(n)
.ok_or(CeltDecodeError::BadArgument)?;
#[cfg(feature = "fixed_point")]
{
debug_assert!(cc <= MAX_CHANNELS);
let mut inputs: [&[FixedCeltSig]; MAX_CHANNELS] = [&[]; MAX_CHANNELS];
for (channel, channel_slice) in decoder
.decode_mem_fixed
.chunks_mut(stride)
.take(cc)
.enumerate()
{
let (_, rest) = channel_slice.split_at(start);
let (output, _) = rest.split_at(n);
inputs[channel] = output;
}
deemphasis_fixed(
&inputs[..cc],
pcm,
n,
cc,
downsample,
&mode.pre_emphasis,
&mut decoder.fixed_preemph_mem_decoder,
accum,
);
for (dst, &src) in decoder
.preemph_mem_decoder
.iter_mut()
.zip(decoder.fixed_preemph_mem_decoder.iter())
{
*dst = fixed_sig_to_float(src);
}
}
#[cfg(not(feature = "fixed_point"))]
{
debug_assert!(cc <= MAX_CHANNELS);
let mut inputs: [&[CeltSig]; MAX_CHANNELS] = [&[]; MAX_CHANNELS];
for (channel, channel_slice) in
decoder.decode_mem.chunks_mut(stride).take(cc).enumerate()
{
let (_, rest) = channel_slice.split_at(start);
let (output, _) = rest.split_at(n);
inputs[channel] = output;
}
deemphasis(
&inputs[..cc],
pcm,
n,
cc,
downsample,
&mode.pre_emphasis,
&mut decoder.preemph_mem_decoder,
accum,
);
}
return Ok(output_samples);
}
let mut range_state = frame
.range_decoder
.take()
.ok_or(CeltDecodeError::InvalidPacket)?;
let FramePreparation {
range_decoder: _,
spread_decision,
is_transient,
short_blocks,
intra_ener: _,
silence,
alloc_trim: _,
anti_collapse_rsv,
intensity,
dual_stereo,
balance,
coded_bands,
postfilter_pitch,
postfilter_gain,
postfilter_tapset,
total_bits,
tell: _,
bits: _,
start,
end,
eff_end,
lm,
m,
n,
c,
cc,
packet_loss: _,
} = frame;
let tf_res = &decoder.decode_tf_res;
let fine_quant = &decoder.decode_fine_quant;
let pulses = &decoder.decode_pulses;
let fine_priority = &decoder.decode_fine_priority;
let move_len = DECODE_BUFFER_SIZE
.checked_sub(n)
.ok_or(CeltDecodeError::BadArgument)?
+ overlap;
for channel_slice in decoder.decode_mem.chunks_mut(stride).take(cc) {
channel_slice.copy_within(n..n + move_len, 0);
}
#[cfg(feature = "fixed_point")]
for channel_slice in decoder.decode_mem_fixed.chunks_mut(stride).take(cc) {
channel_slice.copy_within(n..n + move_len, 0);
}
let mut collapse_masks = vec![0u8; c * nb_ebands];
let total_available = total_bits - anti_collapse_rsv;
#[cfg(feature = "fixed_point")]
let mut spectrum_fixed = vec![0i16; c * n];
#[cfg(not(feature = "fixed_point"))]
let mut spectrum = vec![0.0f32; c * n];
{
let dec = range_state.decoder();
let mut coder = BandCodingState::Decoder(dec);
#[cfg(feature = "fixed_point")]
{
#[cfg(test)]
let use_stereo_float_bridge =
c == 2 && std::env::var("CELT_EXP_STEREO_FLOAT_BRIDGE").is_ok();
#[cfg(not(test))]
let use_stereo_float_bridge = false;
if use_stereo_float_bridge {
let mut spectrum_float = vec![0.0f32; c * n];
let (left, right) = spectrum_float.split_at_mut(n);
crate::celt::bands::quant_all_bands(
false,
mode,
start,
end,
left,
Some(right),
&mut collapse_masks,
&[],
&pulses,
short_blocks != 0,
spread_decision,
dual_stereo != 0,
intensity.max(0) as usize,
&tf_res,
total_available,
balance,
&mut coder,
lm as i32,
coded_bands.max(0) as usize,
&mut decoder.rng,
decoder.complexity,
decoder.arch,
decoder.disable_inv,
);
float_norm_slice_to_fixed(&mut spectrum_fixed, &spectrum_float);
} else {
let (first_channel, second_channel_opt) = if c == 2 {
let (left, right) = spectrum_fixed.split_at_mut(n);
(left, Some(right))
} else {
(&mut spectrum_fixed[..], None)
};
quant_all_bands_decode_fixed(
mode,
start,
end,
first_channel,
second_channel_opt,
&mut collapse_masks,
&pulses,
short_blocks != 0,
spread_decision,
dual_stereo != 0,
intensity.max(0) as usize,
&tf_res,
total_available,
balance,
&mut coder,
lm as i32,
coded_bands.max(0) as usize,
&mut decoder.rng,
decoder.arch,
decoder.disable_inv,
);
}
}
#[cfg(not(feature = "fixed_point"))]
{
let (first_channel, second_channel_opt) = if c == 2 {
let (left, right) = spectrum.split_at_mut(n);
(left, Some(right))
} else {
(&mut spectrum[..], None)
};
quant_all_bands(
false,
mode,
start,
end,
first_channel,
second_channel_opt,
&mut collapse_masks,
&[],
&pulses,
short_blocks != 0,
spread_decision,
dual_stereo != 0,
intensity.max(0) as usize,
&tf_res,
total_available,
balance,
&mut coder,
lm as i32,
coded_bands.max(0) as usize,
&mut decoder.rng,
decoder.complexity,
decoder.arch,
decoder.disable_inv,
);
}
}
let mut anti_collapse_on = false;
let dec = range_state.decoder();
if anti_collapse_rsv > 0 {
anti_collapse_on = dec.dec_bits(1) != 0;
}
let payload_len_bits = (dec.ctx().storage as OpusInt32) * 8;
let remaining_bits = payload_len_bits - entcode::ec_tell(dec.ctx());
#[cfg(all(test, feature = "fixed_point"))]
if std::env::var("CELT_TRACE_POST_BANDS").is_ok() {
let pre_final_hash = decoder.old_ebands_fixed[..mode.num_ebands].iter().fold(
2166136261u32,
|hash, &value| {
let v = value as u32;
let hash = (hash ^ (v & 0xFF)).wrapping_mul(16777619);
let hash = (hash ^ ((v >> 8) & 0xFF)).wrapping_mul(16777619);
let hash = (hash ^ ((v >> 16) & 0xFF)).wrapping_mul(16777619);
(hash ^ ((v >> 24) & 0xFF)).wrapping_mul(16777619)
},
);
crate::test_trace::trace_println!(
"pre_finalise.eb_hash=0x{:08x}.eb_values={:?}.remaining_bits={} fine_quant={:?} fine_priority={:?}",
pre_final_hash,
&decoder.old_ebands_fixed[..mode.num_ebands],
remaining_bits,
&fine_quant[start..end],
&fine_priority[start..end]
);
}
#[cfg(feature = "fixed_point")]
{
unquant_energy_finalise_fixed(
mode,
start,
end,
&mut decoder.old_ebands_fixed,
&fine_quant,
&fine_priority,
remaining_bits,
dec,
c,
);
sync_loge_from_fixed(&mut decoder.old_ebands, &decoder.old_ebands_fixed);
}
#[cfg(not(feature = "fixed_point"))]
{
unquant_energy_finalise(
mode,
start,
end,
&mut decoder.old_ebands,
&fine_quant,
&fine_priority,
remaining_bits,
dec,
c,
);
}
#[cfg(all(test, feature = "fixed_point"))]
if std::env::var("CELT_TRACE_POST_BANDS").is_ok() && spectrum_fixed.len() >= 232 {
crate::test_trace::trace_println!(
"actual_band15.stage=pre_anti.on={anti_collapse_on}.hash=0x{:08x}.coeffs={:?}",
spectrum_fixed.iter().fold(2166136261u32, |hash, &value| {
let v = value as u16;
let hash = (hash ^ u32::from(v & 0xFF)).wrapping_mul(16777619);
(hash ^ u32::from(v >> 8)).wrapping_mul(16777619)
}),
&spectrum_fixed[224..272]
);
}
if anti_collapse_on {
#[cfg(feature = "fixed_point")]
{
anti_collapse_fixed(
mode,
&mut spectrum_fixed,
&collapse_masks,
lm,
c,
n,
start,
end,
&decoder.old_ebands_fixed,
&decoder.old_log_e_fixed,
&decoder.old_log_e2_fixed,
&pulses,
decoder.rng,
false,
decoder.arch,
);
}
#[cfg(not(feature = "fixed_point"))]
{
anti_collapse(
mode,
&mut spectrum,
&collapse_masks,
lm,
c,
n,
start,
end,
&decoder.old_ebands,
&decoder.old_log_e,
&decoder.old_log_e2,
&pulses,
decoder.rng,
false,
decoder.arch,
);
}
}
#[cfg(all(test, feature = "fixed_point"))]
if std::env::var("CELT_TRACE_POST_BANDS").is_ok() && spectrum_fixed.len() >= 232 {
let spectrum_hash = spectrum_fixed.iter().fold(2166136261u32, |hash, &value| {
let v = value as u16;
let hash = (hash ^ u32::from(v & 0xFF)).wrapping_mul(16777619);
(hash ^ u32::from(v >> 8)).wrapping_mul(16777619)
});
let eb_hash = decoder.old_ebands_fixed[..mode.num_ebands].iter().fold(
2166136261u32,
|hash, &value| {
let v = value as u32;
let hash = (hash ^ (v & 0xFF)).wrapping_mul(16777619);
let hash = (hash ^ ((v >> 8) & 0xFF)).wrapping_mul(16777619);
let hash = (hash ^ ((v >> 16) & 0xFF)).wrapping_mul(16777619);
(hash ^ ((v >> 24) & 0xFF)).wrapping_mul(16777619)
},
);
crate::test_trace::trace_println!(
"actual_band15.stage=post_anti.on={anti_collapse_on}.hash=0x{:08x}.eb_hash=0x{:08x}.eb_values={:?}.coeffs={:?}",
spectrum_hash,
eb_hash,
&decoder.old_ebands_fixed[..mode.num_ebands],
&spectrum_fixed[224..272]
);
for band_hash in 0..mode.num_ebands {
let band_start = (mode.e_bands[band_hash] as usize) << lm;
let band_end = (mode.e_bands[band_hash + 1] as usize) << lm;
let hash =
spectrum_fixed[band_start..band_end]
.iter()
.fold(2166136261u32, |hash, &value| {
let v = value as u16;
let hash = (hash ^ u32::from(v & 0xFF)).wrapping_mul(16777619);
(hash ^ u32::from(v >> 8)).wrapping_mul(16777619)
});
crate::test_trace::trace_println!("post_anti_band_hash[{}]=0x{:08x}", band_hash, hash);
}
}
if silence {
decoder.old_ebands.fill(-28.0);
#[cfg(feature = "fixed_point")]
{
sync_loge_to_fixed(&mut decoder.old_ebands_fixed, &decoder.old_ebands);
}
}
if decoder.prefilter_and_fold {
prefilter_and_fold(decoder, n);
}
#[cfg(feature = "fixed_point")]
{
let start_idx = DECODE_BUFFER_SIZE
.checked_sub(n)
.ok_or(CeltDecodeError::BadArgument)?;
#[cfg(test)]
if cc == 2 && std::env::var("CELT_EXP_FLOAT_SYNTH").is_ok() {
let mut spectrum_float = vec![0.0f32; spectrum_fixed.len()];
fixed_norm_slice_to_float(&mut spectrum_float, &spectrum_fixed);
let mut out_slices_float: Vec<&mut [CeltSig]> = Vec::with_capacity(cc);
for channel_slice in decoder.decode_mem.chunks_mut(stride).take(cc) {
out_slices_float.push(&mut channel_slice[start_idx..]);
}
celt_synthesis(
mode,
&spectrum_float,
&mut out_slices_float,
&decoder.old_ebands,
start,
eff_end,
c,
cc,
is_transient,
lm,
downsample,
silence,
(
&decoder.fixed_mdct,
decoder.fixed_window.as_slice(),
decoder.overlap,
),
);
for channel in 0..cc {
let float_base = channel * stride + start_idx;
let fixed_base = channel * stride + start_idx;
for i in 0..(n + overlap) {
decoder.decode_mem_fixed[fixed_base + i] =
celt_sig_to_fixed(decoder.decode_mem[float_base + i]);
}
}
} else {
let mut out_slices_fixed: Vec<&mut [FixedCeltSig]> = Vec::with_capacity(cc);
for channel_slice in decoder.decode_mem_fixed.chunks_mut(stride).take(cc) {
out_slices_fixed.push(&mut channel_slice[start_idx..]);
}
celt_synthesis_fixed_native(
mode,
&spectrum_fixed,
&mut out_slices_fixed,
&decoder.old_ebands_fixed,
start,
eff_end,
c,
cc,
is_transient,
lm,
downsample,
silence,
&decoder.fixed_mdct,
decoder.fixed_window.as_slice(),
decoder.overlap,
);
}
#[cfg(not(test))]
{
let mut out_slices_fixed: Vec<&mut [FixedCeltSig]> = Vec::with_capacity(cc);
for channel_slice in decoder.decode_mem_fixed.chunks_mut(stride).take(cc) {
out_slices_fixed.push(&mut channel_slice[start_idx..]);
}
celt_synthesis_fixed_native(
mode,
&spectrum_fixed,
&mut out_slices_fixed,
&decoder.old_ebands_fixed,
start,
eff_end,
c,
cc,
is_transient,
lm,
downsample,
silence,
&decoder.fixed_mdct,
decoder.fixed_window.as_slice(),
decoder.overlap,
);
}
#[cfg(test)]
if std::env::var("CELT_TRACE_SYNTH_CMP").is_ok() && c == 1 && cc == 1 {
let mut spectrum_float = vec![0.0f32; spectrum_fixed.len()];
fixed_norm_slice_to_float(&mut spectrum_float, &spectrum_fixed);
let mut freq_native = vec![0i32; n];
denormalise_bands_fixed_native(
mode,
&spectrum_fixed,
&mut freq_native,
&decoder.old_ebands_fixed,
start,
eff_end,
1 << lm,
downsample,
silence,
);
let mut freq_bridge = vec![0i32; n];
denormalise_bands_fixed(
mode,
&spectrum_float,
&mut freq_bridge,
&decoder.old_ebands,
start,
eff_end,
1 << lm,
downsample,
silence,
);
let mut hash_freq_native = 2166136261u32;
for &value in freq_native.iter() {
let v = value as u32;
hash_freq_native = (hash_freq_native ^ (v & 0xFF)).wrapping_mul(16777619);
hash_freq_native = (hash_freq_native ^ ((v >> 8) & 0xFF)).wrapping_mul(16777619);
hash_freq_native = (hash_freq_native ^ ((v >> 16) & 0xFF)).wrapping_mul(16777619);
hash_freq_native = (hash_freq_native ^ ((v >> 24) & 0xFF)).wrapping_mul(16777619);
}
let mut hash_freq_bridge = 2166136261u32;
for &value in freq_bridge.iter() {
let v = value as u32;
hash_freq_bridge = (hash_freq_bridge ^ (v & 0xFF)).wrapping_mul(16777619);
hash_freq_bridge = (hash_freq_bridge ^ ((v >> 8) & 0xFF)).wrapping_mul(16777619);
hash_freq_bridge = (hash_freq_bridge ^ ((v >> 16) & 0xFF)).wrapping_mul(16777619);
hash_freq_bridge = (hash_freq_bridge ^ ((v >> 24) & 0xFF)).wrapping_mul(16777619);
}
crate::test_trace::trace_println!(
"freqcmp native=0x{:08x} bridge=0x{:08x} first_diff={:?}",
hash_freq_native,
hash_freq_bridge,
freq_native
.iter()
.zip(freq_bridge.iter())
.position(|(lhs, rhs)| lhs != rhs)
);
let mut bridge_out = vec![0.0f32; n + overlap];
let mut bridge_views: Vec<&mut [CeltSig]> = vec![bridge_out.as_mut_slice()];
celt_synthesis(
mode,
&spectrum_float,
&mut bridge_views,
&decoder.old_ebands,
start,
eff_end,
c,
cc,
is_transient,
lm,
downsample,
silence,
(
&decoder.fixed_mdct,
decoder.fixed_window.as_slice(),
decoder.overlap,
),
);
let synth_start = DECODE_BUFFER_SIZE - n;
let actual = &decoder.decode_mem_fixed[synth_start..synth_start + n];
let mut bridge_fixed = vec![0i32; n];
for (dst, &src) in bridge_fixed.iter_mut().zip(bridge_out.iter()) {
*dst = celt_sig_to_fixed(src);
}
let mut hash_actual = 2166136261u32;
for &value in actual.iter() {
let v = value as u32;
hash_actual = (hash_actual ^ (v & 0xFF)).wrapping_mul(16777619);
hash_actual = (hash_actual ^ ((v >> 8) & 0xFF)).wrapping_mul(16777619);
hash_actual = (hash_actual ^ ((v >> 16) & 0xFF)).wrapping_mul(16777619);
hash_actual = (hash_actual ^ ((v >> 24) & 0xFF)).wrapping_mul(16777619);
}
let mut hash_bridge = 2166136261u32;
for &value in bridge_fixed.iter() {
let v = value as u32;
hash_bridge = (hash_bridge ^ (v & 0xFF)).wrapping_mul(16777619);
hash_bridge = (hash_bridge ^ ((v >> 8) & 0xFF)).wrapping_mul(16777619);
hash_bridge = (hash_bridge ^ ((v >> 16) & 0xFF)).wrapping_mul(16777619);
hash_bridge = (hash_bridge ^ ((v >> 24) & 0xFF)).wrapping_mul(16777619);
}
let first_diff = actual
.iter()
.zip(bridge_fixed.iter())
.position(|(lhs, rhs)| lhs != rhs);
crate::test_trace::trace_println!(
"synthcmp actual=0x{:08x} bridge=0x{:08x} first_diff={:?}",
hash_actual,
hash_bridge,
first_diff
);
if let Some(idx) = first_diff {
crate::test_trace::trace_println!(
"synthcmp coeff[{idx}] actual={} bridge={} tail_actual=0x{:08x} tail_bridge=0x{:08x}",
actual[idx],
bridge_fixed[idx],
{
let mut hash = 2166136261u32;
for &value in actual[n - 64..n].iter() {
let v = value as u32;
hash = (hash ^ (v & 0xFF)).wrapping_mul(16777619);
hash = (hash ^ ((v >> 8) & 0xFF)).wrapping_mul(16777619);
hash = (hash ^ ((v >> 16) & 0xFF)).wrapping_mul(16777619);
hash = (hash ^ ((v >> 24) & 0xFF)).wrapping_mul(16777619);
}
hash
},
{
let mut hash = 2166136261u32;
for &value in bridge_fixed[n - 64..n].iter() {
let v = value as u32;
hash = (hash ^ (v & 0xFF)).wrapping_mul(16777619);
hash = (hash ^ ((v >> 8) & 0xFF)).wrapping_mul(16777619);
hash = (hash ^ ((v >> 16) & 0xFF)).wrapping_mul(16777619);
hash = (hash ^ ((v >> 24) & 0xFF)).wrapping_mul(16777619);
}
hash
}
);
}
}
}
#[cfg(not(feature = "fixed_point"))]
{
let mut out_slices: Vec<&mut [CeltSig]> = Vec::with_capacity(cc);
for channel_slice in decoder.decode_mem.chunks_mut(stride).take(cc) {
let start_idx = DECODE_BUFFER_SIZE
.checked_sub(n)
.ok_or(CeltDecodeError::BadArgument)?;
out_slices.push(&mut channel_slice[start_idx..]);
}
celt_synthesis(
mode,
&spectrum,
&mut out_slices,
&decoder.old_ebands,
start,
eff_end,
c,
cc,
is_transient,
lm,
downsample,
silence,
(),
);
}
decoder.postfilter_period = decoder.postfilter_period.max(COMBFILTER_MINPERIOD as i32);
decoder.postfilter_period_old = decoder
.postfilter_period_old
.max(COMBFILTER_MINPERIOD as i32);
let output_start = DECODE_BUFFER_SIZE - n;
#[cfg(not(feature = "fixed_point"))]
for channel_slice in decoder.decode_mem.chunks_mut(stride).take(cc) {
let first_len = mode.short_mdct_size.min(n);
if first_len > 0 {
comb_filter_in_place(
channel_slice,
output_start,
first_len,
decoder.postfilter_period_old,
decoder.postfilter_period,
decoder.postfilter_gain_old,
decoder.postfilter_gain,
decoder.postfilter_tapset_old.max(0) as usize,
decoder.postfilter_tapset.max(0) as usize,
mode.window,
overlap,
decoder.arch,
);
if lm != 0 && first_len < n {
comb_filter_in_place(
channel_slice,
output_start + first_len,
n - first_len,
decoder.postfilter_period,
postfilter_pitch,
decoder.postfilter_gain,
postfilter_gain,
decoder.postfilter_tapset.max(0) as usize,
postfilter_tapset.max(0) as usize,
mode.window,
overlap,
decoder.arch,
);
}
}
}
#[cfg(feature = "fixed_point")]
{
let g0 = postfilter_gain_to_fixed(decoder.postfilter_gain_old);
let g1 = postfilter_gain_to_fixed(decoder.postfilter_gain);
#[cfg(test)]
if std::env::var("CELT_DUMP_POSTFILTER_INPUT").is_ok() {
static POSTFILTER_DUMP_COUNTER: std::sync::atomic::AtomicUsize =
std::sync::atomic::AtomicUsize::new(0);
if POSTFILTER_DUMP_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed) == 0 {
let synth = &decoder.decode_mem_fixed[output_start..output_start + n];
crate::test_trace::trace_println!(
"postfilter_input len={} first_len={} overlap={} old_period={} cur_period={} next_period={} g0={} g1={} g_next={} tap0={} tap1={} tap_next={}",
n,
mode.short_mdct_size.min(n),
overlap,
decoder
.postfilter_period_old
.max(COMBFILTER_MINPERIOD as i32),
decoder.postfilter_period.max(COMBFILTER_MINPERIOD as i32),
postfilter_pitch,
g0,
g1,
postfilter_gain_to_fixed(postfilter_gain),
decoder.postfilter_tapset_old.max(0),
decoder.postfilter_tapset.max(0),
postfilter_tapset.max(0),
);
for (idx, value) in synth.iter().enumerate() {
crate::test_trace::trace_println!("postfilter_input[{idx}]={value}");
}
}
}
for channel_slice in decoder.decode_mem_fixed.chunks_mut(stride).take(cc) {
let first_len = mode.short_mdct_size.min(n);
if first_len > 0 {
comb_filter_fixed_in_place(
channel_slice,
output_start,
first_len,
decoder.postfilter_period_old,
decoder.postfilter_period,
g0,
g1,
decoder.postfilter_tapset_old.max(0) as usize,
decoder.postfilter_tapset.max(0) as usize,
decoder.fixed_window.as_slice(),
overlap,
decoder.arch,
);
if lm != 0 && first_len < n {
comb_filter_fixed_in_place(
channel_slice,
output_start + first_len,
n - first_len,
decoder.postfilter_period,
postfilter_pitch,
g1,
postfilter_gain_to_fixed(postfilter_gain),
decoder.postfilter_tapset.max(0) as usize,
postfilter_tapset.max(0) as usize,
decoder.fixed_window.as_slice(),
overlap,
decoder.arch,
);
}
}
}
sync_fixed_output_window_to_float_cache(decoder, cc, output_start, n + (overlap >> 1));
}
decoder.postfilter_period_old = decoder.postfilter_period;
decoder.postfilter_gain_old = decoder.postfilter_gain;
decoder.postfilter_tapset_old = decoder.postfilter_tapset;
decoder.postfilter_period = postfilter_pitch;
decoder.postfilter_gain = postfilter_gain;
decoder.postfilter_tapset = postfilter_tapset;
if lm != 0 {
decoder.postfilter_period_old = decoder.postfilter_period;
decoder.postfilter_gain_old = decoder.postfilter_gain;
decoder.postfilter_tapset_old = decoder.postfilter_tapset;
}
if c == 1 {
let (left, right) = decoder.old_ebands.split_at_mut(nb_ebands);
right.copy_from_slice(left);
}
if is_transient {
for (log_e, band_e) in decoder.old_log_e.iter_mut().zip(decoder.old_ebands.iter()) {
*log_e = (*log_e).min(*band_e);
}
} else {
decoder.old_log_e2.copy_from_slice(&decoder.old_log_e);
decoder.old_log_e.copy_from_slice(&decoder.old_ebands);
}
let increase = ((decoder.loss_duration + m as i32).min(160) as f32) * 0.001;
for (background, band_e) in decoder
.background_log_e
.iter_mut()
.zip(decoder.old_ebands.iter())
{
*background = (*background + increase).min(*band_e);
}
for ch in 0..2 {
let base = ch * nb_ebands;
for band in 0..start {
let idx = base + band;
decoder.old_ebands[idx] = 0.0;
decoder.old_log_e[idx] = -28.0;
decoder.old_log_e2[idx] = -28.0;
}
for band in end..nb_ebands {
let idx = base + band;
decoder.old_ebands[idx] = 0.0;
decoder.old_log_e[idx] = -28.0;
decoder.old_log_e2[idx] = -28.0;
}
}
decoder.rng = dec.ctx().rng;
#[cfg(feature = "fixed_point")]
{
debug_assert!(cc <= MAX_CHANNELS);
let mut deemph_inputs: [&[FixedCeltSig]; MAX_CHANNELS] = [&[]; MAX_CHANNELS];
for (channel, channel_slice) in decoder
.decode_mem_fixed
.chunks_mut(stride)
.take(cc)
.enumerate()
{
let start_idx = DECODE_BUFFER_SIZE - n;
let (_, rest) = channel_slice.split_at_mut(start_idx);
let (output, _) = rest.split_at_mut(n);
deemph_inputs[channel] = output;
}
deemphasis_fixed(
&deemph_inputs[..cc],
pcm,
n,
cc,
downsample,
&mode.pre_emphasis,
&mut decoder.fixed_preemph_mem_decoder,
accum,
);
for (dst, &src) in decoder
.preemph_mem_decoder
.iter_mut()
.zip(decoder.fixed_preemph_mem_decoder.iter())
{
*dst = fixed_sig_to_float(src);
}
}
#[cfg(not(feature = "fixed_point"))]
{
debug_assert!(cc <= MAX_CHANNELS);
let mut deemph_inputs: [&[CeltSig]; MAX_CHANNELS] = [&[]; MAX_CHANNELS];
for (channel, channel_slice) in decoder.decode_mem.chunks_mut(stride).take(cc).enumerate() {
let start_idx = DECODE_BUFFER_SIZE - n;
let (_, rest) = channel_slice.split_at_mut(start_idx);
let (output, _) = rest.split_at_mut(n);
deemph_inputs[channel] = output;
}
deemphasis(
&deemph_inputs[..cc],
pcm,
n,
cc,
downsample,
&mode.pre_emphasis,
&mut decoder.preemph_mem_decoder,
accum,
);
}
decoder.loss_duration = 0;
decoder.prefilter_and_fold = false;
if entcode::ec_tell(dec.ctx()) > payload_len_bits {
return Err(CeltDecodeError::InvalidPacket);
}
if dec.ctx().error() != 0 {
decoder.error = 1;
}
Ok(output_samples)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn celt_decode_with_ec<'mode, 'pkt>(
decoder: &mut OpusCustomDecoder<'mode>,
packet: Option<&'pkt [u8]>,
pcm: &mut [OpusRes],
frame_size: usize,
range_decoder: Option<&'pkt mut EcDec<'pkt>>,
accum: bool,
) -> Result<usize, CeltDecodeError>
where
'mode: 'pkt,
{
#[cfg(feature = "deep_plc")]
let plc = None;
#[cfg(not(feature = "deep_plc"))]
let plc = ();
celt_decode_with_ec_dred(decoder, packet, pcm, frame_size, range_decoder, accum, plc)
}
pub(crate) fn opus_custom_decode(
decoder: &mut OpusCustomDecoder<'_>,
packet: Option<&[u8]>,
pcm: &mut [i16],
frame_size: usize,
) -> Result<usize, CeltDecodeError> {
let channels = decoder.channels;
let required = channels
.checked_mul(frame_size)
.ok_or(CeltDecodeError::BadArgument)?;
if pcm.len() < required {
return Err(CeltDecodeError::BadArgument);
}
#[cfg(feature = "fixed_point")]
let start_preemph_mem = decoder.fixed_preemph_mem_decoder;
let mut temp = vec![0.0f32; required];
let samples = celt_decode_with_ec(decoder, packet, &mut temp, frame_size, None, false)?;
#[cfg(all(test, feature = "fixed_point"))]
if channels == 1 && samples >= 891 {
for &idx in &[208usize, 680, 890] {
crate::test_trace::trace_println!(
"opus_custom_decode temp[{idx}]={} scaled={}",
temp[idx],
temp[idx] * CELT_SIG_SCALE
);
}
}
#[cfg(feature = "fixed_point")]
{
let n = samples * decoder.downsample.max(1) as usize;
let stride = DECODE_BUFFER_SIZE + decoder.overlap;
let start_idx = DECODE_BUFFER_SIZE - n;
debug_assert!(channels <= MAX_CHANNELS);
let mut inputs: [&[FixedCeltSig]; MAX_CHANNELS] = [&[]; MAX_CHANNELS];
for (channel, channel_slice) in decoder
.decode_mem_fixed
.chunks(stride)
.take(channels)
.enumerate()
{
inputs[channel] = &channel_slice[start_idx..start_idx + n];
}
let mut mem = start_preemph_mem;
deemphasis_fixed_to_int16(
&inputs[..channels],
pcm,
n,
channels,
decoder.downsample.max(1) as usize,
&decoder.mode.pre_emphasis,
&mut mem,
);
}
#[cfg(not(feature = "fixed_point"))]
for (dst, &src) in pcm.iter_mut().zip(temp.iter().take(samples * channels)) {
*dst = float2int16(src);
}
Ok(samples)
}
pub(crate) fn opus_custom_decode24(
decoder: &mut OpusCustomDecoder<'_>,
packet: Option<&[u8]>,
pcm: &mut [i32],
frame_size: usize,
) -> Result<usize, CeltDecodeError> {
let channels = decoder.channels;
let required = channels
.checked_mul(frame_size)
.ok_or(CeltDecodeError::BadArgument)?;
if pcm.len() < required {
return Err(CeltDecodeError::BadArgument);
}
let mut temp = vec![0.0f32; required];
let samples = celt_decode_with_ec(decoder, packet, &mut temp, frame_size, None, false)?;
for (dst, &src) in pcm.iter_mut().zip(temp.iter().take(samples * channels)) {
*dst = res_to_int24(src);
}
Ok(samples)
}
pub(crate) fn opus_custom_decode_float(
decoder: &mut OpusCustomDecoder<'_>,
packet: Option<&[u8]>,
pcm: &mut [f32],
frame_size: usize,
) -> Result<usize, CeltDecodeError> {
celt_decode_with_ec(decoder, packet, pcm, frame_size, None, false)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CeltDecoderInitError {
InvalidChannelCount,
InvalidStreamChannels,
UnsupportedSampleRate,
CanonicalModeUnavailable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CeltDecoderCtlError {
InvalidArgument,
Unimplemented,
}
pub(crate) enum DecoderCtlRequest<'dec, 'req> {
SetComplexity(i32),
GetComplexity(&'req mut i32),
SetStartBand(i32),
SetEndBand(i32),
SetChannels(usize),
GetAndClearError(&'req mut i32),
GetLookahead(&'req mut i32),
ResetState,
GetPitch(&'req mut i32),
GetMode(&'req mut Option<&'dec OpusCustomMode<'dec>>),
SetSignalling(i32),
GetFinalRange(&'req mut OpusUint32),
SetPhaseInversionDisabled(bool),
GetPhaseInversionDisabled(&'req mut bool),
}
#[derive(Debug, Default)]
pub(crate) struct CeltDecoderAlloc {
#[cfg(feature = "fixed_point")]
decode_mem_fixed: Vec<FixedCeltSig>,
#[cfg(feature = "fixed_point")]
lpc_fixed: Vec<FixedOpusVal16>,
#[cfg(feature = "fixed_point")]
old_ebands_fixed: Vec<FixedCeltGlog>,
#[cfg(feature = "fixed_point")]
old_log_e_fixed: Vec<FixedCeltGlog>,
#[cfg(feature = "fixed_point")]
old_log_e2_fixed: Vec<FixedCeltGlog>,
#[cfg(feature = "fixed_point")]
background_log_e_fixed: Vec<FixedCeltGlog>,
decode_mem: Vec<CeltSig>,
lpc: Vec<OpusVal16>,
old_ebands: Vec<CeltGlog>,
old_log_e: Vec<CeltGlog>,
old_log_e2: Vec<CeltGlog>,
background_log_e: Vec<CeltGlog>,
}
impl CeltDecoderAlloc {
pub(crate) fn new(mode: &OpusCustomMode<'_>, channels: usize) -> Self {
assert!(channels > 0, "decoder must contain at least one channel");
let overlap = mode.overlap;
let decode_mem = channels * (DECODE_BUFFER_SIZE + overlap);
let lpc = LPC_ORDER * channels;
let band_count = 2 * mode.num_ebands;
Self {
#[cfg(feature = "fixed_point")]
decode_mem_fixed: vec![0; decode_mem],
#[cfg(feature = "fixed_point")]
lpc_fixed: vec![0; lpc],
#[cfg(feature = "fixed_point")]
old_ebands_fixed: vec![0; band_count],
#[cfg(feature = "fixed_point")]
old_log_e_fixed: vec![0; band_count],
#[cfg(feature = "fixed_point")]
old_log_e2_fixed: vec![0; band_count],
#[cfg(feature = "fixed_point")]
background_log_e_fixed: vec![0; band_count],
decode_mem: vec![0.0; decode_mem],
lpc: vec![0.0; lpc],
old_ebands: vec![0.0; band_count],
old_log_e: vec![0.0; band_count],
old_log_e2: vec![0.0; band_count],
background_log_e: vec![0.0; band_count],
}
}
pub(crate) fn size_in_bytes(&self) -> usize {
let channels = self.lpc.len() / LPC_ORDER;
debug_assert!(channels > 0 && channels <= MAX_CHANNELS);
let decode_mem = self.decode_mem.len();
debug_assert!(decode_mem >= 1);
let band_history = self.old_ebands.len();
debug_assert_eq!(self.old_log_e.len(), band_history);
debug_assert_eq!(self.old_log_e2.len(), band_history);
debug_assert_eq!(self.background_log_e.len(), band_history);
#[cfg(feature = "fixed_point")]
{
debug_assert_eq!(self.decode_mem_fixed.len(), decode_mem);
debug_assert_eq!(self.lpc_fixed.len(), self.lpc.len());
debug_assert_eq!(self.old_ebands_fixed.len(), band_history);
debug_assert_eq!(self.old_log_e_fixed.len(), band_history);
debug_assert_eq!(self.old_log_e2_fixed.len(), band_history);
debug_assert_eq!(self.background_log_e_fixed.len(), band_history);
}
DECODER_PREFIX_SIZE
+ (decode_mem - 1) * core::mem::size_of::<CeltSig>()
+ self.lpc.len() * core::mem::size_of::<OpusVal16>()
+ 4 * band_history * core::mem::size_of::<CeltGlog>()
+ {
#[cfg(feature = "fixed_point")]
{
self.decode_mem_fixed.len() * core::mem::size_of::<FixedCeltSig>()
+ self.lpc_fixed.len() * core::mem::size_of::<FixedOpusVal16>()
+ (self.old_ebands_fixed.len()
+ self.old_log_e_fixed.len()
+ self.old_log_e2_fixed.len()
+ self.background_log_e_fixed.len())
* core::mem::size_of::<FixedCeltGlog>()
}
#[cfg(not(feature = "fixed_point"))]
{
0
}
}
}
pub(crate) fn as_decoder<'mode>(
&mut self,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
stream_channels: usize,
) -> OpusCustomDecoder<'mode> {
#[cfg(feature = "fixed_point")]
{
OpusCustomDecoder::new(
mode,
channels,
stream_channels,
core::mem::take(&mut self.decode_mem_fixed).into_boxed_slice(),
core::mem::take(&mut self.lpc_fixed).into_boxed_slice(),
core::mem::take(&mut self.old_ebands_fixed).into_boxed_slice(),
core::mem::take(&mut self.old_log_e_fixed).into_boxed_slice(),
core::mem::take(&mut self.old_log_e2_fixed).into_boxed_slice(),
core::mem::take(&mut self.background_log_e_fixed).into_boxed_slice(),
core::mem::take(&mut self.decode_mem).into_boxed_slice(),
core::mem::take(&mut self.lpc).into_boxed_slice(),
core::mem::take(&mut self.old_ebands).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.background_log_e).into_boxed_slice(),
)
}
#[cfg(not(feature = "fixed_point"))]
{
OpusCustomDecoder::new(
mode,
channels,
stream_channels,
core::mem::take(&mut self.decode_mem).into_boxed_slice(),
core::mem::take(&mut self.lpc).into_boxed_slice(),
core::mem::take(&mut self.old_ebands).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.background_log_e).into_boxed_slice(),
)
}
}
pub(crate) fn reset(&mut self) {
for sample in &mut self.decode_mem {
*sample = 0.0;
}
for coeff in &mut self.lpc {
*coeff = 0.0;
}
for history in &mut self.old_ebands {
*history = 0.0;
}
for history in &mut self.old_log_e {
*history = 0.0;
}
for history in &mut self.old_log_e2 {
*history = 0.0;
}
for history in &mut self.background_log_e {
*history = 0.0;
}
#[cfg(feature = "fixed_point")]
{
self.decode_mem_fixed.fill(0);
self.lpc_fixed.fill(0);
self.old_ebands_fixed.fill(0);
self.old_log_e_fixed.fill(0);
self.old_log_e2_fixed.fill(0);
self.background_log_e_fixed.fill(0);
}
}
fn prepare_decoder<'mode>(
&mut self,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
stream_channels: usize,
) -> Result<OpusCustomDecoder<'mode>, CeltDecoderInitError> {
validate_channel_layout(channels, stream_channels)?;
let mut decoder = self.as_decoder(mode, channels, stream_channels);
decoder.reset_runtime_state();
decoder.downsample = 1;
decoder.start_band = 0;
decoder.end_band = mode.effective_ebands as i32;
decoder.signalling = 1;
decoder.disable_inv = channels == 1;
decoder.arch = opus_select_arch();
Ok(decoder)
}
pub(crate) fn init_decoder<'mode>(
&mut self,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
stream_channels: usize,
) -> Result<OpusCustomDecoder<'mode>, CeltDecoderInitError> {
let mut decoder = self.prepare_decoder(mode, channels, stream_channels)?;
let downsample = resampling_factor(mode.sample_rate);
if downsample == 0 {
return Err(CeltDecoderInitError::UnsupportedSampleRate);
}
decoder.downsample = downsample as i32;
Ok(decoder)
}
}
pub(crate) fn opus_custom_decoder_init<'mode>(
alloc: &mut CeltDecoderAlloc,
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
) -> Result<OpusCustomDecoder<'mode>, CeltDecoderInitError> {
alloc.prepare_decoder(mode, channels, channels)
}
pub(crate) fn opus_custom_decoder_create<'mode>(
mode: &'mode OpusCustomMode<'mode>,
channels: usize,
) -> Result<OwnedCeltDecoder<'mode>, CeltDecoderInitError> {
validate_channel_layout(channels, channels)?;
let mut alloc = CeltDecoderAlloc::new(mode, channels);
let decoder = alloc.prepare_decoder(mode, channels, channels)?;
Ok(OwnedCeltDecoder { decoder })
}
#[inline]
pub(crate) fn opus_custom_decoder_destroy(_decoder: OwnedCeltDecoder<'_>) {}
fn validate_channel_layout(
channels: usize,
stream_channels: usize,
) -> Result<(), CeltDecoderInitError> {
if channels == 0 || channels > MAX_CHANNELS {
return Err(CeltDecoderInitError::InvalidChannelCount);
}
if stream_channels == 0 || stream_channels > channels {
return Err(CeltDecoderInitError::InvalidStreamChannels);
}
Ok(())
}
pub(crate) fn opus_custom_decoder_ctl<'dec, 'req>(
decoder: &mut OpusCustomDecoder<'dec>,
request: DecoderCtlRequest<'dec, 'req>,
) -> Result<(), CeltDecoderCtlError> {
match request {
DecoderCtlRequest::SetComplexity(value) => {
if !(0..=10).contains(&value) {
return Err(CeltDecoderCtlError::InvalidArgument);
}
decoder.complexity = value;
}
DecoderCtlRequest::GetComplexity(slot) => {
*slot = decoder.complexity;
}
DecoderCtlRequest::SetStartBand(value) => {
let max = decoder.mode.num_ebands as i32;
if value < 0 || value >= max {
return Err(CeltDecoderCtlError::InvalidArgument);
}
decoder.start_band = value;
}
DecoderCtlRequest::SetEndBand(value) => {
let max = decoder.mode.num_ebands as i32;
if value < 1 || value > max {
return Err(CeltDecoderCtlError::InvalidArgument);
}
decoder.end_band = value;
}
DecoderCtlRequest::SetChannels(value) => {
if value == 0 || value > MAX_CHANNELS {
return Err(CeltDecoderCtlError::InvalidArgument);
}
decoder.stream_channels = value;
}
DecoderCtlRequest::GetAndClearError(slot) => {
*slot = decoder.error;
decoder.error = 0;
}
DecoderCtlRequest::GetLookahead(slot) => {
let downsample = decoder.downsample;
if downsample <= 0 {
return Err(CeltDecoderCtlError::InvalidArgument);
}
*slot = (decoder.overlap as i32) / downsample;
}
DecoderCtlRequest::ResetState => {
decoder.reset_runtime_state();
}
DecoderCtlRequest::GetPitch(slot) => {
*slot = decoder.postfilter_period;
}
DecoderCtlRequest::GetMode(slot) => {
*slot = Some(decoder.mode);
}
DecoderCtlRequest::SetSignalling(value) => {
decoder.signalling = value;
}
DecoderCtlRequest::GetFinalRange(slot) => {
*slot = decoder.rng;
}
DecoderCtlRequest::SetPhaseInversionDisabled(value) => {
decoder.disable_inv = value;
}
DecoderCtlRequest::GetPhaseInversionDisabled(slot) => {
*slot = decoder.disable_inv;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
extern crate std;
#[cfg(feature = "fixed_point")]
use super::FIXED_SIG_SAT;
use super::celt_decode_lost;
use super::celt_plc_pitch_search;
#[cfg(feature = "fixed_point")]
use super::celt_plc_pitch_search_fixed;
#[cfg(feature = "fixed_point")]
use super::decoder_noise_renormalise_runtime;
use super::deemphasis;
#[cfg(feature = "fixed_point")]
use super::opus_custom_decode;
use super::{
CeltDecodeError, CeltDecoderAlloc, CeltDecoderCtlError, CeltDecoderInitError,
DECODE_BUFFER_SIZE, DecoderCtlRequest, LPC_ORDER, MAX_CHANNELS, celt_decoder_get_size,
celt_decoder_init, comb_filter, opus_custom_decoder_create, opus_custom_decoder_ctl,
opus_custom_decoder_get_size, opus_custom_decoder_init, prefilter_and_fold, prepare_frame,
tf_decode, validate_celt_decoder, validate_channel_layout,
};
use crate::celt::EcDec;
#[cfg(feature = "fixed_point")]
use crate::celt::bands::BandCodingState;
#[cfg(feature = "fixed_point")]
use crate::celt::celt_encoder::{
EncoderCtlRequest, opus_custom_encode, opus_custom_encoder_create, opus_custom_encoder_ctl,
};
#[cfg(feature = "fixed_point")]
use crate::celt::cwrs::decode_pulses_debug;
#[cfg(feature = "fixed_point")]
use crate::celt::entcode;
#[cfg(feature = "fixed_point")]
use crate::celt::entcode::celt_sudiv;
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_arch::{DB_SHIFT, sig2word16};
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_ops::{mult16_32_q15, qconst16_clamped, qconst32};
use crate::celt::float_cast::CELT_SIG_SCALE;
#[cfg(feature = "fixed_point")]
use crate::celt::float_cast::float2int16;
use crate::celt::modes::{opus_custom_mode_create, opus_custom_mode_find_static};
use crate::celt::opus_select_arch;
#[cfg(feature = "fixed_point")]
use crate::celt::rate::{bits2pulses, get_pulses};
#[cfg(feature = "fixed_point")]
use crate::celt::types::FixedCeltSig;
use crate::celt::types::{
CeltGlog, CeltNorm, CeltSig, MdctLookup, OpusCustomMode, PulseCacheData,
};
use alloc::vec;
use alloc::vec::Vec;
use core::f32::consts::PI;
use core::ptr;
#[test]
fn tf_decode_returns_default_table_entries() {
let mut range = EcDec::new(&[0u8]);
let mut tf_res = vec![0; 4];
tf_decode(0, tf_res.len(), false, &mut tf_res, 0, &mut range);
assert!(tf_res.iter().all(|&v| v == 0));
}
#[test]
fn celt_plc_pitch_search_detects_mono_period() {
let target_period = 320i32;
let mut channel = vec![0.0; super::DECODE_BUFFER_SIZE];
for (i, sample) in channel.iter_mut().enumerate() {
let phase = 2.0 * PI * (i as f32) / target_period as f32;
*sample = phase.sin();
}
let decode_mem = [&channel[..]];
let pitch = celt_plc_pitch_search(&decode_mem, decode_mem.len(), 0);
let doubled = target_period * 2;
let matches = (pitch - target_period).abs() <= 2
|| (doubled <= super::PLC_PITCH_LAG_MAX && (pitch - doubled).abs() <= 2);
assert!(
matches,
"pitch {pitch} deviates from target {target_period}",
);
}
#[test]
fn celt_plc_pitch_search_handles_stereo_average() {
let target_period = 480i32;
let mut left = vec![0.0; super::DECODE_BUFFER_SIZE];
let mut right = vec![0.0; super::DECODE_BUFFER_SIZE];
for i in 0..super::DECODE_BUFFER_SIZE {
let phase = 2.0 * PI * (i as f32) / target_period as f32;
left[i] = phase.sin();
right[i] = (phase + PI / 3.0).sin();
}
let decode_mem = [&left[..], &right[..]];
let pitch = celt_plc_pitch_search(&decode_mem, decode_mem.len(), 0);
assert!((pitch - target_period).abs() <= 4);
}
#[cfg(feature = "fixed_point")]
fn fill_plc_ctest_periodic(
channel: &mut [f32],
period: usize,
amp: i32,
phase: usize,
invert: bool,
) {
let half = period / 2;
for (i, sample) in channel.iter_mut().enumerate() {
let pos = (i + phase) % period;
let tri = if pos < half { pos } else { period - pos };
let centered = (tri as i32 * 2) - half as i32;
let mut value = (centered * amp) / half as i32;
if invert {
value = -value;
}
*sample = value as f32 / 32768.0;
}
}
#[cfg(feature = "fixed_point")]
fn fill_plc_ctest_periodic_sig(
channel: &mut [f32],
period: usize,
amp: i32,
phase: usize,
invert: bool,
) {
let half = period / 2;
for (i, sample) in channel.iter_mut().enumerate() {
let pos = (i + phase) % period;
let tri = if pos < half { pos } else { period - pos };
let centered = (tri as i32 * 2) - half as i32;
let mut value = (centered * amp) / half as i32;
if invert {
value = -value;
}
*sample = value as f32;
}
}
#[cfg(feature = "fixed_point")]
#[test]
fn celt_plc_pitch_search_matches_ctest_periodic_mono_shape() {
let mut mono = vec![0.0f32; super::DECODE_BUFFER_SIZE];
fill_plc_ctest_periodic(&mut mono, 96, 14_000, 0, false);
let decode_mem = [&mono[..]];
let pitch = celt_plc_pitch_search(&decode_mem, decode_mem.len(), 0);
let pitch_again = celt_plc_pitch_search(&decode_mem, decode_mem.len(), 0);
assert_eq!(pitch, pitch_again, "pitch search should be deterministic");
assert!(
(1..=super::PLC_PITCH_LAG_MAX).contains(&pitch),
"ctest-like mono pattern produced out-of-range pitch {pitch}",
);
}
#[cfg(feature = "fixed_point")]
#[test]
fn celt_plc_pitch_search_matches_ctest_periodic_stereo_shape() {
let mut left = vec![0.0f32; super::DECODE_BUFFER_SIZE];
let mut right = vec![0.0f32; super::DECODE_BUFFER_SIZE];
fill_plc_ctest_periodic(&mut left, 96, 14_000, 0, false);
fill_plc_ctest_periodic(&mut right, 96, 11_500, 23, true);
let decode_mem = [&left[..], &right[..]];
let pitch = celt_plc_pitch_search(&decode_mem, decode_mem.len(), 0);
let pitch_again = celt_plc_pitch_search(&decode_mem, decode_mem.len(), 0);
assert_eq!(pitch, pitch_again, "pitch search should be deterministic");
assert!(
(1..=super::PLC_PITCH_LAG_MAX).contains(&pitch),
"ctest-like stereo pattern produced out-of-range pitch {pitch}",
);
}
#[cfg(feature = "fixed_point")]
#[test]
fn decoder_plc_decay_terms_match_ctest_vectors() {
let mut exc = vec![0.0f32; 320];
fill_plc_ctest_periodic_sig(&mut exc, 96, 14_000, 0, false);
let (e1_a, e2_a, decay_a) = super::plc_decay_terms_fixed(&exc, exc.len());
assert_eq!(e1_a, 148_945_491);
assert_eq!(e2_a, 172_083_276);
assert_eq!(decay_a, 30_486);
fill_plc_ctest_periodic_sig(&mut exc, 96, 14_000, 0, false);
let half = exc.len() / 2;
for sample in &mut exc[half..] {
let quantised = super::fixed_sig_to_word16(*sample);
*sample = (quantised / 4) as f32;
}
let (e1_b, e2_b, decay_b) = super::plc_decay_terms_fixed(&exc, exc.len());
assert_eq!(e1_b, 9_306_492);
assert_eq!(e2_b, 172_083_276);
assert_eq!(decay_b, 7_620);
assert!(decay_b < decay_a);
fill_plc_ctest_periodic_sig(&mut exc, 64, 3_000, 7, true);
let third = exc.len() / 3;
for sample in &mut exc[..third] {
*sample = 0.0;
}
let (e1_c, e2_c, decay_c) = super::plc_decay_terms_fixed(&exc, exc.len());
assert_eq!(e1_c, 45_578_035);
assert_eq!(e2_c, 45_578_035);
assert_eq!(decay_c, 32_767);
}
#[cfg(feature = "fixed_point")]
#[test]
fn decoder_plc_ratio_terms_match_ctest_vectors() {
let mut old_sig = vec![0.0f32; 144];
let mut new_sig = vec![0.0f32; 144];
fill_plc_ctest_periodic_sig(&mut old_sig, 72, 7_000, 0, false);
fill_plc_ctest_periodic_sig(&mut new_sig, 72, 12_000, 11, true);
let (s1_a, s2_a, ratio_a) = super::plc_ratio_terms_fixed(&old_sig, &new_sig);
assert_eq!(s1_a, 2_299_964);
assert_eq!(s2_a, 6_759_820);
assert_eq!(ratio_a, 19_113);
fill_plc_ctest_periodic_sig(&mut old_sig, 72, 12_000, 11, true);
fill_plc_ctest_periodic_sig(&mut new_sig, 72, 7_000, 0, false);
let (s1_b, s2_b, ratio_b) = super::plc_ratio_terms_fixed(&old_sig, &new_sig);
assert_eq!(s1_b, 6_759_820);
assert_eq!(s2_b, 2_299_964);
assert_eq!(ratio_b, 32_767);
old_sig.fill(0.0);
new_sig.fill(0.0);
let (s1_c, s2_c, ratio_c) = super::plc_ratio_terms_fixed(&old_sig, &new_sig);
assert_eq!(s1_c, 0);
assert_eq!(s2_c, 0);
assert_eq!(ratio_c, 32_767);
}
#[test]
fn allocates_expected_band_buffers() {
let e_bands = [0, 2, 5];
let alloc_vectors = [0u8; 4];
let log_n = [0i16; 2];
let window = [0.0f32; 4];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0; 6], vec![0; 6], vec![0; 6]);
let mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 2);
assert_eq!(
alloc.decode_mem.len(),
2 * (super::DECODE_BUFFER_SIZE + mode.overlap)
);
assert_eq!(alloc.lpc.len(), LPC_ORDER * 2);
assert_eq!(alloc.old_ebands.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.background_log_e.len(), 2 * mode.num_ebands);
#[cfg(feature = "fixed_point")]
{
assert_eq!(
alloc.decode_mem_fixed.len(),
2 * (super::DECODE_BUFFER_SIZE + mode.overlap)
);
assert_eq!(alloc.lpc_fixed.len(), LPC_ORDER * 2);
assert_eq!(alloc.old_ebands_fixed.len(), 2 * mode.num_ebands);
assert_eq!(alloc.old_log_e_fixed.len(), 2 * mode.num_ebands);
assert_eq!(alloc.old_log_e2_fixed.len(), 2 * mode.num_ebands);
assert_eq!(alloc.background_log_e_fixed.len(), 2 * mode.num_ebands);
}
alloc.decode_mem.fill(1.0);
alloc.lpc.fill(1.0);
alloc.old_ebands.fill(1.0);
alloc.old_log_e.fill(1.0);
alloc.old_log_e2.fill(1.0);
alloc.background_log_e.fill(1.0);
#[cfg(feature = "fixed_point")]
{
alloc.decode_mem_fixed.fill(7);
alloc.lpc_fixed.fill(7);
alloc.old_ebands_fixed.fill(7);
alloc.old_log_e_fixed.fill(7);
alloc.old_log_e2_fixed.fill(7);
alloc.background_log_e_fixed.fill(7);
}
alloc.reset();
assert!(alloc.decode_mem.iter().all(|&v| v == 0.0));
assert!(alloc.lpc.iter().all(|&v| v == 0.0));
assert!(alloc.old_ebands.iter().all(|&v| v == 0.0));
assert!(alloc.old_log_e.iter().all(|&v| v == 0.0));
assert!(alloc.old_log_e2.iter().all(|&v| v == 0.0));
assert!(alloc.background_log_e.iter().all(|&v| v == 0.0));
#[cfg(feature = "fixed_point")]
{
assert!(alloc.decode_mem_fixed.iter().all(|&v| v == 0));
assert!(alloc.lpc_fixed.iter().all(|&v| v == 0));
assert!(alloc.old_ebands_fixed.iter().all(|&v| v == 0));
assert!(alloc.old_log_e_fixed.iter().all(|&v| v == 0));
assert!(alloc.old_log_e2_fixed.iter().all(|&v| v == 0));
assert!(alloc.background_log_e_fixed.iter().all(|&v| v == 0));
}
let expected_size = opus_custom_decoder_get_size(&mode, 2).expect("decoder size");
assert_eq!(alloc.size_in_bytes(), expected_size);
}
#[test]
fn celt_decoder_get_size_honours_channel_limits() {
assert!(celt_decoder_get_size(0).is_none());
assert!(celt_decoder_get_size(3).is_none());
assert!(celt_decoder_get_size(1).is_some());
assert!(celt_decoder_get_size(2).is_some());
}
#[test]
fn celt_decoder_init_sets_downsampling_factor() {
let mode = super::canonical_mode().expect("canonical mode");
let mut alloc = CeltDecoderAlloc::new(mode, 1);
let decoder = celt_decoder_init(&mut alloc, 16_000, 1).expect("decoder");
assert_eq!(decoder.downsample, 3);
}
#[test]
fn celt_decoder_init_rejects_unsupported_rate() {
let mode = super::canonical_mode().expect("canonical mode");
let mut alloc = CeltDecoderAlloc::new(mode, 1);
let err = celt_decoder_init(&mut alloc, 44_100, 1).unwrap_err();
assert_eq!(err, CeltDecoderInitError::UnsupportedSampleRate);
}
#[test]
fn opus_custom_decoder_create_initialises_state() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
let decoder = opus_custom_decoder_create(&mode, 2).expect("decoder");
assert_eq!(decoder.channels, 2);
assert_eq!(decoder.stream_channels, 2);
assert_eq!(decoder.downsample, 1);
assert_eq!(decoder.start_band, 0);
assert_eq!(decoder.end_band, mode.effective_ebands as i32);
assert_eq!(decoder.signalling, 1);
assert!(!decoder.disable_inv);
assert_eq!(decoder.arch, opus_select_arch());
}
#[test]
fn opus_custom_decoder_create_rejects_invalid_channel_layouts() {
let owned = opus_custom_mode_create(48_000, 960).expect("mode");
let mode = owned.mode();
assert!(opus_custom_decoder_create(&mode, 0).is_err());
assert!(opus_custom_decoder_create(&mode, 3).is_err());
}
#[test]
fn validate_celt_decoder_accepts_default_configuration() {
let e_bands = [0, 2, 5];
let alloc_vectors = [0u8; 4];
let log_n = [0i16; 2];
let window = [0.0f32; 4];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0; 6], vec![0; 6], vec![0; 6]);
let mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let decoder = alloc.as_decoder(&mode, 1, 1);
validate_celt_decoder(&decoder);
}
#[test]
#[cfg_attr(not(debug_assertions), ignore = "debug_assertions disabled in release")]
#[should_panic]
fn validate_celt_decoder_rejects_invalid_channel_count() {
let e_bands = [0, 2, 5];
let alloc_vectors = [0u8; 4];
let log_n = [0i16; 2];
let window = [0.0f32; 4];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0; 6], vec![0; 6], vec![0; 6]);
let mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let mut decoder = alloc.as_decoder(&mode, 1, 1);
decoder.channels = 3;
validate_celt_decoder(&decoder);
}
#[test]
fn validate_channel_layout_rejects_invalid_configurations() {
assert_eq!(
validate_channel_layout(0, 0),
Err(CeltDecoderInitError::InvalidChannelCount)
);
assert_eq!(
validate_channel_layout(MAX_CHANNELS + 1, 1),
Err(CeltDecoderInitError::InvalidChannelCount)
);
assert_eq!(
validate_channel_layout(1, 0),
Err(CeltDecoderInitError::InvalidStreamChannels)
);
assert_eq!(
validate_channel_layout(1, 2),
Err(CeltDecoderInitError::InvalidStreamChannels)
);
}
#[test]
fn init_decoder_populates_expected_defaults() {
let e_bands = [0, 2, 5];
let alloc_vectors = [0u8; 4];
let log_n = [0i16; 2];
let window = [0.0f32; 4];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0; 6], vec![0; 6], vec![0; 6]);
let mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let decoder = alloc
.init_decoder(&mode, 1, 1)
.expect("initialisation should succeed");
assert_eq!(decoder.overlap, mode.overlap);
assert_eq!(decoder.downsample, 1);
assert_eq!(decoder.end_band, mode.effective_ebands as i32);
assert_eq!(decoder.arch, 0);
assert_eq!(decoder.rng, 0);
assert_eq!(decoder.loss_duration, 0);
assert_eq!(decoder.postfilter_period, 0);
assert_eq!(decoder.postfilter_gain, 0.0);
assert_eq!(decoder.postfilter_tapset, 0);
assert!(decoder.skip_plc);
}
#[test]
fn opus_custom_decoder_init_matches_reference_defaults() {
let e_bands = [0, 2, 5];
let alloc_vectors = [0u8; 4];
let log_n = [0i16; 2];
let window = [0.0f32; 4];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0; 6], vec![0; 6], vec![0; 6]);
let mode = OpusCustomMode::new_test(
12_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let decoder = opus_custom_decoder_init(&mut alloc, &mode, 1)
.expect("custom decoder initialisation should succeed");
assert_eq!(decoder.downsample, 1);
assert_eq!(decoder.start_band, 0);
assert_eq!(decoder.end_band, mode.effective_ebands as i32);
assert_eq!(decoder.signalling, 1);
assert!(decoder.disable_inv);
assert!(decoder.skip_plc);
assert_eq!(decoder.arch, 0);
}
#[test]
fn decoder_ctl_handles_configuration_requests() {
let e_bands = [0, 2, 5];
let alloc_vectors = [0u8; 4];
let log_n = [0i16; 2];
let window = [0.0f32; 4];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0; 6], vec![0; 6], vec![0; 6]);
let mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 2);
let mut decoder = alloc
.init_decoder(&mode, 2, 2)
.expect("decoder initialisation should succeed");
let err = opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetComplexity(11))
.unwrap_err();
assert_eq!(err, CeltDecoderCtlError::InvalidArgument);
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetComplexity(7)).unwrap();
let mut complexity = 0;
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::GetComplexity(&mut complexity),
)
.unwrap();
assert_eq!(complexity, 7);
let max = mode.num_ebands as i32;
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetStartBand(1)).unwrap();
assert_eq!(decoder.start_band, 1);
let err = opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetStartBand(max))
.unwrap_err();
assert_eq!(err, CeltDecoderCtlError::InvalidArgument);
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetEndBand(max)).unwrap();
assert_eq!(decoder.end_band, max);
let err =
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetEndBand(0)).unwrap_err();
assert_eq!(err, CeltDecoderCtlError::InvalidArgument);
let err =
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetChannels(3)).unwrap_err();
assert_eq!(err, CeltDecoderCtlError::InvalidArgument);
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetChannels(1)).unwrap();
assert_eq!(decoder.stream_channels, 1);
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetChannels(2)).unwrap();
assert_eq!(decoder.stream_channels, 2);
decoder.error = -57;
let mut reported_error = 0;
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::GetAndClearError(&mut reported_error),
)
.unwrap();
assert_eq!(reported_error, -57);
assert_eq!(decoder.error, 0);
decoder.overlap = 6;
decoder.downsample = 2;
let mut lookahead = 0;
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::GetLookahead(&mut lookahead),
)
.unwrap();
assert_eq!(lookahead, 3);
decoder.postfilter_period = 321;
let mut pitch = 0;
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::GetPitch(&mut pitch)).unwrap();
assert_eq!(pitch, 321);
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetSignalling(5)).unwrap();
assert_eq!(decoder.signalling, 5);
decoder.rng = 0xDEADBEEF;
let mut rng = 0;
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::GetFinalRange(&mut rng)).unwrap();
assert_eq!(rng, 0xDEADBEEF);
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::SetPhaseInversionDisabled(true),
)
.unwrap();
assert!(decoder.disable_inv);
let mut disabled = false;
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::GetPhaseInversionDisabled(&mut disabled),
)
.unwrap();
assert!(disabled);
let mut mode_slot = None;
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::GetMode(&mut mode_slot)).unwrap();
let mode_ref = mode_slot.expect("mode reference");
assert!(ptr::eq(mode_ref, &mode));
}
#[test]
fn decoder_ctl_reset_state_matches_reference() {
let e_bands = [0, 2, 5];
let alloc_vectors = [0u8; 4];
let log_n = [0i16; 2];
let window = [0.0f32; 4];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0; 6], vec![0; 6], vec![0; 6]);
let mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let mut decoder = alloc
.init_decoder(&mode, 1, 1)
.expect("decoder initialisation should succeed");
decoder.rng = 1234;
decoder.error = -1;
decoder.last_pitch_index = 77;
decoder.loss_duration = 99;
decoder.skip_plc = false;
decoder.postfilter_period = 12;
decoder.postfilter_period_old = 34;
decoder.postfilter_gain = 0.5;
decoder.postfilter_gain_old = 0.25;
decoder.postfilter_tapset = 2;
decoder.postfilter_tapset_old = 1;
decoder.prefilter_and_fold = true;
decoder.preemph_mem_decoder = [0.1, -0.2];
decoder.decode_mem.fill(1.0);
decoder.lpc.fill(0.5);
decoder.old_ebands.fill(0.75);
decoder.old_log_e.fill(1.0);
decoder.old_log_e2.fill(1.5);
decoder.background_log_e.fill(0.125);
#[cfg(feature = "fixed_point")]
{
decoder.decode_mem_fixed.fill(7);
decoder.lpc_fixed.fill(7);
decoder.old_ebands_fixed.fill(7);
decoder.old_log_e_fixed.fill(7);
decoder.old_log_e2_fixed.fill(7);
decoder.background_log_e_fixed.fill(7);
}
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::ResetState).unwrap();
assert_eq!(decoder.rng, 0);
assert_eq!(decoder.error, 0);
assert_eq!(decoder.last_pitch_index, 0);
assert_eq!(decoder.loss_duration, 0);
assert!(decoder.skip_plc);
assert_eq!(decoder.postfilter_period, 0);
assert_eq!(decoder.postfilter_period_old, 0);
assert_eq!(decoder.postfilter_gain, 0.0);
assert_eq!(decoder.postfilter_gain_old, 0.0);
assert_eq!(decoder.postfilter_tapset, 0);
assert_eq!(decoder.postfilter_tapset_old, 0);
assert!(!decoder.prefilter_and_fold);
assert_eq!(decoder.preemph_mem_decoder, [0.0, 0.0]);
assert!(decoder.decode_mem.iter().all(|&v| v == 0.0));
assert!(decoder.lpc.iter().all(|&v| v == 0.0));
assert!(decoder.old_ebands.iter().all(|&v| v == 0.0));
assert!(decoder.old_log_e.iter().all(|&v| v == -28.0));
assert!(decoder.old_log_e2.iter().all(|&v| v == -28.0));
#[cfg(feature = "fixed_point")]
{
assert!(decoder.decode_mem_fixed.iter().all(|&v| v == 0));
assert!(decoder.lpc_fixed.iter().all(|&v| v == 0));
assert!(decoder.old_ebands_fixed.iter().all(|&v| v == 0));
assert!(
decoder
.old_log_e_fixed
.iter()
.all(|&v| v == qconst32(-28.0, DB_SHIFT))
);
assert!(
decoder
.old_log_e2_fixed
.iter()
.all(|&v| v == qconst32(-28.0, DB_SHIFT))
);
assert!(decoder.background_log_e_fixed.iter().all(|&v| v == 0));
}
assert!(decoder.background_log_e.iter().all(|&v| v == 0.0));
}
#[test]
fn prepare_frame_handles_packet_loss() {
let e_bands = [0, 2, 5];
let alloc_vectors = [0u8; 4];
let log_n = [0i16; 2];
let window = [0.0f32; 4];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0; 6], vec![0; 6], vec![0; 6]);
let mut mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
mode.short_mdct_size = 2;
mode.max_lm = 2;
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let mut decoder = alloc
.init_decoder(&mode, 1, 1)
.expect("decoder initialisation should succeed");
let frame = prepare_frame(&mut decoder, &[], mode.short_mdct_size, None)
.expect("packet loss preparation should succeed");
assert!(frame.packet_loss);
}
#[test]
fn prepare_frame_rejects_mismatched_frame_size() {
let e_bands = [0, 2, 5];
let alloc_vectors = [0u8; 4];
let log_n = [0i16; 2];
let window = [0.0f32; 4];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0; 6], vec![0; 6], vec![0; 6]);
let mut mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
mode.short_mdct_size = 2;
mode.max_lm = 2;
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let mut decoder = alloc
.init_decoder(&mode, 1, 1)
.expect("decoder initialisation should succeed");
decoder.signalling = 0;
let err = prepare_frame(&mut decoder, &[0u8; 2], mode.short_mdct_size + 1, None)
.expect_err("invalid frame size must be rejected");
assert_eq!(err, CeltDecodeError::BadArgument);
}
#[test]
fn prefilter_and_fold_rebuilds_overlap_tail() {
let e_bands = [0, 1];
let alloc_vectors = [0u8; 1];
let log_n = [0i16; 1];
let window = [0.25f32, 0.5, 0.5, 0.25];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0i16; 2], vec![0; 2], vec![0; 2]);
let mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let mut decoder = alloc
.init_decoder(&mode, 1, 1)
.expect("decoder initialisation should succeed");
let overlap = mode.overlap;
let stride = DECODE_BUFFER_SIZE + overlap;
assert_eq!(decoder.decode_mem.len(), stride);
for (idx, sample) in decoder.decode_mem.iter_mut().enumerate() {
*sample = (idx as f32) * 0.0002;
}
#[cfg(feature = "fixed_point")]
for (dst, &src) in decoder
.decode_mem_fixed
.iter_mut()
.zip(decoder.decode_mem.iter())
{
*dst = super::celt_sig_to_fixed(src);
}
let n = 32;
let start = DECODE_BUFFER_SIZE - n;
let original = decoder.decode_mem[start..start + overlap].to_vec();
decoder.postfilter_gain = 0.0;
decoder.postfilter_gain_old = 0.0;
decoder.postfilter_tapset = 0;
decoder.postfilter_tapset_old = 0;
prefilter_and_fold(&mut decoder, n);
let tolerance = if cfg!(feature = "fixed_point") {
2e-3f32
} else {
1e-6f32
};
for i in 0..(overlap / 2) {
let expected =
window[i] * original[overlap - 1 - i] + window[overlap - 1 - i] * original[i];
let actual = decoder.decode_mem[start + i];
assert!(
(expected - actual).abs() <= tolerance,
"folded sample {i} differs: expected {expected}, got {actual}",
);
#[cfg(feature = "fixed_point")]
assert_eq!(
decoder.decode_mem_fixed[start + i],
super::celt_sig_to_fixed(actual),
"fixed cache mismatch for folded sample {i}",
);
}
for i in overlap / 2..overlap {
let idx = start + i;
assert_eq!(decoder.decode_mem[idx], original[i]);
}
}
#[test]
fn prefilter_and_fold_filters_overlap_tail_with_gain() {
let e_bands = [0, 1];
let alloc_vectors = [0u8; 1];
let log_n = [0i16; 1];
let window = [0.25f32, 0.5, 0.5, 0.25];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0i16; 2], vec![0; 2], vec![0; 2]);
let mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let mut decoder = alloc
.init_decoder(&mode, 1, 1)
.expect("decoder initialisation should succeed");
let overlap = mode.overlap;
let stride = DECODE_BUFFER_SIZE + overlap;
assert_eq!(decoder.decode_mem.len(), stride);
for (idx, sample) in decoder.decode_mem.iter_mut().enumerate() {
*sample = (idx as f32) * 0.0002;
}
#[cfg(feature = "fixed_point")]
for (dst, &src) in decoder
.decode_mem_fixed
.iter_mut()
.zip(decoder.decode_mem.iter())
{
*dst = super::celt_sig_to_fixed(src);
}
let n = 64;
let start = DECODE_BUFFER_SIZE - n;
let baseline: Vec<f32> = decoder.decode_mem.iter().copied().collect();
let original = baseline[start..start + overlap].to_vec();
decoder.postfilter_gain_old = 0.2;
decoder.postfilter_gain = 0.35;
decoder.postfilter_period_old = 24;
decoder.postfilter_period = 32;
decoder.postfilter_tapset_old = 0;
decoder.postfilter_tapset = 1;
prefilter_and_fold(&mut decoder, n);
let mut expected_filtered = vec![0.0; overlap];
comb_filter(
&mut expected_filtered,
&baseline,
start,
overlap,
decoder.postfilter_period_old,
decoder.postfilter_period,
-decoder.postfilter_gain_old,
-decoder.postfilter_gain,
decoder.postfilter_tapset_old as usize,
decoder.postfilter_tapset as usize,
&[],
0,
decoder.arch,
);
let tolerance = if cfg!(feature = "fixed_point") {
2e-3f32
} else {
1e-6f32
};
for i in 0..(overlap / 2) {
let expected = window[i] * expected_filtered[overlap - 1 - i]
+ window[overlap - 1 - i] * expected_filtered[i];
let actual = decoder.decode_mem[start + i];
assert!(
(expected - actual).abs() <= tolerance,
"filtered fold {i} differs: expected {expected}, got {actual}",
);
#[cfg(feature = "fixed_point")]
assert!(
(decoder.decode_mem_fixed[start + i] - super::celt_sig_to_fixed(actual)).abs() <= 1,
"fixed cache mismatch for filtered fold {i}: fixed={}, float2sig={}",
decoder.decode_mem_fixed[start + i],
super::celt_sig_to_fixed(actual),
);
}
for i in overlap / 2..overlap {
let idx = start + i;
assert_eq!(decoder.decode_mem[idx], original[i]);
}
}
#[test]
fn prefilter_and_fold_handles_stereo_channels_independently() {
let e_bands = [0, 1];
let alloc_vectors = [0u8; 1];
let log_n = [0i16; 1];
let window = [0.25f32, 0.5, 0.5, 0.25];
let mdct = MdctLookup::new(8, 0);
let cache = PulseCacheData::new(vec![0i16; 2], vec![0; 2], vec![0; 2]);
let mode = OpusCustomMode::new_test(
48_000,
4,
&e_bands,
&alloc_vectors,
&log_n,
&window,
mdct,
cache,
);
let mut alloc = CeltDecoderAlloc::new(&mode, 2);
let mut decoder = alloc
.init_decoder(&mode, 2, 2)
.expect("decoder initialisation should succeed");
let overlap = mode.overlap;
let stride = DECODE_BUFFER_SIZE + overlap;
assert_eq!(decoder.decode_mem.len(), 2 * stride);
for i in 0..stride {
decoder.decode_mem[i] = (i as f32) * 0.0002;
decoder.decode_mem[stride + i] = -0.3 + (i as f32) * 0.00025;
}
#[cfg(feature = "fixed_point")]
for (dst, &src) in decoder
.decode_mem_fixed
.iter_mut()
.zip(decoder.decode_mem.iter())
{
*dst = super::celt_sig_to_fixed(src);
}
let n = 48;
let start = DECODE_BUFFER_SIZE - n;
let baseline: Vec<f32> = decoder.decode_mem.iter().copied().collect();
decoder.postfilter_gain_old = 0.1;
decoder.postfilter_gain = 0.3;
decoder.postfilter_period_old = 20;
decoder.postfilter_period = 28;
decoder.postfilter_tapset_old = 2;
decoder.postfilter_tapset = 1;
prefilter_and_fold(&mut decoder, n);
let tolerance = if cfg!(feature = "fixed_point") {
2e-3f32
} else {
1e-6f32
};
for channel in 0..2 {
let offset = channel * stride;
let original = &baseline[offset + start..offset + start + overlap];
let mut expected_filtered = vec![0.0; overlap];
comb_filter(
&mut expected_filtered,
&baseline[offset..offset + stride],
start,
overlap,
decoder.postfilter_period_old,
decoder.postfilter_period,
-decoder.postfilter_gain_old,
-decoder.postfilter_gain,
decoder.postfilter_tapset_old as usize,
decoder.postfilter_tapset as usize,
&[],
0,
decoder.arch,
);
for i in 0..(overlap / 2) {
let expected = window[i] * expected_filtered[overlap - 1 - i]
+ window[overlap - 1 - i] * expected_filtered[i];
let actual = decoder.decode_mem[offset + start + i];
assert!(
(expected - actual).abs() <= tolerance,
"channel {channel} fold {i} differs: expected {expected}, got {actual}",
);
#[cfg(feature = "fixed_point")]
assert!(
(decoder.decode_mem_fixed[offset + start + i]
- super::celt_sig_to_fixed(actual))
.abs()
<= 1,
"channel {channel} fixed cache mismatch at fold {i}: fixed={}, float2sig={}",
decoder.decode_mem_fixed[offset + start + i],
super::celt_sig_to_fixed(actual),
);
}
for i in overlap / 2..overlap {
let idx = offset + start + i;
assert_eq!(decoder.decode_mem[idx], original[i]);
}
}
}
#[test]
fn celt_decode_lost_noise_branch_updates_state() {
use crate::celt::modes::opus_custom_mode_find_static;
let mode = opus_custom_mode_find_static(48_000, 960).expect("static mode");
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let mut decoder = alloc.as_decoder(&mode, 1, 1);
decoder.downsample = 1;
decoder.end_band = mode.num_ebands as i32;
decoder.loss_duration = 0;
decoder.skip_plc = true;
decoder.prefilter_and_fold = true;
decoder.rng = 0x1234_5678;
decoder.background_log_e.fill(-8.0);
decoder.old_ebands.fill(-4.0);
#[cfg(feature = "fixed_point")]
{
decoder
.background_log_e_fixed
.fill(qconst32(-8.0, DB_SHIFT));
decoder.old_ebands_fixed.fill(qconst32(-4.0, DB_SHIFT));
}
let n = mode.short_mdct_size;
#[cfg(feature = "deep_plc")]
let plc = None;
#[cfg(not(feature = "deep_plc"))]
let plc = ();
celt_decode_lost(&mut decoder, n, 0, plc);
assert!(decoder.skip_plc);
assert!(!decoder.prefilter_and_fold);
assert_ne!(decoder.rng, 0x1234_5678);
assert_eq!(decoder.loss_duration, 1);
}
#[test]
fn celt_decode_lost_pitch_branch_generates_output() {
use crate::celt::modes::opus_custom_mode_find_static;
let mode = opus_custom_mode_find_static(48_000, 960).expect("static mode");
let mut alloc = CeltDecoderAlloc::new(&mode, 1);
let mut decoder = alloc.as_decoder(&mode, 1, 1);
decoder.downsample = 1;
decoder.end_band = mode.num_ebands as i32;
decoder.loss_duration = 2;
decoder.skip_plc = false;
decoder.last_pitch_index = 200;
decoder.prefilter_and_fold = false;
let _stride = DECODE_BUFFER_SIZE + mode.overlap;
let mut seed = vec![0.0f32; decoder.decode_mem.len()];
fill_plc_ctest_periodic_sig(&mut seed, 96, 14_000, 0, false);
for (idx, sample) in decoder.decode_mem.iter_mut().enumerate() {
let value = seed[idx];
*sample = value;
#[cfg(feature = "fixed_point")]
{
decoder.decode_mem_fixed[idx] = super::celt_sig_to_fixed(value);
}
}
let n = mode.short_mdct_size;
#[cfg(feature = "deep_plc")]
let plc = None;
#[cfg(not(feature = "deep_plc"))]
let plc = ();
celt_decode_lost(&mut decoder, n, 0, plc);
assert!(decoder.prefilter_and_fold);
assert!(!decoder.skip_plc);
assert_eq!(decoder.loss_duration, 3);
let start = DECODE_BUFFER_SIZE - n;
let produced = &decoder.decode_mem[start..start + n];
let energy: f32 = produced.iter().map(|v| v.abs()).sum();
assert!(energy > 0.0);
}
#[cfg(feature = "fixed_point")]
const POSTFILTER_CASE_FRAMES: usize = 6;
#[cfg(feature = "fixed_point")]
const POSTFILTER_SAMPLE_RATE: i32 = 48_000;
#[cfg(feature = "fixed_point")]
const POSTFILTER_FRAME_SIZE: usize = 960;
#[cfg(feature = "fixed_point")]
const POSTFILTER_MAX_PACKET_SIZE: usize = 1276;
#[cfg(feature = "fixed_point")]
const POSTFILTER_MAX_PITCH: i32 = 1024;
#[cfg(feature = "fixed_point")]
struct DecoderPostfilterCase {
name: &'static str,
channels: usize,
bitrate: i32,
max_bytes: usize,
min_nonzero_pitch_frames: usize,
require_pitch_changes: bool,
expected_pitch: [i32; POSTFILTER_CASE_FRAMES],
expected_packet_hash: [u32; POSTFILTER_CASE_FRAMES],
expected_pcm_hash: [u32; POSTFILTER_CASE_FRAMES],
}
#[cfg(feature = "fixed_point")]
fn fnv1a_update(mut hash: u32, byte: u8) -> u32 {
hash ^= u32::from(byte);
hash.wrapping_mul(16_777_619)
}
#[cfg(feature = "fixed_point")]
fn fnv1a_bytes(data: &[u8]) -> u32 {
data.iter()
.fold(2_166_136_261, |hash, byte| fnv1a_update(hash, *byte))
}
#[cfg(feature = "fixed_point")]
fn fnv1a_pcm_le(data: &[i16]) -> u32 {
let mut hash = 2_166_136_261u32;
for sample in data {
for byte in sample.to_le_bytes() {
hash = fnv1a_update(hash, byte);
}
}
hash
}
#[cfg(feature = "fixed_point")]
fn fill_postfilter_case_pcm(
pcm: &mut [i16],
frame_size: usize,
channels: usize,
frame_idx: usize,
) {
for i in 0..frame_size {
let s0 = ((i * 7 + frame_idx * 31) % 3000) as i32 - 1500;
let s1 = ((i * 11 + frame_idx * 19) % 2600) as i32 - 1300;
let shaped0 = s0 + if (i & 7) == 0 { 900 } else { -300 };
let shaped1 = s1 + if (i % 5) == 0 { -700 } else { 250 };
if channels == 1 {
pcm[i] = (shaped0 * 8) as i16;
} else {
pcm[2 * i] = (shaped0 * 7) as i16;
pcm[2 * i + 1] = (shaped1 * 7) as i16;
}
}
}
#[cfg(feature = "fixed_point")]
fn run_decoder_postfilter_case(mode: &OpusCustomMode<'_>, case: &DecoderPostfilterCase) {
let strict_ctest_vectors = std::env::var_os("RUST_CTEST_STRICT_HASHES").is_some();
let mut encoder =
opus_custom_encoder_create(mode, POSTFILTER_SAMPLE_RATE, case.channels, 0)
.unwrap_or_else(|err| panic!("{}: encoder create failed: {err:?}", case.name));
let mut decoder = opus_custom_decoder_create(mode, case.channels)
.unwrap_or_else(|err| panic!("{}: decoder create failed: {err:?}", case.name));
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetBitrate(case.bitrate))
.unwrap_or_else(|err| panic!("{}: set bitrate failed: {err:?}", case.name));
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetVbr(false))
.unwrap_or_else(|err| panic!("{}: set vbr failed: {err:?}", case.name));
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetComplexity(10))
.unwrap_or_else(|err| panic!("{}: set complexity failed: {err:?}", case.name));
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetLsbDepth(16))
.unwrap_or_else(|err| panic!("{}: set lsb_depth failed: {err:?}", case.name));
let mut invalid_output = vec![0i16; 123 * case.channels];
assert!(
opus_custom_decode(&mut decoder, Some(&[0]), &mut invalid_output, 123).is_err(),
"{}: decode with invalid frame size should fail",
case.name
);
assert_eq!(
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetComplexity(99))
.expect_err("invalid decoder complexity must fail"),
CeltDecoderCtlError::InvalidArgument,
"{}: invalid decoder complexity should map to InvalidArgument",
case.name
);
let mut nonzero_pitch_frames = 0usize;
let mut pitch_changes = 0usize;
let mut packet_hash_changes = 0usize;
let mut pcm_hash_changes = 0usize;
let mut prev_pitch: Option<i32> = None;
let mut prev_packet_hash: Option<u32> = None;
let mut prev_pcm_hash: Option<u32> = None;
let mut packet = vec![0u8; POSTFILTER_MAX_PACKET_SIZE];
let mut pcm = vec![0i16; POSTFILTER_FRAME_SIZE * case.channels];
let mut decoded = vec![0i16; POSTFILTER_FRAME_SIZE * case.channels];
for frame_idx in 0..POSTFILTER_CASE_FRAMES {
fill_postfilter_case_pcm(&mut pcm, POSTFILTER_FRAME_SIZE, case.channels, frame_idx);
let packet_len = opus_custom_encode(
&mut encoder,
&pcm,
POSTFILTER_FRAME_SIZE,
&mut packet,
case.max_bytes,
)
.unwrap_or_else(|err| {
panic!("{} frame {frame_idx}: encode failed: {err:?}", case.name)
});
let decoded_len = opus_custom_decode(
&mut decoder,
Some(&packet[..packet_len]),
&mut decoded,
POSTFILTER_FRAME_SIZE,
)
.unwrap_or_else(|err| {
panic!("{} frame {frame_idx}: decode failed: {err:?}", case.name)
});
assert_eq!(
decoded_len, POSTFILTER_FRAME_SIZE,
"{} frame {}: decode length mismatch",
case.name, frame_idx
);
let mut pitch = 0;
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::GetPitch(&mut pitch))
.unwrap_or_else(|err| {
panic!("{} frame {frame_idx}: get pitch failed: {err:?}", case.name)
});
assert!(
(0..=POSTFILTER_MAX_PITCH).contains(&pitch),
"{} frame {}: pitch {} out of range",
case.name,
frame_idx,
pitch
);
if pitch > 0 {
nonzero_pitch_frames += 1;
}
if let Some(prev) = prev_pitch {
if prev != pitch {
pitch_changes += 1;
}
}
prev_pitch = Some(pitch);
let packet_hash = fnv1a_bytes(&packet[..packet_len]);
let pcm_hash = fnv1a_pcm_le(&decoded[..POSTFILTER_FRAME_SIZE * case.channels]);
if let Some(prev) = prev_packet_hash {
if prev != packet_hash {
packet_hash_changes += 1;
}
}
prev_packet_hash = Some(packet_hash);
if let Some(prev) = prev_pcm_hash {
if prev != pcm_hash {
pcm_hash_changes += 1;
}
}
prev_pcm_hash = Some(pcm_hash);
if strict_ctest_vectors {
if case.expected_pitch[frame_idx] != 0 {
assert_eq!(
pitch, case.expected_pitch[frame_idx],
"{} frame {}: pitch mismatch",
case.name, frame_idx
);
}
if case.expected_packet_hash[frame_idx] != 0 {
assert_eq!(
packet_hash, case.expected_packet_hash[frame_idx],
"{} frame {}: packet hash mismatch",
case.name, frame_idx
);
}
if case.expected_pcm_hash[frame_idx] != 0 {
assert_eq!(
pcm_hash, case.expected_pcm_hash[frame_idx],
"{} frame {}: pcm hash mismatch",
case.name, frame_idx
);
}
}
}
if case.min_nonzero_pitch_frames > 0 {
assert!(
nonzero_pitch_frames >= case.min_nonzero_pitch_frames,
"{}: expected at least {} non-zero pitch frames, got {}",
case.name,
case.min_nonzero_pitch_frames,
nonzero_pitch_frames
);
}
if case.require_pitch_changes {
assert!(
pitch_changes > 0,
"{}: expected at least one pitch change across frames",
case.name
);
}
assert!(
packet_hash_changes > 0,
"{}: expected encoded packet hashes to change across frames",
case.name
);
assert!(
pcm_hash_changes > 0,
"{}: expected decoded PCM hashes to change across frames",
case.name
);
assert!(
opus_custom_decode(
&mut decoder,
Some(&[0]),
&mut decoded,
POSTFILTER_FRAME_SIZE
)
.is_err(),
"{}: tiny packet decode should fail",
case.name
);
}
#[cfg(feature = "fixed_point")]
const DECODER_STATE_WARM_FRAMES: usize = 8;
#[cfg(feature = "fixed_point")]
const DECODER_STATE_LOSS_FRAMES: usize = 6;
#[cfg(feature = "fixed_point")]
const DECODER_DATAFLOW_FRAMES: usize = 13;
#[cfg(feature = "fixed_point")]
const DECODER_DATAFLOW_NB_EBANDS: i32 = 21;
#[cfg(feature = "fixed_point")]
fn fill_decoder_state_pcm(pcm: &mut [i16], frame_size: usize, phase: usize) {
let period = 96usize;
let half = period / 2;
let amp = 13_000i32;
for (i, slot) in pcm.iter_mut().take(frame_size).enumerate() {
let pos = (i + phase) % period;
let tri = if pos < half { pos } else { period - pos };
let centered = (tri as i32 * 2) - half as i32;
let mut shaped = centered * amp / half as i32;
shaped += if (i % 9) == 0 { 1200 } else { -300 };
*slot = shaped as i16;
}
}
#[cfg(feature = "fixed_point")]
fn fill_decoder_dataflow_pcm(pcm: &mut [i16], frame_size: usize, phase: usize) {
assert!(
pcm.len() >= frame_size * 2,
"stereo dataflow pcm buffer must contain 2*frame_size samples",
);
let period = 120usize;
let half = period / 2;
let amp_l = 15_000i32;
let amp_r = 11_200i32;
for i in 0..frame_size {
let pos_l = (i + phase) % period;
let pos_r = (i + phase + 29) % period;
let tri_l = if pos_l < half { pos_l } else { period - pos_l };
let tri_r = if pos_r < half { pos_r } else { period - pos_r };
let centered_l = (tri_l as i32 * 2) - half as i32;
let centered_r = (tri_r as i32 * 2) - half as i32;
let mut shaped_l = centered_l * amp_l / half as i32;
let mut shaped_r = -(centered_r * amp_r / half as i32);
shaped_l += if (i % 11) == 0 { 900 } else { -350 };
shaped_r += if (i % 7) == 0 { -700 } else { 220 };
pcm[2 * i] = shaped_l as i16;
pcm[2 * i + 1] = shaped_r as i16;
}
}
#[cfg(feature = "fixed_point")]
fn pcm_energy_i64(samples: &[i16]) -> i64 {
samples.iter().fold(0i64, |acc, &sample| {
acc + i64::from(sample) * i64::from(sample)
})
}
#[cfg(feature = "fixed_point")]
fn count_hash_changes(hashes: &[u32]) -> usize {
hashes.windows(2).filter(|pair| pair[0] != pair[1]).count()
}
#[cfg(feature = "fixed_point")]
fn count_energy_drops(energies: &[i64]) -> usize {
energies.windows(2).filter(|pair| pair[1] < pair[0]).count()
}
#[cfg(feature = "fixed_point")]
const DECODER_PLC_IIR_PITCH_LOSS_FRAMES: usize = 2;
#[cfg(feature = "fixed_point")]
const DECODER_PLC_IIR_NOISE_START_BAND: i32 = 17;
#[cfg(feature = "fixed_point")]
fn fnv1a_sig32_le(data: &[i32]) -> u32 {
let mut hash = 2_166_136_261u32;
for sample in data {
for byte in sample.to_le_bytes() {
hash = fnv1a_update(hash, byte);
}
}
hash
}
#[cfg(feature = "fixed_point")]
fn decoder_plc_iir_lpc_hash(decoder: &super::OpusCustomDecoder<'_>) -> u32 {
fnv1a_pcm_le(&decoder.lpc_fixed)
}
#[cfg(feature = "fixed_point")]
fn decoder_plc_iir_tail_hash(decoder: &super::OpusCustomDecoder<'_>, frame_size: usize) -> u32 {
let stride = DECODE_BUFFER_SIZE + decoder.overlap;
let tail_len = frame_size + decoder.overlap;
let mut hash = 2_166_136_261u32;
for channel in 0..decoder.channels {
let base = channel * stride + DECODE_BUFFER_SIZE - frame_size;
let tail = &decoder.decode_mem_fixed[base..base + tail_len];
hash ^= fnv1a_sig32_le(tail);
hash = hash.wrapping_mul(16_777_619);
}
hash
}
#[cfg(feature = "fixed_point")]
fn parse_hex_packet(hex: &str) -> Vec<u8> {
assert_eq!(
hex.len() % 2,
0,
"hex packet strings must have an even length"
);
let mut packet = Vec::with_capacity(hex.len() / 2);
for idx in (0..hex.len()).step_by(2) {
let byte = u8::from_str_radix(&hex[idx..idx + 2], 16)
.expect("packet hex should contain only hexadecimal digits");
packet.push(byte);
}
packet
}
#[cfg(feature = "fixed_point")]
fn prime_decoder_plc_iir(
decoder: &mut super::OpusCustomDecoder<'_>,
decoded: &mut [i16],
packets: &[&str],
) -> i32 {
let mut pitch = 0;
for packet_hex in packets.iter() {
let packet = parse_hex_packet(packet_hex);
let decoded_len =
opus_custom_decode(decoder, Some(&packet), decoded, POSTFILTER_FRAME_SIZE)
.expect("PLC/IIR priming decode should succeed");
assert_eq!(decoded_len, POSTFILTER_FRAME_SIZE);
opus_custom_decoder_ctl(decoder, DecoderCtlRequest::GetPitch(&mut pitch))
.expect("get pitch should succeed");
let mut final_range = 0u32;
opus_custom_decoder_ctl(decoder, DecoderCtlRequest::GetFinalRange(&mut final_range))
.expect("get final range should succeed");
crate::test_trace::trace_println!(
"prime decoded={} pitch={} range=0x{:08x} pcm=0x{:08x} tail=0x{:08x} lpc=0x{:08x} eb=0x{:08x}",
decoded_len,
pitch,
final_range,
fnv1a_pcm_le(decoded),
decoder_plc_iir_tail_hash(decoder, POSTFILTER_FRAME_SIZE),
decoder_plc_iir_lpc_hash(decoder),
fnv1a_sig32_le(&decoder.old_ebands_fixed)
);
if pitch > 0 {
return pitch;
}
}
panic!(
"failed to prime decoder pitch after {} frames",
packets.len()
);
}
#[cfg(feature = "fixed_point")]
fn decode_packet_band_spectrum(
decoder: &mut super::OpusCustomDecoder<'_>,
packet: &[u8],
use_fixed_native: bool,
) -> (Vec<i16>, Vec<u8>) {
let mode = decoder.mode;
let nb_ebands = mode.num_ebands;
let frame = prepare_frame(decoder, packet, POSTFILTER_FRAME_SIZE, None)
.expect("frame preparation should succeed");
let super::FramePreparation {
mut range_decoder,
spread_decision,
short_blocks,
intra_ener: _,
anti_collapse_rsv,
intensity,
dual_stereo,
balance,
coded_bands,
total_bits,
start,
end,
lm,
n,
c,
..
} = frame;
let tf_res = &decoder.decode_tf_res;
let pulses = &decoder.decode_pulses;
let mut range_state = range_decoder.take().expect("range decoder must exist");
let dec = range_state.decoder();
let mut collapse_masks = vec![0u8; c * nb_ebands];
let total_available = total_bits - anti_collapse_rsv;
let mut spectrum_fixed = vec![0i16; c * n];
let mut coder = BandCodingState::Decoder(dec);
if use_fixed_native {
let (first_channel, second_channel_opt) = if c == 2 {
let (left, right) = spectrum_fixed.split_at_mut(n);
(left, Some(right))
} else {
(&mut spectrum_fixed[..], None)
};
crate::celt::bands::quant_all_bands_decode_fixed(
mode,
start,
end,
first_channel,
second_channel_opt,
&mut collapse_masks,
&pulses,
short_blocks != 0,
spread_decision,
dual_stereo != 0,
intensity.max(0) as usize,
&tf_res,
total_available,
balance,
&mut coder,
lm as i32,
coded_bands.max(0) as usize,
&mut decoder.rng,
decoder.arch,
decoder.disable_inv,
);
} else {
let mut spectrum_float = vec![0.0f32; c * n];
let (first_channel, second_channel_opt) = if c == 2 {
let (left, right) = spectrum_float.split_at_mut(n);
(left, Some(right))
} else {
(&mut spectrum_float[..], None)
};
crate::celt::bands::quant_all_bands(
false,
mode,
start,
end,
first_channel,
second_channel_opt,
&mut collapse_masks,
&[],
&pulses,
short_blocks != 0,
spread_decision,
dual_stereo != 0,
intensity.max(0) as usize,
&tf_res,
total_available,
balance,
&mut coder,
lm as i32,
coded_bands.max(0) as usize,
&mut decoder.rng,
decoder.complexity,
decoder.arch,
decoder.disable_inv,
);
super::float_norm_slice_to_fixed(&mut spectrum_fixed, &spectrum_float);
}
(spectrum_fixed, collapse_masks)
}
#[cfg(feature = "fixed_point")]
fn debug_first_band_pulses(
decoder: &mut super::OpusCustomDecoder<'_>,
packet: &[u8],
) -> (usize, i32, u32, u32, Vec<i32>, u32) {
let mode = decoder.mode;
let frame = prepare_frame(decoder, packet, POSTFILTER_FRAME_SIZE, None)
.expect("frame preparation should succeed");
let super::FramePreparation {
mut range_decoder,
total_bits,
anti_collapse_rsv,
balance,
coded_bands,
start,
lm,
..
} = frame;
let pulses = &decoder.decode_pulses;
let mut range_state = range_decoder.take().expect("range decoder must exist");
let dec = range_state.decoder();
let band = start;
let total_available = total_bits - anti_collapse_rsv;
let tell = entcode::ec_tell_frac(dec.ctx()) as i32;
let remaining_bits = total_available - tell - 1;
let remaining_coded = (coded_bands.max(0) as usize).saturating_sub(band).min(3) as i32;
let curr_balance = if remaining_coded > 0 {
celt_sudiv(balance, remaining_coded)
} else {
0
};
let b = (remaining_bits + 1)
.min(pulses[band] + curr_balance)
.clamp(0, 16_383);
let q = bits2pulses(mode, band, lm as i32, b);
let k = get_pulses(q) as usize;
let n = ((mode.e_bands[band + 1] - mode.e_bands[band]) as usize) << (lm as usize);
let mut pulse_vec = vec![0i32; n];
let (index, total, _) = decode_pulses_debug(&mut pulse_vec, n, k, dec);
let collapse_mask =
crate::celt::vq::extract_collapse_mask(&pulse_vec, n, 1usize << (lm as usize));
(n, q, index, total, pulse_vec, collapse_mask)
}
#[cfg(feature = "fixed_point")]
#[test]
fn decoder_plc_iir_matches_ctest_vectors() {
let owned_mode = opus_custom_mode_create(48_000, POSTFILTER_FRAME_SIZE)
.expect("mode creation should succeed");
let mode = owned_mode.mode();
let mono_prime_packets = [
"f87b5ade71db5cb86cc9a42d87bbde1e71f2afd45d5a88bb16581b72a6ca16de9eae4b",
"f8afe90df1a3c02ade92c45dbb015dd97f46a387235b2befcd07fe0d3f70757d6ab161",
];
let stereo_prime_packets = [
"fc7f06c5823a668788d0fc75ad6cc856faca7625c7fc39ac0bb9a60782cca363b2ac94cc2ead16432c2db2b60c4a1bf5d8b0db4cfb5dcecea0c7e319dc388d203aab9cada1d8fca1735f2273de589eb62975a41340011c8c8752c5848f073f0c8b4b7df18f482a9b96b4f48b988711a3c51703fcd97b59cb338539cdbc007ede6ea8ff6089adae678bb6f91ee118b045bd6b41c255a773f962f87ed3bcf22326",
"fcb4050bfdbad3a7e0e30e26a55e15a50a352c8324108e1b086d04ea69125c1bd5c099b59cae0bf512802803d8bedc99b2ea3ae09ddcadb681924fedb949c16f5d73330cdf509498645b0dbb97b7d8ff3d51ee030b70f88d581a993441324991f4a1e31f8137687fc9b8a9c56298d8657a9437028a34c2a6c8d1a276f73df195974263dad177f3300c027683b7115ac0a91d33b4a3bdb51c79b0041d720d07a1",
];
let mono_expected_lpc = [0x5159_7095u32, 0x5159_7095u32];
let mono_expected_tail = [0xdfaa_14e9u32, 0xefd8_0164u32];
let mono_expected_pcm = [0xc1b6_b08du32, 0xc2a4_65edu32];
let mono_expected_noise_lpc = 0xc655_ff85u32;
let mono_expected_noise_tail = 0x05e4_dd21u32;
let stereo_expected_lpc = [0xd31a_6449u32, 0xd31a_6449u32];
let stereo_expected_tail = [0x7bf7_ff22u32, 0xa5d2_03d2u32];
let stereo_expected_pcm = [0x03cb_2a69u32, 0x16d3_9455u32];
{
let first_packet = parse_hex_packet(mono_prime_packets[0]);
let mut decoder_tf =
opus_custom_decoder_create(&mode, 1).expect("tf decoder creation should succeed");
let mut decoder_native = opus_custom_decoder_create(&mode, 1)
.expect("native decoder creation should succeed");
let mut decoder_bridge = opus_custom_decoder_create(&mode, 1)
.expect("bridge decoder creation should succeed");
let _tf_frame =
prepare_frame(&mut decoder_tf, &first_packet, POSTFILTER_FRAME_SIZE, None)
.expect("frame preparation should succeed");
crate::test_trace::trace_println!(
"frame0 tf_res_first4={:?}",
&decoder_tf.decode_tf_res[..4.min(decoder_tf.decode_tf_res.len())]
);
let (band0_n, band0_q, band0_index, band0_total, band0_pulses, band0_mask) =
debug_first_band_pulses(&mut decoder_native, &first_packet);
crate::test_trace::trace_println!(
"band0 pulses n={} q={} index={} total={} mask=0x{:02x} pulses={:?}",
band0_n,
band0_q,
band0_index,
band0_total,
band0_mask,
band0_pulses
);
let (native_spectrum, native_masks) =
decode_packet_band_spectrum(&mut decoder_native, &first_packet, true);
let (bridge_spectrum, bridge_masks) =
decode_packet_band_spectrum(&mut decoder_bridge, &first_packet, false);
let first_diff = native_spectrum
.iter()
.zip(bridge_spectrum.iter())
.position(|(lhs, rhs)| lhs != rhs);
crate::test_trace::trace_println!(
"bandcmp frame0 native=0x{:08x} bridge=0x{:08x} masks_native=0x{:08x} masks_bridge=0x{:08x} first_diff={:?}",
fnv1a_pcm_le(&native_spectrum),
fnv1a_pcm_le(&bridge_spectrum),
fnv1a_bytes(&native_masks),
fnv1a_bytes(&bridge_masks),
first_diff
);
crate::test_trace::trace_println!(
"bandcmp frame0 masks_native_full={:?}",
native_masks
);
let lm_dbg = (POSTFILTER_FRAME_SIZE / mode.short_mdct_size).ilog2() as usize;
for band_hash in 0..mode.num_ebands {
let band_start = (mode.e_bands[band_hash] as usize) << lm_dbg;
let band_end = (mode.e_bands[band_hash + 1] as usize) << lm_dbg;
let native_hash = fnv1a_pcm_le(&native_spectrum[band_start..band_end]);
crate::test_trace::trace_println!(
"bandcmp frame0 native_band_hash[{}]=0x{:08x}",
band_hash,
native_hash
);
}
if let Some(idx) = first_diff {
let lm_dbg = (POSTFILTER_FRAME_SIZE / mode.short_mdct_size).ilog2() as usize;
let mut band_idx = 0usize;
while band_idx + 1 < mode.num_ebands
&& ((mode.e_bands[band_idx + 1] as usize) << lm_dbg) <= idx
{
band_idx += 1;
}
let band_start = (mode.e_bands[band_idx] as usize) << lm_dbg;
crate::test_trace::trace_println!(
"bandcmp frame0 coeff[{idx}] band={} band_off={} native={} bridge={}",
band_idx,
idx - band_start,
native_spectrum[idx],
bridge_spectrum[idx]
);
}
}
{
let first_packet = parse_hex_packet(stereo_prime_packets[0]);
let mut decoder_native = opus_custom_decoder_create(&mode, 2)
.expect("stereo native decoder creation should succeed");
let mut decoder_bridge = opus_custom_decoder_create(&mode, 2)
.expect("stereo bridge decoder creation should succeed");
let (native_spectrum, native_masks) =
decode_packet_band_spectrum(&mut decoder_native, &first_packet, true);
let (bridge_spectrum, bridge_masks) =
decode_packet_band_spectrum(&mut decoder_bridge, &first_packet, false);
let first_diff = native_spectrum
.iter()
.zip(bridge_spectrum.iter())
.position(|(lhs, rhs)| lhs != rhs);
crate::test_trace::trace_println!(
"stereo bandcmp frame0 native=0x{:08x} bridge=0x{:08x} masks_native=0x{:08x} masks_bridge=0x{:08x} first_diff={:?}",
fnv1a_pcm_le(&native_spectrum),
fnv1a_pcm_le(&bridge_spectrum),
fnv1a_bytes(&native_masks),
fnv1a_bytes(&bridge_masks),
first_diff
);
crate::test_trace::trace_println!(
"stereo bandcmp frame0 first8 native={:?} bridge={:?}",
&native_spectrum[..8],
&bridge_spectrum[..8]
);
}
{
let mut decoder =
opus_custom_decoder_create(&mode, 1).expect("mono decoder creation should succeed");
assert_eq!(
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetComplexity(11))
.expect_err("invalid decoder complexity must fail"),
CeltDecoderCtlError::InvalidArgument
);
let mut decoded = vec![0i16; POSTFILTER_FRAME_SIZE];
let primed_pitch =
prime_decoder_plc_iir(&mut decoder, &mut decoded, &mono_prime_packets);
assert!(primed_pitch > 0);
crate::test_trace::trace_println!("mono primed_pitch={primed_pitch}");
{
let stride = DECODE_BUFFER_SIZE + decoder.overlap;
let float_views: Vec<&[CeltSig]> = decoder
.decode_mem
.chunks(stride)
.take(decoder.channels)
.collect();
let fixed_views: Vec<&[FixedCeltSig]> = decoder
.decode_mem_fixed
.chunks(stride)
.take(decoder.channels)
.collect();
let float_pitch =
celt_plc_pitch_search(&float_views, decoder.channels, decoder.arch);
let fixed_pitch =
celt_plc_pitch_search_fixed(&fixed_views, decoder.channels, decoder.arch);
crate::test_trace::trace_println!(
"mono search float={float_pitch} fixed={fixed_pitch}"
);
}
let baseline_lpc = decoder_plc_iir_lpc_hash(&decoder);
let baseline_tail = decoder_plc_iir_tail_hash(&decoder, POSTFILTER_FRAME_SIZE);
crate::test_trace::trace_println!(
"mono baseline_lpc=0x{baseline_lpc:08x} tail=0x{baseline_tail:08x}"
);
let tiny_packet = [0u8];
assert!(
opus_custom_decode(
&mut decoder,
Some(&tiny_packet),
&mut decoded,
POSTFILTER_FRAME_SIZE,
)
.is_err(),
"tiny mono packet should fail",
);
assert_eq!(decoder_plc_iir_lpc_hash(&decoder), baseline_lpc);
assert_eq!(
decoder_plc_iir_tail_hash(&decoder, POSTFILTER_FRAME_SIZE),
baseline_tail
);
assert!(
opus_custom_decode(&mut decoder, None, &mut decoded, POSTFILTER_FRAME_SIZE - 1)
.is_err(),
"invalid mono frame size should fail",
);
assert_eq!(decoder_plc_iir_lpc_hash(&decoder), baseline_lpc);
assert_eq!(
decoder_plc_iir_tail_hash(&decoder, POSTFILTER_FRAME_SIZE),
baseline_tail
);
let mut lpc_hashes = [0u32; DECODER_PLC_IIR_PITCH_LOSS_FRAMES];
let mut tail_hashes = [0u32; DECODER_PLC_IIR_PITCH_LOSS_FRAMES];
let mut pcm_hashes = [0u32; DECODER_PLC_IIR_PITCH_LOSS_FRAMES];
for loss in 0..DECODER_PLC_IIR_PITCH_LOSS_FRAMES {
let start_preemph_mem = decoder.fixed_preemph_mem_decoder[0];
let decoded_len =
opus_custom_decode(&mut decoder, None, &mut decoded, POSTFILTER_FRAME_SIZE)
.expect("mono PLC loss decode should succeed");
assert_eq!(decoded_len, POSTFILTER_FRAME_SIZE);
lpc_hashes[loss] = decoder_plc_iir_lpc_hash(&decoder);
tail_hashes[loss] = decoder_plc_iir_tail_hash(&decoder, POSTFILTER_FRAME_SIZE);
pcm_hashes[loss] = fnv1a_pcm_le(&decoded);
crate::test_trace::trace_println!(
"mono loss{loss} pitch={} lpc=0x{:08x} tail=0x{:08x} pcm=0x{:08x}",
decoder.last_pitch_index,
lpc_hashes[loss],
tail_hashes[loss],
pcm_hashes[loss]
);
crate::test_trace::trace_println!(
"mono loss{loss} pcm_first16={:?}",
&decoded[..16]
);
crate::test_trace::trace_println!(
"mono loss{loss} pcm_last16={:?}",
&decoded[POSTFILTER_FRAME_SIZE - 16..]
);
crate::test_trace::trace_println!(
"mono loss{loss} pcm_hash_halves=0x{:08x}/0x{:08x}",
fnv1a_pcm_le(&decoded[..POSTFILTER_FRAME_SIZE / 2]),
fnv1a_pcm_le(&decoded[POSTFILTER_FRAME_SIZE / 2..]),
);
for block in 0..(POSTFILTER_FRAME_SIZE / 64) {
let start = block * 64;
crate::test_trace::trace_println!(
"mono loss{loss} block{block:02}=0x{:08x}",
fnv1a_pcm_le(&decoded[start..start + 64])
);
}
if loss == 0 {
crate::test_trace::trace_println!(
"mono loss0 block03_samples={:?}",
&decoded[192..256]
);
crate::test_trace::trace_println!(
"mono loss0 block10_samples={:?}",
&decoded[640..704]
);
} else {
crate::test_trace::trace_println!(
"mono loss1 block13_samples={:?}",
&decoded[832..896]
);
}
if loss == 0 {
crate::test_trace::trace_println!(
"mono loss0 lpc_coeffs={:?}",
&decoder.lpc_fixed[..LPC_ORDER]
);
}
let stride = DECODE_BUFFER_SIZE + decoder.overlap;
let start_idx = DECODE_BUFFER_SIZE - POSTFILTER_FRAME_SIZE;
let signal =
&decoder.decode_mem_fixed[start_idx..start_idx + POSTFILTER_FRAME_SIZE];
let coef0 = qconst16_clamped(f64::from(mode.pre_emphasis[0]), 15);
let trace_points: &[usize] = if loss == 0 { &[208, 680] } else { &[890] };
let mut mem_sig = start_preemph_mem;
for (j, &sample) in signal.iter().enumerate() {
let tmp = (sample + mem_sig).clamp(-FIXED_SIG_SAT, FIXED_SIG_SAT);
let out = sig2word16(tmp);
let next_mem = mult16_32_q15(coef0, tmp);
if trace_points.contains(&j) {
crate::test_trace::trace_println!(
"mono loss{loss} trace[{j}] sample={} mem_before={} tmp={} out={} mem_after={}",
sample,
mem_sig,
tmp,
out,
next_mem
);
}
mem_sig = next_mem;
}
let _ = stride;
assert!(decoded.iter().any(|&sample| sample != 0));
}
assert_eq!(lpc_hashes, mono_expected_lpc);
assert_eq!(tail_hashes, mono_expected_tail);
assert_eq!(pcm_hashes, mono_expected_pcm);
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::ResetState)
.expect("mono reset should succeed");
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::SetStartBand(DECODER_PLC_IIR_NOISE_START_BAND),
)
.expect("mono set noise start band should succeed");
let decoded_len =
opus_custom_decode(&mut decoder, None, &mut decoded, POSTFILTER_FRAME_SIZE)
.expect("mono noise-path PLC should succeed");
assert_eq!(decoded_len, POSTFILTER_FRAME_SIZE);
assert!(decoded.iter().any(|&sample| sample != 0));
assert_eq!(decoder_plc_iir_lpc_hash(&decoder), mono_expected_noise_lpc);
assert_eq!(
decoder_plc_iir_tail_hash(&decoder, POSTFILTER_FRAME_SIZE),
mono_expected_noise_tail
);
}
{
let mut decoder = opus_custom_decoder_create(&mode, 2)
.expect("stereo decoder creation should succeed");
let mut decoded = vec![0i16; POSTFILTER_FRAME_SIZE * 2];
assert!(
opus_custom_decode(
&mut decoder,
Some(&[0u8]),
&mut decoded,
POSTFILTER_FRAME_SIZE - 1,
)
.is_err(),
"invalid stereo frame size should fail",
);
let primed_pitch =
prime_decoder_plc_iir(&mut decoder, &mut decoded, &stereo_prime_packets);
assert!(primed_pitch > 0);
let baseline_lpc = decoder_plc_iir_lpc_hash(&decoder);
let baseline_tail = decoder_plc_iir_tail_hash(&decoder, POSTFILTER_FRAME_SIZE);
let tiny_packet = [0u8];
assert!(
opus_custom_decode(
&mut decoder,
Some(&tiny_packet),
&mut decoded,
POSTFILTER_FRAME_SIZE,
)
.is_err(),
"tiny stereo packet should fail",
);
assert_eq!(decoder_plc_iir_lpc_hash(&decoder), baseline_lpc);
assert_eq!(
decoder_plc_iir_tail_hash(&decoder, POSTFILTER_FRAME_SIZE),
baseline_tail
);
let mut lpc_hashes = [0u32; DECODER_PLC_IIR_PITCH_LOSS_FRAMES];
let mut tail_hashes = [0u32; DECODER_PLC_IIR_PITCH_LOSS_FRAMES];
let mut pcm_hashes = [0u32; DECODER_PLC_IIR_PITCH_LOSS_FRAMES];
for loss in 0..DECODER_PLC_IIR_PITCH_LOSS_FRAMES {
let decoded_len =
opus_custom_decode(&mut decoder, None, &mut decoded, POSTFILTER_FRAME_SIZE)
.expect("stereo PLC loss decode should succeed");
assert_eq!(decoded_len, POSTFILTER_FRAME_SIZE);
lpc_hashes[loss] = decoder_plc_iir_lpc_hash(&decoder);
tail_hashes[loss] = decoder_plc_iir_tail_hash(&decoder, POSTFILTER_FRAME_SIZE);
pcm_hashes[loss] = fnv1a_pcm_le(&decoded);
crate::test_trace::trace_println!(
"stereo loss{loss} pitch={} lpc=0x{:08x} tail=0x{:08x} pcm=0x{:08x}",
decoder.last_pitch_index,
lpc_hashes[loss],
tail_hashes[loss],
pcm_hashes[loss]
);
assert!(decoded.iter().any(|&sample| sample != 0));
}
assert_eq!(lpc_hashes, stereo_expected_lpc);
assert_eq!(tail_hashes, stereo_expected_tail);
assert_eq!(pcm_hashes, stereo_expected_pcm);
}
}
#[cfg(feature = "fixed_point")]
#[test]
fn debug_single_packet_actual_decode_trace() {
let owned_mode = opus_custom_mode_create(48_000, POSTFILTER_FRAME_SIZE)
.expect("mode creation should succeed");
let mode = owned_mode.mode();
let packet = parse_hex_packet(
"f87b5ade71db5cb86cc9a42d87bbde1e71f2afd45d5a88bb16581b72a6ca16de9eae4b",
);
let mut decoder =
opus_custom_decoder_create(&mode, 1).expect("decoder creation should succeed");
if std::env::var("CELT_TRACE_MDCT_TABLES").is_ok() {
let twiddles = decoder.fixed_mdct.twiddles(3);
let mut ht = 2166136261u32;
for &value in twiddles.iter() {
let v = value as u16;
ht = (ht ^ u32::from(v & 0xFF)).wrapping_mul(16777619);
ht = (ht ^ u32::from(v >> 8)).wrapping_mul(16777619);
}
let bitrev = decoder.fixed_mdct.inverse_plan(3).bitrev();
let mut hb = 2166136261u32;
for &value in bitrev.iter() {
let v = value as u16;
hb = (hb ^ u32::from(v & 0xFF)).wrapping_mul(16777619);
hb = (hb ^ u32::from(v >> 8)).wrapping_mul(16777619);
}
crate::test_trace::trace_println!(
"mdct_tables twiddle_hash=0x{:08x} first8={:?} bitrev_hash=0x{:08x} bitrev_first=[{}, {}, {}]",
ht,
&twiddles[..8],
hb,
bitrev[0],
bitrev[1],
bitrev[29],
);
}
let mut pcm = vec![0i16; POSTFILTER_FRAME_SIZE];
let decoded =
opus_custom_decode(&mut decoder, Some(&packet), &mut pcm, POSTFILTER_FRAME_SIZE)
.expect("decode should succeed");
crate::test_trace::trace_println!(
"single_packet decoded={} range=0x{:08x} pcm=0x{:08x} tail=0x{:08x}",
decoded,
decoder.rng,
fnv1a_pcm_le(&pcm[..decoded]),
fnv1a_sig32_le(&decoder.decode_mem_fixed[DECODE_BUFFER_SIZE - 64..DECODE_BUFFER_SIZE])
);
}
#[cfg(feature = "fixed_point")]
#[test]
fn debug_single_stereo_packet_actual_decode_trace() {
let owned_mode = opus_custom_mode_create(48_000, POSTFILTER_FRAME_SIZE)
.expect("mode creation should succeed");
let mode = owned_mode.mode();
let packet = parse_hex_packet(
"fc7f06c5823a668788d0fc75ad6cc856faca7625c7fc39ac0bb9a60782cca363b2ac94cc2ead16432c2db2b60c4a1bf5d8b0db4cfb5dcecea0c7e319dc388d203aab9cada1d8fca1735f2273de589eb62975a41340011c8c8752c5848f073f0c8b4b7df18f482a9b96b4f48b988711a3c51703fcd97b59cb338539cdbc007ede6ea8ff6089adae678bb6f91ee118b045bd6b41c255a773f962f87ed3bcf22326",
);
let mut spectrum_decoder =
opus_custom_decoder_create(&mode, 2).expect("decoder creation should succeed");
let (spectrum, masks) = decode_packet_band_spectrum(&mut spectrum_decoder, &packet, true);
crate::test_trace::trace_println!(
"single_stereo_packet spectrum=0x{:08x} masks=0x{:08x}",
fnv1a_pcm_le(&spectrum),
fnv1a_bytes(&masks)
);
crate::test_trace::trace_println!("single_stereo_packet masks_full={:?}", masks);
let lm_dbg = (POSTFILTER_FRAME_SIZE / mode.short_mdct_size).ilog2() as usize;
let channel_stride = spectrum.len() / 2;
for band in 0..mode.num_ebands {
let band_start = (mode.e_bands[band] as usize) << lm_dbg;
let band_end = (mode.e_bands[band + 1] as usize) << lm_dbg;
crate::test_trace::trace_println!(
"single_stereo_packet band_hash_l[{band}]=0x{:08x}",
fnv1a_pcm_le(&spectrum[band_start..band_end])
);
crate::test_trace::trace_println!(
"single_stereo_packet band_hash_r[{band}]=0x{:08x}",
fnv1a_pcm_le(&spectrum[channel_stride + band_start..channel_stride + band_end])
);
}
let band17_start = (mode.e_bands[17] as usize) << lm_dbg;
crate::test_trace::trace_println!(
"single_stereo_packet band17_left={:?}",
&spectrum[band17_start..band17_start + 8]
);
crate::test_trace::trace_println!(
"single_stereo_packet band17_right={:?}",
&spectrum[channel_stride + band17_start..channel_stride + band17_start + 8]
);
let mut decoder =
opus_custom_decoder_create(&mode, 2).expect("decoder creation should succeed");
let mut pcm = vec![0i16; POSTFILTER_FRAME_SIZE * 2];
let decoded =
opus_custom_decode(&mut decoder, Some(&packet), &mut pcm, POSTFILTER_FRAME_SIZE)
.expect("decode should succeed");
crate::test_trace::trace_println!(
"single_stereo_packet decoded={} range=0x{:08x} pcm=0x{:08x} tail=0x{:08x}",
decoded,
decoder.rng,
fnv1a_pcm_le(&pcm[..decoded * 2]),
decoder_plc_iir_tail_hash(&decoder, POSTFILTER_FRAME_SIZE)
);
crate::test_trace::trace_println!("single_stereo_packet first16={:?}", &pcm[..32]);
crate::test_trace::trace_println!("single_stereo_packet first64={:?}", &pcm[..128]);
crate::test_trace::trace_println!(
"single_stereo_packet full_pcm={:?}",
&pcm[..decoded * 2]
);
for block in 0..(POSTFILTER_FRAME_SIZE / 64) {
let start = block * 64 * 2;
crate::test_trace::trace_println!(
"single_stereo_packet block{block:02}=0x{:08x}",
fnv1a_pcm_le(&pcm[start..start + 128])
);
}
let stride = DECODE_BUFFER_SIZE + decoder.overlap;
let left_tail = &decoder.decode_mem_fixed[DECODE_BUFFER_SIZE - POSTFILTER_FRAME_SIZE
..DECODE_BUFFER_SIZE - POSTFILTER_FRAME_SIZE + POSTFILTER_FRAME_SIZE + decoder.overlap];
let right_base = stride;
let right_tail = &decoder.decode_mem_fixed[right_base + DECODE_BUFFER_SIZE
- POSTFILTER_FRAME_SIZE
..right_base + DECODE_BUFFER_SIZE - POSTFILTER_FRAME_SIZE
+ POSTFILTER_FRAME_SIZE
+ decoder.overlap];
crate::test_trace::trace_println!(
"single_stereo_packet tail0=0x{:08x} tail1=0x{:08x}",
fnv1a_sig32_le(left_tail),
fnv1a_sig32_le(right_tail)
);
}
#[cfg(feature = "fixed_point")]
#[test]
fn decoder_state_transitions_match_ctest_vectors() {
let strict_ctest_vectors = std::env::var_os("RUST_CTEST_STRICT_HASHES").is_some();
let expected_warm_packet_hashes: [u32; DECODER_STATE_WARM_FRAMES] = [
0x1204_0bb9,
0xfa16_704e,
0xc40f_7772,
0x7c72_6ef3,
0x3ec0_1bf0,
0x108e_4248,
0x0341_cfd7,
0x8419_87b6,
];
let expected_warm_pcm_hashes: [u32; DECODER_STATE_WARM_FRAMES] = [
0x7025_93e3,
0xe891_961f,
0xb876_40ee,
0xd9ab_f25c,
0x0d59_b40f,
0xa453_23c0,
0x387e_b7e3,
0xde85_f2a0,
];
let expected_loss_hashes: [u32; DECODER_STATE_LOSS_FRAMES] = [
0x88e9_15a8,
0xca42_f30e,
0x657c_aa87,
0x746d_415b,
0x2e3e_688f,
0xdd03_0e3b,
];
let expected_recover_hash_after_loss = 0x948f_f204u32;
let expected_recover_hash_without_loss = 0x7456_1161u32;
let expected_reset_loss_hash = 0x9811_584du32;
let owned_mode =
opus_custom_mode_create(48_000, 960).expect("mode creation should succeed");
let mode = owned_mode.mode();
let mut encoder = opus_custom_encoder_create(&mode, 48_000, 1, 0)
.expect("encoder creation should succeed");
let mut decoder_a =
opus_custom_decoder_create(&mode, 1).expect("decoder A creation should succeed");
let mut decoder_b =
opus_custom_decoder_create(&mode, 1).expect("decoder B creation should succeed");
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetBitrate(18_000))
.expect("set bitrate should succeed");
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetVbr(false))
.expect("set vbr should succeed");
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetComplexity(10))
.expect("set complexity should succeed");
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetLsbDepth(16))
.expect("set lsb depth should succeed");
opus_custom_decoder_ctl(&mut decoder_a, DecoderCtlRequest::SetComplexity(10))
.expect("decoder A complexity should succeed");
opus_custom_decoder_ctl(&mut decoder_b, DecoderCtlRequest::SetComplexity(10))
.expect("decoder B complexity should succeed");
let mut packet = vec![0u8; POSTFILTER_MAX_PACKET_SIZE];
let mut pcm = vec![0i16; POSTFILTER_FRAME_SIZE];
let mut out_a = vec![0i16; POSTFILTER_FRAME_SIZE];
let mut out_b = vec![0i16; POSTFILTER_FRAME_SIZE];
assert_eq!(
opus_custom_decoder_ctl(&mut decoder_a, DecoderCtlRequest::SetComplexity(11))
.expect_err("invalid complexity must fail"),
CeltDecoderCtlError::InvalidArgument
);
assert!(
opus_custom_decode(&mut decoder_a, None, &mut out_a, POSTFILTER_FRAME_SIZE - 1)
.is_err(),
"decode with invalid frame size should fail",
);
assert!(
opus_custom_decode(
&mut decoder_a,
Some(&[0]),
&mut out_a,
POSTFILTER_FRAME_SIZE
)
.is_err(),
"tiny packet decode should fail",
);
#[cfg(feature = "deep_plc")]
let plc = None;
#[cfg(not(feature = "deep_plc"))]
let plc = ();
let mut tiny_pcm: [f32; 0] = [];
assert!(
super::celt_decode_with_ec_dred(
&mut decoder_a,
None,
&mut tiny_pcm,
POSTFILTER_FRAME_SIZE,
None,
false,
plc
)
.is_err(),
"decode with empty PCM output should fail",
);
let mut warm_packet_hashes = [0u32; DECODER_STATE_WARM_FRAMES];
let mut warm_pcm_hashes = [0u32; DECODER_STATE_WARM_FRAMES];
for frame in 0..DECODER_STATE_WARM_FRAMES {
fill_decoder_state_pcm(&mut pcm, POSTFILTER_FRAME_SIZE, frame * 7);
let packet_len = opus_custom_encode(
&mut encoder,
&pcm,
POSTFILTER_FRAME_SIZE,
&mut packet,
POSTFILTER_MAX_PACKET_SIZE,
)
.expect("warmup encode should succeed");
let decoded_a = opus_custom_decode(
&mut decoder_a,
Some(&packet[..packet_len]),
&mut out_a,
POSTFILTER_FRAME_SIZE,
)
.expect("decoder A warmup decode should succeed");
let decoded_b = opus_custom_decode(
&mut decoder_b,
Some(&packet[..packet_len]),
&mut out_b,
POSTFILTER_FRAME_SIZE,
)
.expect("decoder B warmup decode should succeed");
assert_eq!(decoded_a, POSTFILTER_FRAME_SIZE);
assert_eq!(decoded_b, POSTFILTER_FRAME_SIZE);
warm_packet_hashes[frame] = fnv1a_bytes(&packet[..packet_len]);
warm_pcm_hashes[frame] = fnv1a_pcm_le(&out_a[..POSTFILTER_FRAME_SIZE]);
let warm_pcm_hash_b = fnv1a_pcm_le(&out_b[..POSTFILTER_FRAME_SIZE]);
assert_eq!(warm_pcm_hashes[frame], warm_pcm_hash_b);
}
assert!(count_hash_changes(&warm_packet_hashes) > 0);
assert!(count_hash_changes(&warm_pcm_hashes) > 0);
if strict_ctest_vectors {
assert_eq!(warm_packet_hashes, expected_warm_packet_hashes);
assert_eq!(warm_pcm_hashes, expected_warm_pcm_hashes);
}
let mut loss_hashes = [0u32; DECODER_STATE_LOSS_FRAMES];
let mut loss_energies = [0i64; DECODER_STATE_LOSS_FRAMES];
for i in 0..DECODER_STATE_LOSS_FRAMES {
let decoded =
opus_custom_decode(&mut decoder_a, None, &mut out_a, POSTFILTER_FRAME_SIZE)
.expect("loss decode should succeed");
assert_eq!(decoded, POSTFILTER_FRAME_SIZE);
loss_hashes[i] = fnv1a_pcm_le(&out_a[..POSTFILTER_FRAME_SIZE]);
loss_energies[i] = pcm_energy_i64(&out_a[..POSTFILTER_FRAME_SIZE]);
}
assert!(
decoder_a.decode_mem.iter().any(|&sample| sample != 0.0),
"decoder history should carry non-zero PLC state"
);
if strict_ctest_vectors {
assert_eq!(loss_hashes, expected_loss_hashes);
}
fill_decoder_state_pcm(&mut pcm, POSTFILTER_FRAME_SIZE, 93);
let recover_packet_len = opus_custom_encode(
&mut encoder,
&pcm,
POSTFILTER_FRAME_SIZE,
&mut packet,
POSTFILTER_MAX_PACKET_SIZE,
)
.expect("recovery encode should succeed");
opus_custom_decode(
&mut decoder_a,
Some(&packet[..recover_packet_len]),
&mut out_a,
POSTFILTER_FRAME_SIZE,
)
.expect("decoder A recovery decode should succeed");
opus_custom_decode(
&mut decoder_b,
Some(&packet[..recover_packet_len]),
&mut out_b,
POSTFILTER_FRAME_SIZE,
)
.expect("decoder B recovery decode should succeed");
let recover_hash_after_loss = fnv1a_pcm_le(&out_a[..POSTFILTER_FRAME_SIZE]);
let recover_hash_without_loss = fnv1a_pcm_le(&out_b[..POSTFILTER_FRAME_SIZE]);
if strict_ctest_vectors {
assert_eq!(recover_hash_after_loss, expected_recover_hash_after_loss);
assert_eq!(
recover_hash_without_loss,
expected_recover_hash_without_loss
);
}
opus_custom_decoder_ctl(&mut decoder_a, DecoderCtlRequest::ResetState)
.expect("decoder A reset should succeed");
opus_custom_decoder_ctl(&mut decoder_b, DecoderCtlRequest::ResetState)
.expect("decoder B reset should succeed");
opus_custom_decode(&mut decoder_a, None, &mut out_a, POSTFILTER_FRAME_SIZE)
.expect("decoder A post-reset loss decode should succeed");
opus_custom_decode(&mut decoder_b, None, &mut out_b, POSTFILTER_FRAME_SIZE)
.expect("decoder B post-reset loss decode should succeed");
let reset_hash_a = fnv1a_pcm_le(&out_a[..POSTFILTER_FRAME_SIZE]);
let reset_hash_b = fnv1a_pcm_le(&out_b[..POSTFILTER_FRAME_SIZE]);
assert_eq!(reset_hash_a, reset_hash_b);
assert_ne!(reset_hash_a, 0);
if strict_ctest_vectors {
assert_eq!(reset_hash_a, expected_reset_loss_hash);
assert_eq!(reset_hash_b, expected_reset_loss_hash);
}
}
#[cfg(feature = "fixed_point")]
#[test]
fn decoder_dataflow_matches_ctest_vectors() {
let strict_ctest_vectors = std::env::var_os("RUST_CTEST_STRICT_HASHES").is_some();
let expected_packet_hashes: [u32; DECODER_DATAFLOW_FRAMES] = [
0x1e1e_1269,
0x2dde_7528,
0x8e00_0bce,
0x6790_92e8,
0x143e_3a1e,
0x6986_4649,
0xe63d_689d,
0x9af6_d244,
0x6c7a_cb7a,
0x6c5b_cdd9,
0xbead_c667,
0x3e08_723e,
0x834e_54b0,
];
let expected_pcm_hashes: [u32; DECODER_DATAFLOW_FRAMES] = [
0x72d2_ff52,
0xfdbe_8b96,
0xf442_2f17,
0x15d7_fb47,
0x4e10_ba6e,
0xbb83_37f9,
0x0942_b6c6,
0x8bee_73ad,
0xdd70_c41d,
0x630c_948a,
0xf86a_2a05,
0xf047_b0c5,
0x5502_c350,
];
let expected_final_ranges: [u32; DECODER_DATAFLOW_FRAMES] = [
0x212d_dc00,
0x26b1_e200,
0x0392_e900,
0x1073_2300,
0x6328_5f00,
0x1946_6300,
0x0b72_b000,
0x0462_6100,
0x69b1_6100,
0x1bd0_aa00,
0x067a_c700,
0x0831_5200,
0x3611_a800,
];
let owned_mode = opus_custom_mode_create(48_000, POSTFILTER_FRAME_SIZE)
.expect("mode creation should succeed");
let mode = owned_mode.mode();
assert_eq!(mode.num_ebands as i32, DECODER_DATAFLOW_NB_EBANDS);
let mut encoder = opus_custom_encoder_create(&mode, 48_000, 2, 0)
.expect("encoder creation should succeed");
let mut decoder =
opus_custom_decoder_create(&mode, 2).expect("decoder creation should succeed");
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetBitrate(64_000))
.expect("set bitrate should succeed");
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetVbr(false))
.expect("set vbr should succeed");
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetComplexity(10))
.expect("set complexity should succeed");
opus_custom_encoder_ctl(&mut encoder, EncoderCtlRequest::SetLsbDepth(16))
.expect("set lsb depth should succeed");
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetComplexity(10))
.expect("decoder complexity should succeed");
assert_eq!(
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetComplexity(11))
.expect_err("invalid complexity must fail"),
CeltDecoderCtlError::InvalidArgument
);
assert_eq!(
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetStartBand(-1))
.expect_err("negative start band must fail"),
CeltDecoderCtlError::InvalidArgument
);
assert_eq!(
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::SetStartBand(DECODER_DATAFLOW_NB_EBANDS)
)
.expect_err("out-of-range start band must fail"),
CeltDecoderCtlError::InvalidArgument
);
assert_eq!(
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetEndBand(0))
.expect_err("zero end band must fail"),
CeltDecoderCtlError::InvalidArgument
);
assert_eq!(
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::SetEndBand(DECODER_DATAFLOW_NB_EBANDS + 1)
)
.expect_err("out-of-range end band must fail"),
CeltDecoderCtlError::InvalidArgument
);
assert_eq!(
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetChannels(0))
.expect_err("zero stream channels must fail"),
CeltDecoderCtlError::InvalidArgument
);
assert_eq!(
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetChannels(3))
.expect_err("three stream channels must fail"),
CeltDecoderCtlError::InvalidArgument
);
let mut bad_output = vec![0i16; 123 * 2];
assert!(
opus_custom_decode(&mut decoder, Some(&[0]), &mut bad_output, 123).is_err(),
"decode with invalid frame size should fail",
);
#[cfg(feature = "deep_plc")]
let plc = None;
#[cfg(not(feature = "deep_plc"))]
let plc = ();
let mut empty_pcm: [f32; 0] = [];
assert!(
super::celt_decode_with_ec_dred(
&mut decoder,
None,
&mut empty_pcm,
POSTFILTER_FRAME_SIZE,
None,
false,
plc
)
.is_err(),
"decode with empty PCM output should fail",
);
let mut packet = vec![0u8; POSTFILTER_MAX_PACKET_SIZE];
let mut pcm = vec![0i16; POSTFILTER_FRAME_SIZE * 2];
let mut decoded = vec![0i16; POSTFILTER_FRAME_SIZE * 2];
let mut packet_hashes = [0u32; DECODER_DATAFLOW_FRAMES];
let mut pcm_hashes = [0u32; DECODER_DATAFLOW_FRAMES];
let mut final_ranges = [0u32; DECODER_DATAFLOW_FRAMES];
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetStartBand(0))
.expect("set start band should succeed");
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::SetEndBand(DECODER_DATAFLOW_NB_EBANDS),
)
.expect("set end band should succeed");
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetChannels(2))
.expect("set stream channels should succeed");
for frame in 0..DECODER_DATAFLOW_FRAMES {
if frame == 4 {
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetStartBand(3))
.expect("set start band should succeed");
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetEndBand(18))
.expect("set end band should succeed");
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetChannels(2))
.expect("set stream channels should succeed");
} else if frame == 8 {
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetStartBand(2))
.expect("set start band should succeed");
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetEndBand(19))
.expect("set end band should succeed");
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetChannels(1))
.expect("set stream channels should succeed");
} else if frame == 12 {
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::ResetState)
.expect("decoder reset should succeed");
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetStartBand(0))
.expect("set start band should succeed");
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::SetEndBand(DECODER_DATAFLOW_NB_EBANDS),
)
.expect("set end band should succeed");
opus_custom_decoder_ctl(&mut decoder, DecoderCtlRequest::SetChannels(2))
.expect("set stream channels should succeed");
}
fill_decoder_dataflow_pcm(&mut pcm, POSTFILTER_FRAME_SIZE, frame * 13);
let packet_len = opus_custom_encode(
&mut encoder,
&pcm,
POSTFILTER_FRAME_SIZE,
&mut packet,
POSTFILTER_MAX_PACKET_SIZE,
)
.expect("encode should succeed");
let decoded_len = opus_custom_decode(
&mut decoder,
Some(&packet[..packet_len]),
&mut decoded,
POSTFILTER_FRAME_SIZE,
)
.expect("decode should succeed");
assert_eq!(decoded_len, POSTFILTER_FRAME_SIZE);
let mut final_range = 0u32;
opus_custom_decoder_ctl(
&mut decoder,
DecoderCtlRequest::GetFinalRange(&mut final_range),
)
.expect("get final range should succeed");
packet_hashes[frame] = fnv1a_bytes(&packet[..packet_len]);
pcm_hashes[frame] = fnv1a_pcm_le(&decoded[..POSTFILTER_FRAME_SIZE * 2]);
final_ranges[frame] = final_range;
}
assert!(count_hash_changes(&packet_hashes) > 0);
assert!(count_hash_changes(&pcm_hashes) > 0);
assert!(count_hash_changes(&final_ranges) > 0);
if strict_ctest_vectors {
assert_eq!(packet_hashes, expected_packet_hashes);
assert_eq!(pcm_hashes, expected_pcm_hashes);
assert_eq!(final_ranges, expected_final_ranges);
}
assert!(
opus_custom_decode(
&mut decoder,
Some(&[0]),
&mut decoded,
POSTFILTER_FRAME_SIZE,
)
.is_err(),
"tiny packet decode should fail",
);
assert!(
opus_custom_decode(
&mut decoder,
Some(&vec![0u8; POSTFILTER_MAX_PACKET_SIZE + 1]),
&mut decoded,
POSTFILTER_FRAME_SIZE,
)
.is_err(),
"oversize packet decode should fail",
);
}
#[cfg(feature = "fixed_point")]
#[test]
fn decoder_postfilter_matches_ctest_fixed_vectors() {
let owned_mode = opus_custom_mode_create(POSTFILTER_SAMPLE_RATE, POSTFILTER_FRAME_SIZE)
.expect("mode creation should succeed");
let mode = owned_mode.mode();
let cases = [
DecoderPostfilterCase {
name: "mono_postfilter",
channels: 1,
bitrate: 24_000,
max_bytes: 96,
min_nonzero_pitch_frames: 3,
require_pitch_changes: true,
expected_pitch: [430, 954, 954, 0, 528, 528],
expected_packet_hash: [
0xd9db_cb3a,
0x5e4e_25b9,
0x5de8_bc73,
0x3d80_f8c0,
0x68d6_2f7c,
0x50eb_f0b5,
],
expected_pcm_hash: [
0xba7e_fc92,
0xc441_26c4,
0xa582_4709,
0x4d7c_01ed,
0x85bb_0370,
0x6911_f513,
],
},
DecoderPostfilterCase {
name: "stereo_postfilter",
channels: 2,
bitrate: 64_000,
max_bytes: 180,
min_nonzero_pitch_frames: 0,
require_pitch_changes: false,
expected_pitch: [0, 480, 480, 480, 480, 480],
expected_packet_hash: [
0x5cdf_3e0c,
0x6fc9_dad3,
0x9406_08ff,
0x49c0_9e95,
0xe63d_158e,
0x418b_6748,
],
expected_pcm_hash: [
0x042e_972d,
0x547d_2986,
0x8c2e_ee5d,
0x9d66_7d9a,
0xbb76_e17f,
0xdb24_1031,
],
},
];
for case in &cases {
run_decoder_postfilter_case(&mode, case);
}
}
#[cfg(feature = "fixed_point")]
#[test]
fn decoder_noise_renorm_runtime_matches_ctest_vectors() {
let mut zeros = [0.0f32; 8];
decoder_noise_renormalise_runtime(&mut zeros, 8, 1.0, 0);
let zeros_q15: Vec<i16> = zeros.iter().map(|&sample| float2int16(sample)).collect();
assert_eq!(zeros_q15, [0; 8], "zero-energy vector should remain zero");
let mut mixed = [
1000.0, -2000.0, 3000.0, -4000.0, 500.0, -600.0, 700.0, -800.0,
];
decoder_noise_renormalise_runtime(&mut mixed, 8, 1.0, 0);
let mixed_q15: Vec<i16> = mixed.iter().map(|&sample| float2int16(sample)).collect();
assert_eq!(
mixed_q15,
[2908, -5816, 8724, -11632, 1454, -1745, 2036, -2326],
"mixed renormalise result should match C fixed output",
);
let mut large = [30000.0, -30000.0, 20000.0, -10000.0];
decoder_noise_renormalise_runtime(&mut large, 4, 1.0, 0);
let large_q15: Vec<i16> = large.iter().map(|&sample| float2int16(sample)).collect();
assert_eq!(
large_q15,
[10248, -10248, 6832, -3416],
"large-magnitude renormalise result should match C fixed output",
);
let mut half_gain = [30000.0, -30000.0, 20000.0, -10000.0];
decoder_noise_renormalise_runtime(&mut half_gain, 4, 0.5, 0);
let half_gain_q15: Vec<i16> = half_gain
.iter()
.map(|&sample| float2int16(sample))
.collect();
assert_eq!(
half_gain_q15,
[5124, -5124, 3416, -1708],
"gain-scaled renormalise result should match C fixed output",
);
}
#[cfg(feature = "fixed_point")]
#[test]
#[should_panic(expected = "input vector shorter than band size")]
fn decoder_noise_renorm_runtime_panics_on_short_input() {
let mut short = [1000.0f32, -500.0];
decoder_noise_renormalise_runtime(&mut short, 3, 1.0, 0);
}
#[test]
fn deemphasis_stereo_simple_matches_reference() {
let left = [0.25_f32, -0.5, 0.75];
let right = [-0.125_f32, 0.5, -0.25];
let input: [&[f32]; 2] = [&left, &right];
let mut pcm = vec![0.0_f32; left.len() * 2];
let mut mem = [0.1_f32, -0.2_f32];
let coef = [0.5_f32];
deemphasis(&input, &mut pcm, left.len(), 2, 1, &coef, &mut mem, false);
const VERY_SMALL: f32 = 1.0e-30;
let mut expected_mem = [0.1_f32, -0.2_f32];
let mut expected = [Vec::new(), Vec::new()];
for (channel, samples) in [left.as_slice(), right.as_slice()].iter().enumerate() {
let mut m = expected_mem[channel];
for &sample in *samples {
let tmp = sample + m + VERY_SMALL;
expected[channel].push(tmp);
m = coef[0] * tmp;
}
expected_mem[channel] = m;
}
for j in 0..left.len() {
assert!((pcm[2 * j] - expected[0][j] / CELT_SIG_SCALE).abs() < 1e-6);
assert!((pcm[2 * j + 1] - expected[1][j] / CELT_SIG_SCALE).abs() < 1e-6);
}
assert!((mem[0] - expected_mem[0]).abs() < 1e-6);
assert!((mem[1] - expected_mem[1]).abs() < 1e-6);
}
#[test]
fn deemphasis_downsamples_with_accumulation() {
let samples = [0.5_f32, -0.25, 0.75, -0.5];
let input: [&[f32]; 1] = [&samples];
let mut pcm = vec![0.1_f32, -0.2_f32];
let mut mem = [0.0_f32];
let coef = [0.25_f32];
deemphasis(&input, &mut pcm, samples.len(), 1, 2, &coef, &mut mem, true);
const VERY_SMALL: f32 = 1.0e-30;
let mut m = 0.0_f32;
let mut scratch = Vec::new();
for &sample in &samples {
let tmp = sample + m + VERY_SMALL;
scratch.push(tmp);
m = coef[0] * tmp;
}
let expected = [
0.1_f32 + scratch[0] / CELT_SIG_SCALE,
-0.2_f32 + scratch[2] / CELT_SIG_SCALE,
];
for (actual, expected) in pcm.iter().zip(expected.iter()) {
assert!((actual - expected).abs() < 1e-6);
}
assert!((mem[0] - m).abs() < 1e-6);
}
#[test]
fn celt_synthesis_compare_output() {
let mode = opus_custom_mode_find_static(48_000, 120)
.expect("static 48k/120 mode should be available");
let lm = 0usize;
let is_transient = false;
let coded_channels = 1usize;
let output_channels = 1usize;
let start = 0usize;
let eff_end = mode.effective_ebands;
let downsample = 1usize;
let silence = false;
let n = mode.short_mdct_size << lm;
let nb_ebands = mode.num_ebands;
let shift = if is_transient {
mode.max_lm
} else {
mode.max_lm - lm
};
let mdct_len = mode.mdct.effective_len(shift);
let n2 = mdct_len >> 1;
let output_len = (mode.overlap >> 1) + n2;
let mut x = vec![0.0f32; coded_channels * n];
for (i, sample) in x.iter_mut().enumerate() {
*sample = i as CeltNorm * 0.01 - 0.5;
}
let mut old_band_e = vec![0.0f32; coded_channels * nb_ebands];
for (i, value) in old_band_e.iter_mut().enumerate() {
*value = 0.5 + i as CeltGlog * 0.01;
}
#[cfg(feature = "fixed_point")]
let fixed_mdct =
crate::celt::mdct_fixed::FixedMdctLookup::new(mode.mdct.len(), mode.mdct.max_shift());
#[cfg(feature = "fixed_point")]
let fixed_window: Vec<crate::celt::types::FixedCeltCoef> = mode
.window
.iter()
.map(|&value| crate::celt::fixed_ops::qconst16_clamped(f64::from(value), 15))
.collect();
#[cfg(feature = "fixed_point")]
let fixed_ctx = (&fixed_mdct, fixed_window.as_slice(), mode.overlap);
#[cfg(not(feature = "fixed_point"))]
let fixed_ctx = ();
let mut output = vec![0.0f32; output_len];
{
let mut outputs: Vec<&mut [CeltSig]> = vec![output.as_mut_slice()];
super::celt_synthesis(
&mode,
&x,
&mut outputs,
&old_band_e,
start,
eff_end,
coded_channels,
output_channels,
is_transient,
lm,
downsample,
silence,
fixed_ctx,
);
}
for (i, sample) in output.iter().enumerate() {
crate::test_trace::trace_println!("celt_synthesis_out[{}]={:.9e}", i, sample);
}
}
}