use super::bits::BitReader;
use super::channel_map::ElemKind;
use super::det_math;
use super::error::{Error, Result};
use super::ics::IcsInfo;
use super::ics_body::parse_ics_len;
use super::section::{SectionData, ZERO_HCB};
use super::sf::{self, ScaleFactors};
use super::tns::TnsData;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CceTarget {
pub is_cpe: bool,
pub tag: u8,
pub cc_l: bool,
pub cc_r: bool,
}
#[derive(Debug, Clone)]
pub struct CcePayload {
pub tag: u8,
pub independent: bool,
#[allow(dead_code)]
pub n_coupled: usize,
#[allow(dead_code)]
pub n_gain_lists: usize,
pub domain: u8,
pub targets: Vec<CceTarget>,
pub ics: IcsInfo,
pub sections: SectionData,
pub sf: ScaleFactors,
pub spec: Vec<f32>,
pub tns: Option<TnsData>,
pub gains: Vec<Vec<f32>>,
}
#[derive(Debug, Clone)]
pub(crate) struct PendingChan {
pub kind: ElemKind,
pub tag: u8,
pub part: u8,
pub ics: IcsInfo,
pub tns: Option<TnsData>,
pub spec: Vec<f32>,
pub gain: Option<std::boxed::Box<super::ssr::GainControl>>,
}
pub fn parse_cce(
br: &mut BitReader<'_>,
fs_index: u8,
aot: u8,
frame_len: u16,
) -> Result<CcePayload> {
let tag = br.read(4)? as u8;
let independent = br.read_bit()?;
let n_coupled = br.read(3)? as usize + 1;
let mut n_gain_lists = 0usize;
let mut targets = Vec::with_capacity(n_coupled);
for _ in 0..n_coupled {
n_gain_lists += 1;
let is_cpe = br.read_bit()?;
let tgt_tag = br.read(4)? as u8;
let (cc_l, cc_r) = if is_cpe {
let l = br.read_bit()?;
let r = br.read_bit()?;
if l && r {
n_gain_lists += 1;
}
(l, r)
} else {
(true, false)
};
targets.push(CceTarget {
is_cpe,
tag: tgt_tag,
cc_l,
cc_r,
});
}
let domain = u8::from(br.read_bit()?);
let sign = br.read_bit()?;
let scale_idx = br.read(2)? as u8;
let body = parse_ics_len(br, fs_index, aot, None, frame_len)?;
let groups = body.ics.num_window_groups as usize;
let max_sfb = body.ics.max_sfb as usize;
let n_band = groups.saturating_mul(max_sfb);
let scale_e = [0.125f32, 0.25, 0.5, 1.0][scale_idx as usize];
let mut gains = vec![vec![0.0f32; n_band]; n_gain_lists];
for (c, list) in gains.iter_mut().enumerate() {
let mut cge = true;
let mut gain_acc = 0i32;
let mut cache = 1.0f32;
if c > 0 {
cge = independent || br.read_bit()?;
if cge {
gain_acc = i32::from(sf::decode_dpcm(br)?);
cache = det_math::exp2(-(gain_acc as f32) * scale_e);
}
}
if independent {
if let Some(slot) = list.first_mut() {
*slot = cache;
}
continue;
}
let mut idx = 0usize;
for g in 0..groups {
let row = body.sections.sfb_cb.get(g);
for sfb in 0..max_sfb {
let cb = row.and_then(|r| r.get(sfb)).copied().unwrap_or(ZERO_HCB);
if cb != ZERO_HCB {
if !cge {
let t = i32::from(sf::decode_dpcm(br)?);
if t != 0 {
gain_acc += t;
let mut tt = gain_acc;
let mut s = 1.0f32;
if sign {
s = 1.0 - 2.0 * (tt & 1) as f32;
tt >>= 1;
}
cache = det_math::exp2(-(tt as f32) * scale_e) * s;
}
}
list[idx] = cache;
}
idx += 1;
}
}
}
Ok(CcePayload {
tag,
independent,
n_coupled,
n_gain_lists,
domain,
targets,
ics: body.ics,
sections: body.sections,
sf: body.sf,
spec: body.spec,
tns: body.tns,
gains,
})
}
pub fn apply_dependent(
target: &mut [f32],
cce: &CcePayload,
list: usize,
fs_index: u8,
) -> Result<()> {
let Some(gains) = cce.gains.get(list) else {
return Err(Error::Format("CCE gain list missing"));
};
if target.len() != cce.spec.len() {
return Err(Error::Format("CCE window mismatch"));
}
let ics = &cce.ics;
let offsets = ics.swb_offsets(fs_index)?;
let win_len = ics.window_len();
let mut wbase = 0usize;
let mut idx = 0usize;
for g in 0..ics.num_window_groups as usize {
let glen = ics.window_group_length[g] as usize;
let row = cce.sections.sfb_cb.get(g);
for sfb in 0..ics.max_sfb as usize {
let cb = row.and_then(|r| r.get(sfb)).copied().unwrap_or(ZERO_HCB);
if cb != ZERO_HCB {
let gain = *gains.get(idx).unwrap_or(&0.0);
let start = *offsets.get(sfb).ok_or(Error::SpectrumInvalid)? as usize;
let end = *offsets.get(sfb + 1).ok_or(Error::SpectrumInvalid)? as usize;
for b in 0..glen {
let base = (wbase + b) * win_len;
for k in start..end {
let i = base + k;
if let (Some(d), Some(&s)) = (target.get_mut(i), cce.spec.get(i)) {
*d += gain * s;
}
}
}
}
idx += 1;
}
wbase += glen;
}
Ok(())
}
fn add_pcm(dst: &mut [f32], src: &[f32], gain: f32) {
for (d, s) in dst.iter_mut().zip(src.iter()) {
*d += gain * *s;
}
}
fn plane_index(ids: &[(ElemKind, u8, u8)], kind: ElemKind, tag: u8, part: u8) -> Result<usize> {
ids.iter()
.position(|id| id.0 == kind && id.1 == tag && id.2 == part)
.ok_or(Error::Format("CCE missing target"))
}
pub fn apply_independent_pcm(
planes: &mut [Vec<f32>],
ids: &[(ElemKind, u8, u8)],
cce_pcm: &[f32],
cce: &CcePayload,
) -> Result<()> {
let mut list = 0usize;
for t in &cce.targets {
let gain = |i: usize| {
cce.gains
.get(i)
.and_then(|g| g.first())
.copied()
.unwrap_or(1.0)
};
if t.is_cpe {
let ch_select = (u8::from(t.cc_l) << 1) | u8::from(t.cc_r);
if ch_select != 1 {
let i = plane_index(ids, ElemKind::Cpe, t.tag, 0)?;
add_pcm(&mut planes[i], cce_pcm, gain(list));
if ch_select != 0 {
list += 1;
}
}
if ch_select != 2 {
let i = plane_index(ids, ElemKind::Cpe, t.tag, 1)?;
add_pcm(&mut planes[i], cce_pcm, gain(list));
list += 1;
}
} else {
let i = plane_index(ids, ElemKind::Sce, t.tag, 0)?;
add_pcm(&mut planes[i], cce_pcm, gain(list));
list += 1;
}
}
Ok(())
}
fn find_chan(
pending: &mut [PendingChan],
kind: ElemKind,
tag: u8,
part: u8,
) -> Result<&mut PendingChan> {
pending
.iter_mut()
.find(|p| p.kind == kind && p.tag == tag && p.part == part)
.ok_or(Error::Format("CCE missing target"))
}
pub fn apply_cce_to_pending(
pending: &mut [PendingChan],
cce: &CcePayload,
fs_index: u8,
) -> Result<()> {
let mut list = 0usize;
for t in &cce.targets {
if t.is_cpe {
let ch_select = (u8::from(t.cc_l) << 1) | u8::from(t.cc_r);
if ch_select != 1 {
apply_dependent(
&mut find_chan(pending, ElemKind::Cpe, t.tag, 0)?.spec,
cce,
list,
fs_index,
)?;
if ch_select != 0 {
list += 1;
}
}
if ch_select != 2 {
apply_dependent(
&mut find_chan(pending, ElemKind::Cpe, t.tag, 1)?.spec,
cce,
list,
fs_index,
)?;
list += 1;
}
} else {
apply_dependent(
&mut find_chan(pending, ElemKind::Sce, t.tag, 0)?.spec,
cce,
list,
fs_index,
)?;
list += 1;
}
}
Ok(())
}