#![warn(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap
)]
use crate::bitio::BitReader;
use crate::frame::{BlockType, GranuleSideInfo, SideInfo};
use crate::header::{FrameHeader, MpegVersion};
use crate::tables;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ScaleFactors {
pub long: [u8; crate::frame::SFB_LONG],
pub short: [[u8; crate::frame::SFB_SHORT]; 3],
}
const SCFSI_GROUPS: [(usize, usize); 4] = [(0, 6), (6, 11), (11, 16), (16, 21)];
pub fn decode(
main: &[u8],
bit_pos: &mut usize,
header: &FrameHeader,
si: &SideInfo,
gr: usize,
ch: usize,
prev: Option<&ScaleFactors>,
) -> ScaleFactors {
let mut r = BitReader::new(main);
r.seek_bits(*bit_pos);
let gi = &si.granules[gr][ch];
let mut sf = ScaleFactors::default();
if matches!(header.version, MpegVersion::V1) {
let (slen1, slen2) = tables::SCALEFAC_COMPRESS_V1[gi.scalefac_compress as usize & 0xF];
let (s1, s2) = (u32::from(slen1), u32::from(slen2));
if gi.window_switching && gi.block_type == BlockType::Short {
let start = if gi.mixed_block {
for b in 0..8 {
sf.long[b] = r.read_u8(s1);
}
3
} else {
0
};
for sfb in start..12 {
let slen = if sfb < 6 { s1 } else { s2 };
for window in 0..3 {
sf.short[window][sfb] = r.read_u8(slen);
}
}
} else {
for (g, &(lo, hi)) in SCFSI_GROUPS.iter().enumerate() {
for b in lo..hi {
let slen = if b < 11 { s1 } else { s2 };
if gr == 1 && si.scfsi[ch][g] {
sf.long[b] = prev.map_or(0, |p| p.long[b]);
} else {
sf.long[b] = r.read_u8(slen);
}
}
}
}
} else {
let is_short = gi.window_switching && gi.block_type == BlockType::Short;
let blocktype = if is_short {
if gi.mixed_block {
2
} else {
1
}
} else {
0
};
let (slen, nr) = if is_intensity_right(header, ch) {
tables::lsf_scale_params_intensity(gi.scalefac_compress, blocktype)
} else {
tables::lsf_scale_params(gi.scalefac_compress, blocktype)
};
if is_short {
let long_vals = if gi.mixed_block { 6 } else { 0 };
let first_short = if gi.mixed_block { 3 } else { 0 };
let mut idx = 0usize;
for g in 0..4 {
for _ in 0..nr[g] {
let v = r.read_u8(u32::from(slen[g]));
if idx < long_vals {
sf.long[idx] = v;
} else {
let j = idx - long_vals;
let (sfb, window) = (first_short + j / 3, j % 3);
if sfb < crate::frame::SFB_SHORT {
sf.short[window][sfb] = v;
}
}
idx += 1;
}
}
} else {
let mut sfb = 0usize;
for g in 0..4 {
for _ in 0..nr[g] {
let v = r.read_u8(u32::from(slen[g]));
if sfb < crate::frame::SFB_LONG {
sf.long[sfb] = v;
}
sfb += 1;
}
}
}
}
*bit_pos = r.bit_pos();
sf
}
#[must_use]
pub fn is_intensity_right(header: &FrameHeader, ch: usize) -> bool {
ch == 1
&& !matches!(header.version, MpegVersion::V1)
&& matches!(
header.channel_mode,
crate::frame::ChannelMode::JointStereo {
intensity_stereo: true,
..
}
)
}
#[must_use]
pub fn lsf_intensity_illegal(gi: &GranuleSideInfo) -> ScaleFactors {
let is_short = gi.window_switching && gi.block_type == BlockType::Short;
let blocktype = match (is_short, gi.mixed_block) {
(false, _) => 0,
(true, false) => 1,
(true, true) => 2,
};
let (slen, nr) = tables::lsf_scale_params_intensity(gi.scalefac_compress, blocktype);
let mut out = ScaleFactors {
long: [0xFF; crate::frame::SFB_LONG],
short: [[0xFF; crate::frame::SFB_SHORT]; 3],
};
let (long_vals, first_short) = match blocktype {
0 => (usize::MAX, 0),
1 => (0, 0),
_ => (6, 3),
};
let mut idx = 0usize;
for g in 0..4 {
let max = if slen[g] == 0 {
0xFF
} else {
(((1u32 << slen[g]) - 1) & 0xFF) as u8
};
for _ in 0..nr[g] {
if idx < long_vals {
if idx < crate::frame::SFB_LONG {
out.long[idx] = max;
}
} else {
let j = idx - long_vals;
let (sfb, w) = (first_short + j / 3, j % 3);
if sfb < crate::frame::SFB_SHORT {
out.short[w][sfb] = max;
}
}
idx += 1;
}
}
out
}
#[cfg(test)]
#[allow(clippy::cast_possible_truncation)] mod tests {
use super::*;
use crate::bitio::BitWriter;
use crate::frame::{BlockType, ChannelMode, GranuleSideInfo};
fn hdr() -> FrameHeader {
FrameHeader {
version: MpegVersion::V1,
crc_protected: false,
bitrate_kbps: 128,
sample_rate: 44100,
padding: false,
channel_mode: ChannelMode::Mono,
copyright: false,
original: true,
emphasis: 0,
}
}
#[test]
fn short_block_scalefactors_are_band_major() {
let mut w = BitWriter::new();
let mut expect = [[0u8; crate::frame::SFB_SHORT]; 3];
for sfb in 0..12 {
let slen = if sfb < 6 { 3 } else { 2 };
for window in 0..3 {
let v = ((sfb * 3 + window) as u32) & ((1 << slen) - 1);
w.write(v, slen);
expect[window][sfb] = v as u8;
}
}
let data = w.finish();
let mut si = SideInfo::default();
si.granules[0][0] = GranuleSideInfo {
scalefac_compress: 12,
window_switching: true,
block_type: BlockType::Short,
..Default::default()
};
let mut pos = 0;
let sf = decode(&data, &mut pos, &hdr(), &si, 0, 0, None);
assert_eq!(sf.short, expect);
assert_eq!(pos, 3 * (6 * 3 + 6 * 2), "band-major bit accounting");
}
#[test]
fn long_block_scalefactors_and_bit_accounting() {
let mut w = BitWriter::new();
let mut expect = [0u8; 22];
for b in 0..21 {
let slen = if b < 11 { 4 } else { 3 };
let v = (b as u32 + 1) & ((1 << slen) - 1);
w.write(v, slen);
expect[b] = v as u8;
}
let data = w.finish();
let mut si = SideInfo::default();
si.granules[0][0] = GranuleSideInfo {
scalefac_compress: 15,
..Default::default()
};
let mut pos = 0;
let sf = decode(&data, &mut pos, &hdr(), &si, 0, 0, None);
assert_eq!(sf.long[..21], expect[..21]);
assert_eq!(pos, 74, "must consume exactly the scalefactor bits");
}
#[test]
fn scfsi_reuses_granule0_in_granule1() {
let mut prev = ScaleFactors::default();
for b in 0..6 {
prev.long[b] = (b as u8) + 1;
}
let mut si = SideInfo::default();
si.scfsi[0][0] = true; si.granules[1][0] = GranuleSideInfo {
scalefac_compress: 0,
..Default::default()
};
let mut pos = 0;
let sf = decode(&[0u8; 4], &mut pos, &hdr(), &si, 1, 0, Some(&prev));
assert_eq!(&sf.long[0..6], &[1, 2, 3, 4, 5, 6]);
assert_eq!(pos, 0, "reused + zero-length bands read no bits");
}
}