use oxideav_core::bits::{BitReader, BitWriter};
use crate::ics_body::IcsBody;
use crate::ics_info::IcsInfo;
use crate::scale_factor_data::{hcod_sf_decode, hcod_sf_encode};
use crate::section_data::ZERO_HCB;
use crate::spectral_data::SpectralData;
use crate::{Error, Result};
pub const IND_SW_CCE_FLAG_BITS: u32 = 1;
pub const NUM_COUPLED_ELEMENTS_BITS: u32 = 3;
pub const CC_TARGET_TAG_SELECT_BITS: u32 = 4;
pub const GAIN_ELEMENT_SCALE_BITS: u32 = 2;
pub const CC_SCALE_TABLE: [f64; 4] = [
1.090_507_732_665_257_7, 1.189_207_115_002_721, std::f64::consts::SQRT_2, 2.0, ];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CoupledTarget {
pub is_cpe: bool,
pub tag_select: u8,
pub cc_l: bool,
pub cc_r: bool,
}
impl CoupledTarget {
fn gain_list_increment(&self) -> u32 {
if self.is_cpe && self.cc_l && self.cc_r {
2
} else {
1
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CouplingHeader {
pub ind_sw_cce_flag: bool,
pub num_coupled_elements: u8,
pub targets: Vec<CoupledTarget>,
pub cc_domain: bool,
pub gain_element_sign: bool,
pub gain_element_scale: u8,
pub num_gain_element_lists: u32,
}
impl CouplingHeader {
pub fn parse(reader: &mut BitReader<'_>) -> Result<Self> {
let ind_sw_cce_flag = read_bit(reader)?;
let num_coupled_elements = read_u8(reader, NUM_COUPLED_ELEMENTS_BITS)?;
let mut num_gain_element_lists: u32 = 0;
let mut targets = Vec::with_capacity(usize::from(num_coupled_elements) + 1);
for _c in 0..(u32::from(num_coupled_elements) + 1) {
num_gain_element_lists += 1;
let is_cpe = read_bit(reader)?;
let tag_select = read_u8(reader, CC_TARGET_TAG_SELECT_BITS)?;
let (cc_l, cc_r) = if is_cpe {
let cc_l = read_bit(reader)?;
let cc_r = read_bit(reader)?;
if cc_l && cc_r {
num_gain_element_lists += 1;
}
(cc_l, cc_r)
} else {
(false, false)
};
targets.push(CoupledTarget {
is_cpe,
tag_select,
cc_l,
cc_r,
});
}
let cc_domain = read_bit(reader)?;
let gain_element_sign = read_bit(reader)?;
let gain_element_scale = read_u8(reader, GAIN_ELEMENT_SCALE_BITS)?;
Ok(CouplingHeader {
ind_sw_cce_flag,
num_coupled_elements,
targets,
cc_domain,
gain_element_sign,
gain_element_scale,
num_gain_element_lists,
})
}
pub fn write(&self, writer: &mut BitWriter) -> Result<()> {
if self.targets.len() != usize::from(self.num_coupled_elements) + 1 {
return Err(Error::CceInvalid);
}
if self.gain_element_scale > 3 {
return Err(Error::CceInvalid);
}
let mut derived_lists: u32 = 0;
for t in &self.targets {
if !t.is_cpe && (t.cc_l || t.cc_r) {
return Err(Error::CceInvalid);
}
derived_lists += t.gain_list_increment();
}
if derived_lists != self.num_gain_element_lists {
return Err(Error::CceInvalid);
}
writer.write_bit(self.ind_sw_cce_flag);
writer.write_u32(
u32::from(self.num_coupled_elements),
NUM_COUPLED_ELEMENTS_BITS,
);
for t in &self.targets {
writer.write_bit(t.is_cpe);
writer.write_u32(u32::from(t.tag_select), CC_TARGET_TAG_SELECT_BITS);
if t.is_cpe {
writer.write_bit(t.cc_l);
writer.write_bit(t.cc_r);
}
}
writer.write_bit(self.cc_domain);
writer.write_bit(self.gain_element_sign);
writer.write_u32(u32::from(self.gain_element_scale), GAIN_ELEMENT_SCALE_BITS);
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct DpcmGain {
pub negative: bool,
pub gain: i32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GainList {
Common(i32),
Dpcm(Vec<Vec<DpcmGain>>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct CouplingGains {
pub cc_scale: f64,
pub gain_element_sign: bool,
pub lists: Vec<GainList>,
}
impl CouplingGains {
pub fn parse(
reader: &mut BitReader<'_>,
header: &CouplingHeader,
num_window_groups: usize,
max_sfb: usize,
sfb_cb: &[Vec<u8>],
) -> Result<Self> {
let cc_scale = CC_SCALE_TABLE[usize::from(header.gain_element_scale & 0x3)];
let mut lists = Vec::new();
for _c in 1..header.num_gain_element_lists {
let cge = if header.ind_sw_cce_flag {
true
} else {
read_bit(reader)?
};
if cge {
let common = i32::from(hcod_sf_decode(reader)?);
lists.push(GainList::Common(common));
} else {
if header.ind_sw_cce_flag {
return Err(Error::CceInvalid);
}
let mut acc: i32 = 0;
let mut grid = vec![vec![DpcmGain::default(); max_sfb]; num_window_groups];
for (g, row) in grid.iter_mut().enumerate() {
let cb_row = sfb_cb.get(g).ok_or(Error::CceInvalid)?;
for (sfb, cell) in row.iter_mut().enumerate() {
let cb = *cb_row.get(sfb).ok_or(Error::CceInvalid)?;
if cb != ZERO_HCB {
let dpcm = i32::from(hcod_sf_decode(reader)?);
if header.gain_element_sign {
acc += dpcm >> 1;
*cell = DpcmGain {
negative: (dpcm & 1) != 0,
gain: acc,
};
} else {
acc += dpcm;
*cell = DpcmGain {
negative: false,
gain: acc,
};
}
} else {
*cell = DpcmGain {
negative: false,
gain: acc,
};
}
}
}
lists.push(GainList::Dpcm(grid));
}
}
Ok(CouplingGains {
cc_scale,
gain_element_sign: header.gain_element_sign,
lists,
})
}
pub fn write(
&self,
writer: &mut BitWriter,
header: &CouplingHeader,
sfb_cb: &[Vec<u8>],
) -> Result<()> {
if self.lists.len() + 1 != header.num_gain_element_lists as usize {
return Err(Error::CceInvalid);
}
for list in &self.lists {
match list {
GainList::Common(common) => {
if !header.ind_sw_cce_flag {
writer.write_bit(true);
}
let dpcm = i8::try_from(*common).map_err(|_| Error::CceInvalid)?;
let (len, cw) = hcod_sf_encode(dpcm)?;
writer.write_u32(cw, u32::from(len));
}
GainList::Dpcm(grid) => {
if header.ind_sw_cce_flag {
return Err(Error::CceInvalid);
}
writer.write_bit(false);
let mut prev: i32 = 0;
for (g, row) in grid.iter().enumerate() {
let cb_row = sfb_cb.get(g).ok_or(Error::CceInvalid)?;
for (sfb, cell) in row.iter().enumerate() {
let cb = *cb_row.get(sfb).ok_or(Error::CceInvalid)?;
if cb != ZERO_HCB {
let delta = cell.gain - prev;
let dpcm = if header.gain_element_sign {
(delta << 1) | i32::from(cell.negative)
} else {
if cell.negative {
return Err(Error::CceInvalid);
}
delta
};
let dpcm = i8::try_from(dpcm).map_err(|_| Error::CceInvalid)?;
let (len, cw) = hcod_sf_encode(dpcm)?;
writer.write_u32(cw, u32::from(len));
prev = cell.gain;
}
}
}
}
}
}
Ok(())
}
pub fn cc_gain(&self, list_index: usize, g: usize, sfb: usize) -> Result<f64> {
if list_index == 0 {
return Ok(1.0);
}
let list = self.lists.get(list_index - 1).ok_or(Error::CceInvalid)?;
let (cc_sign, gain) = match list {
GainList::Common(common) => (1.0, *common),
GainList::Dpcm(grid) => {
let cell = grid
.get(g)
.and_then(|row| row.get(sfb))
.ok_or(Error::CceInvalid)?;
(if cell.negative { -1.0 } else { 1.0 }, cell.gain)
}
};
Ok(cc_sign * self.cc_scale.powi(-gain))
}
#[allow(clippy::too_many_arguments)]
pub fn couple_channel(
&self,
source: &[f64],
dest: &mut [f64],
list_index: usize,
sfb_cb: &[Vec<u8>],
window_group_length: &[u8],
max_sfb: usize,
offsets: &[u16],
) -> Result<()> {
if source.len() != dest.len() {
return Err(Error::CceInvalid);
}
if offsets.is_empty() {
return Err(Error::CceInvalid);
}
let window_len = usize::from(*offsets.last().expect("non-empty checked above"));
if window_len == 0 || source.len() % window_len != 0 {
return Err(Error::CceInvalid);
}
let num_swb = offsets.len() - 1;
if max_sfb > num_swb {
return Err(Error::CceInvalid);
}
if sfb_cb.len() != window_group_length.len() {
return Err(Error::CceInvalid);
}
let mut window_base = 0usize;
for (g, &wgl) in window_group_length.iter().enumerate() {
let cb_row = sfb_cb.get(g).ok_or(Error::CceInvalid)?;
if cb_row.len() < max_sfb {
return Err(Error::CceInvalid);
}
let wgl = usize::from(wgl);
for sfb in 0..max_sfb {
if cb_row[sfb] == ZERO_HCB {
continue;
}
let start = usize::from(offsets[sfb]);
let end = usize::from(offsets[sfb + 1]);
let cc_gain = self.cc_gain(list_index, g, sfb)?;
for b in 0..wgl {
let base = (window_base + b)
.checked_mul(window_len)
.ok_or(Error::CceInvalid)?;
let dst_end = base + end;
if dst_end > dest.len() {
return Err(Error::CceInvalid);
}
for i in start..end {
dest[base + i] += cc_gain * source[base + i];
}
}
}
window_base += wgl;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CouplingChannelElement {
pub element_instance_tag: u8,
pub header: CouplingHeader,
pub body: IcsBody,
pub ics_info: IcsInfo,
pub spectral: SpectralData,
pub gains: CouplingGains,
}
impl CouplingChannelElement {
pub fn parse(reader: &mut BitReader<'_>, aot: u8, fs_index: u8) -> Result<Self> {
let element_instance_tag = read_u8(reader, 4)?;
Self::parse_after_tag(reader, element_instance_tag, aot, fs_index)
}
pub fn parse_after_tag(
reader: &mut BitReader<'_>,
element_instance_tag: u8,
aot: u8,
fs_index: u8,
) -> Result<Self> {
Self::parse_after_tag_family(
reader,
crate::swb_offset::FrameFamily::Lc1024,
element_instance_tag,
aot,
fs_index,
)
}
pub fn parse_after_tag_family(
reader: &mut BitReader<'_>,
family: crate::swb_offset::FrameFamily,
element_instance_tag: u8,
aot: u8,
fs_index: u8,
) -> Result<Self> {
let header = CouplingHeader::parse(reader)?;
let body = IcsBody::parse_family(reader, family, aot, fs_index, false)?;
let ics_info = body.ics_info.clone().ok_or(Error::CceInvalid)?;
let spectral = SpectralData::parse(reader, &ics_info, &body.section_data, fs_index)?;
let gains = CouplingGains::parse(
reader,
&header,
usize::from(ics_info.num_window_groups),
usize::from(ics_info.max_sfb),
&body.section_data.sfb_cb,
)?;
Ok(CouplingChannelElement {
element_instance_tag,
header,
body,
ics_info,
spectral,
gains,
})
}
}
fn read_bit(reader: &mut BitReader<'_>) -> Result<bool> {
reader.read_bit().map_err(|_| Error::UnexpectedEnd)
}
fn read_u8(reader: &mut BitReader<'_>, bits: u32) -> Result<u8> {
Ok(reader.read_u32(bits).map_err(|_| Error::UnexpectedEnd)? as u8)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cc_scale_table_matches_spec_resolutions() {
for (scale, &v) in CC_SCALE_TABLE.iter().enumerate() {
let expected = 2f64.powf((1u32 << scale) as f64 / 8.0);
assert!(
(v - expected).abs() < 1e-12,
"cc_scale[{scale}] = {v} != {expected}"
);
}
}
#[test]
fn single_sce_target_has_one_gain_list() {
let mut writer = BitWriter::new();
writer.write_bit(false); writer.write_u32(0, 3); writer.write_bit(false); writer.write_u32(0, 4); writer.write_bit(false); writer.write_bit(false); writer.write_u32(0, 2); let bytes = writer.into_bytes();
let mut reader = BitReader::new(&bytes);
let h = CouplingHeader::parse(&mut reader).unwrap();
assert_eq!(h.num_gain_element_lists, 1);
assert_eq!(h.targets.len(), 1);
assert!(!h.targets[0].is_cpe);
}
#[test]
fn cpe_target_with_both_channels_adds_a_list() {
let mut writer = BitWriter::new();
writer.write_bit(false); writer.write_u32(0, 3); writer.write_bit(true); writer.write_u32(3, 4); writer.write_bit(true); writer.write_bit(true); writer.write_bit(false); writer.write_bit(false); writer.write_u32(1, 2); let bytes = writer.into_bytes();
let mut reader = BitReader::new(&bytes);
let h = CouplingHeader::parse(&mut reader).unwrap();
assert_eq!(h.num_gain_element_lists, 2);
assert!(h.targets[0].is_cpe);
assert!(h.targets[0].cc_l && h.targets[0].cc_r);
assert_eq!(h.targets[0].tag_select, 3);
}
#[test]
fn header_round_trips() {
let h = CouplingHeader {
ind_sw_cce_flag: true,
num_coupled_elements: 1,
targets: vec![
CoupledTarget {
is_cpe: false,
tag_select: 2,
cc_l: false,
cc_r: false,
},
CoupledTarget {
is_cpe: true,
tag_select: 5,
cc_l: true,
cc_r: false,
},
],
cc_domain: true,
gain_element_sign: true,
gain_element_scale: 2,
num_gain_element_lists: 2,
};
let mut writer = BitWriter::new();
h.write(&mut writer).unwrap();
let bytes = writer.into_bytes();
let mut reader = BitReader::new(&bytes);
let parsed = CouplingHeader::parse(&mut reader).unwrap();
assert_eq!(parsed, h);
}
#[test]
fn write_rejects_sce_target_with_cc_flag() {
let h = CouplingHeader {
ind_sw_cce_flag: false,
num_coupled_elements: 0,
targets: vec![CoupledTarget {
is_cpe: false,
tag_select: 0,
cc_l: true,
cc_r: false,
}],
cc_domain: false,
gain_element_sign: false,
gain_element_scale: 0,
num_gain_element_lists: 1,
};
let mut writer = BitWriter::new();
assert_eq!(h.write(&mut writer), Err(Error::CceInvalid));
}
#[test]
fn cc_gain_list_zero_is_unity() {
let gains = CouplingGains {
cc_scale: CC_SCALE_TABLE[3],
gain_element_sign: false,
lists: vec![],
};
assert_eq!(gains.cc_gain(0, 0, 0).unwrap(), 1.0);
}
#[test]
fn cc_gain_common_no_sign() {
let gains = CouplingGains {
cc_scale: 2.0, gain_element_sign: false,
lists: vec![GainList::Common(3)],
};
assert!((gains.cc_gain(1, 0, 0).unwrap() - 0.125).abs() < 1e-12);
}
#[test]
fn cc_gain_common_never_sign_split() {
let gains = CouplingGains {
cc_scale: 2.0,
gain_element_sign: true,
lists: vec![GainList::Common(3)],
};
assert!((gains.cc_gain(1, 0, 0).unwrap() - 0.125).abs() < 1e-12);
}
#[test]
fn cc_gain_dpcm_delta_split() {
let sfb_cb = vec![vec![2u8, 2u8]];
let header = CouplingHeader {
ind_sw_cce_flag: false,
num_coupled_elements: 1,
targets: vec![
CoupledTarget {
is_cpe: false,
tag_select: 0,
cc_l: false,
cc_r: false,
},
CoupledTarget {
is_cpe: false,
tag_select: 1,
cc_l: false,
cc_r: false,
},
],
cc_domain: false,
gain_element_sign: true,
gain_element_scale: 3, num_gain_element_lists: 2,
};
let mut writer = BitWriter::new();
writer.write_bit(false); for _ in 0..2 {
let (len, cw) = hcod_sf_encode(3).unwrap();
writer.write_u32(cw, u32::from(len));
}
let bytes = writer.into_bytes();
let mut reader = BitReader::new(&bytes);
let gains = CouplingGains::parse(&mut reader, &header, 1, 2, &sfb_cb).unwrap();
assert_eq!(
gains.lists,
vec![GainList::Dpcm(vec![vec![
DpcmGain {
negative: true,
gain: 1
},
DpcmGain {
negative: true,
gain: 2
},
]])]
);
assert!((gains.cc_gain(1, 0, 0).unwrap() + 0.5).abs() < 1e-12);
assert!((gains.cc_gain(1, 0, 1).unwrap() + 0.25).abs() < 1e-12);
}
#[test]
fn dpcm_sign_split_round_trips() {
let sfb_cb = vec![vec![2u8, ZERO_HCB, 4u8, 4u8]];
let header = CouplingHeader {
ind_sw_cce_flag: false,
num_coupled_elements: 1,
targets: vec![
CoupledTarget {
is_cpe: false,
tag_select: 0,
cc_l: false,
cc_r: false,
},
CoupledTarget {
is_cpe: false,
tag_select: 1,
cc_l: false,
cc_r: false,
},
],
cc_domain: false,
gain_element_sign: true,
gain_element_scale: 1,
num_gain_element_lists: 2,
};
let grid = vec![vec![
DpcmGain {
negative: true,
gain: -2,
},
DpcmGain {
negative: false,
gain: -2,
},
DpcmGain {
negative: false,
gain: 1,
},
DpcmGain {
negative: true,
gain: 1,
},
]];
let gains = CouplingGains {
cc_scale: CC_SCALE_TABLE[1],
gain_element_sign: true,
lists: vec![GainList::Dpcm(grid.clone())],
};
let mut writer = BitWriter::new();
gains.write(&mut writer, &header, &sfb_cb).unwrap();
let bytes = writer.into_bytes();
let mut reader = BitReader::new(&bytes);
let parsed = CouplingGains::parse(&mut reader, &header, 1, 4, &sfb_cb).unwrap();
assert_eq!(parsed.lists, vec![GainList::Dpcm(grid)]);
}
#[test]
fn write_rejects_negative_band_without_sign_bit() {
let sfb_cb = vec![vec![2u8]];
let header = CouplingHeader {
ind_sw_cce_flag: false,
num_coupled_elements: 1,
targets: vec![
CoupledTarget {
is_cpe: false,
tag_select: 0,
cc_l: false,
cc_r: false,
},
CoupledTarget {
is_cpe: false,
tag_select: 1,
cc_l: false,
cc_r: false,
},
],
cc_domain: false,
gain_element_sign: false,
gain_element_scale: 0,
num_gain_element_lists: 2,
};
let gains = CouplingGains {
cc_scale: CC_SCALE_TABLE[0],
gain_element_sign: false,
lists: vec![GainList::Dpcm(vec![vec![DpcmGain {
negative: true,
gain: 0,
}]])],
};
let mut writer = BitWriter::new();
assert_eq!(
gains.write(&mut writer, &header, &sfb_cb),
Err(Error::CceInvalid)
);
}
#[test]
fn dpcm_gain_list_round_trips() {
let sfb_cb = vec![vec![2u8, ZERO_HCB, 4u8]];
let header = CouplingHeader {
ind_sw_cce_flag: false,
num_coupled_elements: 1,
targets: vec![
CoupledTarget {
is_cpe: false,
tag_select: 0,
cc_l: false,
cc_r: false,
},
CoupledTarget {
is_cpe: false,
tag_select: 1,
cc_l: false,
cc_r: false,
},
],
cc_domain: false,
gain_element_sign: false,
gain_element_scale: 0,
num_gain_element_lists: 2,
};
let grid = vec![vec![
DpcmGain {
negative: false,
gain: 2,
},
DpcmGain {
negative: false,
gain: 2,
},
DpcmGain {
negative: false,
gain: 5,
},
]];
let gains = CouplingGains {
cc_scale: CC_SCALE_TABLE[0],
gain_element_sign: false,
lists: vec![GainList::Dpcm(grid.clone())],
};
let mut writer = BitWriter::new();
gains.write(&mut writer, &header, &sfb_cb).unwrap();
let bytes = writer.into_bytes();
let mut reader = BitReader::new(&bytes);
let parsed = CouplingGains::parse(&mut reader, &header, 1, 3, &sfb_cb).unwrap();
assert_eq!(parsed.lists.len(), 1);
match &parsed.lists[0] {
GainList::Dpcm(g) => assert_eq!(g, &grid),
other => panic!("expected Dpcm, got {other:?}"),
}
}
#[test]
fn couple_channel_list_zero_adds_natural_scaling() {
let offsets = [0u16, 4, 8];
let sfb_cb = vec![vec![2u8, ZERO_HCB]];
let wgl = [1u8];
let gains = CouplingGains {
cc_scale: CC_SCALE_TABLE[3],
gain_element_sign: false,
lists: vec![],
};
let source = vec![1.0f64, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let mut dest = vec![10.0f64; 8];
gains
.couple_channel(&source, &mut dest, 0, &sfb_cb, &wgl, 2, &offsets)
.unwrap();
assert_eq!(&dest[0..4], &[11.0, 12.0, 13.0, 14.0]);
assert_eq!(&dest[4..8], &[10.0, 10.0, 10.0, 10.0]);
}
#[test]
fn couple_channel_common_gain_scales() {
let offsets = [0u16, 4];
let sfb_cb = vec![vec![2u8]];
let wgl = [1u8];
let gains = CouplingGains {
cc_scale: 2.0,
gain_element_sign: false,
lists: vec![GainList::Common(-1)],
};
let source = vec![1.0f64, 2.0, 3.0, 4.0];
let mut dest = vec![0.0f64; 4];
gains
.couple_channel(&source, &mut dest, 1, &sfb_cb, &wgl, 1, &offsets)
.unwrap();
assert_eq!(dest, vec![2.0, 4.0, 6.0, 8.0]);
}
#[test]
fn couple_channel_multi_window_group() {
let offsets = [0u16, 4];
let sfb_cb = vec![vec![2u8]];
let wgl = [2u8];
let gains = CouplingGains {
cc_scale: 2.0,
gain_element_sign: false,
lists: vec![GainList::Common(0)], };
let source = vec![1.0f64, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0];
let mut dest = vec![0.0f64; 8];
gains
.couple_channel(&source, &mut dest, 1, &sfb_cb, &wgl, 1, &offsets)
.unwrap();
assert_eq!(dest, source);
}
#[test]
fn couple_channel_dpcm_per_band_gains() {
let offsets = [0u16, 2, 4];
let sfb_cb = vec![vec![2u8, 2u8]];
let wgl = [1u8];
let gains = CouplingGains {
cc_scale: 2.0,
gain_element_sign: false,
lists: vec![GainList::Dpcm(vec![vec![
DpcmGain {
negative: false,
gain: 0,
},
DpcmGain {
negative: false,
gain: -1,
},
]])],
};
let source = vec![3.0f64, 3.0, 3.0, 3.0];
let mut dest = vec![0.0f64; 4];
gains
.couple_channel(&source, &mut dest, 1, &sfb_cb, &wgl, 2, &offsets)
.unwrap();
assert_eq!(dest, vec![3.0, 3.0, 6.0, 6.0]);
}
#[test]
fn couple_channel_rejects_length_mismatch() {
let offsets = [0u16, 4];
let sfb_cb = vec![vec![2u8]];
let gains = CouplingGains {
cc_scale: 2.0,
gain_element_sign: false,
lists: vec![],
};
let source = vec![0.0f64; 4];
let mut dest = vec![0.0f64; 8];
assert_eq!(
gains.couple_channel(&source, &mut dest, 0, &sfb_cb, &[1u8], 1, &offsets),
Err(Error::CceInvalid)
);
}
#[test]
fn ind_sw_cce_uses_common_gain_only() {
let header = CouplingHeader {
ind_sw_cce_flag: true,
num_coupled_elements: 1,
targets: vec![
CoupledTarget {
is_cpe: false,
tag_select: 0,
cc_l: false,
cc_r: false,
},
CoupledTarget {
is_cpe: false,
tag_select: 1,
cc_l: false,
cc_r: false,
},
],
cc_domain: false,
gain_element_sign: false,
gain_element_scale: 0,
num_gain_element_lists: 2,
};
let gains = CouplingGains {
cc_scale: CC_SCALE_TABLE[0],
gain_element_sign: false,
lists: vec![GainList::Common(1)],
};
let mut writer = BitWriter::new();
gains.write(&mut writer, &header, &[]).unwrap();
let bytes = writer.into_bytes();
let mut reader = BitReader::new(&bytes);
let parsed = CouplingGains::parse(&mut reader, &header, 1, 1, &[]).unwrap();
assert_eq!(parsed.lists, vec![GainList::Common(1)]);
}
}