use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
use crate::asf_data;
use crate::aspx;
use crate::sfb_offset;
use crate::tables;
use crate::toc::variable_bits;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonoCodecMode {
Simple,
Aspx,
}
impl MonoCodecMode {
pub fn from_bit(v: u32) -> Self {
if v == 0 {
Self::Simple
} else {
Self::Aspx
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StereoCodecMode {
Simple,
Aspx,
AspxAcpl1,
AspxAcpl2,
}
impl StereoCodecMode {
pub fn from_u32(v: u32) -> Self {
match v & 0b11 {
0 => Self::Simple,
1 => Self::Aspx,
2 => Self::AspxAcpl1,
_ => Self::AspxAcpl2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpecFrontend {
Asf,
Ssf,
}
impl SpecFrontend {
pub fn from_bit(v: u32) -> Self {
if v == 0 {
Self::Asf
} else {
Self::Ssf
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct AsfTransformInfo {
pub b_long_frame: bool,
pub transf_length: [u32; 2],
pub transform_length_0: u32,
pub transform_length_1: u32,
}
#[derive(Debug, Clone, Default)]
pub struct AsfPsyInfo {
pub b_different_framing: bool,
pub max_sfb_0: u32,
pub max_sfb_side_0: u32,
pub max_sfb_1: u32,
pub max_sfb_side_1: u32,
pub scale_factor_grouping: Vec<u8>,
pub num_windows: u32,
pub num_window_groups: u32,
}
pub fn n_grp_bits_ge_1536(tl0: u32, tl1: u32) -> u32 {
match (tl0 & 0b11, tl1 & 0b11) {
(0, 0) => 15,
(0, 1) => 10,
(0, 2) => 8,
(0, 3) => 7,
(1, 0) => 10,
(1, 1) => 7,
(1, 2) => 4,
(1, 3) => 3,
(2, 0) => 8,
(2, 1) => 4,
(2, 2) => 3,
(2, 3) => 1,
(3, 0) => 7,
(3, 1) => 3,
(3, 2) => 1,
(3, 3) => 1,
_ => 0,
}
}
pub fn n_grp_bits_lt_1536(frame_len_base: u32, tl: u32) -> u32 {
match (frame_len_base, tl & 0b11) {
(1024, 0) | (960, 0) | (768, 0) => 7,
(1024, 1) | (960, 1) | (768, 1) => 3,
(1024, 2) | (960, 2) | (768, 2) => 1,
(1024, 3) | (960, 3) | (768, 3) => 0,
(512, 0) | (384, 0) => 3,
(512, 1) | (384, 1) => 1,
(512, 2) | (384, 2) => 0,
_ => 0,
}
}
pub fn parse_asf_psy_info(
br: &mut BitReader<'_>,
transform_info: &AsfTransformInfo,
frame_len_base: u32,
b_dual_maxsfb: bool,
b_side_limited: bool,
) -> Result<AsfPsyInfo> {
let b_different_framing = frame_len_base >= 1536
&& !transform_info.b_long_frame
&& transform_info.transf_length[0] != transform_info.transf_length[1];
let mut info = AsfPsyInfo {
b_different_framing,
..AsfPsyInfo::default()
};
let (nm0, ns0, _nml0) = tables::n_msfb_bits_48(transform_info.transform_length_0)
.ok_or_else(|| Error::invalid("ac4: asf_psy_info: unsupported transform_length[0]"))?;
if b_side_limited {
info.max_sfb_0 = br.read_u32(ns0)?;
} else {
info.max_sfb_0 = br.read_u32(nm0)?;
if b_dual_maxsfb {
info.max_sfb_side_0 = br.read_u32(nm0)?;
}
}
if info.b_different_framing {
let (nm1, ns1, _) = tables::n_msfb_bits_48(transform_info.transform_length_1)
.ok_or_else(|| Error::invalid("ac4: asf_psy_info: unsupported transform_length[1]"))?;
if b_side_limited {
info.max_sfb_1 = br.read_u32(ns1)?;
} else {
info.max_sfb_1 = br.read_u32(nm1)?;
if b_dual_maxsfb {
info.max_sfb_side_1 = br.read_u32(nm1)?;
}
}
}
let n_grp_bits = if transform_info.b_long_frame {
0
} else if frame_len_base >= 1536 {
n_grp_bits_ge_1536(
transform_info.transf_length[0],
transform_info.transf_length[1],
)
} else {
n_grp_bits_lt_1536(frame_len_base, transform_info.transf_length[0])
};
let mut grouping = Vec::with_capacity(n_grp_bits as usize);
for _ in 0..n_grp_bits {
grouping.push(br.read_u32(1)? as u8);
}
info.scale_factor_grouping = grouping;
if transform_info.b_long_frame {
info.num_windows = 1;
info.num_window_groups = 1;
} else {
info.num_windows = n_grp_bits + 1;
info.num_window_groups = 1;
for &b in &info.scale_factor_grouping {
if b == 0 {
info.num_window_groups += 1;
}
}
if info.num_windows == 0 {
info.num_windows = 1;
}
}
Ok(info)
}
#[derive(Debug, Clone, Default)]
pub struct SubstreamTools {
pub channel_mode_channels: u16,
pub mono_mode: Option<MonoCodecMode>,
pub stereo_mode: Option<StereoCodecMode>,
pub spec_frontend_primary: Option<SpecFrontend>,
pub spec_frontend_secondary: Option<SpecFrontend>,
pub transform_info_primary: Option<AsfTransformInfo>,
pub transform_info_secondary: Option<AsfTransformInfo>,
pub psy_info_primary: Option<AsfPsyInfo>,
pub psy_info_secondary: Option<AsfPsyInfo>,
pub mdct_stereo_proc: bool,
pub scaled_spec_primary: Option<Vec<f32>>,
pub scaled_spec_secondary: Option<Vec<f32>>,
pub ms_used: Option<Vec<bool>>,
pub aspx_config: Option<aspx::AspxConfig>,
pub companding: Option<aspx::CompandingControl>,
pub aspx_framing_primary: Option<aspx::AspxFraming>,
pub aspx_framing_secondary: Option<aspx::AspxFraming>,
pub aspx_balance: Option<bool>,
pub aspx_xover_subband_offset: Option<u8>,
pub aspx_delta_dir_primary: Option<aspx::AspxDeltaDir>,
pub aspx_delta_dir_secondary: Option<aspx::AspxDeltaDir>,
pub aspx_qmode_env_primary: Option<aspx::AspxQuantStep>,
pub aspx_qmode_env_secondary: Option<aspx::AspxQuantStep>,
pub aspx_frequency_tables: Option<aspx::AspxFrequencyTables>,
pub aspx_hfgen_iwc_1ch: Option<aspx::AspxHfgenIwc1Ch>,
pub aspx_hfgen_iwc_2ch: Option<aspx::AspxHfgenIwc2Ch>,
pub aspx_data_sig_primary: Option<Vec<aspx::AspxHuffEnv>>,
pub aspx_data_sig_secondary: Option<Vec<aspx::AspxHuffEnv>>,
pub aspx_data_noise_primary: Option<Vec<aspx::AspxHuffEnv>>,
pub aspx_data_noise_secondary: Option<Vec<aspx::AspxHuffEnv>>,
}
#[derive(Debug, Clone, Default)]
pub struct Ac4SubstreamInfo {
pub audio_size: u32,
pub audio_data_offset: u32,
pub tools: SubstreamTools,
}
pub fn resolve_transf_length(frame_len_base: u32, b_long_frame: bool, idx: u32) -> u32 {
if b_long_frame {
return frame_len_base;
}
if frame_len_base >= 1536 {
match (frame_len_base, idx & 0b11) {
(2048, 0) => 128,
(2048, 1) => 256,
(2048, 2) => 512,
(2048, 3) => 1024,
(1920, 0) => 120,
(1920, 1) => 240,
(1920, 2) => 480,
(1920, 3) => 960,
(1536, 0) => 96,
(1536, 1) => 192,
(1536, 2) => 384,
(1536, 3) => 768,
_ => 0,
}
} else {
match (frame_len_base, idx & 0b11) {
(1024, 0) => 128,
(1024, 1) => 256,
(1024, 2) => 512,
(1024, 3) => 1024,
(960, 0) => 120,
(960, 1) => 240,
(960, 2) => 480,
(960, 3) => 960,
(768, 0) => 96,
(768, 1) => 192,
(768, 2) => 384,
(768, 3) => 768,
(512, 0) => 128,
(512, 1) => 256,
(512, 2) => 512,
(384, 0) => 96,
(384, 1) => 192,
(384, 2) => 384,
_ => 0,
}
}
}
pub fn parse_asf_transform_info(
br: &mut BitReader<'_>,
frame_len_base: u32,
) -> Result<AsfTransformInfo> {
if frame_len_base >= 1536 {
let b_long_frame = br.read_bit()?;
if b_long_frame {
let tl = resolve_transf_length(frame_len_base, true, 0);
Ok(AsfTransformInfo {
b_long_frame: true,
transf_length: [0, 0],
transform_length_0: tl,
transform_length_1: tl,
})
} else {
let t0 = br.read_u32(2)?;
let t1 = br.read_u32(2)?;
Ok(AsfTransformInfo {
b_long_frame: false,
transf_length: [t0, t1],
transform_length_0: resolve_transf_length(frame_len_base, false, t0),
transform_length_1: resolve_transf_length(frame_len_base, false, t1),
})
}
} else {
let t0 = br.read_u32(2)?;
let len = resolve_transf_length(frame_len_base, false, t0);
Ok(AsfTransformInfo {
b_long_frame: false,
transf_length: [t0, t0],
transform_length_0: len,
transform_length_1: len,
})
}
}
pub fn parse_mono_audio_data_outer(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
let mode_bit = br.read_u32(1)?;
let mode = MonoCodecMode::from_bit(mode_bit);
tools.mono_mode = Some(mode);
if mode != MonoCodecMode::Simple {
if b_iframe {
tools.aspx_config = Some(aspx::parse_aspx_config(br)?);
}
tools.companding = Some(aspx::parse_companding_control(br, 1)?);
}
let sf_bit = br.read_u32(1)?;
let frontend = SpecFrontend::from_bit(sf_bit);
tools.spec_frontend_primary = Some(frontend);
if !b_iframe {
}
if let SpecFrontend::Asf = frontend {
let ti = parse_asf_transform_info(br, frame_len_base)?;
tools.transform_info_primary = Some(ti);
if let Ok(psy) = parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
tools.psy_info_primary = Some(psy);
let mut body_ok = false;
if ti.b_long_frame && tools.psy_info_primary.as_ref().unwrap().num_window_groups == 1 {
let psy_ref = tools.psy_info_primary.as_ref().unwrap().clone();
if let Some(scaled) = decode_asf_long_mono_body(br, &ti, &psy_ref) {
tools.scaled_spec_primary = Some(scaled);
body_ok = true;
}
}
if mode != MonoCodecMode::Simple && b_iframe && body_ok {
if let Some(cfg) = tools.aspx_config {
let xover = br.read_u32(3)? as u8;
tools.aspx_xover_subband_offset = Some(xover);
let nats = aspx::num_aspx_timeslots(frame_len_base);
let framing = aspx::parse_aspx_framing(br, &cfg, b_iframe, nats > 8)?;
let qmode = if matches!(framing.int_class, aspx::AspxIntClass::FixFix)
&& framing.num_env == 1
{
aspx::AspxQuantStep::Fine
} else {
cfg.quant_mode_env
};
tools.aspx_qmode_env_primary = Some(qmode);
let dd = aspx::parse_aspx_delta_dir(br, &framing)?;
if let Ok(tables) = aspx::derive_aspx_frequency_tables(&cfg, xover as u32) {
let hfgen = aspx::parse_aspx_hfgen_iwc_1ch(
br,
cfg.num_noise_sbgroups(),
tables.counts.num_sbg_sig_highres,
nats,
)?;
tools.aspx_hfgen_iwc_1ch = Some(hfgen);
let sig = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Signal,
framing.num_env,
&framing.freq_res,
qmode,
aspx::AspxStereoMode::Level,
&dd.sig_delta_dir,
tables.counts,
)?;
tools.aspx_data_sig_primary = Some(sig);
let noise = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Noise,
framing.num_noise,
&[],
aspx::AspxQuantStep::Fine,
aspx::AspxStereoMode::Level,
&dd.noise_delta_dir,
tables.counts,
)?;
tools.aspx_data_noise_primary = Some(noise);
tools.aspx_frequency_tables = Some(tables);
}
tools.aspx_delta_dir_primary = Some(dd);
tools.aspx_framing_primary = Some(framing);
}
}
}
}
Ok(())
}
fn decode_asf_long_mono_body(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
) -> Option<Vec<f32>> {
let tl = ti.transform_length_0;
let tl_idx = ti.transf_length[0];
let max_sfb_cap = tables::num_sfb_48(tl)?;
let max_sfb = psy.max_sfb_0.min(max_sfb_cap);
if max_sfb == 0 {
return None;
}
let sfbo = sfb_offset::sfb_offset_48(tl)?;
let sections = asf_data::parse_asf_section_data(br, tl_idx, tl, max_sfb).ok()?;
let (qspec, mqi) = asf_data::parse_asf_spectral_data(br, §ions, sfbo, max_sfb).ok()?;
let sf_gain = asf_data::parse_asf_scalefac_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let _snf = asf_data::parse_asf_snf_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let scaled = asf_data::dequantise_and_scale(&qspec, &sf_gain, sfbo, max_sfb);
Some(scaled)
}
pub fn parse_stereo_audio_data_outer(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
b_iframe: bool,
frame_len_base: u32,
) -> Result<()> {
let mode_bits = br.read_u32(2)?;
let mode = StereoCodecMode::from_u32(mode_bits);
tools.stereo_mode = Some(mode);
if mode != StereoCodecMode::Simple {
if b_iframe {
tools.aspx_config = Some(aspx::parse_aspx_config(br)?);
if matches!(
mode,
StereoCodecMode::AspxAcpl1 | StereoCodecMode::AspxAcpl2
) {
return Ok(());
}
}
let nc = match mode {
StereoCodecMode::Aspx => 2,
_ => 1,
};
tools.companding = Some(aspx::parse_companding_control(br, nc)?);
if matches!(mode, StereoCodecMode::Aspx) {
let body_ok = parse_stereo_data_body(br, tools, frame_len_base);
if b_iframe && body_ok {
if let Some(cfg) = tools.aspx_config {
let xover = br.read_u32(3)? as u8;
tools.aspx_xover_subband_offset = Some(xover);
let nats = aspx::num_aspx_timeslots(frame_len_base);
let framing_ch0 = aspx::parse_aspx_framing(br, &cfg, b_iframe, nats > 8)?;
let qmode_ch0 = if matches!(framing_ch0.int_class, aspx::AspxIntClass::FixFix)
&& framing_ch0.num_env == 1
{
aspx::AspxQuantStep::Fine
} else {
cfg.quant_mode_env
};
tools.aspx_qmode_env_primary = Some(qmode_ch0);
let balance = br.read_bit()?;
tools.aspx_balance = Some(balance);
let framing_ch1_ref;
if !balance {
let framing_ch1 = aspx::parse_aspx_framing(br, &cfg, b_iframe, nats > 8)?;
let qmode_ch1 =
if matches!(framing_ch1.int_class, aspx::AspxIntClass::FixFix)
&& framing_ch1.num_env == 1
{
aspx::AspxQuantStep::Fine
} else {
cfg.quant_mode_env
};
tools.aspx_qmode_env_secondary = Some(qmode_ch1);
tools.aspx_framing_secondary = Some(framing_ch1);
framing_ch1_ref = tools.aspx_framing_secondary.as_ref();
} else {
tools.aspx_qmode_env_secondary = Some(qmode_ch0);
framing_ch1_ref = Some(&framing_ch0);
}
let dd0 = aspx::parse_aspx_delta_dir(br, &framing_ch0)?;
let f_ch1 = framing_ch1_ref.unwrap_or(&framing_ch0);
let dd1 = aspx::parse_aspx_delta_dir(br, f_ch1)?;
if let Ok(tables) = aspx::derive_aspx_frequency_tables(&cfg, xover as u32) {
let hfgen = aspx::parse_aspx_hfgen_iwc_2ch(
br,
balance,
cfg.num_noise_sbgroups(),
tables.counts.num_sbg_sig_highres,
nats,
)?;
tools.aspx_hfgen_iwc_2ch = Some(hfgen);
let qmode_ch1_effective =
tools.aspx_qmode_env_secondary.unwrap_or(qmode_ch0);
let sig0 = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Signal,
framing_ch0.num_env,
&framing_ch0.freq_res,
qmode_ch0,
aspx::AspxStereoMode::Level,
&dd0.sig_delta_dir,
tables.counts,
)?;
tools.aspx_data_sig_primary = Some(sig0);
let sm_ch1 = if balance {
aspx::AspxStereoMode::Balance
} else {
aspx::AspxStereoMode::Level
};
let sig1 = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Signal,
f_ch1.num_env,
&f_ch1.freq_res,
qmode_ch1_effective,
sm_ch1,
&dd1.sig_delta_dir,
tables.counts,
)?;
tools.aspx_data_sig_secondary = Some(sig1);
let noise0 = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Noise,
framing_ch0.num_noise,
&[],
aspx::AspxQuantStep::Fine,
aspx::AspxStereoMode::Level,
&dd0.noise_delta_dir,
tables.counts,
)?;
tools.aspx_data_noise_primary = Some(noise0);
let noise1 = aspx::parse_aspx_ec_data(
br,
aspx::AspxDataType::Noise,
f_ch1.num_noise,
&[],
aspx::AspxQuantStep::Fine,
sm_ch1,
&dd1.noise_delta_dir,
tables.counts,
)?;
tools.aspx_data_noise_secondary = Some(noise1);
tools.aspx_frequency_tables = Some(tables);
}
tools.aspx_delta_dir_primary = Some(dd0);
tools.aspx_delta_dir_secondary = Some(dd1);
tools.aspx_framing_primary = Some(framing_ch0);
}
}
}
return Ok(());
}
let _ = parse_stereo_data_body(br, tools, frame_len_base);
let _ = b_iframe; Ok(())
}
fn parse_stereo_data_body(
br: &mut BitReader<'_>,
tools: &mut SubstreamTools,
frame_len_base: u32,
) -> bool {
let b_mdct_stereo = match br.read_bit() {
Ok(v) => v,
Err(_) => return false,
};
tools.mdct_stereo_proc = b_mdct_stereo;
if b_mdct_stereo {
tools.spec_frontend_primary = Some(SpecFrontend::Asf);
tools.spec_frontend_secondary = Some(SpecFrontend::Asf);
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_primary = Some(ti);
tools.transform_info_secondary = Some(ti);
let psy = match parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
Ok(p) => p,
Err(_) => return false,
};
tools.psy_info_primary = Some(psy.clone());
if ti.b_long_frame && psy.num_window_groups == 1 {
match decode_asf_long_stereo_joint_body(br, &ti, &psy) {
Some((l, r, ms)) => {
tools.scaled_spec_primary = Some(l);
tools.scaled_spec_secondary = Some(r);
tools.ms_used = Some(ms);
true
}
None => false,
}
} else {
false
}
} else {
let l = match br.read_u32(1) {
Ok(v) => SpecFrontend::from_bit(v),
Err(_) => return false,
};
tools.spec_frontend_primary = Some(l);
let mut ti_l: Option<AsfTransformInfo> = None;
let mut psy_l: Option<AsfPsyInfo> = None;
if let SpecFrontend::Asf = l {
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_primary = Some(ti);
ti_l = Some(ti);
if let Ok(psy) = parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
tools.psy_info_primary = Some(psy.clone());
psy_l = Some(psy);
} else {
return false;
}
}
let r = match br.read_u32(1) {
Ok(v) => SpecFrontend::from_bit(v),
Err(_) => return false,
};
tools.spec_frontend_secondary = Some(r);
let mut ti_r: Option<AsfTransformInfo> = None;
let mut psy_r: Option<AsfPsyInfo> = None;
if let SpecFrontend::Asf = r {
let ti = match parse_asf_transform_info(br, frame_len_base) {
Ok(t) => t,
Err(_) => return false,
};
tools.transform_info_secondary = Some(ti);
ti_r = Some(ti);
if let Ok(psy) = parse_asf_psy_info(br, &ti, frame_len_base, false, false) {
tools.psy_info_secondary = Some(psy.clone());
psy_r = Some(psy);
} else {
return false;
}
}
let mut body_ok = true;
if let (Some(ti), Some(psy)) = (ti_l, psy_l.as_ref()) {
if ti.b_long_frame && psy.num_window_groups == 1 {
tools.scaled_spec_primary = decode_asf_long_mono_body(br, &ti, psy);
if tools.scaled_spec_primary.is_none() {
body_ok = false;
}
} else {
body_ok = false;
}
}
if let (Some(ti), Some(psy)) = (ti_r, psy_r.as_ref()) {
if ti.b_long_frame && psy.num_window_groups == 1 {
tools.scaled_spec_secondary = decode_asf_long_mono_body(br, &ti, psy);
if tools.scaled_spec_secondary.is_none() {
body_ok = false;
}
} else {
body_ok = false;
}
}
body_ok
}
}
fn decode_asf_long_stereo_joint_body(
br: &mut BitReader<'_>,
ti: &AsfTransformInfo,
psy: &AsfPsyInfo,
) -> Option<(Vec<f32>, Vec<f32>, Vec<bool>)> {
let tl = ti.transform_length_0;
let tl_idx = ti.transf_length[0];
let max_sfb_cap = tables::num_sfb_48(tl)?;
let max_sfb = psy.max_sfb_0.min(max_sfb_cap);
if max_sfb == 0 {
return None;
}
let sfbo = sfb_offset::sfb_offset_48(tl)?;
let sections = asf_data::parse_asf_section_data(br, tl_idx, tl, max_sfb).ok()?;
let (q_l, mqi_l) = asf_data::parse_asf_spectral_data(br, §ions, sfbo, max_sfb).ok()?;
let (q_r, mqi_r) = asf_data::parse_asf_spectral_data(br, §ions, sfbo, max_sfb).ok()?;
let mqi: Vec<u32> = mqi_l
.iter()
.zip(mqi_r.iter())
.map(|(a, b)| (*a).max(*b))
.collect();
let sf_gain = asf_data::parse_asf_scalefac_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let mut ms_used = vec![false; max_sfb as usize];
for sfb in 0..max_sfb as usize {
let cb = sections.sfb_cb[sfb];
if cb == 0 || mqi[sfb] == 0 {
continue;
}
ms_used[sfb] = br.read_bit().ok()?;
}
let _ = asf_data::parse_asf_snf_data(br, §ions, &mqi, max_sfb, tl).ok()?;
let mut scaled_l = asf_data::dequantise_and_scale(&q_l, &sf_gain, sfbo, max_sfb);
let mut scaled_r = asf_data::dequantise_and_scale(&q_r, &sf_gain, sfbo, max_sfb);
for (sfb, &used) in ms_used.iter().enumerate() {
if !used {
continue;
}
let a = sfbo[sfb] as usize;
let b = sfbo[sfb + 1] as usize;
let bmax = b.min(scaled_l.len()).min(scaled_r.len());
for k in a..bmax {
let m = scaled_l[k];
let s = scaled_r[k];
scaled_l[k] = m + s;
scaled_r[k] = m - s;
}
}
Some((scaled_l, scaled_r, ms_used))
}
pub fn walk_ac4_substream(
substream_bytes: &[u8],
channels: u16,
b_iframe: bool,
frame_len_base: u32,
) -> Result<Ac4SubstreamInfo> {
if substream_bytes.is_empty() {
return Err(Error::invalid("ac4: empty substream"));
}
let mut br = BitReader::new(substream_bytes);
let audio_size_short = br.read_u32(15)?;
let b_more_bits = br.read_bit()?;
let audio_size = if b_more_bits {
audio_size_short + (variable_bits(&mut br, 7)? << 15)
} else {
audio_size_short
};
br.align_to_byte();
let audio_data_offset = br.byte_position() as u32;
let mut tools = SubstreamTools {
channel_mode_channels: channels,
..Default::default()
};
match channels {
1 => parse_mono_audio_data_outer(&mut br, &mut tools, b_iframe, frame_len_base)?,
2 => parse_stereo_audio_data_outer(&mut br, &mut tools, b_iframe, frame_len_base)?,
_ => {}
}
Ok(Ac4SubstreamInfo {
audio_size,
audio_data_offset,
tools,
})
}
#[cfg(test)]
mod tests {
use super::*;
use oxideav_core::bits::BitWriter;
#[test]
fn resolve_transf_length_long_frame_table_99() {
assert_eq!(resolve_transf_length(2048, true, 0), 2048);
assert_eq!(resolve_transf_length(1920, true, 3), 1920);
}
#[test]
fn resolve_transf_length_table_100_rows() {
assert_eq!(resolve_transf_length(2048, false, 0), 128);
assert_eq!(resolve_transf_length(2048, false, 1), 256);
assert_eq!(resolve_transf_length(2048, false, 2), 512);
assert_eq!(resolve_transf_length(2048, false, 3), 1024);
assert_eq!(resolve_transf_length(1920, false, 2), 480);
assert_eq!(resolve_transf_length(1536, false, 1), 192);
}
#[test]
fn resolve_transf_length_table_103_rows() {
assert_eq!(resolve_transf_length(1024, false, 3), 1024);
assert_eq!(resolve_transf_length(960, false, 2), 480);
assert_eq!(resolve_transf_length(512, false, 0), 128);
assert_eq!(resolve_transf_length(384, false, 2), 384);
}
#[test]
fn asf_transform_info_long_frame_path() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ti = parse_asf_transform_info(&mut br, 1920).unwrap();
assert!(ti.b_long_frame);
assert_eq!(ti.transform_length_0, 1920);
assert_eq!(ti.transform_length_1, 1920);
}
#[test]
fn asf_transform_info_short_pair() {
let mut bw = BitWriter::new();
bw.write_bit(false); bw.write_u32(2, 2);
bw.write_u32(3, 2);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let ti = parse_asf_transform_info(&mut br, 1920).unwrap();
assert!(!ti.b_long_frame);
assert_eq!(ti.transf_length, [2, 3]);
assert_eq!(ti.transform_length_0, 480);
assert_eq!(ti.transform_length_1, 960);
}
fn build_mono_substream() -> Vec<u8> {
let mut bw = BitWriter::new();
bw.write_u32(10, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 1);
bw.write_u32(0, 1);
bw.write_bit(true);
bw.write_u32(40, 6);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
bw.finish()
}
#[test]
fn walk_mono_asf_substream_extracts_tools() {
let bytes = build_mono_substream();
let info = walk_ac4_substream(&bytes, 1, true, 1920).unwrap();
assert_eq!(info.audio_size, 10);
assert_eq!(info.tools.mono_mode, Some(MonoCodecMode::Simple));
assert_eq!(info.tools.spec_frontend_primary, Some(SpecFrontend::Asf));
let ti = info.tools.transform_info_primary.unwrap();
assert!(ti.b_long_frame);
assert_eq!(ti.transform_length_0, 1920);
let psy = info.tools.psy_info_primary.as_ref().unwrap();
assert_eq!(psy.max_sfb_0, 40);
assert_eq!(psy.num_windows, 1);
assert_eq!(psy.num_window_groups, 1);
}
fn build_stereo_simple_substream() -> Vec<u8> {
let mut bw = BitWriter::new();
bw.write_u32(20, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 2);
bw.write_bit(false);
bw.write_u32(0, 1);
bw.write_bit(true); bw.write_u32(30, 6);
bw.write_u32(0, 1);
bw.write_bit(true);
bw.write_u32(32, 6);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
bw.finish()
}
#[test]
fn walk_stereo_simple_substream_extracts_two_frontends() {
let bytes = build_stereo_simple_substream();
let info = walk_ac4_substream(&bytes, 2, true, 1920).unwrap();
assert_eq!(info.audio_size, 20);
assert_eq!(info.tools.stereo_mode, Some(StereoCodecMode::Simple));
assert!(!info.tools.mdct_stereo_proc);
assert_eq!(info.tools.spec_frontend_primary, Some(SpecFrontend::Asf));
assert_eq!(info.tools.spec_frontend_secondary, Some(SpecFrontend::Asf));
assert!(info.tools.transform_info_primary.is_some());
assert!(info.tools.transform_info_secondary.is_some());
let psy_l = info.tools.psy_info_primary.as_ref().unwrap();
let psy_r = info.tools.psy_info_secondary.as_ref().unwrap();
assert_eq!(psy_l.max_sfb_0, 30);
assert_eq!(psy_r.max_sfb_0, 32);
}
#[test]
fn walk_stereo_mdct_joint_shares_transform_info() {
let mut bw = BitWriter::new();
bw.write_u32(20, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0, 2); bw.write_bit(true); bw.write_bit(true); bw.write_u32(25, 6); bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 2, true, 1920).unwrap();
assert!(info.tools.mdct_stereo_proc);
assert_eq!(info.tools.spec_frontend_primary, Some(SpecFrontend::Asf));
assert_eq!(info.tools.spec_frontend_secondary, Some(SpecFrontend::Asf));
assert_eq!(
info.tools
.transform_info_primary
.unwrap()
.transform_length_0,
info.tools
.transform_info_secondary
.unwrap()
.transform_length_0
);
let psy = info.tools.psy_info_primary.as_ref().unwrap();
assert_eq!(psy.max_sfb_0, 25);
}
#[test]
fn asf_psy_info_long_frame_path() {
let ti = AsfTransformInfo {
b_long_frame: true,
transf_length: [0, 0],
transform_length_0: 1920,
transform_length_1: 1920,
};
let mut bw = BitWriter::new();
bw.write_u32(50, 6); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let psy = parse_asf_psy_info(&mut br, &ti, 1920, false, false).unwrap();
assert!(!psy.b_different_framing);
assert_eq!(psy.max_sfb_0, 50);
assert_eq!(psy.num_windows, 1);
assert_eq!(psy.num_window_groups, 1);
assert!(psy.scale_factor_grouping.is_empty());
}
#[test]
fn asf_psy_info_short_pair_with_grouping() {
let ti = AsfTransformInfo {
b_long_frame: false,
transf_length: [2, 2],
transform_length_0: 480,
transform_length_1: 480,
};
let mut bw = BitWriter::new();
bw.write_u32(20, 6); bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(1, 1);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let psy = parse_asf_psy_info(&mut br, &ti, 1920, false, false).unwrap();
assert!(!psy.b_different_framing);
assert_eq!(psy.max_sfb_0, 20);
assert_eq!(psy.scale_factor_grouping, vec![1, 0, 1]);
assert_eq!(psy.num_windows, 4);
assert_eq!(psy.num_window_groups, 2);
}
#[test]
fn asf_psy_info_different_framing_path() {
let ti = AsfTransformInfo {
b_long_frame: false,
transf_length: [1, 2],
transform_length_0: 240,
transform_length_1: 480,
};
let mut bw = BitWriter::new();
bw.write_u32(10, 5); bw.write_u32(15, 6); bw.write_u32(0, 4);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let psy = parse_asf_psy_info(&mut br, &ti, 1920, false, false).unwrap();
assert!(psy.b_different_framing);
assert_eq!(psy.max_sfb_0, 10);
assert_eq!(psy.max_sfb_1, 15);
assert_eq!(psy.num_windows, 5);
}
#[test]
fn walk_mono_aspx_substream_parses_config_and_companding() {
let mut bw = BitWriter::new();
bw.write_u32(5, 15); bw.write_bit(false); bw.align_to_byte();
bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(3, 3);
bw.write_u32(1, 2);
bw.write_u32(1, 1);
bw.write_bit(true);
bw.write_bit(false);
bw.write_bit(true);
bw.write_u32(2, 2);
bw.write_u32(0, 1);
bw.write_u32(0, 2);
bw.write_bit(true);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 1, true, 1920).unwrap();
assert_eq!(info.tools.mono_mode, Some(MonoCodecMode::Aspx));
let cfg = info.tools.aspx_config.unwrap();
assert_eq!(cfg.start_freq, 3);
assert_eq!(cfg.stop_freq, 1);
assert_eq!(cfg.noise_sbg, 2);
assert_eq!(cfg.num_noise_sbgroups(), 3);
assert!(cfg.interpolation);
assert!(!cfg.preflat);
assert!(cfg.limiter);
assert!(cfg.signals_freq_res());
let cc = info.tools.companding.as_ref().unwrap();
assert_eq!(cc.compand_on, vec![true]);
assert!(cc.sync_flag.is_none());
assert!(cc.compand_avg.is_none());
}
#[test]
fn walk_stereo_aspx_substream_parses_config() {
let mut bw = BitWriter::new();
bw.write_u32(5, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0b01, 2); bw.write_u32(0, 15);
bw.write_bit(false);
bw.write_bit(true);
bw.write_bit(true);
bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 2, true, 1920).unwrap();
assert_eq!(info.tools.stereo_mode, Some(StereoCodecMode::Aspx));
let cfg = info.tools.aspx_config.unwrap();
assert_eq!(cfg.start_freq, 0);
let cc = info.tools.companding.as_ref().unwrap();
assert_eq!(cc.sync_flag, Some(false));
assert_eq!(cc.compand_on, vec![true, true]);
}
#[test]
fn walk_stereo_aspx_non_iframe_skips_config() {
let mut bw = BitWriter::new();
bw.write_u32(5, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0b01, 2); bw.write_bit(true); bw.write_bit(true); bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 2, false, 1920).unwrap();
assert_eq!(info.tools.stereo_mode, Some(StereoCodecMode::Aspx));
assert!(info.tools.aspx_config.is_none());
let cc = info.tools.companding.as_ref().unwrap();
assert_eq!(cc.sync_flag, Some(true));
assert_eq!(cc.compand_on, vec![true]);
}
fn write_sect_len_incr(bw: &mut BitWriter, sect_len: u32, n_sect_bits: u32, esc: u32) {
let base = sect_len.saturating_sub(1);
let k = base / esc;
let incr = base % esc;
for _ in 0..k {
bw.write_u32(esc, n_sect_bits);
}
bw.write_u32(incr, n_sect_bits);
}
#[test]
fn walk_mono_aspx_iframe_reads_framing_after_mono_data() {
use crate::huffman;
let mut bw = BitWriter::new();
bw.write_u32(200, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(1, 1);
bw.write_u32(0, 1); bw.write_u32(0, 3); bw.write_u32(0, 2); bw.write_u32(0, 1); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(0, 2); bw.write_u32(0, 1); bw.write_u32(3, 2); bw.write_bit(true);
bw.write_u32(0, 1);
bw.write_bit(true);
bw.write_u32(10, 6);
bw.write_u32(5, 4);
write_sect_len_incr(&mut bw, 10, 3, 7);
let sfbo = crate::sfb_offset::sfb_offset_48(1920).unwrap();
let end_line = sfbo[10] as u32;
let hcb = huffman::asf_hcb(5).unwrap();
let pairs = end_line / 2;
for _ in 0..pairs {
bw.write_u32(hcb.cw[40], hcb.len[40] as u32); }
bw.write_u32(120, 8);
bw.write_u32(0, 1);
bw.write_u32(3, 3);
bw.write_bit(false); bw.write_bit(true); bw.align_to_byte();
while bw.byte_len() < 220 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 1, true, 1920).unwrap();
assert_eq!(info.tools.mono_mode, Some(MonoCodecMode::Aspx));
assert!(info.tools.aspx_config.is_some());
assert!(info.tools.companding.is_some());
assert!(info.tools.transform_info_primary.is_some());
assert_eq!(info.tools.aspx_xover_subband_offset, Some(3));
let framing = info.tools.aspx_framing_primary.expect("framing");
assert_eq!(framing.int_class, aspx::AspxIntClass::FixFix);
assert_eq!(framing.num_env, 2);
assert_eq!(framing.num_noise, 2);
assert!(framing.freq_res.is_empty()); assert!(info.tools.aspx_framing_secondary.is_none());
assert!(info.tools.aspx_balance.is_none());
let dd = info.tools.aspx_delta_dir_primary.expect("delta dir");
assert_eq!(dd.sig_delta_dir, vec![false; 2]);
assert_eq!(dd.noise_delta_dir, vec![false; 2]);
assert_eq!(
info.tools.aspx_qmode_env_primary,
Some(aspx::AspxQuantStep::Fine)
);
}
#[test]
fn walk_mono_aspx_non_iframe_does_not_emit_framing() {
use crate::huffman;
let mut bw = BitWriter::new();
bw.write_u32(200, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(1, 1); bw.write_bit(true); bw.write_u32(0, 1); bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(5, 4); write_sect_len_incr(&mut bw, 10, 3, 7);
let sfbo = crate::sfb_offset::sfb_offset_48(1920).unwrap();
let end_line = sfbo[10] as u32;
let hcb = huffman::asf_hcb(5).unwrap();
for _ in 0..(end_line / 2) {
bw.write_u32(hcb.cw[40], hcb.len[40] as u32);
}
bw.write_u32(120, 8);
bw.write_u32(0, 1);
bw.align_to_byte();
while bw.byte_len() < 220 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 1, false, 1920).unwrap();
assert_eq!(info.tools.mono_mode, Some(MonoCodecMode::Aspx));
assert!(info.tools.aspx_config.is_none());
assert!(info.tools.aspx_framing_primary.is_none());
assert!(info.tools.aspx_xover_subband_offset.is_none());
}
#[test]
fn walk_mono_aspx_iframe_reads_ec_data_end_to_end() {
use crate::aspx::{
ASPX_HCB_ENV_LEVEL_15_DF, ASPX_HCB_ENV_LEVEL_15_F0, ASPX_HCB_NOISE_LEVEL_F0,
};
use crate::huffman;
let mut bw = BitWriter::new();
bw.write_u32(500, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(1, 1); bw.write_u32(0, 1); bw.write_u32(3, 3); bw.write_u32(3, 2); bw.write_u32(1, 1); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(0, 2); bw.write_u32(0, 1); bw.write_u32(3, 2); bw.write_bit(true);
bw.write_u32(0, 1); bw.write_bit(true); bw.write_u32(10, 6); bw.write_u32(5, 4);
write_sect_len_incr(&mut bw, 10, 3, 7);
let sfbo = crate::sfb_offset::sfb_offset_48(1920).unwrap();
let end_line = sfbo[10] as u32;
let hcb = huffman::asf_hcb(5).unwrap();
for _ in 0..(end_line / 2) {
bw.write_u32(hcb.cw[40], hcb.len[40] as u32);
}
bw.write_u32(120, 8); bw.write_u32(0, 1); bw.write_u32(7, 3); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false);
bw.write_bit(false);
bw.write_u32(0, 2);
bw.write_bit(false);
bw.write_bit(false);
bw.write_bit(false);
let f0_cb = &ASPX_HCB_ENV_LEVEL_15_F0;
let df_cb = &ASPX_HCB_ENV_LEVEL_15_DF;
bw.write_u32(f0_cb.cw[30], f0_cb.len[30] as u32);
bw.write_u32(df_cb.cw[70], df_cb.len[70] as u32);
bw.write_u32(df_cb.cw[70], df_cb.len[70] as u32);
let noise_f0 = &ASPX_HCB_NOISE_LEVEL_F0;
bw.write_u32(noise_f0.cw[6], noise_f0.len[6] as u32);
bw.align_to_byte();
while bw.byte_len() < 520 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 1, true, 1920).unwrap();
let tables = info.tools.aspx_frequency_tables.as_ref().expect("tables");
assert_eq!(tables.num_sbg_master, 10);
assert_eq!(tables.sba, 24);
assert_eq!(tables.sbz, 44);
assert_eq!(tables.counts.num_sbg_sig_highres, 3);
assert_eq!(tables.counts.num_sbg_sig_lowres, 2);
assert_eq!(tables.counts.num_sbg_noise, 1);
let hfgen = info.tools.aspx_hfgen_iwc_1ch.as_ref().expect("hfgen");
assert_eq!(hfgen.tna_mode, vec![0]);
assert!(!hfgen.ah_present);
assert!(!hfgen.fic_present);
assert!(!hfgen.tic_present);
let sig = info.tools.aspx_data_sig_primary.as_ref().expect("sig");
assert_eq!(sig.len(), 1);
assert_eq!(sig[0].values, vec![30, 0, 0]);
assert!(!sig[0].direction_time);
let noise = info.tools.aspx_data_noise_primary.as_ref().expect("noise");
assert_eq!(noise.len(), 1);
assert_eq!(noise[0].values, vec![6]);
assert!(!noise[0].direction_time);
}
#[test]
fn walk_stereo_aspx_acpl1_reads_config_and_stops_before_acpl() {
let mut bw = BitWriter::new();
bw.write_u32(5, 15);
bw.write_bit(false);
bw.align_to_byte();
bw.write_u32(0b10, 2); bw.write_u32(0, 15); bw.align_to_byte();
while bw.byte_len() < 32 {
bw.write_u32(0, 8);
}
let bytes = bw.finish();
let info = walk_ac4_substream(&bytes, 2, true, 1920).unwrap();
assert_eq!(info.tools.stereo_mode, Some(StereoCodecMode::AspxAcpl1));
assert!(info.tools.aspx_config.is_some());
assert!(info.tools.companding.is_none());
}
}