use std::{cmp, io::Read};
use log::trace;
use tinyvec::TinyVec;
use vorbis_bitpack::BitpackReader;
use super::{
ilog,
setup_header_parse::{
CodebookConfiguration, Floor1Configuration, ResidueConfiguration, VorbisSetupData
},
VorbisIdentificationHeaderData, VorbisOptimizerError
};
use crate::vorbis::{
codebook::VorbisCodebook, optimizer::setup_header_parse::Mode, ResidueType, VectorLookupType
};
pub(super) fn process_audio_packet<
R: Read,
T,
F: FnMut(u32, u8, &mut T) -> Result<(), VorbisOptimizerError>,
G: FnMut(u16, u32, &mut T) -> Result<(), VorbisOptimizerError>
>(
identification_data: &VorbisIdentificationHeaderData,
codec_setup: &VorbisSetupData,
packet_length: usize,
bitpacker: &mut BitpackReader<R>,
mut bitpack_read_callback: F,
codebook_entry_decode_callback: G,
mut shared_callback_data: T
) -> Result<(bool, Option<u16>), VorbisOptimizerError> {
let (mode_configuration, decode_blocksize) = eval_on_eop!(
process_audio_packet_first_part(
identification_data,
codec_setup,
packet_length,
bitpacker,
&mut bitpack_read_callback,
&mut shared_callback_data
),
{
trace!("Discarding audio packet due to premature end of packet");
return Ok((false, None));
}
)?;
eval_on_eop!(
process_audio_packet_second_part(
identification_data,
codec_setup,
packet_length,
bitpacker,
mode_configuration,
decode_blocksize,
bitpack_read_callback,
codebook_entry_decode_callback,
shared_callback_data
)
.map(|_| (true, Some(decode_blocksize))),
Ok((true, Some(decode_blocksize)))
)
}
fn process_audio_packet_first_part<
'setup,
R: Read,
T,
F: FnMut(u32, u8, &mut T) -> Result<(), VorbisOptimizerError>
>(
identification_data: &VorbisIdentificationHeaderData,
codec_setup: &'setup VorbisSetupData,
packet_length: usize,
bitpacker: &mut BitpackReader<R>,
mut bitpack_read_callback: F,
shared_callback_data: &mut T
) -> Result<(&'setup Mode, u16), VorbisOptimizerError> {
let mode_bits = ilog(codec_setup.modes.len() as i32 - 1);
let mode = bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
packet_length,
mut mode_bits,
u8
)?;
let mode_configuration = codec_setup
.modes
.get(mode as usize)
.ok_or(VorbisOptimizerError::InvalidModeNumber(mode))?;
trace!("Audio packet mode {}", mode);
bitpack_read_callback(mode as u32, mode_bits, shared_callback_data)?;
let decode_blocksize = if mode_configuration.big_block {
identification_data.blocksizes.1
} else {
identification_data.blocksizes.0
};
if mode_configuration.big_block {
bitpack_read_callback(
bitpack_packet_read!(bitpacker, read_flag, packet_length)? as u32,
1,
shared_callback_data
)?;
bitpack_read_callback(
bitpack_packet_read!(bitpacker, read_flag, packet_length)? as u32,
1,
shared_callback_data
)?;
}
Ok((mode_configuration, decode_blocksize))
}
#[allow(clippy::too_many_arguments)]
fn process_audio_packet_second_part<
R: Read,
T,
F: FnMut(u32, u8, &mut T) -> Result<(), VorbisOptimizerError>,
G: FnMut(u16, u32, &mut T) -> Result<(), VorbisOptimizerError>
>(
identification_data: &VorbisIdentificationHeaderData,
codec_setup: &VorbisSetupData,
packet_length: usize,
bitpacker: &mut BitpackReader<R>,
mode_configuration: &Mode,
decode_blocksize: u16,
mut bitpack_read_callback: F,
mut codebook_entry_decode_callback: G,
mut shared_callback_data: T
) -> Result<(), VorbisOptimizerError> {
let mapping_configuration =
&codec_setup.mapping_configurations[mode_configuration.mapping_number as usize];
let mut no_residue =
TinyVec::<[bool; 8]>::with_capacity(identification_data.channels.get() as usize);
for submap_number in mapping_configuration.mapping_mux.iter().copied() {
let floor_number =
mapping_configuration.floor_and_residue_mappings[submap_number as usize].floor_number;
let floor_configuration = &codec_setup.floor_configurations[floor_number as usize];
trace!("Processing floor vector, submap {}", submap_number);
let has_audio_energy = process_floor1(
bitpacker,
packet_length,
floor_configuration,
&codec_setup.codebook_configurations,
&mut bitpack_read_callback,
&mut codebook_entry_decode_callback,
&mut shared_callback_data
)?;
no_residue.push(!has_audio_energy);
}
for channel_mapping in &mapping_configuration.channel_mappings {
let magnitude_channel = channel_mapping.magnitude_channel as usize;
let angle_channel = channel_mapping.angle_channel as usize;
let propagated_no_residue = no_residue[magnitude_channel] && no_residue[angle_channel];
no_residue[magnitude_channel] = propagated_no_residue;
no_residue[angle_channel] = propagated_no_residue;
}
for (i, floor_and_residue_mapping) in mapping_configuration
.floor_and_residue_mappings
.iter()
.enumerate()
{
let mut residue_vectors_masks =
TinyVec::<[bool; 8]>::with_capacity(identification_data.channels.get() as usize);
let mut no_residue_vector_to_decode = true;
for (j, mapping_mux) in mapping_configuration
.mapping_mux
.iter()
.copied()
.enumerate()
{
if mapping_mux as usize == i {
let do_not_decode = no_residue[j];
residue_vectors_masks.push(do_not_decode);
no_residue_vector_to_decode &= do_not_decode;
}
}
if no_residue_vector_to_decode {
trace!("All residue vectors are marked do not decode, skipping residue processing");
continue;
}
trace!("Processing residue vectors, submap {}", i);
process_residue(
bitpacker,
&codec_setup.residue_configurations[floor_and_residue_mapping.residue_number as usize],
&codec_setup.codebook_configurations,
&residue_vectors_masks,
decode_blocksize,
&mut codebook_entry_decode_callback,
&mut shared_callback_data
)?;
}
Ok(())
}
fn process_floor1<
R: Read,
T,
F: FnMut(u32, u8, &mut T) -> Result<(), VorbisOptimizerError>,
G: FnMut(u16, u32, &mut T) -> Result<(), VorbisOptimizerError>
>(
bitpacker: &mut BitpackReader<R>,
packet_length: usize,
floor_configuration: &Floor1Configuration,
codebook_configurations: &[CodebookConfiguration],
mut bitpack_read_callback: F,
mut codebook_entry_decode_callback: G,
shared_callback_data: &mut T
) -> Result<bool, VorbisOptimizerError> {
let has_audio_energy = bitpack_packet_read!(bitpacker, read_flag, packet_length)?;
trace!("Audio energy this frame: {}", has_audio_energy);
bitpack_read_callback(has_audio_energy as u32, 1, shared_callback_data)?;
if has_audio_energy {
const RANGE_BITS_ARRAY: [u8; 4] =
[ilog(256 - 1), ilog(128 - 1), ilog(86 - 1), ilog(64 - 1)];
let range_bits = RANGE_BITS_ARRAY[(floor_configuration.multiplier - 1) as usize];
for _ in 0..2 {
let value = bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
packet_length,
mut range_bits,
u8
)?;
bitpack_read_callback(value as u32, range_bits, shared_callback_data)?;
}
for class in &floor_configuration.partition_class_list {
let class = *class as usize;
let class_dimension = floor_configuration.class_dimensions[class];
let class_bits = floor_configuration.class_subclasses[class];
let csub = (1 << class_bits) - 1;
let mut cval = if class_bits > 0 {
let class_masterbook =
floor_configuration.class_masterbooks[class].unwrap() as usize;
decode_codebook_entry_number(
&codebook_configurations[class_masterbook].codebook,
bitpacker,
&mut codebook_entry_decode_callback,
shared_callback_data
)?
} else {
0
};
for _ in 0..class_dimension {
let book = floor_configuration.subclass_books[class][usize::try_from(cval & csub)?];
cval >>= class_bits;
if let Some(book) = book {
decode_codebook_entry_number(
&codebook_configurations[book as usize].codebook,
bitpacker,
&mut codebook_entry_decode_callback,
shared_callback_data
)?;
}
}
}
}
Ok(has_audio_energy)
}
fn process_residue<R: Read, T, F: FnMut(u16, u32, &mut T) -> Result<(), VorbisOptimizerError>>(
bitpacker: &mut BitpackReader<R>,
residue_configuration: &ResidueConfiguration,
codebook_configurations: &[CodebookConfiguration],
original_residue_vectors_masks: &[bool],
current_blocksize: u16,
mut codebook_entry_decode_callback: F,
shared_callback_data: &mut T
) -> Result<(), VorbisOptimizerError> {
let residue_vectors_masks;
let vector_size; if residue_configuration.residue_type == ResidueType::InterleavedVectors {
residue_vectors_masks = &[false][..];
vector_size = current_blocksize as u32 / 2 * original_residue_vectors_masks.len() as u32;
} else {
residue_vectors_masks = original_residue_vectors_masks;
vector_size = current_blocksize as u32 / 2;
};
let residue_vector_count = residue_vectors_masks.len();
let residue_begin = cmp::min(residue_configuration.begin, vector_size);
let residue_end = cmp::min(residue_configuration.end, vector_size);
let n_to_read = residue_end - residue_begin;
if n_to_read == 0 {
trace!("No residue to decode, skipping");
return Ok(());
}
let classbook_configuration =
&codebook_configurations[residue_configuration.classbook as usize];
let classwords_per_codeword = classbook_configuration.dimensions;
let partitions_to_read = n_to_read / residue_configuration.partition_size;
let classifications_stride = classwords_per_codeword as u32 + partitions_to_read;
let classification_count = (residue_vector_count as u32 * classifications_stride).try_into()?;
let mut classifications = if classification_count <= 128 {
TinyVec::from_array_len([0; 128], classification_count)
} else {
TinyVec::Heap(vec![0; classification_count])
};
let partitions_to_read = partitions_to_read as usize;
let classifications_stride = classifications_stride as usize;
let classwords_per_codeword = classwords_per_codeword as usize;
for pass in 0..8 {
let mut partition_count = 0;
while partition_count < partitions_to_read {
if pass == 0 {
for (j, do_not_decode) in residue_vectors_masks.iter().enumerate() {
if *do_not_decode {
continue;
}
let mut temp = decode_codebook_entry_number(
&classbook_configuration.codebook,
bitpacker,
&mut codebook_entry_decode_callback,
shared_callback_data
)?;
for i in (0..classwords_per_codeword).rev() {
classifications[j * classifications_stride + i + partition_count] =
temp % residue_configuration.classifications as u32;
temp /= residue_configuration.classifications as u32;
}
}
}
for _ in 0..classwords_per_codeword {
for (j, do_not_decode) in residue_vectors_masks.iter().enumerate() {
if *do_not_decode {
continue;
}
let vq_class = usize::try_from(
classifications[j * classifications_stride + partition_count]
)?;
if let Some(vq_book) = residue_configuration.books.get(vq_class).ok_or(
VorbisOptimizerError::InvalidVectorQuantizationClassbook(vq_class)
)?[pass]
{
let vq_book_configuration = &codebook_configurations[vq_book as usize];
if vq_book_configuration.vector_lookup_type == VectorLookupType::NoLookup {
return Err(VorbisOptimizerError::ScalarCodebookUsedInVectorContext(
vq_book
));
}
residue_configuration
.partition_size
.checked_rem(vq_book_configuration.dimensions as u32)
.filter(|remainder| *remainder == 0)
.ok_or(VorbisOptimizerError::InvalidCodebookDimension {
codebook: vq_book,
dimensions: vq_book_configuration.dimensions,
expected_dimensions_multiple_of: residue_configuration
.partition_size
})?;
process_residue_partition_vector(
bitpacker,
residue_configuration,
vq_book_configuration,
&mut codebook_entry_decode_callback,
shared_callback_data
)?;
}
}
partition_count += 1;
if partition_count >= partitions_to_read {
break;
}
}
}
}
Ok(())
}
fn process_residue_partition_vector<
R: Read,
T,
F: FnMut(u16, u32, &mut T) -> Result<(), VorbisOptimizerError>
>(
bitpacker: &mut BitpackReader<R>,
residue_configuration: &ResidueConfiguration,
vq_book_configuration: &CodebookConfiguration,
mut codebook_entry_decode_callback: F,
shared_callback_data: &mut T
) -> Result<(), VorbisOptimizerError> {
let partition_count =
residue_configuration.partition_size / vq_book_configuration.dimensions as u32;
for _ in 0..partition_count {
decode_codebook_entry_number(
&vq_book_configuration.codebook,
bitpacker,
&mut codebook_entry_decode_callback,
shared_callback_data
)?;
}
Ok(())
}
fn decode_codebook_entry_number<
R: Read,
T,
F: FnMut(u16, u32, &mut T) -> Result<(), VorbisOptimizerError>
>(
codebook: &VorbisCodebook,
bitpacker: &mut BitpackReader<R>,
mut codebook_entry_decode_callback: F,
shared_callback_data: &mut T
) -> Result<u32, VorbisOptimizerError> {
let entry_number = codebook.decode_entry_number(bitpacker)?;
codebook_entry_decode_callback(codebook.codebook_number, entry_number, shared_callback_data)?;
Ok(entry_number)
}