use crate::dequant::rescale_spectrum;
use crate::ics_body::IcsBody;
use crate::ics_info::IcsInfo;
use crate::scale_factor_data::accumulate;
use crate::spectral_data::SpectralData;
use crate::swb_offset::apply_pulse_data_family;
use crate::tns_frame::tns_decode_frame_ics;
use crate::{Error, Result};
pub fn quant_to_spec(groups: &[Vec<f64>], ics_info: &IcsInfo, fs_index: u8) -> Result<Vec<f64>> {
let window_len = ics_info.window_len()?;
let offsets = ics_info.swb_offsets(fs_index)?;
let num_swb = offsets.len() - 1;
let num_windows = ics_info.num_windows as usize;
let num_groups = ics_info.num_window_groups as usize;
if groups.len() != num_groups
|| ics_info.window_group_length.len() != num_groups
|| ics_info
.window_group_length
.iter()
.map(|&w| w as usize)
.sum::<usize>()
!= num_windows
{
return Err(Error::QuantToSpecInvalid);
}
let mut spec = vec![0.0f64; num_windows * window_len];
let mut window_base = 0usize;
for (g, group) in groups.iter().enumerate() {
let wgl = ics_info.window_group_length[g] as usize;
if group.len() != wgl * window_len {
return Err(Error::QuantToSpecInvalid);
}
let mut src = group.iter();
let mut j = 0usize;
for sfb in 0..num_swb {
let width = (offsets[sfb + 1] - offsets[sfb]) as usize;
for win in 0..wgl {
let dst = (window_base + win) * window_len + j;
for bin in 0..width {
spec[dst + bin] = *src.next().expect("group length checked above");
}
}
j += width;
}
window_base += wgl;
}
Ok(spec)
}
pub fn decode_channel_spectrum(
body: &IcsBody,
ics_info: &IcsInfo,
spectral: &SpectralData,
aot: u8,
fs_index: u8,
) -> Result<Vec<f64>> {
let x_quant: &SpectralData = &if let Some(pd) = &body.pulse_data {
let mut patched = spectral.clone();
let group0 = patched.x_quant.first_mut().ok_or(Error::DequantInvalid)?;
apply_pulse_data_family(group0, ics_info.family, fs_index, pd)?;
patched
} else {
spectral.clone()
};
let scale_factors = accumulate(
&body.scale_factor_data,
&body.section_data.sfb_cb,
body.global_gain,
)?;
let rescaled = rescale_spectrum(
x_quant,
&scale_factors,
&body.section_data.sfb_cb,
ics_info,
fs_index,
)?;
let mut spec = quant_to_spec(&rescaled, ics_info, fs_index)?;
if let Some(tns) = &body.tns_data {
tns_decode_frame_ics(&mut spec, tns, ics_info, aot, fs_index)?;
}
Ok(spec)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ics_info::{WindowSequence, WindowShape};
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 short_ics_info(max_sfb: u8, window_group_length: Vec<u8>) -> IcsInfo {
let num_window_groups = window_group_length.len() as u8;
IcsInfo {
family: crate::swb_offset::FrameFamily::Lc1024,
ics_reserved_bit: false,
window_sequence: WindowSequence::EightShort,
window_shape: WindowShape::Sine,
max_sfb,
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,
window_group_length,
num_swb: crate::ics_info::NUM_SWB_SHORT_WINDOW[4],
}
}
#[test]
fn quant_to_spec_long_is_identity() {
let info = long_ics_info(10);
let group: Vec<f64> = (0..1024).map(|i| i as f64 * 0.5 - 100.0).collect();
let spec = quant_to_spec(core::slice::from_ref(&group), &info, 4).unwrap();
assert_eq!(spec, group);
}
#[test]
fn quant_to_spec_short_deinterleaves_grouped_windows() {
let info = short_ics_info(2, vec![5, 3]);
let mut g0 = vec![0.0f64; 5 * 128];
let mut g1 = vec![0.0f64; 3 * 128];
g0[9] = 1.0;
g0[39] = 2.0;
g1[0] = 3.0;
g1[22] = 4.0;
let spec = quant_to_spec(&[g0, g1], &info, 4).unwrap();
assert_eq!(spec.len(), 1024);
assert_eq!(spec[2 * 128 + 1], 1.0);
assert_eq!(spec[4 * 128 + 7], 2.0);
assert_eq!(spec[5 * 128], 3.0);
assert_eq!(spec[7 * 128 + 6], 4.0);
let placed = spec.iter().filter(|&&v| v != 0.0).count();
assert_eq!(placed, 4);
}
#[test]
fn quant_to_spec_short_full_table_round_trips_every_coefficient() {
let info = short_ics_info(14, vec![1, 2, 1, 4]);
let mut groups = Vec::new();
let mut tag = 1.0f64;
for &wgl in &info.window_group_length {
let mut g = vec![0.0f64; wgl as usize * 128];
for slot in g.iter_mut() {
*slot = tag;
tag += 1.0;
}
groups.push(g);
}
let spec = quant_to_spec(&groups, &info, 4).unwrap();
let mut seen: Vec<f64> = spec.clone();
seen.sort_by(|a, b| a.partial_cmp(b).unwrap());
let want: Vec<f64> = (1..=1024).map(|i| i as f64).collect();
assert_eq!(seen, want);
}
#[test]
fn quant_to_spec_rejects_shape_mismatches() {
let info = short_ics_info(2, vec![5, 3]);
assert!(matches!(
quant_to_spec(&[vec![0.0; 5 * 128]], &info, 4),
Err(Error::QuantToSpecInvalid)
));
assert!(matches!(
quant_to_spec(&[vec![0.0; 5 * 128], vec![0.0; 2 * 128]], &info, 4),
Err(Error::QuantToSpecInvalid)
));
let bad = short_ics_info(2, vec![5, 2]);
assert!(matches!(
quant_to_spec(&[vec![0.0; 5 * 128], vec![0.0; 2 * 128]], &bad, 4),
Err(Error::QuantToSpecInvalid)
));
let info = long_ics_info(2);
assert!(matches!(
quant_to_spec(&[vec![0.0; 1024]], &info, 12),
Err(Error::IcsInfoUnsupportedSampleRateIndex(12))
));
}
}