use oxideav_core::bits::{BitReader, BitWriter};
use crate::ics_info::WindowSequence;
use crate::{Error, Result};
pub const MAX_BAND_BITS: u32 = 2;
pub const ADJUST_NUM_BITS: u32 = 3;
pub const ALEVCODE_BITS: u32 = 4;
pub const MAX_BAND_CAP: u8 = 0x03;
pub const MAX_ADJUST_NUM: u8 = 0x07;
pub const MAX_ALEVCODE: u8 = 0x0f;
pub fn num_windows(window_sequence: WindowSequence) -> usize {
match window_sequence {
WindowSequence::OnlyLong => 1,
WindowSequence::LongStart => 2,
WindowSequence::EightShort => 8,
WindowSequence::LongStop => 2,
}
}
pub fn aloccode_bits(window_sequence: WindowSequence, wd: usize) -> u32 {
match window_sequence {
WindowSequence::OnlyLong => {
if wd == 0 {
5
} else {
0
}
}
WindowSequence::LongStart => match wd {
0 => 4,
1 => 2,
_ => 0,
},
WindowSequence::EightShort => {
if wd < 8 {
2
} else {
0
}
}
WindowSequence::LongStop => match wd {
0 => 4,
1 => 5,
_ => 0,
},
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GainAdjust {
pub alevcode: u8,
pub aloccode: u8,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct GainWindow {
pub adjustments: Vec<GainAdjust>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct GainBand {
pub windows: Vec<GainWindow>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct GainControlData {
pub max_band: u8,
pub bands: Vec<GainBand>,
}
impl GainControlData {
pub fn parse(reader: &mut BitReader<'_>, window_sequence: WindowSequence) -> Result<Self> {
let max_band = read_u8(reader, MAX_BAND_BITS)?;
let n_win = num_windows(window_sequence);
let mut bands = Vec::with_capacity(max_band as usize);
for _bd in 1..=max_band as usize {
let mut windows = Vec::with_capacity(n_win);
for wd in 0..n_win {
let adjust_num = read_u8(reader, ADJUST_NUM_BITS)?;
let aloc_bits = aloccode_bits(window_sequence, wd);
let mut adjustments = Vec::with_capacity(adjust_num as usize);
for _ad in 0..adjust_num as usize {
let alevcode = read_u8(reader, ALEVCODE_BITS)?;
let aloccode = read_u8(reader, aloc_bits)?;
adjustments.push(GainAdjust { alevcode, aloccode });
}
windows.push(GainWindow { adjustments });
}
bands.push(GainBand { windows });
}
Ok(GainControlData { max_band, bands })
}
pub fn write(&self, writer: &mut BitWriter, window_sequence: WindowSequence) -> Result<()> {
if self.max_band > MAX_BAND_CAP {
return Err(Error::GainControlDataEncodeInvalid);
}
if self.bands.len() != self.max_band as usize {
return Err(Error::GainControlDataEncodeInvalid);
}
let n_win = num_windows(window_sequence);
for band in &self.bands {
if band.windows.len() != n_win {
return Err(Error::GainControlDataEncodeInvalid);
}
for (wd, window) in band.windows.iter().enumerate() {
if window.adjustments.len() > MAX_ADJUST_NUM as usize {
return Err(Error::GainControlDataEncodeInvalid);
}
let aloc_bits = aloccode_bits(window_sequence, wd);
let aloc_cap: u32 = if aloc_bits == 0 {
0
} else {
(1u32 << aloc_bits) - 1
};
for adj in &window.adjustments {
if adj.alevcode > MAX_ALEVCODE {
return Err(Error::GainControlDataEncodeInvalid);
}
if adj.aloccode as u32 > aloc_cap {
return Err(Error::GainControlDataEncodeInvalid);
}
}
}
}
writer.write_u32(self.max_band as u32, MAX_BAND_BITS);
for band in &self.bands {
for (wd, window) in band.windows.iter().enumerate() {
let adjust_num = window.adjustments.len() as u32;
writer.write_u32(adjust_num, ADJUST_NUM_BITS);
let aloc_bits = aloccode_bits(window_sequence, wd);
for adj in &window.adjustments {
writer.write_u32(adj.alevcode as u32, ALEVCODE_BITS);
writer.write_u32(adj.aloccode as u32, aloc_bits);
}
}
}
Ok(())
}
}
fn read_u8(reader: &mut BitReader<'_>, n: u32) -> Result<u8> {
debug_assert!(n <= 8);
Ok(reader.read_u32(n).map_err(|_| Error::UnexpectedEnd)? as u8)
}