use std::{io::Read, mem};
use indexmap::IndexSet;
use log::{debug, info, trace};
use vorbis_bitpack::BitpackReader;
use super::{
AudioPacketAnalyze, VorbisCommentData, VorbisIdentificationHeaderData, VorbisOptimizerError,
common_header_validation
};
use crate::vorbis::{
PacketType, ResidueType, VectorLookupType, codebook::VorbisCodebook, ilog, lookup1_values
};
type PacketSlice<'pref, 'packet> = &'pref mut &'packet [u8];
#[derive(Default)]
pub(super) struct VorbisSetupData {
pub(super) codebook_configurations: Vec<CodebookConfiguration>,
pub(super) floor_configurations: Vec<Floor1Configuration>,
pub(super) residue_configurations: Vec<ResidueConfiguration>,
pub(super) mapping_configurations: Vec<MappingConfiguration>,
pub(super) modes: Vec<Mode>
}
pub(super) struct SetupHeaderParse {
pub(super) comment_data: VorbisCommentData
}
pub(super) struct ChannelMapping {
pub(super) magnitude_channel: u8,
pub(super) angle_channel: u8
}
pub(super) struct FloorAndResidueMapping {
pub(super) floor_number: u8,
pub(super) residue_number: u8
}
pub(super) struct CodebookConfiguration {
pub(super) codebook: VorbisCodebook,
pub(super) entry_count: u32,
pub(super) dimensions: u16,
pub(super) vector_lookup_type: VectorLookupType,
pub(super) codebook_vector_minimum_value: f64,
pub(super) codebook_vector_delta_value: f64,
pub(super) codebook_vector_multiplicands: Vec<u16>,
pub(super) codebook_vector_value_bits: u8,
pub(super) codebook_vector_sequence_flag: bool
}
pub(super) struct Floor1Configuration {
pub(super) multiplier: u8,
pub(super) range_bits: u8,
pub(super) partition_class_list: Vec<u8>,
pub(super) class_dimensions: Vec<u8>,
pub(super) class_subclasses: Vec<u8>,
pub(super) class_masterbooks: Vec<Option<u8>>,
pub(super) subclass_books: Vec<Vec<Option<u8>>>,
pub(super) x_list: Vec<u16>
}
pub(super) struct ResidueConfiguration {
pub(super) residue_type: ResidueType,
pub(super) begin: u32,
pub(super) end: u32,
pub(super) partition_size: u32,
pub(super) classifications: u8,
pub(super) classbook: u8,
pub(super) books: Vec<[Option<u8>; 8]>
}
pub(super) struct MappingConfiguration {
pub(super) channel_mappings: Vec<ChannelMapping>,
pub(super) mapping_mux: Vec<u8>,
pub(super) floor_and_residue_mappings: Vec<FloorAndResidueMapping>
}
pub(super) struct Mode {
pub(super) big_block: bool,
pub(super) mapping_number: u8
}
impl SetupHeaderParse {
pub(super) fn analyze_packet(
&mut self,
packet: &[u8],
identification_data: &VorbisIdentificationHeaderData
) -> Result<(Option<u16>, Option<AudioPacketAnalyze>), VorbisOptimizerError> {
trace!("Decoding setup header Vorbis packet");
let mut setup_header = common_header_validation(packet, PacketType::SetupHeader)?;
let (codebook_configurations, mut bitpacker, header_length) =
parse_codebook_configurations(&mut setup_header)?;
let time_domain_transform_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!("Time domain transforms (unused): {time_domain_transform_count}");
for _ in 0..time_domain_transform_count {
let transform = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if cfg!(debug_assertions) && transform != 0 {
return Err(VorbisOptimizerError::InvalidPattern);
}
}
let floor_configurations = parse_floor1_configurations(
&mut bitpacker,
header_length,
codebook_configurations.len()
)?;
let residue_configurations =
parse_residue_configurations(&mut bitpacker, header_length, &codebook_configurations)?;
let mapping_configurations = parse_mapping_configurations(
&mut bitpacker,
header_length,
identification_data.channels.get(),
floor_configurations.len(),
residue_configurations.len()
)?;
let modes = parse_modes(&mut bitpacker, header_length, mapping_configurations.len())?;
Ok((
None,
Some(AudioPacketAnalyze {
comment_data: mem::take(&mut self.comment_data),
codec_setup: VorbisSetupData {
codebook_configurations,
floor_configurations,
residue_configurations,
mapping_configurations,
modes
}
})
))
}
}
fn parse_codebook_configurations<'pref, 'packet>(
setup_header: PacketSlice<'pref, 'packet>
) -> Result<
(
Vec<CodebookConfiguration>,
BitpackReader<PacketSlice<'pref, 'packet>>,
usize
),
VorbisOptimizerError
> {
let header_length = setup_header.len() + 7; if setup_header.is_empty() {
return Err(VorbisOptimizerError::TooSmallPacket(header_length));
}
let codebook_count = setup_header
.split_first()
.map(|(codebook_count, setup_header_tail)| {
*setup_header = setup_header_tail;
*codebook_count as u16 + 1
})
.unwrap();
info!("Codebook count: {codebook_count}");
let mut bitpacker = BitpackReader::new(setup_header);
let mut codebook_configurations = Vec::with_capacity(codebook_count as usize);
for i in 0..codebook_count {
let sync_pattern =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?;
if cfg!(debug_assertions) && sync_pattern != 0x564342 {
return Err(VorbisOptimizerError::InvalidPattern);
}
let codebook_dimensions =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
let codebook_entries =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?;
let codebook_entries_usize = codebook_entries.try_into()?;
let ordered = bitpack_packet_read!(bitpacker, read_flag, header_length)?;
debug!(
"Codebook {i}: {codebook_dimensions} dimensions, {codebook_entries} entries, ordered: {ordered}",
);
let mut codeword_lengths = vec![0; codebook_entries_usize];
if ordered {
let mut start_entry = 0;
let mut codeword_length = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 5, u8)?
+ 1;
while start_entry < codebook_entries_usize {
if codeword_length > 32 {
return Err(VorbisOptimizerError::TooBigCodewordLength);
}
let entries_with_this_cw_length = usize::try_from(bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut ilog((codebook_entries_usize - start_entry) as i32),
u32
)?)?;
let next_start_entry = start_entry + entries_with_this_cw_length;
if next_start_entry > codebook_entries_usize {
return Err(VorbisOptimizerError::InvalidSetupValue);
}
codeword_lengths[start_entry..next_start_entry].fill(codeword_length);
start_entry = next_start_entry;
codeword_length += 1;
}
} else {
let sparse = bitpack_packet_read!(bitpacker, read_flag, header_length)?;
trace!("Codebook {i} is sparse: {sparse}");
for codeword_length in &mut codeword_lengths {
if !sparse || bitpack_packet_read!(bitpacker, read_flag, header_length)? {
*codeword_length = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 5, u8)?
+ 1;
}
}
}
let lookup_type = VectorLookupType::try_from(
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?
)?;
let codebook_vector_minimum_value;
let codebook_vector_delta_value;
let codebook_vector_value_bits;
let codebook_vector_sequence_flag;
let mut codebook_vector_multiplicands;
if lookup_type == VectorLookupType::NoLookup {
debug!("Codebook {i} is not used for vector decoding");
codebook_vector_minimum_value = 0.0;
codebook_vector_delta_value = 0.0;
codebook_vector_value_bits = 0;
codebook_vector_sequence_flag = false;
codebook_vector_multiplicands = vec![];
} else {
codebook_vector_minimum_value =
bitpack_packet_read!(bitpacker, read_float32, header_length)?;
codebook_vector_delta_value =
bitpack_packet_read!(bitpacker, read_float32, header_length)?;
codebook_vector_value_bits = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?
+ 1;
codebook_vector_sequence_flag =
bitpack_packet_read!(bitpacker, read_flag, header_length)?;
let codebook_lookup_value_count =
if lookup_type == VectorLookupType::ImplicitlyPopulated {
lookup1_values(codebook_entries, codebook_dimensions) as u64
} else {
codebook_entries as u64 * codebook_dimensions as u64
};
debug!(
"Codebook {i} vector lookup: type {} ({lookup_type:?}), \
minimum value {codebook_vector_minimum_value}, \
delta value {codebook_vector_delta_value}, \
value bits {codebook_vector_value_bits}, \
value count {codebook_lookup_value_count}",
lookup_type as u8,
);
codebook_vector_multiplicands =
Vec::with_capacity(usize::try_from(codebook_lookup_value_count)?.min(65535));
for _ in 0..codebook_lookup_value_count {
codebook_vector_multiplicands.push(bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut codebook_vector_value_bits,
u16
)?);
}
}
codebook_configurations.push(CodebookConfiguration {
codebook: VorbisCodebook::new(i, &codeword_lengths)?,
entry_count: codebook_entries,
vector_lookup_type: lookup_type,
codebook_vector_minimum_value,
codebook_vector_delta_value,
codebook_vector_multiplicands,
codebook_vector_value_bits,
codebook_vector_sequence_flag,
dimensions: codebook_dimensions
});
}
Ok((codebook_configurations, bitpacker, header_length))
}
fn parse_floor1_configurations<R: Read>(
bitpacker: &mut BitpackReader<R>,
header_length: usize,
codebook_count: usize
) -> Result<Vec<Floor1Configuration>, VorbisOptimizerError> {
let floor_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!("Floor configurations count: {floor_count}");
let mut floor_configurations = Vec::with_capacity(floor_count as usize);
for i in 0..floor_count {
let floor_type =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if floor_type != 1 {
return Err(VorbisOptimizerError::UnsupportedFloorType(floor_type));
}
let partitions =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 5, u8)?;
let mut maximum_class = -1;
let partition_class_list = (0..partitions)
.map(|_| {
let class = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?;
maximum_class = (class as i8).max(maximum_class);
Ok::<_, VorbisOptimizerError>(class)
})
.collect::<Result<Vec<_>, _>>()?;
debug!(
"Floor {}: type {}, {} partitions, {} classes",
i,
floor_type,
partitions,
maximum_class + 1
);
let mut class_dimensions = Vec::with_capacity((maximum_class + 1) as usize);
let mut class_subclasses = Vec::with_capacity((maximum_class + 1) as usize);
let mut class_masterbooks = Vec::with_capacity((maximum_class + 1) as usize);
let mut subclass_books = Vec::with_capacity((maximum_class + 1) as usize * 8);
let mut maximum_class_dimension = 1;
for _ in 0..=maximum_class {
let class_dimension = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 3, u8)?
+ 1;
class_dimensions.push(class_dimension);
maximum_class_dimension = class_dimension.max(maximum_class_dimension);
let current_subclass =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 2, u8)?;
class_subclasses.push(current_subclass);
if current_subclass != 0 {
let codebook_number = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if codebook_number as usize >= codebook_count {
return Err(VorbisOptimizerError::InvalidCodebookNumber(codebook_number));
}
debug!("Floor {i}, subclass {current_subclass} codebook: {codebook_number}");
class_masterbooks.push(Some(codebook_number));
} else {
debug!("Floor {i}, subclass {current_subclass} has no codebook");
class_masterbooks.push(None);
}
let current_subclass_books_count = 1 << current_subclass;
let current_subclass_books = (0..current_subclass_books_count)
.map(|_| {
let codebook_number =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?
.checked_sub(1);
if let Some(codebook_number) =
codebook_number.filter(|n| *n as usize >= codebook_count)
{
return Err(VorbisOptimizerError::InvalidCodebookNumber(codebook_number));
}
debug!("Floor {i} vector partition codebook: {codebook_number:?}");
Ok(codebook_number)
})
.collect::<Result<Vec<_>, _>>()?;
subclass_books.push(current_subclass_books);
}
let multiplier =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 2, u8)? + 1;
let range_bits =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?;
let mut x_list =
IndexSet::with_capacity(partition_class_list.len() * maximum_class_dimension as usize);
for current_class in partition_class_list.iter().map(|&c| c as usize) {
for _ in 0..class_dimensions[current_class] {
if !x_list.insert(bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut range_bits,
u16
)?) {
return Err(VorbisOptimizerError::RepeatedFloor1Point(i));
}
}
}
if x_list.len() > 65 {
return Err(VorbisOptimizerError::TooManyFloor1Points(i));
}
floor_configurations.push(Floor1Configuration {
multiplier,
range_bits,
partition_class_list,
class_dimensions,
class_subclasses,
class_masterbooks,
subclass_books,
x_list: x_list.into_iter().collect()
});
}
Ok(floor_configurations)
}
fn parse_residue_configurations<R: Read>(
bitpacker: &mut BitpackReader<R>,
header_length: usize,
codebook_configurations: &[CodebookConfiguration]
) -> Result<Vec<ResidueConfiguration>, VorbisOptimizerError> {
let residue_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!("Residue count: {residue_count}");
let mut residue_configurations = Vec::with_capacity(residue_count as usize);
for i in 0..residue_count {
let residue_type = ResidueType::try_from(
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?
)?;
let residue_begin =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?;
let residue_end =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?
.max(residue_begin);
let residue_partition_size = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 24, u32)?
+ 1;
let residue_classifications =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
let residue_classbook =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
debug!(
"Residue {i}: \
type {} ({residue_type:?}), \
begin {residue_begin}, \
end {residue_end}, \
partition size {residue_partition_size}, \
classifications {residue_classifications}, \
classbook {residue_classbook}",
residue_type as u8,
);
if codebook_configurations
.get(residue_classbook as usize)
.filter(|classbook_configuration| {
let minimum_entry_count = (residue_classifications as u32)
.saturating_pow(classbook_configuration.dimensions as u32);
minimum_entry_count <= classbook_configuration.entry_count
})
.is_none()
{
return Err(VorbisOptimizerError::InvalidCodebookNumber(
residue_classbook
));
}
let residue_cascade = (0..residue_classifications).map(|_| {
let low_bits =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 3, u8)?;
let high_bits = if bitpack_packet_read!(bitpacker, read_flag, header_length)? {
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 5, u8)?
} else {
0
};
Ok((high_bits << 3) | low_bits)
}).collect::<Result<Vec<_>, VorbisOptimizerError>>()?;
let residue_books = residue_cascade
.iter()
.enumerate()
.map(|(i, &residue_cascade)| {
let mut residue_books = [None; 8];
for (j, class_residue_book) in residue_books.iter_mut().enumerate() {
if residue_cascade & (1 << j) != 0 {
let residue_book = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if codebook_configurations.get(residue_book as usize).is_none() {
return Err(VorbisOptimizerError::InvalidCodebookNumber(residue_book));
}
trace!("Read residue book {residue_book} for classification {i}");
*class_residue_book = Some(residue_book);
} else {
}
}
Ok(residue_books)
})
.collect::<Result<Vec<_>, _>>()?;
residue_configurations.push(ResidueConfiguration {
residue_type,
begin: residue_begin,
end: residue_end,
partition_size: residue_partition_size,
classifications: residue_classifications,
classbook: residue_classbook,
books: residue_books
});
}
Ok(residue_configurations)
}
fn parse_mapping_configurations<R: Read>(
bitpacker: &mut BitpackReader<R>,
header_length: usize,
audio_channels: u8,
floor_count: usize,
residue_count: usize
) -> Result<Vec<MappingConfiguration>, VorbisOptimizerError> {
let mapping_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!("Mapping count: {mapping_count}");
let mut mapping_configurations = Vec::with_capacity(mapping_count as usize);
for i in 0..mapping_count {
let mapping_type =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if mapping_type != 0 {
return Err(VorbisOptimizerError::ReservedMappingType(mapping_type));
}
let mapping_submap_count = if bitpack_packet_read!(bitpacker, read_flag, header_length)? {
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)? + 1
} else {
1
};
debug!("Mapping {i} floor and residue submap count: {mapping_submap_count}");
let channel_mappings = if bitpack_packet_read!(bitpacker, read_flag, header_length)? {
let mapping_coupling_steps = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u16)?
+ 1;
let mapping_component_bits = ilog(audio_channels as i32 - 1);
(0..mapping_coupling_steps)
.map(|j| {
let magnitude_channel = bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut mapping_component_bits,
u8
)?;
let angle_channel = bitpack_packet_read!(
bitpacker,
read_unsigned_integer,
header_length,
mut mapping_component_bits,
u8
)?;
if magnitude_channel == angle_channel
|| magnitude_channel >= audio_channels
|| angle_channel >= audio_channels
{
return Err(VorbisOptimizerError::InvalidChannelMapping {
magnitude_channel,
angle_channel,
audio_channels
});
}
trace!(
"Mapping {i}, channel mapping {j}: \
magnitude channel {magnitude_channel}, angle channel {angle_channel}"
);
Ok(ChannelMapping {
magnitude_channel,
angle_channel
})
})
.collect::<Result<Vec<_>, _>>()?
} else {
trace!("Not using channel mappings for mapping {i}");
vec![]
};
let reserved_field =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 2, u8)?;
if cfg!(debug_assertions) && reserved_field != 0 {
return Err(VorbisOptimizerError::InvalidPattern);
}
let mapping_mux = if mapping_submap_count > 1 {
(0..audio_channels).map(|_| {
let mux_submap = bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 4, u8)?;
if mux_submap >= mapping_submap_count {
return Err(VorbisOptimizerError::InvalidChannelMultiplexing {
mux_submap,
mapping_submap_count
});
}
Ok(mux_submap)
}).collect::<Result<Vec<_>, _>>()?
} else {
vec![0; audio_channels as usize]
};
trace!("Read mapping {i} channel floor and residue submapping settings: {mapping_mux:?}");
let floor_and_residue_mappings = (0..mapping_submap_count).map(|j| {
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
let floor_number =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if floor_number as usize >= floor_count {
return Err(VorbisOptimizerError::InvalidFloorNumber(floor_number));
}
let residue_number =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if residue_number as usize >= residue_count {
return Err(VorbisOptimizerError::InvalidResidueNumber(residue_number));
}
info!("Mapping {i}, submap {j}: floor {floor_number}, residue {residue_number}");
Ok(FloorAndResidueMapping {
floor_number,
residue_number
})
}).collect::<Result<Vec<_>, _>>()?;
mapping_configurations.push(MappingConfiguration {
channel_mappings,
mapping_mux,
floor_and_residue_mappings
});
}
Ok(mapping_configurations)
}
fn parse_modes<R: Read>(
bitpacker: &mut BitpackReader<R>,
header_length: usize,
mapping_count: usize
) -> Result<Vec<Mode>, VorbisOptimizerError> {
let mode_count =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 6, u8)? + 1;
info!("Mode count: {mode_count}");
let mut modes = Vec::with_capacity(mode_count as usize);
for i in 0..mode_count {
let big_block = bitpack_packet_read!(bitpacker, read_flag, header_length)?;
let window_type =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if cfg!(debug_assertions) && window_type != 0 {
return Err(VorbisOptimizerError::InvalidPattern);
}
let time_transform_type =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 16, u16)?;
if cfg!(debug_assertions) && time_transform_type != 0 {
return Err(VorbisOptimizerError::InvalidPattern);
}
let mapping_number =
bitpack_packet_read!(bitpacker, read_unsigned_integer, header_length, const 8, u8)?;
if mapping_number as usize >= mapping_count {
return Err(VorbisOptimizerError::InvalidMappingNumber(mapping_number));
}
debug!("Mode {i}: uses big block size: {big_block}, mapping number {mapping_number}");
modes.push(Mode {
big_block,
mapping_number
});
}
if cfg!(debug_assertions) && !bitpack_packet_read!(bitpacker, read_flag, header_length)? {
return Err(VorbisOptimizerError::InvalidPattern);
}
Ok(modes)
}