use bitvec::{order::Lsb0, prelude::BitVec, view::BitView as _};
use symphonia_core::errors::{Error, Result};
use crate::bits::{read, read_bool, read_write, read_write_bool, write};
#[derive(Debug)]
pub struct CodebookLibrary<'a> {
offsets: Vec<u32>,
data: &'a [u8],
}
impl<'a> CodebookLibrary<'a> {
pub fn from_bytes(bytes: &'a [u8]) -> Result<Self> {
let (_, offsets_offset) = bytes.split_at(bytes.len() - size_of::<u32>());
let offsets_offset = u32::from_le_bytes(
offsets_offset
.try_into()
.map_err(|_| Error::DecodeError("wem: codebook can't read last bytes"))?,
) as usize;
let codebook_count = (bytes.len() - offsets_offset) / size_of::<u32>();
let data = &bytes[..offsets_offset];
let offsets = (0..codebook_count)
.map(|i| {
let offset = offsets_offset + i * size_of::<u32>();
Ok(u32::from_le_bytes(
bytes[offset..(offset + size_of::<u32>())]
.try_into()
.map_err(|_| Error::DecodeError("wem: codebook can't read offset"))?,
))
})
.collect::<Result<Vec<_>>>()?;
Ok(Self { offsets, data })
}
pub fn rebuild(&self, codebook_index: usize) -> Result<BitVec<u8, Lsb0>> {
let codebook_data = self.codebook(codebook_index)?;
let i = codebook_data.view_bits();
let mut out = BitVec::<_, Lsb0>::new();
write(0x56_43_42_u32, &mut out, 24);
let (i, dimensions): (_, u32) = read(i, 4);
write(dimensions, &mut out, 16);
let (i, entry_count): (_, u32) = read(i, 14);
write(entry_count, &mut out, 24);
let (mut i, ordered) = read_write_bool(i, &mut out);
if ordered {
let _initial_length: u8;
(i, _initial_length) = read_write(i, &mut out, 5);
let mut current_entry = 0;
while current_entry < entry_count {
let number: u32;
(i, number) = read_write(
i,
&mut out,
crate::math::log2(entry_count - current_entry) as usize,
);
current_entry += number;
}
} else {
let codeword_lengths_length: u8;
(i, codeword_lengths_length) = read(i, 3);
if codeword_lengths_length == 0 || codeword_lengths_length > 5 {
return symphonia_core::errors::decode_error(
"wem: nonsense codeword lengths length",
);
}
let sparse;
(i, sparse) = read_write_bool(i, &mut out);
for _ in 0..entry_count {
let present = if sparse {
let present;
(i, present) = read_write_bool(i, &mut out);
present
} else {
true
};
if present {
let codeword_length: u8;
(i, codeword_length) = read(i, codeword_lengths_length as usize);
write(codeword_length, &mut out, 5);
}
}
}
let (mut i, lookup_type): (_, u8) = read(i, 1);
write(lookup_type, &mut out, 4);
if lookup_type == 1 {
let _min: u32;
(i, _min) = read_write(i, &mut out, 32);
let _max: u32;
(i, _max) = read_write(i, &mut out, 32);
let value_length: u8;
(i, value_length) = read_write(i, &mut out, 4);
let sequence_flag;
(i, sequence_flag) = read_bool(i);
out.push(sequence_flag);
let quantvals = CodebookLibrary::quantvals(entry_count, dimensions);
for _ in 0..quantvals {
let _val: u32;
(i, _val) = read_write(i, &mut out, value_length as usize + 1);
}
} else if lookup_type != 0 {
return symphonia_core::errors::decode_error("wem: lookup type");
}
let bits_read = codebook_data.view_bits::<Lsb0>().len() - i.len();
if codebook_data.len() == bits_read / 8 + 1 {
Ok(out)
} else {
symphonia_core::errors::decode_error("wem: codebook size mismatch")
}
}
pub fn codebook(&self, index: usize) -> Result<&'a [u8]> {
let first_byte_offset = self
.offsets
.get(index)
.map(|offset| *offset as usize)
.ok_or(Error::DecodeError("wem: codebook index"))?;
let last_byte_offset = self
.offsets
.get(index + 1)
.map_or(self.data.len(), |offset| *offset as usize);
Ok(&self.data[first_byte_offset..last_byte_offset])
}
pub const fn quantvals(entries: u32, dimensions: u32) -> u32 {
let bits = crate::math::log2(entries);
let mut vals = entries >> ((bits - 1) * (dimensions - 1) / dimensions);
loop {
let acc = vals.pow(dimensions);
let acc1 = (vals + 1).pow(dimensions);
if acc <= entries && acc1 > entries {
return vals;
} else if acc > entries {
vals -= 1;
} else {
vals += 1;
}
}
}
}
impl CodebookLibrary<'static> {
pub fn from_aotuv() -> Self {
Self::from_bytes(include_bytes!("../assets/packed_codebooks_aoTuV_603.bin"))
.expect("Built-time codebook corrupt")
}
}
#[cfg(test)]
mod tests {
use super::CodebookLibrary;
#[test]
fn load_aotuv() {
let lib = CodebookLibrary::from_aotuv();
assert_eq!(lib.offsets.len(), 599);
assert_eq!(lib.offsets[1], 8);
assert_eq!(lib.offsets.last(), Some(&71991));
}
}