use oxideav_core::bits::{BitReader, BitWriter};
use crate::ics_info::IcsInfo;
use crate::section_data::{Codebook, Section, SectionData};
use crate::spectral_codebook::{
apply_sign_bits, decode_esc_value, decode_index_to_tuple, derive_sign_bits, encode_esc_value,
encode_tuple_to_index, table_4_95, MAX_QUANT,
};
use crate::spectrum_huffman::{
hcod10_decode, hcod10_write, hcod11_decode, hcod11_write, hcod1_decode, hcod1_write,
hcod2_decode, hcod2_write, hcod3_decode, hcod3_write, hcod4_decode, hcod4_write, hcod5_decode,
hcod5_write, hcod6_decode, hcod6_write, hcod7_decode, hcod7_write, hcod8_decode, hcod8_write,
hcod9_decode, hcod9_write,
};
#[cfg(test)]
use crate::swb_offset::{long_window_offsets, short_window_offsets};
use crate::{Error, Result};
pub const QUAD_LEN: usize = 4;
pub const PAIR_LEN: usize = 2;
pub const ESC_FLAG: i32 = 16;
pub fn sect_sfb_offset(ics_info: &IcsInfo, fs_index: u8) -> Result<Vec<Vec<u32>>> {
let max_sfb = ics_info.max_sfb as usize;
if ics_info.window_sequence.is_eight_short() {
let swb = ics_info.swb_offsets(fs_index)?;
if max_sfb + 1 > swb.len() {
return Err(Error::SpectralDataInvalid);
}
let mut per_group = Vec::with_capacity(ics_info.num_window_groups as usize);
for g in 0..ics_info.num_window_groups as usize {
let wgl = u32::from(ics_info.window_group_length[g]);
let mut offsets = Vec::with_capacity(max_sfb + 1);
let mut offset = 0u32;
offsets.push(offset);
for i in 0..max_sfb {
let width = u32::from(swb[i + 1] - swb[i]) * wgl;
offset += width;
offsets.push(offset);
}
per_group.push(offsets);
}
Ok(per_group)
} else {
let swb = ics_info.swb_offsets(fs_index)?;
if max_sfb + 1 > swb.len() {
return Err(Error::SpectralDataInvalid);
}
let offsets = swb[..=max_sfb].iter().map(|&o| u32::from(o)).collect();
Ok(vec![offsets])
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpectralData {
pub x_quant: Vec<Vec<i32>>,
}
impl SpectralData {
fn group_len(ics_info: &IcsInfo, g: usize) -> usize {
let window_len = ics_info
.window_len()
.unwrap_or_else(|_| ics_info.family.frame_len());
if ics_info.window_sequence.is_eight_short() {
ics_info.window_group_length[g] as usize * window_len
} else {
window_len
}
}
pub fn parse(
reader: &mut BitReader<'_>,
ics_info: &IcsInfo,
section_data: &SectionData,
fs_index: u8,
) -> Result<Self> {
let offsets = sect_sfb_offset(ics_info, fs_index)?;
let num_groups = ics_info.num_window_groups as usize;
if section_data.sections.len() != num_groups {
return Err(Error::SpectralDataInvalid);
}
let mut x_quant = Vec::with_capacity(num_groups);
for (g, group_offsets) in offsets.iter().enumerate() {
let mut buf = vec![0i32; Self::group_len(ics_info, g)];
for sec in §ion_data.sections[g] {
let (cb, dim) = match section_codebook(sec)? {
Some(pair) => pair,
None => continue,
};
let start = group_offsets[sec.start as usize] as usize;
let end = group_offsets[sec.end as usize] as usize;
debug_assert!(end <= buf.len(), "offsets bounded by group span");
let mut k = start;
while k < end {
if k + dim > end {
return Err(Error::SpectralDataInvalid);
}
let idx = decode_codeword(reader, cb)?;
let tuple = decode_index_to_tuple(cb, idx)?;
let tuple = read_and_apply_signs(reader, cb, dim, tuple)?;
for (j, &v) in tuple.iter().take(dim).enumerate() {
buf[k + j] = if cb == 11 && v.abs() == ESC_FLAG {
let mag = read_escape_sequence(reader)? as i32;
if v < 0 {
-mag
} else {
mag
}
} else {
v
};
}
k += dim;
}
}
x_quant.push(buf);
}
Ok(SpectralData { x_quant })
}
pub fn write(
&self,
writer: &mut BitWriter,
ics_info: &IcsInfo,
section_data: &SectionData,
fs_index: u8,
) -> Result<()> {
let offsets = sect_sfb_offset(ics_info, fs_index)?;
let num_groups = ics_info.num_window_groups as usize;
if section_data.sections.len() != num_groups {
return Err(Error::SpectralDataInvalid);
}
if self.x_quant.len() != num_groups {
return Err(Error::SpectralDataEncodeInvalid);
}
for (g, group_offsets) in offsets.iter().enumerate() {
let buf = &self.x_quant[g];
if buf.len() != Self::group_len(ics_info, g) {
return Err(Error::SpectralDataEncodeInvalid);
}
let mut covered = vec![false; buf.len()];
for sec in §ion_data.sections[g] {
if section_codebook(sec)?.is_none() {
continue;
}
let start = group_offsets[sec.start as usize] as usize;
let end = group_offsets[sec.end as usize] as usize;
covered[start..end].fill(true);
}
if buf.iter().zip(covered.iter()).any(|(&v, &c)| v != 0 && !c) {
return Err(Error::SpectralDataEncodeInvalid);
}
for sec in §ion_data.sections[g] {
let (cb, dim) = match section_codebook(sec)? {
Some(pair) => pair,
None => continue,
};
let start = group_offsets[sec.start as usize] as usize;
let end = group_offsets[sec.end as usize] as usize;
let mut k = start;
while k < end {
if k + dim > end {
return Err(Error::SpectralDataInvalid);
}
write_tuple(writer, cb, dim, &buf[k..k + dim])?;
k += dim;
}
}
}
Ok(())
}
}
fn section_codebook(sec: &Section) -> Result<Option<(u8, usize)>> {
match sec.codebook_kind() {
Codebook::Zero
| Codebook::Noise
| Codebook::IntensityInPhase
| Codebook::IntensityOutOfPhase => Ok(None),
Codebook::Quad { number, .. } => Ok(Some((number, QUAD_LEN))),
Codebook::Pair { number, .. } => Ok(Some((number, PAIR_LEN))),
Codebook::Esc => Ok(Some((11, PAIR_LEN))),
Codebook::Reserved12 => Err(Error::SpectralDataInvalid),
}
}
pub(crate) fn decode_codeword(reader: &mut BitReader<'_>, cb: u8) -> Result<u32> {
match cb {
1 => hcod1_decode(reader),
2 => hcod2_decode(reader),
3 => hcod3_decode(reader),
4 => hcod4_decode(reader),
5 => hcod5_decode(reader),
6 => hcod6_decode(reader),
7 => hcod7_decode(reader),
8 => hcod8_decode(reader),
9 => hcod9_decode(reader),
10 => hcod10_decode(reader),
11 => hcod11_decode(reader),
_ => Err(Error::SpectralDataInvalid),
}
}
fn write_codeword(writer: &mut BitWriter, cb: u8, idx: u32) -> Result<()> {
match cb {
1 => hcod1_write(writer, idx),
2 => hcod2_write(writer, idx),
3 => hcod3_write(writer, idx),
4 => hcod4_write(writer, idx),
5 => hcod5_write(writer, idx),
6 => hcod6_write(writer, idx),
7 => hcod7_write(writer, idx),
8 => hcod8_write(writer, idx),
9 => hcod9_write(writer, idx),
10 => hcod10_write(writer, idx),
11 => hcod11_write(writer, idx),
_ => Err(Error::SpectralDataInvalid),
}
}
pub(crate) fn read_and_apply_signs(
reader: &mut BitReader<'_>,
cb: u8,
dim: usize,
tuple: [i32; 4],
) -> Result<[i32; 4]> {
let row = table_4_95(cb)?;
if !row.is_unsigned() {
return Ok(tuple);
}
let nonzero = tuple.iter().take(dim).filter(|&&v| v != 0).count();
let mut signs = Vec::with_capacity(nonzero);
for _ in 0..nonzero {
signs.push(reader.read_bit().map_err(|_| Error::UnexpectedEnd)?);
}
apply_sign_bits(cb, tuple, &signs)
}
pub(crate) fn read_escape_sequence(reader: &mut BitReader<'_>) -> Result<u32> {
let mut prefix_len = 0u32;
while reader.read_bit().map_err(|_| Error::UnexpectedEnd)? {
prefix_len += 1;
if prefix_len > 24 {
return Err(Error::SpectralCodebookEscOutOfRange);
}
}
let escape_word = reader
.read_u32(prefix_len + 4)
.map_err(|_| Error::UnexpectedEnd)?;
decode_esc_value(prefix_len, escape_word)
}
pub(crate) fn write_tuple(
writer: &mut BitWriter,
cb: u8,
dim: usize,
coeffs: &[i32],
) -> Result<()> {
let mut tuple = [0i32; 4];
for (slot, &v) in tuple.iter_mut().zip(coeffs.iter()) {
if cb == 11 && v.abs() >= ESC_FLAG {
if v.abs() > MAX_QUANT {
return Err(Error::SpectralCodebookEscOutOfRange);
}
*slot = v.signum() * ESC_FLAG;
} else {
*slot = v;
}
}
let row = table_4_95(cb)?;
let index_tuple: Vec<i32> = if row.is_unsigned() {
tuple.iter().take(dim).map(|v| v.abs()).collect()
} else {
tuple[..dim].to_vec()
};
let idx = encode_tuple_to_index(cb, &index_tuple)?;
write_codeword(writer, cb, idx)?;
if row.is_unsigned() {
for neg in derive_sign_bits(cb, &tuple[..dim])? {
writer.write_bit(neg);
}
}
if cb == 11 {
for (&clamped, &v) in tuple.iter().zip(coeffs.iter()).take(dim) {
if clamped.abs() == ESC_FLAG {
let (prefix_len, escape_word) = encode_esc_value(v.unsigned_abs())?;
for _ in 0..prefix_len {
writer.write_bit(true);
}
writer.write_bit(false);
writer.write_u32(escape_word, prefix_len + 4);
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ics_info::WindowSequence;
use crate::section_data::ZERO_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: crate::ics_info::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: crate::ics_info::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],
}
}
fn one_section(num_groups: usize, codebook: u8, max_sfb: u8) -> SectionData {
let sections = (0..num_groups)
.map(|_| {
vec![Section {
codebook,
start: 0,
end: max_sfb,
}]
})
.collect::<Vec<_>>();
let sfb_cb = (0..num_groups)
.map(|_| vec![codebook; max_sfb as usize])
.collect::<Vec<_>>();
SectionData { sections, sfb_cb }
}
fn round_trip(
data: &SpectralData,
ics_info: &IcsInfo,
section_data: &SectionData,
fs_index: u8,
) -> SpectralData {
let mut writer = BitWriter::new();
data.write(&mut writer, ics_info, section_data, fs_index)
.expect("write");
let bytes = writer.finish();
let mut reader = BitReader::new(&bytes);
SpectralData::parse(&mut reader, ics_info, section_data, fs_index).expect("parse")
}
#[test]
fn sect_sfb_offset_long_mirrors_swb_table() {
let info = long_ics_info(10);
let offsets = sect_sfb_offset(&info, 4).expect("offsets");
assert_eq!(offsets.len(), 1);
let swb = long_window_offsets(4).expect("table");
assert_eq!(offsets[0].len(), 11);
for (i, &o) in offsets[0].iter().enumerate() {
assert_eq!(o, u32::from(swb[i]));
}
}
#[test]
fn sect_sfb_offset_short_scales_by_group_length() {
let info = short_ics_info(4, vec![5, 3]);
let offsets = sect_sfb_offset(&info, 4).expect("offsets");
assert_eq!(offsets.len(), 2);
let swb = short_window_offsets(4).expect("table");
for (g, wgl) in [(0usize, 5u32), (1, 3)] {
for i in 0..4 {
let width = u32::from(swb[i + 1] - swb[i]) * wgl;
assert_eq!(offsets[g][i + 1] - offsets[g][i], width);
}
}
}
#[test]
fn sect_sfb_offset_rejects_max_sfb_above_num_swb() {
let mut info = long_ics_info(50);
info.max_sfb = 50; assert!(matches!(
sect_sfb_offset(&info, 4),
Err(Error::SpectralDataInvalid)
));
}
#[test]
fn all_zero_sections_consume_no_bits() {
let info = long_ics_info(10);
let sd = one_section(1, ZERO_HCB, 10);
let mut reader = BitReader::new(&[0xff, 0xff]);
let parsed = SpectralData::parse(&mut reader, &info, &sd, 4).expect("parse");
assert_eq!(reader.bit_position(), 0);
assert_eq!(parsed.x_quant.len(), 1);
assert_eq!(parsed.x_quant[0].len(), 1024);
assert!(parsed.x_quant[0].iter().all(|&v| v == 0));
}
#[test]
fn quad_signed_book_round_trip() {
let info = long_ics_info(2);
let sd = one_section(1, 1, 2);
let mut data = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
data.x_quant[0][..8].copy_from_slice(&[1, -1, 0, 1, -1, 0, 0, 1]);
assert_eq!(round_trip(&data, &info, &sd, 4), data);
}
#[test]
fn unsigned_pair_book_round_trip_with_signs() {
let info = long_ics_info(2);
let sd = one_section(1, 7, 2);
let mut data = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
data.x_quant[0][..8].copy_from_slice(&[7, -7, 0, 3, -1, 2, 0, -5]);
assert_eq!(round_trip(&data, &info, &sd, 4), data);
}
#[test]
fn esc_book_round_trip_with_escapes() {
let info = long_ics_info(2);
let sd = one_section(1, 11, 2);
let mut data = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
data.x_quant[0][..8].copy_from_slice(&[15, -15, 16, -16, 8191, -8191, 0, 100]);
assert_eq!(round_trip(&data, &info, &sd, 4), data);
}
#[test]
fn esc_magnitude_16_uses_escape_sequence_00000() {
let info = long_ics_info(1);
let sd = one_section(1, 11, 1);
let mut data = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
data.x_quant[0][..4].copy_from_slice(&[16, 0, 0, 0]);
let mut writer = BitWriter::new();
data.write(&mut writer, &info, &sd, 4).expect("write");
assert_eq!(writer.bit_position(), 19);
let bytes = writer.finish();
let mut reader = BitReader::new(&bytes);
let parsed = SpectralData::parse(&mut reader, &info, &sd, 4).expect("parse");
assert_eq!(reader.bit_position(), 19);
assert_eq!(parsed, data);
}
#[test]
fn short_grouped_round_trip() {
let info = short_ics_info(4, vec![5, 3]);
let sd = one_section(2, 2, 4);
let offsets = sect_sfb_offset(&info, 4).expect("offsets");
let mut data = SpectralData {
x_quant: vec![vec![0i32; 5 * 128], vec![0i32; 3 * 128]],
};
for (g, group_offsets) in offsets.iter().enumerate() {
let end = group_offsets[4] as usize;
for k in 0..end {
data.x_quant[g][k] = match k % 3 {
0 => 1,
1 => -1,
_ => 0,
};
}
}
assert_eq!(round_trip(&data, &info, &sd, 4), data);
}
#[test]
fn parse_rejects_reserved_codebook_12() {
let info = long_ics_info(2);
let sd = one_section(1, 12, 2);
let mut reader = BitReader::new(&[0x00; 8]);
assert!(matches!(
SpectralData::parse(&mut reader, &info, &sd, 4),
Err(Error::SpectralDataInvalid)
));
}
#[test]
fn parse_rejects_group_count_mismatch() {
let info = long_ics_info(2);
let sd = one_section(2, 1, 2); let mut reader = BitReader::new(&[0x00; 8]);
assert!(matches!(
SpectralData::parse(&mut reader, &info, &sd, 4),
Err(Error::SpectralDataInvalid)
));
}
#[test]
fn parse_rejects_truncated_codeword() {
let info = long_ics_info(2);
let sd = one_section(1, 9, 2);
let mut reader = BitReader::new(&[0xff]);
assert!(matches!(
SpectralData::parse(&mut reader, &info, &sd, 4),
Err(Error::UnexpectedEnd)
));
}
#[test]
fn write_rejects_nonzero_outside_sections() {
let info = long_ics_info(2);
let sd = one_section(1, ZERO_HCB, 2);
let mut data = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
data.x_quant[0][0] = 1;
let mut writer = BitWriter::new();
assert!(matches!(
data.write(&mut writer, &info, &sd, 4),
Err(Error::SpectralDataEncodeInvalid)
));
}
#[test]
fn write_rejects_nonzero_above_max_sfb() {
let info = long_ics_info(2);
let sd = one_section(1, 1, 2);
let mut data = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
data.x_quant[0][1023] = 1;
let mut writer = BitWriter::new();
assert!(matches!(
data.write(&mut writer, &info, &sd, 4),
Err(Error::SpectralDataEncodeInvalid)
));
}
#[test]
fn write_rejects_magnitude_above_lav() {
let info = long_ics_info(2);
let sd = one_section(1, 1, 2); let mut data = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
data.x_quant[0][0] = 2;
let mut writer = BitWriter::new();
assert!(matches!(
data.write(&mut writer, &info, &sd, 4),
Err(Error::SpectralCodebookTupleOutOfRange(1))
));
}
#[test]
fn write_rejects_esc_magnitude_above_max_quant() {
let info = long_ics_info(2);
let sd = one_section(1, 11, 2);
let mut data = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
data.x_quant[0][0] = MAX_QUANT + 1;
let mut writer = BitWriter::new();
assert!(matches!(
data.write(&mut writer, &info, &sd, 4),
Err(Error::SpectralCodebookEscOutOfRange)
));
}
#[test]
fn write_rejects_wrong_group_buffer_length() {
let info = long_ics_info(2);
let sd = one_section(1, 1, 2);
let data = SpectralData {
x_quant: vec![vec![0i32; 512]],
};
let mut writer = BitWriter::new();
assert!(matches!(
data.write(&mut writer, &info, &sd, 4),
Err(Error::SpectralDataEncodeInvalid)
));
}
#[test]
fn escape_prefix_run_past_24_rejected() {
let mut reader = BitReader::new(&[0xff, 0xff, 0xff, 0xff]);
assert!(matches!(
read_escape_sequence(&mut reader),
Err(Error::SpectralCodebookEscOutOfRange)
));
}
#[test]
fn escape_sequence_examples_from_spec() {
for (bits, len, expect) in [
(0b00000u32, 5u32, 16u32),
(0b01111, 5, 31),
(0b1000000, 7, 32),
(0b1011111, 7, 63),
] {
let mut writer = BitWriter::new();
writer.write_u32(bits, len);
let bytes = writer.finish();
let mut reader = BitReader::new(&bytes);
assert_eq!(read_escape_sequence(&mut reader).expect("esc"), expect);
assert_eq!(reader.bit_position(), u64::from(len));
}
}
#[test]
fn multi_section_mixed_codebooks_round_trip() {
let info = long_ics_info(6);
let sections = vec![vec![
Section {
codebook: 1,
start: 0,
end: 2,
},
Section {
codebook: ZERO_HCB,
start: 2,
end: 4,
},
Section {
codebook: 11,
start: 4,
end: 6,
},
]];
let sfb_cb = vec![vec![1, 1, ZERO_HCB, ZERO_HCB, 11, 11]];
let sd = SectionData { sections, sfb_cb };
let mut data = SpectralData {
x_quant: vec![vec![0i32; 1024]],
};
data.x_quant[0][..8].copy_from_slice(&[1, 0, -1, 0, 0, 1, 1, -1]);
data.x_quant[0][16..24].copy_from_slice(&[20, -3, 0, 0, 1000, -16, 15, 0]);
assert_eq!(round_trip(&data, &info, &sd, 4), data);
}
}