use crate::ics_info::IcsInfo;
use crate::scale_factor_data::{AbsoluteScaleFactorEntry, AbsoluteScaleFactors};
use crate::section_data::{Codebook, ZERO_HCB};
use crate::spectral_data::{sect_sfb_offset, SpectralData};
use crate::{Error, Result};
pub const SF_OFFSET: i32 = 100;
#[inline]
pub fn inverse_quantize(x_quant: i32) -> f64 {
let abs = f64::from(x_quant.unsigned_abs());
let mag = abs * abs.cbrt();
if x_quant < 0 {
-mag
} else {
mag
}
}
#[inline]
pub fn scale_factor_gain(sf: u8) -> f64 {
(0.25 * f64::from(i32::from(sf) - SF_OFFSET)).exp2()
}
pub fn rescale_spectrum(
spectral: &SpectralData,
scale_factors: &AbsoluteScaleFactors,
sfb_cb: &[Vec<u8>],
ics_info: &IcsInfo,
fs_index: u8,
) -> Result<Vec<Vec<f64>>> {
let offsets = sect_sfb_offset(ics_info, fs_index)?;
let num_groups = ics_info.num_window_groups as usize;
if spectral.x_quant.len() != num_groups
|| scale_factors.entries.len() != num_groups
|| sfb_cb.len() != num_groups
{
return Err(Error::DequantInvalid);
}
let mut out = Vec::with_capacity(num_groups);
for (g, group_offsets) in offsets.iter().enumerate() {
let x_quant = &spectral.x_quant[g];
let window_len = ics_info.window_len().map_err(|_| Error::DequantInvalid)?;
let expected_len = if ics_info.window_sequence.is_eight_short() {
ics_info.window_group_length[g] as usize * window_len
} else {
window_len
};
if x_quant.len() != expected_len || sfb_cb[g].len() != ics_info.max_sfb as usize {
return Err(Error::DequantInvalid);
}
let mut rescal = vec![0.0f64; x_quant.len()];
let mut entries = scale_factors.entries[g].iter();
for (sfb, &cb) in sfb_cb[g].iter().enumerate() {
if cb == ZERO_HCB {
continue;
}
let entry = entries.next().ok_or(Error::DequantInvalid)?;
let kind = Codebook::from_value(cb);
match entry {
AbsoluteScaleFactorEntry::Sf(sf)
if matches!(
kind,
Codebook::Quad { .. } | Codebook::Pair { .. } | Codebook::Esc
) =>
{
let gain = scale_factor_gain(*sf);
let start = group_offsets[sfb] as usize;
let end = group_offsets[sfb + 1] as usize;
for k in start..end {
rescal[k] = inverse_quantize(x_quant[k]) * gain;
}
}
AbsoluteScaleFactorEntry::NoiseNrg(_) if kind.is_noise() => {}
AbsoluteScaleFactorEntry::IsPos(_) if kind.is_intensity() => {}
_ => return Err(Error::DequantInvalid),
}
}
if entries.next().is_some() {
return Err(Error::DequantInvalid);
}
out.push(rescal);
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ics_info::{WindowSequence, WindowShape};
use crate::section_data::{INTENSITY_HCB, NOISE_HCB};
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],
}
}
#[test]
fn inverse_quantize_pins_exact_cubes() {
assert_eq!(inverse_quantize(0), 0.0);
assert_eq!(inverse_quantize(1), 1.0);
assert_eq!(inverse_quantize(-1), -1.0);
assert_eq!(inverse_quantize(8), 16.0);
assert_eq!(inverse_quantize(-8), -16.0);
assert_eq!(inverse_quantize(27), 81.0);
assert_eq!(inverse_quantize(-27), -81.0);
assert_eq!(inverse_quantize(64), 256.0);
assert_eq!(inverse_quantize(729), 6561.0);
assert_eq!(inverse_quantize(4096), 65536.0);
assert_eq!(inverse_quantize(-4096), -65536.0);
}
#[test]
fn inverse_quantize_is_odd_and_monotonic_up_to_max_quant() {
let mut prev = 0.0;
for x in 1..=8191 {
let y = inverse_quantize(x);
assert!(y > prev, "monotonic at {x}");
assert_eq!(inverse_quantize(-x), -y, "odd symmetry at {x}");
prev = y;
}
assert!(prev > 160_000.0 && prev < 170_000.0);
}
#[test]
fn scale_factor_gain_pins_exact_powers() {
assert_eq!(scale_factor_gain(100), 1.0);
assert_eq!(scale_factor_gain(104), 2.0);
assert_eq!(scale_factor_gain(108), 4.0);
assert_eq!(scale_factor_gain(96), 0.5);
assert_eq!(scale_factor_gain(92), 0.25);
assert_eq!(scale_factor_gain(0), (-25.0f64).exp2());
assert_eq!(scale_factor_gain(255), 38.75f64.exp2());
assert_eq!(scale_factor_gain(101), 0.25f64.exp2());
}
#[test]
fn rescale_applies_per_band_gain_over_swb_ranges() {
let info = long_ics_info(2);
let sfb_cb = vec![vec![1u8, 1]];
let mut x_quant = vec![0i32; 1024];
x_quant[..8].copy_from_slice(&[1, -1, 0, 8, -8, 1, 0, -1]);
let spectral = SpectralData {
x_quant: vec![x_quant],
};
let sf = AbsoluteScaleFactors {
entries: vec![vec![
AbsoluteScaleFactorEntry::Sf(104),
AbsoluteScaleFactorEntry::Sf(96),
]],
};
let out = rescale_spectrum(&spectral, &sf, &sfb_cb, &info, 4).unwrap();
assert_eq!(out.len(), 1);
assert_eq!(out[0].len(), 1024);
assert_eq!(out[0][..4], [2.0, -2.0, 0.0, 32.0]);
assert_eq!(out[0][4..8], [-8.0, 0.5, 0.0, -0.5]);
assert!(out[0][8..].iter().all(|&v| v == 0.0));
}
#[test]
fn rescale_leaves_noise_and_intensity_bands_at_zero() {
let info = long_ics_info(3);
let sfb_cb = vec![vec![NOISE_HCB, INTENSITY_HCB, 2]];
let mut x_quant = vec![0i32; 1024];
x_quant[8..12].copy_from_slice(&[1, 1, -1, 0]);
let spectral = SpectralData {
x_quant: vec![x_quant],
};
let sf = AbsoluteScaleFactors {
entries: vec![vec![
AbsoluteScaleFactorEntry::NoiseNrg(-50),
AbsoluteScaleFactorEntry::IsPos(3),
AbsoluteScaleFactorEntry::Sf(100),
]],
};
let out = rescale_spectrum(&spectral, &sf, &sfb_cb, &info, 4).unwrap();
assert!(out[0][..8].iter().all(|&v| v == 0.0));
assert_eq!(out[0][8..12], [1.0, 1.0, -1.0, 0.0]);
}
#[test]
fn rescale_skips_zero_hcb_bands_without_consuming_entries() {
let info = long_ics_info(3);
let sfb_cb = vec![vec![ZERO_HCB, 1, ZERO_HCB]];
let mut x_quant = vec![0i32; 1024];
x_quant[4..8].copy_from_slice(&[1, 0, 0, -1]);
let spectral = SpectralData {
x_quant: vec![x_quant],
};
let sf = AbsoluteScaleFactors {
entries: vec![vec![AbsoluteScaleFactorEntry::Sf(108)]],
};
let out = rescale_spectrum(&spectral, &sf, &sfb_cb, &info, 4).unwrap();
assert_eq!(out[0][4..8], [4.0, 0.0, 0.0, -4.0]);
assert!(out[0][..4].iter().all(|&v| v == 0.0));
assert!(out[0][8..].iter().all(|&v| v == 0.0));
}
#[test]
fn rescale_short_grouped_band_shares_one_gain() {
let info = IcsInfo {
family: crate::swb_offset::FrameFamily::Lc1024,
ics_reserved_bit: false,
window_sequence: WindowSequence::EightShort,
window_shape: WindowShape::Sine,
max_sfb: 1,
scale_factor_grouping: Some(0),
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: 8,
num_window_groups: 2,
window_group_length: vec![5, 3],
num_swb: crate::ics_info::NUM_SWB_SHORT_WINDOW[4],
};
let sfb_cb = vec![vec![1u8], vec![1u8]];
let mut g0 = vec![0i32; 5 * 128];
for (i, slot) in g0.iter_mut().take(20).enumerate() {
*slot = if i % 2 == 0 { 1 } else { -1 };
}
let mut g1 = vec![0i32; 3 * 128];
for slot in g1.iter_mut().take(12) {
*slot = 8;
}
let spectral = SpectralData {
x_quant: vec![g0, g1],
};
let sf = AbsoluteScaleFactors {
entries: vec![
vec![AbsoluteScaleFactorEntry::Sf(104)],
vec![AbsoluteScaleFactorEntry::Sf(96)],
],
};
let out = rescale_spectrum(&spectral, &sf, &sfb_cb, &info, 4).unwrap();
for (i, &v) in out[0].iter().take(20).enumerate() {
let want = if i % 2 == 0 { 2.0 } else { -2.0 };
assert_eq!(v, want, "g0[{i}]");
}
assert!(out[0][20..].iter().all(|&v| v == 0.0));
for (i, &v) in out[1].iter().take(12).enumerate() {
assert_eq!(v, 8.0, "g1[{i}]");
}
assert!(out[1][12..].iter().all(|&v| v == 0.0));
}
#[test]
fn rescale_rejects_entry_codebook_mismatch() {
let info = long_ics_info(1);
let sfb_cb = vec![vec![1u8]];
let spectral = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
let sf = AbsoluteScaleFactors {
entries: vec![vec![AbsoluteScaleFactorEntry::IsPos(0)]],
};
assert!(matches!(
rescale_spectrum(&spectral, &sf, &sfb_cb, &info, 4),
Err(Error::DequantInvalid)
));
}
#[test]
fn rescale_rejects_reserved_codebook_12() {
let info = long_ics_info(1);
let sfb_cb = vec![vec![12u8]];
let spectral = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
let sf = AbsoluteScaleFactors {
entries: vec![vec![AbsoluteScaleFactorEntry::Sf(100)]],
};
assert!(matches!(
rescale_spectrum(&spectral, &sf, &sfb_cb, &info, 4),
Err(Error::DequantInvalid)
));
}
#[test]
fn rescale_rejects_surplus_and_missing_entries() {
let info = long_ics_info(1);
let sfb_cb = vec![vec![1u8]];
let spectral = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
let missing = AbsoluteScaleFactors {
entries: vec![vec![]],
};
assert!(matches!(
rescale_spectrum(&spectral, &missing, &sfb_cb, &info, 4),
Err(Error::DequantInvalid)
));
let surplus = AbsoluteScaleFactors {
entries: vec![vec![
AbsoluteScaleFactorEntry::Sf(100),
AbsoluteScaleFactorEntry::Sf(100),
]],
};
assert!(matches!(
rescale_spectrum(&spectral, &surplus, &sfb_cb, &info, 4),
Err(Error::DequantInvalid)
));
}
#[test]
fn rescale_rejects_wrong_group_buffer_length() {
let info = long_ics_info(1);
let sfb_cb = vec![vec![1u8]];
let spectral = SpectralData {
x_quant: vec![vec![0i32; 512]],
};
let sf = AbsoluteScaleFactors {
entries: vec![vec![AbsoluteScaleFactorEntry::Sf(100)]],
};
assert!(matches!(
rescale_spectrum(&spectral, &sf, &sfb_cb, &info, 4),
Err(Error::DequantInvalid)
));
}
#[test]
fn rescale_rejects_group_count_mismatch() {
let info = long_ics_info(1);
let sfb_cb = vec![vec![1u8], vec![1u8]];
let spectral = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
let sf = AbsoluteScaleFactors {
entries: vec![vec![AbsoluteScaleFactorEntry::Sf(100)]],
};
assert!(matches!(
rescale_spectrum(&spectral, &sf, &sfb_cb, &info, 4),
Err(Error::DequantInvalid)
));
}
}