use symphonia_core::errors::{Result, decode_error};
use symphonia_core::io::{
ReadBitsRtl,
vlc::{BitOrder, Codebook, CodebookBuilder, Entry32x32},
};
use super::common::*;
#[inline(always)]
fn float32_unpack(x: u32) -> f32 {
let mantissa = x & 0x1fffff;
let sign = x & 0x80000000;
let exponent = (x & 0x7fe00000) >> 21;
let value = (mantissa as f32) * 2.0f32.powi(exponent as i32 - 788);
if sign == 0 { value } else { -value }
}
#[inline(always)]
fn lookup1_values(entries: u32, dimensions: u16) -> u32 {
let value = (entries as f32)
.powf(1.0f32 / f32::from(dimensions))
.floor() as u32;
assert!(value.pow(u32::from(dimensions)) <= entries);
assert!((value + 1).pow(u32::from(dimensions)) > entries);
value
}
fn unpack_vq_lookup_type1(
multiplicands: &[u16],
min_value: f32,
delta_value: f32,
sequence_p: bool,
codebook_entries: u32,
codebook_dimensions: u16,
lookup_values: u32,
) -> Vec<f32> {
let mut vq_lookup = vec![0.0; codebook_entries as usize * codebook_dimensions as usize];
for (v, value_vector) in vq_lookup
.chunks_exact_mut(codebook_dimensions as usize)
.enumerate()
{
let lookup_offset = v as u32;
let mut last = 0.0;
let mut index_divisor = 1;
for value in value_vector.iter_mut() {
let multiplicand_offset = ((lookup_offset / index_divisor) % lookup_values) as usize;
*value = f32::from(multiplicands[multiplicand_offset]) * delta_value + min_value + last;
if sequence_p {
last = *value;
}
index_divisor *= lookup_values;
}
}
vq_lookup
}
fn unpack_vq_lookup_type2(
multiplicands: &[u16],
min_value: f32,
delta_value: f32,
sequence_p: bool,
codebook_entries: u32,
codebook_dimensions: u16,
) -> Vec<f32> {
let mut vq_lookup = vec![0.0; codebook_entries as usize * codebook_dimensions as usize];
for (lookup_offset, value_vector) in vq_lookup
.chunks_exact_mut(codebook_dimensions as usize)
.enumerate()
{
let mut last = 0.0;
let first = lookup_offset * codebook_dimensions as usize;
for (value, &multiplicand) in value_vector.iter_mut().zip(&multiplicands[first..]) {
*value = f32::from(multiplicand) * delta_value + min_value + last;
if sequence_p {
last = *value;
}
}
}
vq_lookup
}
fn synthesize_codewords(code_lens: &[u8]) -> Result<Vec<u32>> {
let mut codewords = Vec::new();
let mut next_codeword = [0u32; 33];
let mut num_sparse = 0;
for &len in code_lens.iter() {
debug_assert!(len <= 32);
if len == 0 {
num_sparse += 1;
codewords.push(0);
continue;
}
let codeword_len = usize::from(len);
let codeword = next_codeword[codeword_len];
if len < 32 && (codeword >> len) > 0 {
return decode_error("vorbis: codebook overspecified");
}
for i in (0..codeword_len + 1).rev() {
if next_codeword[i] & 1 == 1 {
next_codeword[i] = next_codeword[i - 1] << 1;
break;
}
next_codeword[i] += 1;
}
let branch = next_codeword[codeword_len];
for (i, next) in next_codeword[codeword_len..].iter_mut().enumerate().skip(1) {
if *next == codeword << i {
*next = branch << i;
} else {
break;
}
}
codewords.push(codeword);
}
let is_underspecified = next_codeword
.iter()
.enumerate()
.skip(1)
.any(|(i, &c)| c & (u32::MAX >> (32 - i)) != 0);
let is_single_entry_codebook = code_lens.len() - num_sparse == 1;
if is_underspecified && !is_single_entry_codebook {
return decode_error("vorbis: codebook underspecified");
}
Ok(codewords)
}
pub struct VorbisCodebook {
codebook: Codebook<Entry32x32>,
dimensions: u16,
vq_vec: Option<Vec<f32>>,
}
impl VorbisCodebook {
pub fn read<B: ReadBitsRtl>(bs: &mut B) -> Result<Self> {
let sync = bs.read_bits_leq32(24)?;
if sync != 0x564342 {
return decode_error("vorbis: invalid codebook sync");
}
let codebook_dimensions = bs.read_bits_leq32(16)? as u16;
let codebook_entries = bs.read_bits_leq32(24)?;
let is_length_ordered = bs.read_bool()?;
let mut code_lens = Vec::<u8>::with_capacity(codebook_entries as usize);
if !is_length_ordered {
let is_sparse = bs.read_bool()?;
if is_sparse {
for _ in 0..codebook_entries {
let is_used = bs.read_bool()?;
let code_len = if is_used {
bs.read_bits_leq32(5)? as u8 + 1
} else {
0
};
code_lens.push(code_len);
}
} else {
for _ in 0..codebook_entries {
let code_len = bs.read_bits_leq32(5)? as u8 + 1;
code_lens.push(code_len)
}
}
} else {
let mut cur_entry = 0;
let mut cur_len = bs.read_bits_leq32(5)? + 1;
loop {
let num_bits = if codebook_entries > cur_entry {
ilog(codebook_entries - cur_entry)
} else {
0
};
let num = bs.read_bits_leq32(num_bits)?;
code_lens.extend(std::iter::repeat_n(cur_len as u8, num as usize));
cur_len += 1;
cur_entry += num;
if cur_entry > codebook_entries {
return decode_error("vorbis: invalid codebook");
}
if cur_entry == codebook_entries {
break;
}
}
}
let lookup_type = bs.read_bits_leq32(4)?;
let vq_vec = match lookup_type & 0xf {
0 => None,
1 | 2 => {
let min_value = float32_unpack(bs.read_bits_leq32(32)?);
let delta_value = float32_unpack(bs.read_bits_leq32(32)?);
let value_bits = bs.read_bits_leq32(4)? + 1;
let sequence_p = bs.read_bool()?;
let lookup_values = match lookup_type {
1 => lookup1_values(codebook_entries, codebook_dimensions),
2 => codebook_entries * u32::from(codebook_dimensions),
_ => unreachable!(),
};
let mut multiplicands = Vec::<u16>::new();
for _ in 0..lookup_values {
multiplicands.push(bs.read_bits_leq32(value_bits)? as u16);
}
let vq_lookup = match lookup_type {
1 => unpack_vq_lookup_type1(
&multiplicands,
min_value,
delta_value,
sequence_p,
codebook_entries,
codebook_dimensions,
lookup_values,
),
2 => unpack_vq_lookup_type2(
&multiplicands,
min_value,
delta_value,
sequence_p,
codebook_entries,
codebook_dimensions,
),
_ => unreachable!(),
};
Some(vq_lookup)
}
_ => return decode_error("vorbis: invalid codeword lookup type"),
};
let code_words = synthesize_codewords(&code_lens)?;
let values: Vec<u32> = (0..codebook_entries).collect();
let mut builder = CodebookBuilder::new_sparse(BitOrder::Reverse);
builder.bits_per_read(8);
let codebook = builder.make::<Entry32x32>(&code_words, &code_lens, &values)?;
Ok(VorbisCodebook {
codebook,
dimensions: codebook_dimensions,
vq_vec,
})
}
#[inline(always)]
pub fn read_scalar<B: ReadBitsRtl>(&self, bs: &mut B) -> Result<u32> {
Ok(bs.read_codebook(&self.codebook)?.0)
}
#[inline(always)]
pub fn read_vq<B: ReadBitsRtl>(&self, bs: &mut B) -> Result<&[f32]> {
let entry = bs.read_codebook(&self.codebook)?.0;
if let Some(vq) = &self.vq_vec {
let dim = self.dimensions as usize;
let start = dim * entry as usize;
Ok(&vq[start..start + dim])
} else {
decode_error("vorbis: not a vq codebook")
}
}
#[inline(always)]
pub fn dimensions(&self) -> u16 {
self.dimensions
}
}
#[cfg(test)]
mod tests {
use super::{ilog, lookup1_values, synthesize_codewords};
#[test]
fn verify_ilog() {
assert_eq!(ilog(0), 0);
assert_eq!(ilog(1), 1);
assert_eq!(ilog(2), 2);
assert_eq!(ilog(3), 2);
assert_eq!(ilog(4), 3);
assert_eq!(ilog(7), 3);
}
fn naive_lookup1_values(entries: u32, dimensions: u16) -> u32 {
let mut x = 1u32;
loop {
let xpow = x.pow(u32::from(dimensions));
if xpow > entries {
break;
}
x += 1;
}
x - 1
}
#[test]
fn verify_lookup1_values() {
assert_eq!(lookup1_values(1, 1), naive_lookup1_values(1, 1));
assert_eq!(lookup1_values(361, 2), naive_lookup1_values(361, 2));
}
#[test]
fn verify_synthesize_codewords() {
const CODEWORD_LENGTHS: &[u8] = &[2, 4, 4, 4, 4, 2, 3, 3];
const EXPECTED_CODEWORDS: &[u32] = &[0, 0x4, 0x5, 0x6, 0x7, 0x2, 0x6, 0x7];
let codewords = synthesize_codewords(CODEWORD_LENGTHS).unwrap();
assert_eq!(&codewords, EXPECTED_CODEWORDS);
}
}