use crate::cce::CouplingChannelElement;
use crate::decoded_spectrum::quant_to_spec;
use crate::dequant::rescale_spectrum;
use crate::filterbank::Filterbank;
use crate::ics_body::IcsBody;
use crate::ics_info::IcsInfo;
use crate::intensity_stereo::{apply_intensity_stereo, IntensityPairSpectra};
use crate::ltp::LtpState;
use crate::ms_stereo::{apply_ms_stereo, ChannelPairSpectra, MsMaskPresent};
use crate::pns::{apply_pns, apply_pns_pair, gen_rand_vector, PnsChannel};
use crate::predictor::PredictorBank;
use crate::scale_factor_data::{accumulate, AbsoluteScaleFactorEntry, AbsoluteScaleFactors};
use crate::section_data::ZERO_HCB;
use crate::spectral_data::SpectralData;
use crate::ssr::SsrChannelDecoder;
use crate::swb_offset::apply_pulse_data;
use crate::tns_frame::{tns_analysis_frame_ics, tns_decode_frame_ics};
use crate::{Error, Result};
#[derive(Debug)]
pub struct ChannelInput<'a> {
pub body: &'a IcsBody,
pub ics_info: &'a IcsInfo,
pub spectral: &'a SpectralData,
}
fn band_indexed_track<F>(
abs: &AbsoluteScaleFactors,
sfb_cb: &[Vec<u8>],
max_sfb: usize,
default: i32,
pick: F,
) -> Result<Vec<Vec<i32>>>
where
F: Fn(&AbsoluteScaleFactorEntry) -> Option<i32>,
{
if abs.entries.len() != sfb_cb.len() {
return Err(Error::ElementDecodeInvalid);
}
let mut out: Vec<Vec<i32>> = Vec::with_capacity(sfb_cb.len());
for (group_records, group_cb) in abs.entries.iter().zip(sfb_cb.iter()) {
if group_cb.len() < max_sfb {
return Err(Error::ElementDecodeInvalid);
}
let mut row = vec![default; max_sfb];
let mut rec = group_records.iter();
for (sfb, &cb) in group_cb.iter().enumerate() {
if cb == ZERO_HCB {
continue;
}
let entry = rec.next().ok_or(Error::ElementDecodeInvalid)?;
if sfb < max_sfb {
if let Some(v) = pick(entry) {
row[sfb] = v;
}
}
}
out.push(row);
}
Ok(out)
}
pub(crate) fn is_pos_table(
abs: &AbsoluteScaleFactors,
sfb_cb: &[Vec<u8>],
max_sfb: usize,
) -> Result<Vec<Vec<i32>>> {
band_indexed_track(abs, sfb_cb, max_sfb, 0, |e| match e {
AbsoluteScaleFactorEntry::IsPos(p) => Some(i32::from(*p)),
_ => None,
})
}
pub(crate) fn noise_nrg_table(
abs: &AbsoluteScaleFactors,
sfb_cb: &[Vec<u8>],
max_sfb: usize,
) -> Result<Vec<Vec<i32>>> {
band_indexed_track(abs, sfb_cb, max_sfb, 0, |e| match e {
AbsoluteScaleFactorEntry::NoiseNrg(n) => Some(*n),
_ => None,
})
}
fn reconstruct_pre_pair(
ch: &ChannelInput<'_>,
fs_index: u8,
) -> Result<(Vec<f64>, AbsoluteScaleFactors)> {
let x_quant: SpectralData = if let Some(pd) = &ch.body.pulse_data {
let mut patched = ch.spectral.clone();
let group0 = patched.x_quant.first_mut().ok_or(Error::DequantInvalid)?;
apply_pulse_data(group0, fs_index, pd)?;
patched
} else {
ch.spectral.clone()
};
let abs = accumulate(
&ch.body.scale_factor_data,
&ch.body.section_data.sfb_cb,
ch.body.global_gain,
)?;
let rescaled = rescale_spectrum(
&x_quant,
&abs,
&ch.body.section_data.sfb_cb,
ch.ics_info,
fs_index,
)?;
let spec = quant_to_spec(&rescaled, ch.ics_info, fs_index)?;
Ok((spec, abs))
}
#[allow(clippy::too_many_arguments)]
fn finish_channel(
spec: &mut [f64],
body: &IcsBody,
ics_info: &IcsInfo,
ltp: Option<&crate::ics_info::LtpData>,
aot: u8,
fs_index: u8,
fb: &mut Filterbank,
ltp_state: &mut LtpState,
predictor_bank: &mut Option<PredictorBank>,
ssr: &mut Option<Box<SsrChannelDecoder>>,
coupling: &[CouplingApply<'_>],
) -> Result<Vec<f64>> {
if aot == 1 {
let bank = match predictor_bank {
Some(b) => b,
None => {
*predictor_bank = Some(PredictorBank::new(fs_index)?);
predictor_bank.as_mut().expect("just inserted")
}
};
bank.apply_long(spec, ics_info, ics_info.predictor_data.as_ref(), fs_index)?;
}
if let Some(ltp) = ltp {
let prev_shape = fb.prev_shape();
let tns = body.tns_data.as_ref();
ltp_state.apply_long_with_analysis(spec, ics_info, ltp, prev_shape, fs_index, |x_est| {
if let Some(tns) = tns {
tns_analysis_frame_ics(x_est, tns, ics_info, aot, fs_index)?;
}
Ok(())
})?;
}
apply_freq_coupling(spec, ics_info, fs_index, coupling, false)?;
if let Some(tns) = &body.tns_data {
tns_decode_frame_ics(spec, tns, ics_info, aot, fs_index)?;
}
apply_freq_coupling(spec, ics_info, fs_index, coupling, true)?;
let mut out = if aot == 3 {
let dec = ssr.get_or_insert_with(Default::default);
dec.decode_frame(spec, ics_info, body.gain_control_data.as_ref())?
} else {
let out = fb.synthesize(spec, ics_info)?;
ltp_state.push_frame(&out, fb.aliased_tail());
out
};
apply_time_coupling(&mut out, coupling)?;
Ok(out)
}
#[derive(Debug, Clone, Copy)]
pub struct CouplingApply<'a> {
pub cce: &'a CouplingChannelElement,
pub decoded: &'a DecodedCce,
pub list_index: usize,
}
fn apply_freq_coupling(
spec: &mut [f64],
ics_info: &IcsInfo,
fs_index: u8,
coupling: &[CouplingApply<'_>],
after_tns: bool,
) -> Result<()> {
for c in coupling {
if c.cce.header.ind_sw_cce_flag || c.cce.header.cc_domain != after_tns {
continue;
}
let cce_ics = &c.cce.ics_info;
if cce_ics.window_sequence != ics_info.window_sequence
|| cce_ics.num_window_groups != ics_info.num_window_groups
|| cce_ics.window_group_length != ics_info.window_group_length
{
return Err(Error::CceInvalid);
}
let offsets = cce_ics.swb_offsets(fs_index)?;
c.cce.gains.couple_channel(
&c.decoded.spectrum,
spec,
c.list_index,
&c.cce.body.section_data.sfb_cb,
&cce_ics.window_group_length,
usize::from(cce_ics.max_sfb),
offsets,
)?;
}
Ok(())
}
fn apply_time_coupling(out: &mut [f64], coupling: &[CouplingApply<'_>]) -> Result<()> {
for c in coupling {
if !c.cce.header.ind_sw_cce_flag {
continue;
}
let time = c.decoded.time.as_deref().ok_or(Error::CceInvalid)?;
if time.len() != out.len() {
return Err(Error::CceInvalid);
}
let cc_gain = c.cce.gains.cc_gain(c.list_index, 0, 0)?;
for (o, &t) in out.iter_mut().zip(time.iter()) {
*o += cc_gain * t;
}
}
Ok(())
}
#[derive(Debug, Clone)]
pub struct DecodedCce {
pub spectrum: Vec<f64>,
pub time: Option<Vec<f64>>,
}
#[derive(Debug, Clone)]
pub struct CceDecoder {
fb: Filterbank,
pns_state: u32,
}
impl Default for CceDecoder {
fn default() -> Self {
Self::new()
}
}
impl CceDecoder {
#[must_use]
pub fn new() -> Self {
Self::new_family(crate::swb_offset::FrameFamily::Lc1024)
}
#[must_use]
pub fn new_family(family: crate::swb_offset::FrameFamily) -> Self {
CceDecoder {
fb: Filterbank::new_family(family),
pns_state: 0x0001_2345,
}
}
pub fn decode(
&mut self,
cce: &CouplingChannelElement,
aot: u8,
fs_index: u8,
) -> Result<DecodedCce> {
let ch = ChannelInput {
body: &cce.body,
ics_info: &cce.ics_info,
spectral: &cce.spectral,
};
let (mut spec, abs) = reconstruct_pre_pair(&ch, fs_index)?;
let max_sfb = usize::from(cce.ics_info.max_sfb);
let noise_nrg = noise_nrg_table(&abs, &cce.body.section_data.sfb_cb, max_sfb)?;
let state = &mut self.pns_state;
let mut pns_chan = PnsChannel {
spec: &mut spec,
sfb_cb: &cce.body.section_data.sfb_cb,
noise_nrg: &noise_nrg,
};
apply_pns(&mut pns_chan, &cce.ics_info, fs_index, |out| {
gen_rand_vector(out, state)
})?;
if let Some(tns) = &cce.body.tns_data {
tns_decode_frame_ics(&mut spec, tns, &cce.ics_info, aot, fs_index)?;
}
let time = if cce.header.ind_sw_cce_flag {
Some(self.fb.synthesize(&spec, &cce.ics_info)?)
} else {
None
};
Ok(DecodedCce {
spectrum: spec,
time,
})
}
}
#[derive(Debug, Clone)]
pub struct CpeJointStereo {
pub ms_mask_present: MsMaskPresent,
pub ms_used: Vec<Vec<bool>>,
}
impl Default for CpeJointStereo {
fn default() -> Self {
CpeJointStereo {
ms_mask_present: MsMaskPresent::AllZeros,
ms_used: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ElementDecoder {
filterbanks: [Filterbank; 2],
ltp_states: [LtpState; 2],
predictor_banks: [Option<PredictorBank>; 2],
ssr_decoders: [Option<Box<SsrChannelDecoder>>; 2],
pns_state: u32,
}
impl Default for ElementDecoder {
fn default() -> Self {
Self::new()
}
}
impl ElementDecoder {
pub fn new() -> Self {
Self::new_family(crate::swb_offset::FrameFamily::Lc1024)
}
pub fn new_family(family: crate::swb_offset::FrameFamily) -> Self {
ElementDecoder {
filterbanks: [
Filterbank::new_family(family),
Filterbank::new_family(family),
],
ltp_states: [LtpState::new_family(family), LtpState::new_family(family)],
predictor_banks: [None, None],
ssr_decoders: [None, None],
pns_state: 0x0001_2345,
}
}
pub fn with_pns_seed(seed: u32) -> Self {
ElementDecoder {
filterbanks: [Filterbank::new(), Filterbank::new()],
ltp_states: [LtpState::new(), LtpState::new()],
predictor_banks: [None, None],
ssr_decoders: [None, None],
pns_state: seed,
}
}
pub fn decode_sce(&mut self, ch: &ChannelInput<'_>, aot: u8, fs_index: u8) -> Result<Vec<f64>> {
self.decode_sce_coupled(ch, aot, fs_index, &[])
}
pub fn decode_sce_coupled(
&mut self,
ch: &ChannelInput<'_>,
aot: u8,
fs_index: u8,
coupling: &[CouplingApply<'_>],
) -> Result<Vec<f64>> {
let (mut spec, abs) = reconstruct_pre_pair(ch, fs_index)?;
let max_sfb = ch.ics_info.max_sfb as usize;
let noise_nrg = noise_nrg_table(&abs, &ch.body.section_data.sfb_cb, max_sfb)?;
let state = &mut self.pns_state;
let mut pns_chan = PnsChannel {
spec: &mut spec,
sfb_cb: &ch.body.section_data.sfb_cb,
noise_nrg: &noise_nrg,
};
apply_pns(&mut pns_chan, ch.ics_info, fs_index, |out| {
gen_rand_vector(out, state)
})?;
let ltp = ltp_for_channel(ch.ics_info, false);
finish_channel(
&mut spec,
ch.body,
ch.ics_info,
ltp,
aot,
fs_index,
&mut self.filterbanks[0],
&mut self.ltp_states[0],
&mut self.predictor_banks[0],
&mut self.ssr_decoders[0],
coupling,
)
}
pub fn decode_cpe(
&mut self,
left: &ChannelInput<'_>,
right: &ChannelInput<'_>,
joint: &CpeJointStereo,
aot: u8,
fs_index: u8,
) -> Result<(Vec<f64>, Vec<f64>)> {
self.decode_cpe_coupled(left, right, joint, aot, fs_index, &[], &[])
}
#[allow(clippy::too_many_arguments)]
pub fn decode_cpe_coupled(
&mut self,
left: &ChannelInput<'_>,
right: &ChannelInput<'_>,
joint: &CpeJointStereo,
aot: u8,
fs_index: u8,
left_coupling: &[CouplingApply<'_>],
right_coupling: &[CouplingApply<'_>],
) -> Result<(Vec<f64>, Vec<f64>)> {
if left.ics_info.window_sequence != right.ics_info.window_sequence
|| left.ics_info.num_window_groups != right.ics_info.num_window_groups
|| left.ics_info.window_group_length != right.ics_info.window_group_length
{
return Err(Error::ElementDecodeInvalid);
}
let geom = left.ics_info;
let max_sfb = geom.max_sfb as usize;
let (mut left_spec, left_abs) = reconstruct_pre_pair(left, fs_index)?;
let (mut right_spec, right_abs) = reconstruct_pre_pair(right, fs_index)?;
let ms_used_slice: &[Vec<bool>] = if joint.ms_mask_present == MsMaskPresent::Mask {
validate_ms_used(&joint.ms_used, geom)?;
&joint.ms_used
} else {
&[]
};
{
let mut pair = ChannelPairSpectra {
left: &mut left_spec,
right: &mut right_spec,
left_sfb_cb: &left.body.section_data.sfb_cb,
right_sfb_cb: &right.body.section_data.sfb_cb,
};
apply_ms_stereo(
&mut pair,
joint.ms_mask_present,
ms_used_slice,
geom,
fs_index,
)?;
}
let right_is_pos = is_pos_table(&right_abs, &right.body.section_data.sfb_cb, max_sfb)?;
let is_mask = joint.ms_mask_present == MsMaskPresent::Mask;
{
let mut pair = IntensityPairSpectra {
left: &left_spec,
right: &mut right_spec,
right_sfb_cb: &right.body.section_data.sfb_cb,
is_pos: &right_is_pos,
};
apply_intensity_stereo(&mut pair, is_mask, ms_used_slice, geom, fs_index)?;
}
let left_nrg = noise_nrg_table(&left_abs, &left.body.section_data.sfb_cb, max_sfb)?;
let right_nrg = noise_nrg_table(&right_abs, &right.body.section_data.sfb_cb, max_sfb)?;
let all_shared = joint.ms_mask_present == MsMaskPresent::AllOnes;
{
let mut left_chan = PnsChannel {
spec: &mut left_spec,
sfb_cb: &left.body.section_data.sfb_cb,
noise_nrg: &left_nrg,
};
let mut right_chan = PnsChannel {
spec: &mut right_spec,
sfb_cb: &right.body.section_data.sfb_cb,
noise_nrg: &right_nrg,
};
let state = &mut self.pns_state;
apply_pns_pair(
&mut left_chan,
&mut right_chan,
is_mask,
all_shared,
ms_used_slice,
geom,
fs_index,
|out| gen_rand_vector(out, state),
)?;
}
let left_ltp = ltp_for_channel(left.ics_info, false);
let right_ltp = ltp_for_channel(right.ics_info, true);
let out_left = finish_channel(
&mut left_spec,
left.body,
left.ics_info,
left_ltp,
aot,
fs_index,
&mut self.filterbanks[0],
&mut self.ltp_states[0],
&mut self.predictor_banks[0],
&mut self.ssr_decoders[0],
left_coupling,
)?;
let out_right = finish_channel(
&mut right_spec,
right.body,
right.ics_info,
right_ltp,
aot,
fs_index,
&mut self.filterbanks[1],
&mut self.ltp_states[1],
&mut self.predictor_banks[1],
&mut self.ssr_decoders[1],
right_coupling,
)?;
Ok((out_left, out_right))
}
}
fn ltp_for_channel(ics_info: &IcsInfo, is_pair_slot: bool) -> Option<&crate::ics_info::LtpData> {
if is_pair_slot {
if let Some(pair) = ics_info.ltp_data_pair.as_ref() {
return Some(pair);
}
}
ics_info.ltp_data.as_ref()
}
fn validate_ms_used(ms_used: &[Vec<bool>], ics_info: &IcsInfo) -> Result<()> {
let num_groups = ics_info.num_window_groups as usize;
let max_sfb = ics_info.max_sfb as usize;
if ms_used.len() != num_groups {
return Err(Error::ElementDecodeInvalid);
}
for row in ms_used {
if row.len() < max_sfb {
return Err(Error::ElementDecodeInvalid);
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ics_info::{WindowSequence, WindowShape};
use crate::scale_factor_data::{ScaleFactorData, ScaleFactorEntry};
use crate::section_data::{Section, SectionData, INTENSITY_HCB, NOISE_HCB};
fn sfb_cb_one_group(cbs: &[u8]) -> Vec<Vec<u8>> {
vec![cbs.to_vec()]
}
#[test]
fn band_indexed_track_scatters_by_wire_order() {
let sfb_cb = sfb_cb_one_group(&[ZERO_HCB, INTENSITY_HCB, NOISE_HCB, 2]);
let abs = AbsoluteScaleFactors {
entries: vec![vec![
AbsoluteScaleFactorEntry::IsPos(7),
AbsoluteScaleFactorEntry::NoiseNrg(42),
AbsoluteScaleFactorEntry::Sf(120),
]],
};
let is_pos = is_pos_table(&abs, &sfb_cb, 4).unwrap();
assert_eq!(is_pos[0], vec![0, 7, 0, 0]);
let nrg = noise_nrg_table(&abs, &sfb_cb, 4).unwrap();
assert_eq!(nrg[0], vec![0, 0, 42, 0]);
}
#[test]
fn band_indexed_track_rejects_record_shortfall() {
let sfb_cb = sfb_cb_one_group(&[INTENSITY_HCB, NOISE_HCB]);
let abs = AbsoluteScaleFactors {
entries: vec![vec![AbsoluteScaleFactorEntry::IsPos(1)]],
};
assert!(matches!(
is_pos_table(&abs, &sfb_cb, 2),
Err(Error::ElementDecodeInvalid)
));
}
#[test]
fn band_indexed_track_rejects_group_count_mismatch() {
let sfb_cb = vec![vec![2u8], vec![2u8]];
let abs = AbsoluteScaleFactors {
entries: vec![vec![AbsoluteScaleFactorEntry::Sf(100)]],
};
assert!(matches!(
noise_nrg_table(&abs, &sfb_cb, 1),
Err(Error::ElementDecodeInvalid)
));
}
fn long_ics_info(max_sfb: u8) -> IcsInfo {
IcsInfo {
family: crate::swb_offset::FrameFamily::Lc1024,
ics_reserved_bit: false,
window_sequence: WindowSequence::OnlyLong,
window_shape: WindowShape::Sine,
max_sfb,
scale_factor_grouping: None,
predictor_data_present: false,
predictor_data: None,
ltp_data_present: false,
ltp_data: None,
ltp_data_present_pair: None,
ltp_data_pair: None,
num_windows: 1,
num_window_groups: 1,
window_group_length: vec![1],
num_swb: crate::ics_info::NUM_SWB_LONG_WINDOW[4],
}
}
fn make_body(max_sfb: u8, cb: u8, sf_deltas: &[i16]) -> IcsBody {
let sfb_cb = vec![vec![cb; max_sfb as usize]];
let sections = vec![vec![Section {
codebook: cb,
start: 0,
end: max_sfb,
}]];
let section_data = SectionData { sections, sfb_cb };
let entries: Vec<ScaleFactorEntry> = if cb == NOISE_HCB {
sf_deltas
.iter()
.enumerate()
.map(|(i, &d)| {
if i == 0 {
ScaleFactorEntry::NoisePcm(d as u16)
} else {
ScaleFactorEntry::NoiseDpcm(d as i8)
}
})
.collect()
} else {
sf_deltas
.iter()
.map(|&d| ScaleFactorEntry::Dpcm(d as i8))
.collect()
};
let scale_factor_data = ScaleFactorData {
entries: vec![entries],
};
IcsBody {
global_gain: 100,
ics_info: Some(long_ics_info(max_sfb)),
section_data,
scale_factor_data,
pulse_data_present: false,
pulse_data: None,
tns_data_present: false,
tns_data: None,
gain_control_data_present: false,
gain_control_data: None,
spectral_data_bit_offset: 0,
er_scale_factor_data: None,
reordered_spectral_lengths: None,
}
}
fn make_spectral(value: i32) -> SpectralData {
SpectralData {
x_quant: vec![vec![value; 1024]],
}
}
#[test]
fn decode_sce_produces_finite_pcm() {
let body = make_body(4, 2, &[0, 0, 0, 0]);
let ics = body.ics_info.clone().unwrap();
let spectral = make_spectral(3);
let ch = ChannelInput {
body: &body,
ics_info: &ics,
spectral: &spectral,
};
let mut dec = ElementDecoder::new();
let pcm = dec.decode_sce(&ch, 2, 4).unwrap();
assert_eq!(pcm.len(), 1024);
assert!(pcm.iter().all(|v| v.is_finite()));
assert!(pcm.iter().any(|&v| v != 0.0));
}
#[test]
fn decode_sce_overlap_couples_frames() {
let body = make_body(4, 2, &[0, 0, 0, 0]);
let ics = body.ics_info.clone().unwrap();
let spectral = make_spectral(3);
let ch = ChannelInput {
body: &body,
ics_info: &ics,
spectral: &spectral,
};
let mut dec = ElementDecoder::new();
let f0 = dec.decode_sce(&ch, 2, 4).unwrap();
let f1 = dec.decode_sce(&ch, 2, 4).unwrap();
assert_ne!(f0, f1);
}
#[test]
fn decode_sce_ssr_matches_direct_pipeline_and_threads_state() {
let body = make_body(4, 2, &[0, 0, 0, 0]);
let ics = body.ics_info.clone().unwrap();
let spectral = make_spectral(3);
let ch = ChannelInput {
body: &body,
ics_info: &ics,
spectral: &spectral,
};
let mut dec = ElementDecoder::new();
let mut lc = ElementDecoder::new();
let mut direct = SsrChannelDecoder::new();
for frame in 0..3 {
let f_ssr = dec.decode_sce(&ch, 3, 4).unwrap();
assert_eq!(f_ssr.len(), 1024);
assert!(f_ssr.iter().all(|v| v.is_finite()));
let (spec, _) = reconstruct_pre_pair(&ch, 4).unwrap();
let expect = direct.decode_frame(&spec, &ics, None).unwrap();
assert_eq!(f_ssr, expect, "frame {frame}");
let f_lc = lc.decode_sce(&ch, 2, 4).unwrap();
assert_ne!(f_lc, f_ssr, "frame {frame}");
}
}
#[test]
fn decode_sce_ssr_gain_control_data_changes_output() {
use crate::gain_control_data::{GainAdjust, GainBand, GainControlData, GainWindow};
let plain = make_body(40, 2, &[0; 40]);
let mut gained = make_body(40, 2, &[0; 40]);
gained.gain_control_data_present = true;
gained.gain_control_data = Some(GainControlData {
max_band: 1,
bands: vec![GainBand {
windows: vec![GainWindow {
adjustments: vec![GainAdjust {
alevcode: 7, aloccode: 0,
}],
}],
}],
});
let ics = plain.ics_info.clone().unwrap();
let spectral = make_spectral(3);
let ch_plain = ChannelInput {
body: &plain,
ics_info: &ics,
spectral: &spectral,
};
let ch_gained = ChannelInput {
body: &gained,
ics_info: &ics,
spectral: &spectral,
};
let mut a = ElementDecoder::new();
let mut b = ElementDecoder::new();
let fa = a.decode_sce(&ch_plain, 3, 4).unwrap();
let fb = b.decode_sce(&ch_gained, 3, 4).unwrap();
assert_eq!(fa.len(), fb.len());
assert_ne!(fa, fb, "gain ladder must alter the SSR synthesis");
}
#[test]
fn decode_cpe_ssr_both_channels() {
let left_body = make_body(4, 2, &[0, 0, 0, 0]);
let right_body = make_body(4, 2, &[0, 0, 0, 0]);
let ics = left_body.ics_info.clone().unwrap();
let left_spec = make_spectral(5);
let right_spec = make_spectral(2);
let left = ChannelInput {
body: &left_body,
ics_info: &ics,
spectral: &left_spec,
};
let right = ChannelInput {
body: &right_body,
ics_info: &ics,
spectral: &right_spec,
};
let mut dec = ElementDecoder::new();
let (l, r) = dec
.decode_cpe(&left, &right, &CpeJointStereo::default(), 3, 4)
.unwrap();
assert_eq!(l.len(), 1024);
assert_eq!(r.len(), 1024);
assert!(l.iter().chain(r.iter()).all(|v| v.is_finite()));
assert_ne!(l, r);
}
#[test]
fn decode_cpe_ms_reconstructs_left_right() {
let left_body = make_body(4, 2, &[0, 0, 0, 0]);
let right_body = make_body(4, 2, &[0, 0, 0, 0]);
let ics = left_body.ics_info.clone().unwrap();
let left_spec = make_spectral(5);
let right_spec = make_spectral(2);
let left = ChannelInput {
body: &left_body,
ics_info: &ics,
spectral: &left_spec,
};
let right = ChannelInput {
body: &right_body,
ics_info: &ics,
spectral: &right_spec,
};
let joint = CpeJointStereo {
ms_mask_present: MsMaskPresent::AllOnes,
ms_used: vec![],
};
let mut dec = ElementDecoder::new();
let (l, r) = dec.decode_cpe(&left, &right, &joint, 2, 4).unwrap();
assert_eq!(l.len(), 1024);
assert_eq!(r.len(), 1024);
assert!(l.iter().all(|v| v.is_finite()));
assert!(r.iter().all(|v| v.is_finite()));
assert_ne!(l, r);
}
#[test]
fn decode_cpe_mask_off_is_independent_channels() {
let left_body = make_body(4, 2, &[0, 0, 0, 0]);
let right_body = make_body(4, 2, &[0, 0, 0, 0]);
let ics = left_body.ics_info.clone().unwrap();
let same = make_spectral(4);
let left = ChannelInput {
body: &left_body,
ics_info: &ics,
spectral: &same,
};
let right = ChannelInput {
body: &right_body,
ics_info: &ics,
spectral: &same,
};
let joint = CpeJointStereo::default();
let mut dec = ElementDecoder::new();
let (l, r) = dec.decode_cpe(&left, &right, &joint, 2, 4).unwrap();
assert_eq!(l, r);
}
#[test]
fn decode_cpe_rejects_window_sequence_mismatch() {
let left_body = make_body(4, 2, &[0, 0, 0, 0]);
let mut right_body = make_body(4, 2, &[0, 0, 0, 0]);
let mut right_ics = right_body.ics_info.clone().unwrap();
right_ics.window_sequence = WindowSequence::LongStop;
right_body.ics_info = Some(right_ics.clone());
let left_ics = left_body.ics_info.clone().unwrap();
let left_spec = make_spectral(1);
let right_spec = make_spectral(1);
let left = ChannelInput {
body: &left_body,
ics_info: &left_ics,
spectral: &left_spec,
};
let right = ChannelInput {
body: &right_body,
ics_info: &right_ics,
spectral: &right_spec,
};
let joint = CpeJointStereo::default();
let mut dec = ElementDecoder::new();
assert!(matches!(
dec.decode_cpe(&left, &right, &joint, 2, 4),
Err(Error::ElementDecodeInvalid)
));
}
#[test]
fn decode_sce_synthesizes_noise_band() {
let body = make_body(4, NOISE_HCB, &[10, 0, 0, 0]);
let ics = body.ics_info.clone().unwrap();
let spectral = make_spectral(0);
let ch = ChannelInput {
body: &body,
ics_info: &ics,
spectral: &spectral,
};
let mut dec = ElementDecoder::new();
let pcm = dec.decode_sce(&ch, 2, 4).unwrap();
assert!(pcm.iter().all(|v| v.is_finite()));
assert!(
pcm.iter().any(|&v| v != 0.0),
"PNS-filled noise band should produce non-silent PCM"
);
}
use crate::ics_info::LtpData;
fn with_ltp(mut body: IcsBody, coef: u8, lag: u16, long_used: Vec<bool>) -> IcsBody {
let mut ics = body.ics_info.clone().unwrap();
ics.ltp_data_present = true;
ics.ltp_data = Some(LtpData {
lag_update: None,
lag: Some(lag),
coef,
long_used,
short: None,
});
body.ics_info = Some(ics);
body
}
#[test]
fn ltp_off_first_frame_zero_history_matches_no_ltp() {
let plain = make_body(4, 2, &[0, 0, 0, 0]);
let ltp_body = with_ltp(make_body(4, 2, &[0, 0, 0, 0]), 7, 50, vec![true; 4]);
let spectral = make_spectral(3);
let p_ics = plain.ics_info.clone().unwrap();
let l_ics = ltp_body.ics_info.clone().unwrap();
let plain_ch = ChannelInput {
body: &plain,
ics_info: &p_ics,
spectral: &spectral,
};
let ltp_ch = ChannelInput {
body: <p_body,
ics_info: &l_ics,
spectral: &spectral,
};
let f_plain = ElementDecoder::new().decode_sce(&plain_ch, 2, 4).unwrap();
let f_ltp = ElementDecoder::new().decode_sce(<p_ch, 2, 4).unwrap();
for (a, b) in f_plain.iter().zip(f_ltp.iter()) {
assert!((a - b).abs() < 1e-12, "first-frame LTP add must be zero");
}
}
#[test]
fn ltp_fires_on_second_frame_and_diverges() {
let plain = make_body(4, 2, &[0, 0, 0, 0]);
let ltp_body = with_ltp(make_body(4, 2, &[0, 0, 0, 0]), 5, 30, vec![true; 4]);
let spectral = make_spectral(4);
let p_ics = plain.ics_info.clone().unwrap();
let l_ics = ltp_body.ics_info.clone().unwrap();
let plain_ch = ChannelInput {
body: &plain,
ics_info: &p_ics,
spectral: &spectral,
};
let ltp_ch = ChannelInput {
body: <p_body,
ics_info: &l_ics,
spectral: &spectral,
};
let mut dec_plain = ElementDecoder::new();
let mut dec_ltp = ElementDecoder::new();
let _ = dec_plain.decode_sce(&plain_ch, 2, 4).unwrap();
let _ = dec_ltp.decode_sce(<p_ch, 2, 4).unwrap();
let f1_plain = dec_plain.decode_sce(&plain_ch, 2, 4).unwrap();
let f1_ltp = dec_ltp.decode_sce(<p_ch, 2, 4).unwrap();
assert!(f1_ltp.iter().all(|v| v.is_finite()));
let diff = f1_plain
.iter()
.zip(f1_ltp.iter())
.any(|(a, b)| (a - b).abs() > 1e-9);
assert!(diff, "second-frame LTP should change the output");
}
#[test]
fn ltp_with_tns_stays_finite() {
use crate::tns_data::{TnsData, TnsFilter, TnsWindow};
let mut body = with_ltp(make_body(20, 2, &[0i16; 20]), 4, 64, vec![true; 20]);
body.tns_data_present = true;
body.tns_data = Some(TnsData {
windows: vec![TnsWindow {
coef_res: false,
filters: vec![TnsFilter {
length: 10,
order: 3,
direction: false,
coef_compress: false,
coef: vec![1, 7, 2],
}],
}],
});
let ics = body.ics_info.clone().unwrap();
let spectral = make_spectral(3);
let ch = ChannelInput {
body: &body,
ics_info: &ics,
spectral: &spectral,
};
let mut dec = ElementDecoder::new();
let f0 = dec.decode_sce(&ch, 2, 4).unwrap();
let f1 = dec.decode_sce(&ch, 2, 4).unwrap();
assert!(f0.iter().all(|v| v.is_finite()));
assert!(f1.iter().all(|v| v.is_finite()));
assert_ne!(f0, f1);
}
fn with_main_prediction(mut body: IcsBody, max_sfb: u8) -> IcsBody {
use crate::ics_info::PredictorData;
let ics = body.ics_info.as_mut().unwrap();
ics.predictor_data_present = true;
ics.predictor_data = Some(PredictorData {
reset: false,
reset_group_number: None,
prediction_used: vec![true; max_sfb as usize],
});
body
}
#[test]
fn decode_sce_main_aot_runs_predictor() {
let body = with_main_prediction(make_body(20, 2, &[0i16; 20]), 20);
let ics = body.ics_info.clone().unwrap();
let spectral = make_spectral(3);
let ch = ChannelInput {
body: &body,
ics_info: &ics,
spectral: &spectral,
};
let mut main_dec = ElementDecoder::new();
let mut main_frames = Vec::new();
for _ in 0..6 {
let f = main_dec.decode_sce(&ch, 1, 4).unwrap();
assert!(f.iter().all(|v| v.is_finite()));
main_frames.push(f);
}
let lc_body = make_body(20, 2, &[0i16; 20]);
let lc_ics = lc_body.ics_info.clone().unwrap();
let lc_ch = ChannelInput {
body: &lc_body,
ics_info: &lc_ics,
spectral: &spectral,
};
let mut lc_dec = ElementDecoder::new();
let mut lc_frames = Vec::new();
for _ in 0..6 {
lc_frames.push(lc_dec.decode_sce(&lc_ch, 2, 4).unwrap());
}
assert_ne!(
main_frames.last().unwrap(),
lc_frames.last().unwrap(),
"Main predictor produced no spectral change"
);
}
}