#![warn(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap
)]
use crate::{Error, Result};
use crate::bitio::BitReader;
use crate::frame::{BlockType, SideInfo};
use crate::header::{FrameHeader, MpegVersion};
pub fn parse(header: &FrameHeader, bytes: &[u8]) -> Result<SideInfo> {
let mut r = BitReader::new(bytes);
let mut si = SideInfo::default();
let nch = header.channel_mode.channels();
let mpeg1 = matches!(header.version, MpegVersion::V1);
if mpeg1 {
si.main_data_begin = r.read_u16(9);
r.read(if nch == 1 { 5 } else { 3 }); for ch in 0..nch {
for band in 0..4 {
si.scfsi[ch][band] = r.read_bool();
}
}
} else {
si.main_data_begin = r.read_u16(8);
r.read(if nch == 1 { 1 } else { 2 }); }
for gr in 0..header.version.granules() {
for ch in 0..nch {
let g = &mut si.granules[gr][ch];
g.part2_3_length = r.read_u16(12);
g.big_values = r.read_u16(9);
g.global_gain = r.read_u8(8);
g.scalefac_compress = r.read_u16(if mpeg1 { 4 } else { 9 });
g.window_switching = r.read_bool();
if g.window_switching {
g.block_type = match r.read(2) {
1 => BlockType::Start,
2 => BlockType::Short,
3 => BlockType::Stop,
_ => {
return Err(Error::invalid(
"mp3 side-info: long block_type with window switching",
))
}
};
g.mixed_block = r.read_bool();
for t in g.table_select.iter_mut().take(2) {
*t = r.read_u8(5);
}
for sg in &mut g.subblock_gain {
*sg = r.read_u8(3);
}
} else {
g.block_type = BlockType::Long;
for t in &mut g.table_select {
*t = r.read_u8(5);
}
g.region0_count = r.read_u8(4);
g.region1_count = r.read_u8(3);
}
if mpeg1 {
g.preflag = r.read_bool();
} else {
let is_right = ch == 1
&& matches!(
header.channel_mode,
crate::frame::ChannelMode::JointStereo {
intensity_stereo: true,
..
}
);
g.preflag = !is_right && g.scalefac_compress >= 500;
}
g.scalefac_scale = r.read_bool();
g.count1table_select = r.read_bool();
}
}
debug_assert_eq!(
r.bit_pos(),
header.side_info_len() * 8,
"mp3 side-info bit accounting"
);
Ok(si)
}