use oxideav_core::bits::{BitReader, BitWriter};
use crate::ics_info::WindowSequence;
use crate::section_data::{INTENSITY_HCB, INTENSITY_HCB2, NOISE_HCB, ZERO_HCB};
use crate::{Error, Result};
pub const SF_INDEX_OFFSET: i8 = -60;
pub const NOISE_PCM_BITS: u32 = 9;
pub const HCOD_SF_NUM_ENTRIES: usize = 121;
pub const HCOD_SF_MAX_LEN: u32 = 19;
const HCOD_SF: [(u8, u32); HCOD_SF_NUM_ENTRIES] = [
(18, 0x3ffe8), (18, 0x3ffe6), (18, 0x3ffe7), (18, 0x3ffe5), (19, 0x7fff5), (19, 0x7fff1), (19, 0x7ffed), (19, 0x7fff6), (19, 0x7ffee), (19, 0x7ffef), (19, 0x7fff0), (19, 0x7fffc), (19, 0x7fffd), (19, 0x7ffff), (19, 0x7fffe), (19, 0x7fff7), (19, 0x7fff8), (19, 0x7fffb), (19, 0x7fff9), (18, 0x3ffe4), (19, 0x7fffa), (18, 0x3ffe3), (17, 0x1ffef), (17, 0x1fff0), (16, 0x0fff5), (17, 0x1ffee), (16, 0x0fff2), (16, 0x0fff3), (16, 0x0fff4), (16, 0x0fff1), (15, 0x07ff6), (15, 0x07ff7), (14, 0x03ff9), (14, 0x03ff5), (14, 0x03ff7), (14, 0x03ff3), (14, 0x03ff6), (14, 0x03ff2), (13, 0x01ff7), (13, 0x01ff5), (12, 0x00ff9), (12, 0x00ff7), (12, 0x00ff6), (11, 0x007f9), (12, 0x00ff4), (11, 0x007f8), (10, 0x003f9), (10, 0x003f7), (10, 0x003f5), (9, 0x001f8), (9, 0x001f7), (8, 0x000fa), (8, 0x000f8), (8, 0x000f6), (7, 0x00079), (6, 0x0003a), (6, 0x00038), (5, 0x0001a), (4, 0x0000b), (3, 0x00004), (1, 0x00000), (4, 0x0000a), (4, 0x0000c), (5, 0x0001b), (6, 0x00039), (6, 0x0003b), (7, 0x00078), (7, 0x0007a), (8, 0x000f7), (8, 0x000f9), (9, 0x001f6), (9, 0x001f9), (10, 0x003f4), (10, 0x003f6), (10, 0x003f8), (11, 0x007f5), (11, 0x007f4), (11, 0x007f6), (11, 0x007f7), (12, 0x00ff5), (12, 0x00ff8), (13, 0x01ff4), (13, 0x01ff6), (13, 0x01ff8), (14, 0x03ff8), (14, 0x03ff4), (16, 0x0fff0), (15, 0x07ff4), (16, 0x0fff6), (15, 0x07ff5), (18, 0x3ffe2), (19, 0x7ffd9), (19, 0x7ffda), (19, 0x7ffdb), (19, 0x7ffdc), (19, 0x7ffdd), (19, 0x7ffde), (19, 0x7ffd8), (19, 0x7ffd2), (19, 0x7ffd3), (19, 0x7ffd4), (19, 0x7ffd5), (19, 0x7ffd6), (19, 0x7fff2), (19, 0x7ffdf), (19, 0x7ffe7), (19, 0x7ffe8), (19, 0x7ffe9), (19, 0x7ffea), (19, 0x7ffeb), (19, 0x7ffe6), (19, 0x7ffe0), (19, 0x7ffe1), (19, 0x7ffe2), (19, 0x7ffe3), (19, 0x7ffe4), (19, 0x7ffe5), (19, 0x7ffd7), (19, 0x7ffec), (19, 0x7fff4), (19, 0x7fff3), ];
pub fn hcod_sf_encode(dpcm: i8) -> Result<(u8, u32)> {
let idx = (dpcm as i32) - (SF_INDEX_OFFSET as i32);
if !(0..HCOD_SF_NUM_ENTRIES as i32).contains(&idx) {
return Err(Error::ScaleFactorDataEncodeInvalid);
}
Ok(HCOD_SF[idx as usize])
}
pub fn hcod_sf_decode(reader: &mut BitReader<'_>) -> Result<i8> {
let mut acc: u32 = 0;
for len in 1..=HCOD_SF_MAX_LEN {
let bit = reader.read_u32(1).map_err(|_| Error::UnexpectedEnd)?;
acc = (acc << 1) | bit;
for (idx, &(entry_len, entry_cw)) in HCOD_SF.iter().enumerate() {
if u32::from(entry_len) == len && entry_cw == acc {
return Ok((idx as i8) + SF_INDEX_OFFSET);
}
}
}
unreachable!("HCOD_SF is a complete 19-bit prefix code; the 19-bit walk must match");
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScaleFactorEntry {
Dpcm(i8),
Intensity(i8),
NoisePcm(u16),
NoiseDpcm(i8),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScaleFactorData {
pub entries: Vec<Vec<ScaleFactorEntry>>,
}
impl ScaleFactorData {
pub fn parse(reader: &mut BitReader<'_>, sfb_cb: &[Vec<u8>]) -> Result<Self> {
let mut noise_pcm_flag = true;
let mut entries: Vec<Vec<ScaleFactorEntry>> = Vec::with_capacity(sfb_cb.len());
for group in sfb_cb {
let mut group_entries: Vec<ScaleFactorEntry> = Vec::new();
for &cb in group {
if cb == ZERO_HCB {
continue;
}
let entry = if is_intensity(cb) {
let dpcm = hcod_sf_decode(reader)?;
ScaleFactorEntry::Intensity(dpcm)
} else if is_noise(cb) {
if noise_pcm_flag {
noise_pcm_flag = false;
let pcm = reader
.read_u32(NOISE_PCM_BITS)
.map_err(|_| Error::UnexpectedEnd)?
as u16;
ScaleFactorEntry::NoisePcm(pcm)
} else {
let dpcm = hcod_sf_decode(reader)?;
ScaleFactorEntry::NoiseDpcm(dpcm)
}
} else {
let dpcm = hcod_sf_decode(reader)?;
ScaleFactorEntry::Dpcm(dpcm)
};
group_entries.push(entry);
}
entries.push(group_entries);
}
Ok(ScaleFactorData { entries })
}
pub fn write(&self, writer: &mut BitWriter, sfb_cb: &[Vec<u8>]) -> Result<()> {
if self.entries.len() != sfb_cb.len() {
return Err(Error::ScaleFactorDataEncodeInvalid);
}
let mut noise_pcm_flag = true;
for (group_entries, group_cb) in self.entries.iter().zip(sfb_cb.iter()) {
let mut entry_iter = group_entries.iter();
for &cb in group_cb {
if cb == ZERO_HCB {
continue;
}
let entry = entry_iter
.next()
.ok_or(Error::ScaleFactorDataEncodeInvalid)?;
match (entry, cb) {
(ScaleFactorEntry::Intensity(dpcm), cb) if is_intensity(cb) => {
let (len, cw) = hcod_sf_encode(*dpcm)?;
writer.write_u32(cw, u32::from(len));
}
(ScaleFactorEntry::NoisePcm(pcm), cb) if is_noise(cb) => {
if !noise_pcm_flag {
return Err(Error::ScaleFactorDataEncodeInvalid);
}
if u32::from(*pcm) >= (1u32 << NOISE_PCM_BITS) {
return Err(Error::ScaleFactorDataEncodeInvalid);
}
noise_pcm_flag = false;
writer.write_u32(u32::from(*pcm), NOISE_PCM_BITS);
}
(ScaleFactorEntry::NoiseDpcm(dpcm), cb) if is_noise(cb) => {
if noise_pcm_flag {
return Err(Error::ScaleFactorDataEncodeInvalid);
}
let (len, cw) = hcod_sf_encode(*dpcm)?;
writer.write_u32(cw, u32::from(len));
}
(ScaleFactorEntry::Dpcm(dpcm), cb) if !is_intensity(cb) && !is_noise(cb) => {
let (len, cw) = hcod_sf_encode(*dpcm)?;
writer.write_u32(cw, u32::from(len));
}
_ => return Err(Error::ScaleFactorDataEncodeInvalid),
}
}
if entry_iter.next().is_some() {
return Err(Error::ScaleFactorDataEncodeInvalid);
}
}
Ok(())
}
}
pub const NOISE_OFFSET: i32 = 90;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AbsoluteScaleFactorEntry {
Sf(u8),
IsPos(i16),
NoiseNrg(i32),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AbsoluteScaleFactors {
pub entries: Vec<Vec<AbsoluteScaleFactorEntry>>,
}
pub fn accumulate(
sfd: &ScaleFactorData,
sfb_cb: &[Vec<u8>],
global_gain: u8,
) -> Result<AbsoluteScaleFactors> {
if sfd.entries.len() != sfb_cb.len() {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
let mut last_sf: i32 = i32::from(global_gain);
let mut last_is: i32 = 0;
let mut last_nrg: i32 = i32::from(global_gain) - NOISE_OFFSET - 256;
let mut noise_pcm_flag = true;
let mut out: Vec<Vec<AbsoluteScaleFactorEntry>> = Vec::with_capacity(sfb_cb.len());
for (group_entries, group_cb) in sfd.entries.iter().zip(sfb_cb.iter()) {
let mut entry_iter = group_entries.iter();
let mut group_out: Vec<AbsoluteScaleFactorEntry> = Vec::new();
for &cb in group_cb {
if cb == ZERO_HCB {
continue;
}
let entry = entry_iter
.next()
.ok_or(Error::ScaleFactorAccumulatorInvalid)?;
let abs_entry = match (entry, cb) {
(ScaleFactorEntry::Intensity(dpcm), cb) if is_intensity(cb) => {
last_is += i32::from(*dpcm);
AbsoluteScaleFactorEntry::IsPos(last_is as i16)
}
(ScaleFactorEntry::NoisePcm(pcm), cb) if is_noise(cb) => {
if !noise_pcm_flag {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
noise_pcm_flag = false;
last_nrg += i32::from(*pcm);
AbsoluteScaleFactorEntry::NoiseNrg(last_nrg)
}
(ScaleFactorEntry::NoiseDpcm(dpcm), cb) if is_noise(cb) => {
if noise_pcm_flag {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
last_nrg += i32::from(*dpcm);
AbsoluteScaleFactorEntry::NoiseNrg(last_nrg)
}
(ScaleFactorEntry::Dpcm(dpcm), cb) if !is_intensity(cb) && !is_noise(cb) => {
last_sf += i32::from(*dpcm);
if !(0..=255).contains(&last_sf) {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
AbsoluteScaleFactorEntry::Sf(last_sf as u8)
}
_ => return Err(Error::ScaleFactorAccumulatorInvalid),
};
group_out.push(abs_entry);
}
if entry_iter.next().is_some() {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
out.push(group_out);
}
Ok(AbsoluteScaleFactors { entries: out })
}
pub fn differentiate(
abs: &AbsoluteScaleFactors,
sfb_cb: &[Vec<u8>],
global_gain: u8,
) -> Result<ScaleFactorData> {
if abs.entries.len() != sfb_cb.len() {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
let mut last_sf: i32 = i32::from(global_gain);
let mut last_is: i32 = 0;
let mut last_nrg: i32 = i32::from(global_gain) - NOISE_OFFSET - 256;
let mut noise_pcm_flag = true;
let mut out: Vec<Vec<ScaleFactorEntry>> = Vec::with_capacity(sfb_cb.len());
for (group_abs, group_cb) in abs.entries.iter().zip(sfb_cb.iter()) {
let mut abs_iter = group_abs.iter();
let mut group_out: Vec<ScaleFactorEntry> = Vec::new();
for &cb in group_cb {
if cb == ZERO_HCB {
continue;
}
let abs_entry = abs_iter
.next()
.ok_or(Error::ScaleFactorAccumulatorInvalid)?;
let entry = match (abs_entry, cb) {
(AbsoluteScaleFactorEntry::IsPos(cur), cb) if is_intensity(cb) => {
let delta = i32::from(*cur) - last_is;
if !(-60..=60).contains(&delta) {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
last_is = i32::from(*cur);
ScaleFactorEntry::Intensity(delta as i8)
}
(AbsoluteScaleFactorEntry::NoiseNrg(cur), cb) if is_noise(cb) => {
if noise_pcm_flag {
let delta = *cur - last_nrg;
if !(0..=511).contains(&delta) {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
noise_pcm_flag = false;
last_nrg = *cur;
ScaleFactorEntry::NoisePcm(delta as u16)
} else {
let delta = *cur - last_nrg;
if !(-60..=60).contains(&delta) {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
last_nrg = *cur;
ScaleFactorEntry::NoiseDpcm(delta as i8)
}
}
(AbsoluteScaleFactorEntry::Sf(cur), cb) if !is_intensity(cb) && !is_noise(cb) => {
let delta = i32::from(*cur) - last_sf;
if !(-60..=60).contains(&delta) {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
last_sf = i32::from(*cur);
ScaleFactorEntry::Dpcm(delta as i8)
}
_ => return Err(Error::ScaleFactorAccumulatorInvalid),
};
group_out.push(entry);
}
if abs_iter.next().is_some() {
return Err(Error::ScaleFactorAccumulatorInvalid);
}
out.push(group_out);
}
Ok(ScaleFactorData { entries: out })
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ErScaleFactorData {
pub sf_concealment: bool,
pub rev_global_gain: u8,
pub data: ScaleFactorData,
pub dpcm_is_last_position: Option<i16>,
pub dpcm_noise_last_position: Option<u16>,
}
fn length_of_rvlc_sf_bits(window_sequence: WindowSequence) -> u32 {
if window_sequence.is_eight_short() {
11
} else {
9
}
}
const LENGTH_OF_RVLC_ESCAPES_BITS: u32 = 8;
fn fold_escape(base: i8, esc_magnitude: u8) -> i8 {
if base >= 0 {
crate::rvlc::RVLC_ESC_FLAG + esc_magnitude as i8
} else {
-crate::rvlc::RVLC_ESC_FLAG - esc_magnitude as i8
}
}
impl ErScaleFactorData {
pub fn parse(
reader: &mut BitReader<'_>,
sfb_cb: &[Vec<u8>],
window_sequence: WindowSequence,
) -> Result<Self> {
let sf_concealment = reader.read_u32(1).map_err(|_| Error::UnexpectedEnd)? != 0;
let rev_global_gain = reader.read_u32(8).map_err(|_| Error::UnexpectedEnd)? as u8;
let len_rvlc_sf = u64::from(
reader
.read_u32(length_of_rvlc_sf_bits(window_sequence))
.map_err(|_| Error::UnexpectedEnd)?,
);
let rvlc_start = reader.bit_position();
let mut intensity_used = false;
let mut noise_used = false;
let mut base: Vec<Vec<ScaleFactorEntry>> = Vec::with_capacity(sfb_cb.len());
let mut esc_records: Vec<(usize, usize)> = Vec::new();
for (g, group) in sfb_cb.iter().enumerate() {
let mut group_entries: Vec<ScaleFactorEntry> = Vec::new();
for &cb in group {
if cb == ZERO_HCB {
continue;
}
let idx_in_group = group_entries.len();
let entry = if is_intensity(cb) {
intensity_used = true;
let d = crate::rvlc::rvlc_decode(reader)?;
if d.abs() == crate::rvlc::RVLC_ESC_FLAG {
esc_records.push((g, idx_in_group));
}
ScaleFactorEntry::Intensity(d)
} else if is_noise(cb) {
if !noise_used {
noise_used = true;
let pcm = reader
.read_u32(NOISE_PCM_BITS)
.map_err(|_| Error::UnexpectedEnd)?
as u16;
ScaleFactorEntry::NoisePcm(pcm)
} else {
let d = crate::rvlc::rvlc_decode(reader)?;
if d.abs() == crate::rvlc::RVLC_ESC_FLAG {
esc_records.push((g, idx_in_group));
}
ScaleFactorEntry::NoiseDpcm(d)
}
} else {
let d = crate::rvlc::rvlc_decode(reader)?;
if d.abs() == crate::rvlc::RVLC_ESC_FLAG {
esc_records.push((g, idx_in_group));
}
ScaleFactorEntry::Dpcm(d)
};
group_entries.push(entry);
}
base.push(group_entries);
}
let mut is_last_base: Option<i8> = None;
if intensity_used {
let d = crate::rvlc::rvlc_decode(reader)?;
is_last_base = Some(d);
}
let rvlc_consumed = reader.bit_position() - rvlc_start;
if rvlc_consumed != len_rvlc_sf {
return Err(Error::RvlcScaleFactorDataInvalid);
}
let sf_escapes_present = reader.read_u32(1).map_err(|_| Error::UnexpectedEnd)? != 0;
let mut is_last_esc: Option<u8> = None;
if sf_escapes_present {
let len_escapes = u64::from(
reader
.read_u32(LENGTH_OF_RVLC_ESCAPES_BITS)
.map_err(|_| Error::UnexpectedEnd)?,
);
let esc_start = reader.bit_position();
for &(g, i) in &esc_records {
let mag = crate::rvlc::rvlc_esc_decode(reader)?;
let folded = match base[g][i] {
ScaleFactorEntry::Dpcm(d) => ScaleFactorEntry::Dpcm(fold_escape(d, mag)),
ScaleFactorEntry::Intensity(d) => {
ScaleFactorEntry::Intensity(fold_escape(d, mag))
}
ScaleFactorEntry::NoiseDpcm(d) => {
ScaleFactorEntry::NoiseDpcm(fold_escape(d, mag))
}
ScaleFactorEntry::NoisePcm(_) => {
return Err(Error::RvlcScaleFactorDataInvalid);
}
};
base[g][i] = folded;
}
if let Some(d) = is_last_base {
if d.abs() == crate::rvlc::RVLC_ESC_FLAG {
let mag = crate::rvlc::rvlc_esc_decode(reader)?;
is_last_esc = Some(mag);
}
}
let esc_consumed = reader.bit_position() - esc_start;
if esc_consumed != len_escapes {
return Err(Error::RvlcScaleFactorDataInvalid);
}
}
let dpcm_noise_last_position = if noise_used && sf_escapes_present {
Some(
reader
.read_u32(NOISE_PCM_BITS)
.map_err(|_| Error::UnexpectedEnd)? as u16,
)
} else {
None
};
let dpcm_is_last_position = is_last_base.map(|d| {
let folded = match is_last_esc {
Some(mag) => fold_escape(d, mag),
None => d,
};
i16::from(folded)
});
Ok(ErScaleFactorData {
sf_concealment,
rev_global_gain,
data: ScaleFactorData { entries: base },
dpcm_is_last_position,
dpcm_noise_last_position,
})
}
pub fn write(
&self,
writer: &mut BitWriter,
sfb_cb: &[Vec<u8>],
window_sequence: WindowSequence,
) -> Result<()> {
if self.data.entries.len() != sfb_cb.len() {
return Err(Error::RvlcScaleFactorDataInvalid);
}
writer.write_u32(u32::from(self.sf_concealment), 1);
writer.write_u32(u32::from(self.rev_global_gain), 8);
let mut rvlc_part = BitWriter::new();
let mut escapes: Vec<u8> = Vec::new();
let mut intensity_used = false;
let mut noise_used = false;
for (group_entries, group_cb) in self.data.entries.iter().zip(sfb_cb.iter()) {
let mut entry_iter = group_entries.iter();
for &cb in group_cb {
if cb == ZERO_HCB {
continue;
}
let entry = entry_iter.next().ok_or(Error::RvlcScaleFactorDataInvalid)?;
match (entry, cb) {
(ScaleFactorEntry::Intensity(d), cb) if is_intensity(cb) => {
intensity_used = true;
write_rvlc_delta(&mut rvlc_part, *d, &mut escapes)?;
}
(ScaleFactorEntry::NoisePcm(pcm), cb) if is_noise(cb) => {
if noise_used {
return Err(Error::RvlcScaleFactorDataInvalid);
}
if u32::from(*pcm) >= (1u32 << NOISE_PCM_BITS) {
return Err(Error::RvlcScaleFactorDataInvalid);
}
noise_used = true;
rvlc_part.write_u32(u32::from(*pcm), NOISE_PCM_BITS);
}
(ScaleFactorEntry::NoiseDpcm(d), cb) if is_noise(cb) => {
if !noise_used {
return Err(Error::RvlcScaleFactorDataInvalid);
}
write_rvlc_delta(&mut rvlc_part, *d, &mut escapes)?;
}
(ScaleFactorEntry::Dpcm(d), cb) if !is_intensity(cb) && !is_noise(cb) => {
write_rvlc_delta(&mut rvlc_part, *d, &mut escapes)?;
}
_ => return Err(Error::RvlcScaleFactorDataInvalid),
}
}
if entry_iter.next().is_some() {
return Err(Error::RvlcScaleFactorDataInvalid);
}
}
let mut is_last_escape: Option<u8> = None;
match (intensity_used, self.dpcm_is_last_position) {
(true, Some(d)) => {
let d8 = i8::try_from(d).map_err(|_| Error::RvlcScaleFactorDataInvalid)?;
let mut tail: Vec<u8> = Vec::new();
write_rvlc_delta(&mut rvlc_part, d8, &mut tail)?;
is_last_escape = tail.into_iter().next();
}
(true, None) | (false, Some(_)) => {
return Err(Error::RvlcScaleFactorDataInvalid);
}
(false, None) => {}
}
let len_rvlc_sf = rvlc_part.bit_position();
let field_bits = length_of_rvlc_sf_bits(window_sequence);
if len_rvlc_sf >= (1u64 << field_bits) {
return Err(Error::RvlcScaleFactorDataInvalid);
}
writer.write_u32(len_rvlc_sf as u32, field_bits);
append_bits(writer, len_rvlc_sf, &rvlc_part.finish());
let any_escape = !escapes.is_empty() || is_last_escape.is_some();
writer.write_u32(u32::from(any_escape), 1);
if any_escape {
let mut esc_part = BitWriter::new();
for &mag in &escapes {
let (len, cw) = crate::rvlc::rvlc_esc_encode(mag)?;
esc_part.write_u32(cw, u32::from(len));
}
if let Some(mag) = is_last_escape {
let (len, cw) = crate::rvlc::rvlc_esc_encode(mag)?;
esc_part.write_u32(cw, u32::from(len));
}
let len_escapes = esc_part.bit_position();
if len_escapes >= (1u64 << LENGTH_OF_RVLC_ESCAPES_BITS) {
return Err(Error::RvlcScaleFactorDataInvalid);
}
writer.write_u32(len_escapes as u32, LENGTH_OF_RVLC_ESCAPES_BITS);
append_bits(writer, len_escapes, &esc_part.finish());
}
let expect_noise_seed = noise_used && any_escape;
match (expect_noise_seed, self.dpcm_noise_last_position) {
(true, Some(pcm)) => {
if u32::from(pcm) >= (1u32 << NOISE_PCM_BITS) {
return Err(Error::RvlcScaleFactorDataInvalid);
}
writer.write_u32(u32::from(pcm), NOISE_PCM_BITS);
}
(false, None) => {}
_ => return Err(Error::RvlcScaleFactorDataInvalid),
}
Ok(())
}
}
fn write_rvlc_delta(part: &mut BitWriter, delta: i8, escapes: &mut Vec<u8>) -> Result<()> {
let flag = crate::rvlc::RVLC_ESC_FLAG; if delta.abs() < flag {
let (len, cw) = crate::rvlc::rvlc_encode(delta)?;
part.write_u32(cw, u32::from(len));
} else {
let (base, mag) = if delta >= 0 {
(flag, (delta - flag) as u8)
} else {
(-flag, (-delta - flag) as u8)
};
let (len, cw) = crate::rvlc::rvlc_encode(base)?;
part.write_u32(cw, u32::from(len));
if mag as usize >= crate::rvlc::RVLC_ESC_NUM_ENTRIES {
return Err(Error::RvlcScaleFactorDataInvalid);
}
escapes.push(mag);
}
Ok(())
}
fn append_bits(dst: &mut BitWriter, total: u64, bytes: &[u8]) {
let mut remaining = total;
let mut byte_idx = 0usize;
while remaining >= 8 {
dst.write_u32(u32::from(bytes[byte_idx]), 8);
byte_idx += 1;
remaining -= 8;
}
if remaining > 0 {
let last = bytes[byte_idx];
let value = u32::from(last) >> (8 - remaining);
dst.write_u32(value, remaining as u32);
}
}
fn is_intensity(cb: u8) -> bool {
cb == INTENSITY_HCB || cb == INTENSITY_HCB2
}
fn is_noise(cb: u8) -> bool {
cb == NOISE_HCB
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hcod_sf_table_known_rows() {
assert_eq!(HCOD_SF[60], (1, 0x0));
assert_eq!(HCOD_SF[59], (3, 0x4));
assert_eq!(HCOD_SF[61], (4, 0xa));
assert_eq!(HCOD_SF[0], (18, 0x3ffe8));
assert_eq!(HCOD_SF[120], (19, 0x7fff3));
}
#[test]
fn hcod_sf_table_is_prefix_free() {
for (i, &(li, vi)) in HCOD_SF.iter().enumerate() {
for (j, &(lj, vj)) in HCOD_SF.iter().enumerate() {
if i == j || lj < li {
continue;
}
let lo = u32::from(lj - li);
let prefix = vj >> lo;
assert_ne!(
prefix, vi,
"entry {} (L={}, v={:x}) is prefix of entry {} (L={}, v={:x})",
i, li, vi, j, lj, vj
);
}
}
}
#[test]
fn encode_dpcm_zero_is_single_bit() {
let (len, cw) = hcod_sf_encode(0).unwrap();
assert_eq!(len, 1);
assert_eq!(cw, 0);
}
#[test]
fn encode_dpcm_boundaries() {
assert!(hcod_sf_encode(-60).is_ok());
assert!(hcod_sf_encode(60).is_ok());
assert_eq!(
hcod_sf_encode(-61),
Err(Error::ScaleFactorDataEncodeInvalid)
);
assert_eq!(hcod_sf_encode(61), Err(Error::ScaleFactorDataEncodeInvalid));
}
#[test]
fn hcod_sf_roundtrip_every_entry() {
for dpcm in -60i8..=60 {
let (len, cw) = hcod_sf_encode(dpcm).unwrap();
let mut bw = BitWriter::new();
bw.write_u32(cw, u32::from(len));
let bits_written = bw.bit_position();
let buf = bw.finish();
let mut br = BitReader::new(&buf);
let recovered = hcod_sf_decode(&mut br).unwrap();
assert_eq!(recovered, dpcm);
assert_eq!(br.bit_position(), bits_written);
}
}
#[test]
fn er_roundtrip_spectrum_only_no_escapes() {
let sfb_cb = vec![vec![2u8, 2, 2], vec![2u8, 2]];
let block = ErScaleFactorData {
sf_concealment: true,
rev_global_gain: 137,
data: ScaleFactorData {
entries: vec![
vec![
ScaleFactorEntry::Dpcm(0),
ScaleFactorEntry::Dpcm(3),
ScaleFactorEntry::Dpcm(-5),
],
vec![ScaleFactorEntry::Dpcm(6), ScaleFactorEntry::Dpcm(-6)],
],
},
dpcm_is_last_position: None,
dpcm_noise_last_position: None,
};
let mut w = BitWriter::new();
block
.write(&mut w, &sfb_cb, WindowSequence::OnlyLong)
.unwrap();
let bytes = w.finish();
let mut r = BitReader::new(&bytes);
let parsed = ErScaleFactorData::parse(&mut r, &sfb_cb, WindowSequence::OnlyLong).unwrap();
assert_eq!(parsed, block);
}
#[test]
fn er_roundtrip_with_escapes() {
let sfb_cb = vec![vec![3u8, 3, 3]];
let block = ErScaleFactorData {
sf_concealment: false,
rev_global_gain: 200,
data: ScaleFactorData {
entries: vec![vec![
ScaleFactorEntry::Dpcm(7), ScaleFactorEntry::Dpcm(-20), ScaleFactorEntry::Dpcm(60), ]],
},
dpcm_is_last_position: None,
dpcm_noise_last_position: None,
};
let mut w = BitWriter::new();
block
.write(&mut w, &sfb_cb, WindowSequence::EightShort)
.unwrap();
let bytes = w.finish();
let mut r = BitReader::new(&bytes);
let parsed = ErScaleFactorData::parse(&mut r, &sfb_cb, WindowSequence::EightShort).unwrap();
assert_eq!(parsed, block);
}
#[test]
fn er_roundtrip_intensity_and_pns() {
let sfb_cb = vec![vec![2u8, INTENSITY_HCB2, NOISE_HCB, NOISE_HCB]];
let block = ErScaleFactorData {
sf_concealment: true,
rev_global_gain: 100,
data: ScaleFactorData {
entries: vec![vec![
ScaleFactorEntry::Dpcm(2),
ScaleFactorEntry::Intensity(-3),
ScaleFactorEntry::NoisePcm(0x1a5), ScaleFactorEntry::NoiseDpcm(10), ]],
},
dpcm_is_last_position: Some(5),
dpcm_noise_last_position: Some(0x0c2),
};
let mut w = BitWriter::new();
block
.write(&mut w, &sfb_cb, WindowSequence::OnlyLong)
.unwrap();
let bytes = w.finish();
let mut r = BitReader::new(&bytes);
let parsed = ErScaleFactorData::parse(&mut r, &sfb_cb, WindowSequence::OnlyLong).unwrap();
assert_eq!(parsed, block);
}
#[test]
fn er_pns_without_escapes_has_no_noise_seed() {
let sfb_cb = vec![vec![NOISE_HCB, NOISE_HCB]];
let block = ErScaleFactorData {
sf_concealment: false,
rev_global_gain: 80,
data: ScaleFactorData {
entries: vec![vec![
ScaleFactorEntry::NoisePcm(0x010),
ScaleFactorEntry::NoiseDpcm(3), ]],
},
dpcm_is_last_position: None,
dpcm_noise_last_position: None,
};
let mut w = BitWriter::new();
block
.write(&mut w, &sfb_cb, WindowSequence::OnlyLong)
.unwrap();
let bytes = w.finish();
let mut r = BitReader::new(&bytes);
let parsed = ErScaleFactorData::parse(&mut r, &sfb_cb, WindowSequence::OnlyLong).unwrap();
assert_eq!(parsed, block);
assert_eq!(parsed.dpcm_noise_last_position, None);
let bad = ErScaleFactorData {
dpcm_noise_last_position: Some(0x0aa),
..block
};
let mut bw = BitWriter::new();
assert!(matches!(
bad.write(&mut bw, &sfb_cb, WindowSequence::OnlyLong),
Err(Error::RvlcScaleFactorDataInvalid)
));
}
#[test]
fn er_forward_decode_matches_huffman_path() {
let sfb_cb = vec![vec![2u8, 2, 2, 2]];
let global_gain = 120u8;
let entries = vec![vec![
ScaleFactorEntry::Dpcm(0),
ScaleFactorEntry::Dpcm(5),
ScaleFactorEntry::Dpcm(-30), ScaleFactorEntry::Dpcm(2),
]];
let sfd = ScaleFactorData {
entries: entries.clone(),
};
let mut hw = BitWriter::new();
sfd.write(&mut hw, &sfb_cb).unwrap();
let hbytes = hw.finish();
let mut hr = BitReader::new(&hbytes);
let hsfd = ScaleFactorData::parse(&mut hr, &sfb_cb).unwrap();
let habs = accumulate(&hsfd, &sfb_cb, global_gain).unwrap();
let er = ErScaleFactorData {
sf_concealment: false,
rev_global_gain: 0,
data: ScaleFactorData { entries },
dpcm_is_last_position: None,
dpcm_noise_last_position: None,
};
let mut ew = BitWriter::new();
er.write(&mut ew, &sfb_cb, WindowSequence::OnlyLong)
.unwrap();
let ebytes = ew.finish();
let mut er_reader = BitReader::new(&ebytes);
let parsed_er =
ErScaleFactorData::parse(&mut er_reader, &sfb_cb, WindowSequence::OnlyLong).unwrap();
let eabs = accumulate(&parsed_er.data, &sfb_cb, global_gain).unwrap();
assert_eq!(habs, eabs, "RVLC forward decode must equal Huffman path");
}
#[test]
fn er_forbidden_codeword_is_detected() {
let mut w = BitWriter::new();
w.write_u32(0, 1); w.write_u32(0, 8); w.write_u32(6, 9); w.write_u32(0b110010, 6); let bytes = w.finish();
let sfb_cb = vec![vec![2u8]];
let mut r = BitReader::new(&bytes);
assert!(matches!(
ErScaleFactorData::parse(&mut r, &sfb_cb, WindowSequence::OnlyLong),
Err(Error::RvlcForbiddenCodeword)
));
}
#[test]
fn er_length_mismatch_rejected() {
let sfb_cb = vec![vec![2u8, 2]];
let block = ErScaleFactorData {
sf_concealment: false,
rev_global_gain: 50,
data: ScaleFactorData {
entries: vec![vec![ScaleFactorEntry::Dpcm(1), ScaleFactorEntry::Dpcm(-1)]],
},
dpcm_is_last_position: None,
dpcm_noise_last_position: None,
};
let mut w = BitWriter::new();
block
.write(&mut w, &sfb_cb, WindowSequence::OnlyLong)
.unwrap();
let mut bytes = w.finish();
bytes[2] ^= 0x40; let mut r = BitReader::new(&bytes);
let res = ErScaleFactorData::parse(&mut r, &sfb_cb, WindowSequence::OnlyLong);
assert!(res.is_err(), "corrupted length field must be rejected");
}
}