mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-14 06:22:24 +00:00
The SBR decoders now record the layout they were built for and are rebuilt when the decoder's layout differs, instead of being cleared in set_layout. An access unit whose new program config fails puts the layout back, and with it the SBR decoders still match. Refs #1121 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
893 lines
38 KiB
Rust
893 lines
38 KiB
Rust
//! AAC-LC decoder: raw access units (`raw_data_block`, ISO/IEC 14496-3 §4.4.2.1) → planar f32.
|
||
|
||
use craft_media::bitstream::BitReader;
|
||
|
||
use crate::aac::config::{AudioSpecificConfig, ElementType, ProgramConfig};
|
||
use crate::aac::huffman::{self, INTENSITY_HCB, INTENSITY_HCB2, NOISE_HCB, ZERO_HCB};
|
||
use crate::aac::ics::{IcsInfo, WindowSequence};
|
||
use crate::aac::mdct::{MdctScratch, WindowShape, mdct_long, mdct_short, window};
|
||
use crate::aac::sbr;
|
||
use crate::aac::tables::{pow43, pow43_table, sf_gain};
|
||
use crate::aac::tns::{self, TnsData};
|
||
use crate::aac::{Error, Result};
|
||
|
||
const ID_SCE: u32 = 0;
|
||
const ID_CPE: u32 = 1;
|
||
const ID_CCE: u32 = 2;
|
||
const ID_LFE: u32 = 3;
|
||
const ID_DSE: u32 = 4;
|
||
const ID_PCE: u32 = 5;
|
||
const ID_FIL: u32 = 6;
|
||
#[cfg(test)]
|
||
const ID_END: u32 = 7;
|
||
|
||
/// `extension_type` of a fill element carrying SBR data (ISO/IEC 14496-3 §4.5.2.8.2, Table 4.121).
|
||
const EXT_SBR_DATA: u32 = 13;
|
||
const EXT_SBR_DATA_CRC: u32 = 14;
|
||
|
||
/// The noise generator's state at the start of a stream and after [`Decoder::reset`].
|
||
const NOISE_SEED: u32 = 0x2545_F491;
|
||
|
||
/// One decoded `individual_channel_stream` before stereo processing and synthesis.
|
||
struct Ics {
|
||
info: IcsInfo,
|
||
/// Band type per (group, sfb).
|
||
band_type: [[u8; 64]; 8],
|
||
/// Scalefactor / noise energy / intensity position per (group, sfb).
|
||
sf: [[i32; 64]; 8],
|
||
tns: Option<TnsData>,
|
||
/// Dequantised spectrum (short windows at stride 128).
|
||
spec: Vec<f32>,
|
||
}
|
||
|
||
#[derive(Clone)]
|
||
struct ChannelState {
|
||
overlap: Vec<f32>,
|
||
prev_shape: WindowShape,
|
||
}
|
||
|
||
/// Buffers reused by every frame so the steady state does not allocate per channel.
|
||
#[derive(Clone)]
|
||
struct Scratch {
|
||
/// Quantised values of the channel being parsed.
|
||
q: Vec<i32>,
|
||
/// Windowed output of the current block (2048) and the raw inverse transform (2048 / 256).
|
||
buf: Vec<f32>,
|
||
y: Vec<f32>,
|
||
long: MdctScratch,
|
||
short: MdctScratch,
|
||
}
|
||
|
||
impl Scratch {
|
||
fn new() -> Scratch {
|
||
Scratch { q: vec![0; 1024], buf: vec![0.0; 2048], y: vec![0.0; 2048], long: mdct_long().scratch(), short: mdct_short().scratch() }
|
||
}
|
||
}
|
||
|
||
/// Where the SBR data of a channel element sits in its access unit: the bit range of the fill
|
||
/// element's payload after its extension type, and whether a CRC leads it.
|
||
type SbrRef = (usize, usize, bool);
|
||
|
||
/// An access unit's core output, and the SBR data of each layout entry.
|
||
type Decoded = (Vec<Vec<f32>>, Vec<Option<SbrRef>>);
|
||
|
||
/// AAC-LC decoder, with the SBR tool of HE-AAC when [`Decoder::set_sbr`] turns it on.
|
||
#[derive(Clone)]
|
||
pub struct Decoder {
|
||
asc: AudioSpecificConfig,
|
||
layout: Vec<(ElementType, u8)>,
|
||
/// First output channel of each layout entry.
|
||
offsets: Vec<usize>,
|
||
channels: Vec<ChannelState>,
|
||
noise_state: u32,
|
||
scratch: Scratch,
|
||
/// A fill element carried SBR data (implicitly signalled HE-AAC).
|
||
sbr_seen: bool,
|
||
/// The SBR decoder of each layout entry, when output is at twice the core rate.
|
||
sbr_out: Option<Vec<sbr::Element>>,
|
||
/// The layout `sbr_out` was built for. Kept apart from `layout`, so that an access unit whose
|
||
/// new program config fails (and whose layout is put back) leaves the SBR history alone.
|
||
sbr_layout: Vec<(ElementType, u8)>,
|
||
}
|
||
|
||
impl Decoder {
|
||
/// Create a decoder from an `AudioSpecificConfig`.
|
||
pub fn new(asc: &[u8]) -> Result<Decoder> {
|
||
Self::from_config(AudioSpecificConfig::parse(asc)?)
|
||
}
|
||
|
||
/// Create a decoder for an ADTS stream from its first header. With channel configuration 0 the
|
||
/// channel layout is taken from the program config element carried in the first frame that
|
||
/// decodes with it.
|
||
pub fn from_adts(header: &crate::aac::config::AdtsHeader) -> Result<Decoder> {
|
||
Self::from_config(header.to_asc())
|
||
}
|
||
|
||
/// Create a decoder from a parsed `AudioSpecificConfig`. Only audio object type 2 (AAC LC) is accepted.
|
||
pub fn from_config(asc: AudioSpecificConfig) -> Result<Decoder> {
|
||
if asc.object_type != 2 {
|
||
return Err(Error::Unsupported("audio object type other than AAC LC"));
|
||
}
|
||
if usize::from(asc.sf_index) >= crate::aac::SAMPLE_RATES.len() || asc.sample_rate == 0 {
|
||
return Err(Error::InvalidConfig("a sampling frequency index with no rate"));
|
||
}
|
||
let layout = asc.layout();
|
||
if layout.is_empty() && asc.channel_config != 0 {
|
||
return Err(Error::InvalidConfig("unsupported channel configuration"));
|
||
}
|
||
let mut d = Decoder {
|
||
asc,
|
||
layout: Vec::new(),
|
||
offsets: Vec::new(),
|
||
channels: Vec::new(),
|
||
noise_state: NOISE_SEED,
|
||
scratch: Scratch::new(),
|
||
sbr_seen: false,
|
||
sbr_out: None,
|
||
sbr_layout: Vec::new(),
|
||
};
|
||
d.set_layout(layout);
|
||
Ok(d)
|
||
}
|
||
|
||
fn set_layout(&mut self, layout: Vec<(ElementType, u8)>) {
|
||
let mut offsets = Vec::with_capacity(layout.len());
|
||
let mut n = 0;
|
||
for e in &layout {
|
||
offsets.push(n);
|
||
n += e.0.channels();
|
||
}
|
||
self.layout = layout;
|
||
self.offsets = offsets;
|
||
self.channels = vec![ChannelState { overlap: vec![0.0; 1024], prev_shape: WindowShape::Sine }; n];
|
||
}
|
||
|
||
/// The `AudioSpecificConfig` the decoder was created with.
|
||
pub fn config(&self) -> &AudioSpecificConfig {
|
||
&self.asc
|
||
}
|
||
/// Output sampling rate in Hz: the AAC-LC core rate, or twice it with [`Self::set_sbr`] on.
|
||
pub fn sample_rate(&self) -> u32 {
|
||
if self.sbr_out.is_some() { self.asc.sample_rate.saturating_mul(2) } else { self.asc.sample_rate }
|
||
}
|
||
/// Whether the stream is HE-AAC: SBR signalled in the `AudioSpecificConfig` (explicit) or SBR
|
||
/// data seen in a fill element of a decoded access unit (implicit). Without [`Self::set_sbr`],
|
||
/// only the AAC-LC core is decoded.
|
||
pub fn sbr(&self) -> bool {
|
||
self.asc.sbr || self.sbr_seen
|
||
}
|
||
/// Decode HE-AAC at twice the core rate (`true`) or the AAC-LC core alone (`false`, the
|
||
/// default). With SBR on, [`Self::decode`] returns 2048 samples per channel: the core through
|
||
/// the SBR filter banks, delayed by [`SBR_DELAY`](crate::aac::SBR_DELAY) samples, with the high
|
||
/// band rebuilt from each frame's SBR data. Until the stream's first SBR header, and for a
|
||
/// frame whose SBR data is missing or damaged, the high band is empty. Parametric stereo
|
||
/// (HE-AAC v2) is not decoded: its mono core stays mono.
|
||
pub fn set_sbr(&mut self, on: bool) {
|
||
if on != self.sbr_out.is_some() {
|
||
self.sbr_out = on.then(Vec::new);
|
||
}
|
||
}
|
||
/// Output channels of the current element layout.
|
||
pub fn channels(&self) -> usize {
|
||
self.channels.len()
|
||
}
|
||
/// Element layout in output channel order (C, L/R, surround pairs, LFE for configurations 1–7).
|
||
pub fn layout(&self) -> &[(ElementType, u8)] {
|
||
&self.layout
|
||
}
|
||
|
||
/// Clear the overlap and noise state (e.g. after a seek), so a seek to the same place always
|
||
/// gives the same samples.
|
||
pub fn reset(&mut self) {
|
||
self.noise_state = NOISE_SEED;
|
||
for c in &mut self.channels {
|
||
c.overlap.fill(0.0);
|
||
c.prev_shape = WindowShape::Sine;
|
||
}
|
||
for e in self.sbr_out.iter_mut().flatten() {
|
||
e.reset();
|
||
}
|
||
}
|
||
|
||
fn sf_index(&self) -> u8 {
|
||
self.asc.sf_index
|
||
}
|
||
|
||
/// Decode one access unit (one `raw_data_block`) into 1024 samples per channel (2048 with
|
||
/// [`Self::set_sbr`] on). An access unit that fails leaves the decoder as it was, but for
|
||
/// [`Self::sbr`]: its damage, including a damaged program config element, does not reach the
|
||
/// access units after it. Damaged SBR data alone does not fail it.
|
||
pub fn decode(&mut self, au: &[u8]) -> Result<Vec<Vec<f32>>> {
|
||
let before = (self.asc.pce.clone(), self.layout.clone(), self.offsets.clone(), self.channels.clone(), self.noise_state);
|
||
match self.decode_block(au, true) {
|
||
Ok((pcm, sbr_refs)) => Ok(self.apply_sbr(au, pcm, &sbr_refs)),
|
||
Err(e) => {
|
||
(self.asc.pce, self.layout, self.offsets, self.channels, self.noise_state) = before;
|
||
Err(e)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Run each layout entry's channels through its SBR decoder, when SBR is on.
|
||
fn apply_sbr(&mut self, au: &[u8], pcm: Vec<Vec<f32>>, refs: &[Option<SbrRef>]) -> Vec<Vec<f32>> {
|
||
let rate = self.sample_rate();
|
||
let Some(elements) = self.sbr_out.as_mut() else {
|
||
return pcm;
|
||
};
|
||
// a new layout (a program config element) starts the SBR decoders afresh: their history
|
||
// belongs to the old element order, even where the channel counts match
|
||
if elements.len() != self.layout.len() || self.sbr_layout != self.layout {
|
||
*elements = self.layout.iter().map(|l| sbr::Element::new(l.0.channels(), rate)).collect();
|
||
self.sbr_layout.clone_from(&self.layout);
|
||
}
|
||
let mut out = Vec::with_capacity(pcm.len());
|
||
for (i, e) in elements.iter_mut().enumerate() {
|
||
let first = self.offsets.get(i).copied().unwrap_or(0);
|
||
let core = pcm.get(first..first + e.channels()).unwrap_or_default();
|
||
let payload = refs.get(i).copied().flatten().map(|(start, end, crc)| sbr::Payload { data: au, start, end, crc });
|
||
out.extend(e.process(core, payload));
|
||
}
|
||
out
|
||
}
|
||
|
||
fn decode_block(&mut self, au: &[u8], allow_reconfig: bool) -> Result<Decoded> {
|
||
let mut br = BitReader::new(au);
|
||
let nch = self.channels.len();
|
||
let mut saw_element = false;
|
||
let mut out: Vec<Option<Vec<f32>>> = vec![None; nch];
|
||
let mut used = vec![false; self.layout.len()];
|
||
// SBR data belongs to the single channel or pair element before its fill element
|
||
let mut sbr_refs: Vec<Option<SbrRef>> = vec![None; self.layout.len()];
|
||
let mut last_slot = None;
|
||
loop {
|
||
let id = br.read_bits(3)?;
|
||
saw_element |= matches!(id, ID_SCE | ID_CPE | ID_LFE);
|
||
match id {
|
||
ID_SCE | ID_LFE => {
|
||
let tag = br.read_bits(4)? as u8;
|
||
let ty = if id == ID_SCE { ElementType::Sce } else { ElementType::Lfe };
|
||
let slot = self.find_slot(ty, tag, &used)?;
|
||
used[slot] = true;
|
||
last_slot = (id == ID_SCE).then_some(slot);
|
||
let mut ics = self.decode_ics(&mut br, None)?;
|
||
let ch = self.offsets[slot];
|
||
self.fill_noise(&mut ics, None);
|
||
out[ch] = Some(self.finish_channel(ch, &mut ics));
|
||
}
|
||
ID_CPE => {
|
||
let tag = br.read_bits(4)? as u8;
|
||
let slot = self.find_slot(ElementType::Cpe, tag, &used)?;
|
||
used[slot] = true;
|
||
last_slot = Some(slot);
|
||
let ch = self.offsets[slot];
|
||
let [a, b] = self.decode_cpe(&mut br, ch)?;
|
||
out[ch] = Some(a);
|
||
out[ch + 1] = Some(b);
|
||
}
|
||
ID_CCE => return Err(Error::Unsupported("coupling channel element")),
|
||
ID_DSE => {
|
||
br.skip(4)?;
|
||
let align = br.read_bits(1)? == 1;
|
||
let mut count = br.read_bits(8)? as usize;
|
||
if count == 255 {
|
||
count += br.read_bits(8)? as usize;
|
||
}
|
||
if align {
|
||
br.byte_align();
|
||
}
|
||
br.skip(count * 8)?;
|
||
}
|
||
ID_PCE => {
|
||
let pce = ProgramConfig::parse(&mut br)?;
|
||
if allow_reconfig && !saw_element && self.asc.channel_config == 0 && self.asc.pce.as_ref() != Some(&pce) {
|
||
let layout = pce.layout();
|
||
if !layout.is_empty() && layout.iter().map(|e| e.0.channels()).sum::<usize>() <= 64 {
|
||
self.asc.pce = Some(pce);
|
||
self.set_layout(layout);
|
||
return self.decode_block(au, false);
|
||
}
|
||
}
|
||
}
|
||
ID_FIL => {
|
||
let mut count = br.read_bits(4)? as usize;
|
||
if count == 15 {
|
||
count += br.read_bits(8)? as usize;
|
||
count -= 1;
|
||
}
|
||
if count > 0 {
|
||
let ext = br.read_bits(4)?;
|
||
let is_sbr = matches!(ext, EXT_SBR_DATA | EXT_SBR_DATA_CRC);
|
||
self.sbr_seen |= is_sbr;
|
||
let start = br.position();
|
||
br.skip(count * 8 - 4)?;
|
||
if let (true, Some(slot)) = (is_sbr, last_slot.take()) {
|
||
sbr_refs[slot] = Some((start, br.position(), ext == EXT_SBR_DATA_CRC));
|
||
}
|
||
}
|
||
}
|
||
_ => break, // ID_END
|
||
}
|
||
}
|
||
if nch == 0 && !saw_element {
|
||
return Err(Error::InvalidConfig("channel configuration 0 without a program config element"));
|
||
}
|
||
let mut res = Vec::with_capacity(nch);
|
||
for o in out {
|
||
res.push(o.ok_or(Error::Bitstream("access unit is missing a channel element"))?);
|
||
}
|
||
Ok((res, sbr_refs))
|
||
}
|
||
|
||
fn find_slot(&self, ty: ElementType, tag: u8, used: &[bool]) -> Result<usize> {
|
||
let free = |want: &dyn Fn(ElementType, u8) -> bool| self.layout.iter().enumerate().position(|(i, &(t, g))| want(t, g) && !used[i]);
|
||
let slot = if self.asc.channel_config == 0 {
|
||
// A program config names its elements' tags.
|
||
free(&|t, g| t == ty && g == tag)
|
||
} else {
|
||
// An indexed configuration does not: its elements are placed by the order they arrive
|
||
// in, whatever their tags. Some encoders code an LFE as an SCE, as ffmpeg allows.
|
||
free(&|t, _| t == ty).or_else(|| free(&|t, _| ty == ElementType::Sce && t == ElementType::Lfe))
|
||
};
|
||
slot.ok_or(Error::Bitstream("unexpected channel element for the channel configuration"))
|
||
}
|
||
|
||
fn decode_cpe(&mut self, br: &mut BitReader, ch: usize) -> Result<[Vec<f32>; 2]> {
|
||
let common_window = br.read_bits(1)? == 1;
|
||
let mut ms_mask_present = 0;
|
||
let mut ms_used = [[false; 64]; 8];
|
||
let mut info = None;
|
||
if common_window {
|
||
let i = IcsInfo::parse(br, self.sf_index())?;
|
||
ms_mask_present = br.read_bits(2)?;
|
||
if ms_mask_present == 3 {
|
||
return Err(Error::Bitstream("reserved ms_mask_present"));
|
||
}
|
||
if ms_mask_present == 1 {
|
||
for g in 0..i.num_groups {
|
||
for sfb in 0..i.max_sfb {
|
||
ms_used[g][sfb] = br.read_bits(1)? == 1;
|
||
}
|
||
}
|
||
} else if ms_mask_present == 2 {
|
||
ms_used = [[true; 64]; 8];
|
||
}
|
||
info = Some(i);
|
||
}
|
||
let mut l = self.decode_ics(br, info)?;
|
||
let mut r = self.decode_ics(br, info)?;
|
||
let ms = if ms_mask_present > 0 { Some(&ms_used) } else { None };
|
||
self.fill_noise(&mut l, None);
|
||
self.fill_noise(&mut r, if common_window { Some((&l, ms)) } else { None });
|
||
if common_window {
|
||
let swb = l.info.swb_offsets(self.sf_index());
|
||
for g in 0..l.info.num_groups {
|
||
let w0 = l.info.group_start(g);
|
||
for sfb in 0..l.info.max_sfb {
|
||
let (bl, br_) = (l.band_type[g][sfb], r.band_type[g][sfb]);
|
||
let range = swb[sfb] as usize..swb[sfb + 1] as usize;
|
||
if br_ == INTENSITY_HCB || br_ == INTENSITY_HCB2 {
|
||
let mut c: f32 = if br_ == INTENSITY_HCB { 1.0 } else { -1.0 };
|
||
if ms_mask_present > 0 && ms_used[g][sfb] {
|
||
c = -c;
|
||
}
|
||
let scale = c * (-0.25 * r.sf[g][sfb].clamp(-400, 400) as f64).exp2() as f32;
|
||
for w in w0..w0 + l.info.group_len[g] as usize {
|
||
for k in range.clone() {
|
||
r.spec[w * 128 + k] = l.spec[w * 128 + k] * scale;
|
||
}
|
||
}
|
||
} else if ms_mask_present > 0 && ms_used[g][sfb] && bl < NOISE_HCB && br_ < NOISE_HCB {
|
||
for w in w0..w0 + l.info.group_len[g] as usize {
|
||
for k in range.clone() {
|
||
let (m, s) = (l.spec[w * 128 + k], r.spec[w * 128 + k]);
|
||
l.spec[w * 128 + k] = m + s;
|
||
r.spec[w * 128 + k] = m - s;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Ok([self.finish_channel(ch, &mut l), self.finish_channel(ch + 1, &mut r)])
|
||
}
|
||
|
||
fn decode_ics(&mut self, br: &mut BitReader, common: Option<IcsInfo>) -> Result<Ics> {
|
||
let sfi = self.sf_index();
|
||
let global_gain = br.read_bits(8)? as i32;
|
||
let info = match common {
|
||
Some(i) => i,
|
||
None => IcsInfo::parse(br, sfi)?,
|
||
};
|
||
let swb = info.swb_offsets(sfi);
|
||
let num_swb = swb.len() - 1;
|
||
let mut ics = Ics { info, band_type: [[0; 64]; 8], sf: [[0; 64]; 8], tns: None, spec: vec![0.0; 1024] };
|
||
|
||
// section_data
|
||
let (sect_bits, esc) = if info.is_short() { (3, 7) } else { (5, 31) };
|
||
for g in 0..info.num_groups {
|
||
let mut k = 0;
|
||
while k < info.max_sfb {
|
||
let cb = br.read_bits(4)? as u8;
|
||
if cb == 12 {
|
||
return Err(Error::Bitstream("reserved codebook 12"));
|
||
}
|
||
let mut len = 0usize;
|
||
loop {
|
||
let inc = br.read_bits(sect_bits)?;
|
||
len += inc as usize;
|
||
if inc != esc {
|
||
break;
|
||
}
|
||
}
|
||
if k + len > info.max_sfb {
|
||
return Err(Error::Bitstream("section exceeds max_sfb"));
|
||
}
|
||
if len == 0 {
|
||
// zero-length sections are legal but useless; guard against endless loops
|
||
if br.bits_left() == 0 {
|
||
return Err(Error::Eof);
|
||
}
|
||
}
|
||
for sfb in k..k + len {
|
||
ics.band_type[g][sfb] = cb;
|
||
}
|
||
k += len;
|
||
}
|
||
}
|
||
|
||
// scale_factor_data
|
||
let mut sf = global_gain;
|
||
let mut noise = global_gain - 90;
|
||
let mut is_pos = 0i32;
|
||
let mut noise_first = true;
|
||
for g in 0..info.num_groups {
|
||
for sfb in 0..info.max_sfb {
|
||
match ics.band_type[g][sfb] {
|
||
ZERO_HCB => ics.sf[g][sfb] = 0,
|
||
INTENSITY_HCB | INTENSITY_HCB2 => {
|
||
is_pos += huffman::decode_sf(br)?;
|
||
ics.sf[g][sfb] = is_pos;
|
||
}
|
||
NOISE_HCB => {
|
||
if noise_first {
|
||
noise += br.read_bits(9)? as i32 - 256;
|
||
noise_first = false;
|
||
} else {
|
||
noise += huffman::decode_sf(br)?;
|
||
}
|
||
ics.sf[g][sfb] = noise;
|
||
}
|
||
_ => {
|
||
sf += huffman::decode_sf(br)?;
|
||
if !(0..=255).contains(&sf) {
|
||
return Err(Error::Bitstream("scalefactor out of range"));
|
||
}
|
||
ics.sf[g][sfb] = sf;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// pulse_data
|
||
let mut pulses: Vec<(usize, i32)> = Vec::new();
|
||
if br.read_bits(1)? == 1 {
|
||
if info.is_short() {
|
||
return Err(Error::Bitstream("pulse data in short window"));
|
||
}
|
||
let n = br.read_bits(2)? as usize + 1;
|
||
let start = br.read_bits(6)? as usize;
|
||
if start >= num_swb {
|
||
return Err(Error::Bitstream("pulse_start_sfb out of range"));
|
||
}
|
||
let mut k = swb[start] as usize;
|
||
for _ in 0..n {
|
||
k += br.read_bits(5)? as usize;
|
||
let amp = br.read_bits(4)? as i32;
|
||
if k >= 1024 {
|
||
return Err(Error::Bitstream("pulse position out of range"));
|
||
}
|
||
pulses.push((k, amp));
|
||
}
|
||
}
|
||
if br.read_bits(1)? == 1 {
|
||
ics.tns = Some(TnsData::parse(br, &info)?);
|
||
}
|
||
if br.read_bits(1)? == 1 {
|
||
return Err(Error::Unsupported("gain control data (AAC SSR)"));
|
||
}
|
||
|
||
// spectral_data
|
||
let mut q = std::mem::take(&mut self.scratch.q);
|
||
q.clear();
|
||
q.resize(1024, 0);
|
||
let mut vals = [0i32; 4];
|
||
for g in 0..info.num_groups {
|
||
let w0 = info.group_start(g);
|
||
let glen = info.group_len[g] as usize;
|
||
for sfb in 0..info.max_sfb {
|
||
let cb = ics.band_type[g][sfb];
|
||
if cb == ZERO_HCB || cb >= NOISE_HCB {
|
||
continue;
|
||
}
|
||
let dim = huffman::book(cb).dim;
|
||
let (lo, hi) = (swb[sfb] as usize, swb[sfb + 1] as usize);
|
||
for w in w0..w0 + glen {
|
||
let base = w * 128;
|
||
let mut k = lo;
|
||
while k < hi {
|
||
huffman::decode_spectral(br, cb, &mut vals)?;
|
||
q[base + k..base + k + dim].copy_from_slice(&vals[..dim]);
|
||
k += dim;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
for (k, amp) in pulses {
|
||
if q[k] > 0 {
|
||
q[k] += amp;
|
||
} else {
|
||
q[k] -= amp;
|
||
}
|
||
}
|
||
|
||
// dequantise
|
||
let t43 = pow43_table();
|
||
for g in 0..info.num_groups {
|
||
let w0 = info.group_start(g);
|
||
for sfb in 0..info.max_sfb {
|
||
let cb = ics.band_type[g][sfb];
|
||
if cb == ZERO_HCB || cb >= NOISE_HCB {
|
||
continue;
|
||
}
|
||
let gain = sf_gain(ics.sf[g][sfb]);
|
||
for w in w0..w0 + info.group_len[g] as usize {
|
||
let r = w * 128 + swb[sfb] as usize..w * 128 + swb[sfb + 1] as usize;
|
||
for (o, &v) in ics.spec[r.clone()].iter_mut().zip(&q[r]) {
|
||
let a = v.unsigned_abs();
|
||
let m = t43.get(a as usize).copied().unwrap_or_else(|| pow43(a)) * gain;
|
||
*o = if v < 0 { -m } else { m };
|
||
}
|
||
}
|
||
}
|
||
}
|
||
self.scratch.q = q;
|
||
Ok(ics)
|
||
}
|
||
|
||
fn next_random(&mut self) -> f32 {
|
||
self.noise_state = self.noise_state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
|
||
(self.noise_state as i32) as f32
|
||
}
|
||
|
||
/// Perceptual noise substitution. `pair` = (left channel, ms mask) for the right channel of a CPE:
|
||
/// bands that are noise in both channels with `ms_used` reuse the left channel's noise vector.
|
||
fn fill_noise(&mut self, ics: &mut Ics, pair: Option<(&Ics, Option<&[[bool; 64]; 8]>)>) {
|
||
let info = ics.info;
|
||
let swb = info.swb_offsets(self.sf_index());
|
||
for g in 0..info.num_groups {
|
||
let w0 = info.group_start(g);
|
||
for sfb in 0..info.max_sfb {
|
||
if ics.band_type[g][sfb] != NOISE_HCB {
|
||
continue;
|
||
}
|
||
let (lo, hi) = (swb[sfb] as usize, swb[sfb + 1] as usize);
|
||
let target = (0.25 * ics.sf[g][sfb].clamp(-400, 400) as f64).exp2();
|
||
let correlated = match pair {
|
||
Some((l, Some(ms))) => ms[g][sfb] && l.band_type[g][sfb] == NOISE_HCB,
|
||
_ => false,
|
||
};
|
||
for w in w0..w0 + info.group_len[g] as usize {
|
||
let band = w * 128 + lo..w * 128 + hi;
|
||
if correlated && let Some((l, _)) = pair {
|
||
ics.spec[band.clone()].copy_from_slice(&l.spec[band.clone()]);
|
||
} else {
|
||
for k in band.clone() {
|
||
ics.spec[k] = self.next_random();
|
||
}
|
||
}
|
||
let energy: f64 = ics.spec[band.clone()].iter().map(|&v| v as f64 * v as f64).sum();
|
||
let scale = if energy > 0.0 { (target / energy.sqrt()) as f32 } else { 0.0 };
|
||
for k in band {
|
||
ics.spec[k] *= scale;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// TNS + filterbank for a single channel element; returns output samples.
|
||
fn finish_channel(&mut self, ch: usize, ics: &mut Ics) -> Vec<f32> {
|
||
if let Some(t) = &ics.tns {
|
||
tns::apply_decoder(&mut ics.spec, &ics.info, self.sf_index(), t);
|
||
}
|
||
synth(&mut self.channels[ch], &mut self.scratch, &ics.info, &ics.spec)
|
||
}
|
||
}
|
||
|
||
/// Inverse MDCT, windowing and overlap-add (§4.6.18), output scaled to ±1.0 full scale.
|
||
fn synth(state: &mut ChannelState, sc: &mut Scratch, info: &IcsInfo, spec: &[f32]) -> Vec<f32> {
|
||
let Scratch { buf, y, long, short, .. } = sc;
|
||
buf.fill(0.0);
|
||
let prev = state.prev_shape;
|
||
let cur = info.window_shape;
|
||
let norm = 1.0 / 32768.0;
|
||
match info.window_sequence {
|
||
WindowSequence::EightShort => {
|
||
let t = mdct_short();
|
||
let y = &mut y[..256];
|
||
let wc = window(cur, false);
|
||
for w in 0..8 {
|
||
t.inverse_with(&spec[w * 128..(w + 1) * 128], y, short);
|
||
let wl = window(if w == 0 { prev } else { cur }, false);
|
||
let off = 448 + 128 * w;
|
||
let s = norm / 128.0;
|
||
for n in 0..128 {
|
||
buf[off + n] += y[n] * wl[n] * s;
|
||
buf[off + 128 + n] += y[128 + n] * wc[127 - n] * s;
|
||
}
|
||
}
|
||
}
|
||
seq => {
|
||
mdct_long().inverse_with(&spec[..1024], y, long);
|
||
let s = norm / 1024.0;
|
||
match seq {
|
||
WindowSequence::LongStop => {
|
||
let ws = window(prev, false);
|
||
for n in 448..576 {
|
||
buf[n] = y[n] * ws[n - 448] * s;
|
||
}
|
||
for n in 576..1024 {
|
||
buf[n] = y[n] * s;
|
||
}
|
||
}
|
||
_ => {
|
||
let wl = window(prev, true);
|
||
for n in 0..1024 {
|
||
buf[n] = y[n] * wl[n] * s;
|
||
}
|
||
}
|
||
}
|
||
match seq {
|
||
WindowSequence::LongStart => {
|
||
let ws = window(cur, false);
|
||
for n in 1024..1472 {
|
||
buf[n] = y[n] * s;
|
||
}
|
||
for n in 1472..1600 {
|
||
buf[n] = y[n] * ws[127 - (n - 1472)] * s;
|
||
}
|
||
}
|
||
_ => {
|
||
let wl = window(cur, true);
|
||
for n in 1024..2048 {
|
||
buf[n] = y[n] * wl[2047 - n] * s;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// A hostile stream can overflow f32 (PNS at full energy through a resonant TNS filter); that must
|
||
// not reach the output or the next frame's overlap.
|
||
for s in buf.iter_mut().filter(|s| !s.is_finite()) {
|
||
*s = 0.0;
|
||
}
|
||
let out: Vec<f32> = (0..1024).map(|n| state.overlap[n] + buf[n]).collect();
|
||
state.overlap.copy_from_slice(&buf[1024..]);
|
||
state.prev_shape = cur;
|
||
out
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use craft_media::bitstream::BitWriter;
|
||
|
||
/// Implicitly signalled HE-AAC (an AAC-LC config) is recognised from the SBR fill element.
|
||
#[test]
|
||
fn sbr_fill_element_marks_implicit_he_aac() {
|
||
// AAC-LC, 22.05 kHz, stereo
|
||
let mut dec = Decoder::new(&[0x13, 0x90]).unwrap();
|
||
assert!(!dec.sbr());
|
||
// ID_FIL, count 1, extension_type EXT_SBR_DATA (+ 4 bits of payload), ID_END
|
||
let _ = dec.decode(&[0b1100_0011, 0b1010_0001, 0b1100_0000]);
|
||
assert!(dec.sbr());
|
||
// other fill payloads (EXT_FILL) don't
|
||
let mut dec = Decoder::new(&[0x13, 0x90]).unwrap();
|
||
let _ = dec.decode(&[0b1100_0010, 0b0000_0001, 0b1100_0000]);
|
||
assert!(!dec.sbr());
|
||
// explicit signalling: AOT 5, core 22.05 kHz, stereo, extension 44.1 kHz, core AOT 2
|
||
let dec = Decoder::new(&[0x2B, 0x92, 0x08, 0x00]).unwrap();
|
||
assert!(dec.sbr());
|
||
assert_eq!(dec.sample_rate(), 22_050);
|
||
}
|
||
|
||
/// With SBR on, an access unit decodes to 2048 samples, and over a silent core the high band
|
||
/// its SBR fill element describes is there; with SBR off, the core alone (silence).
|
||
#[test]
|
||
fn sbr_fill_elements_rebuild_the_high_band() {
|
||
let au = sbr_au(0);
|
||
// AAC-LC, 22.05 kHz, mono
|
||
let asc = AudioSpecificConfig::lc(7, 1);
|
||
let mut dec = Decoder::from_config(asc.clone()).unwrap();
|
||
let core = dec.decode(&au).unwrap();
|
||
assert_eq!((core[0].len(), dec.sample_rate()), (1024, 22_050));
|
||
assert!(dec.sbr() && core[0].iter().all(|&s| s == 0.0));
|
||
let mut dec = Decoder::from_config(asc).unwrap();
|
||
dec.set_sbr(true);
|
||
assert_eq!(dec.sample_rate(), 44_100);
|
||
let mut out = Vec::new();
|
||
for _ in 0..4 {
|
||
out.extend(dec.decode(&au).unwrap().remove(0));
|
||
}
|
||
assert_eq!(out.len(), 4 * 2048);
|
||
let rms = (out[2048..].iter().map(|s| s * s).sum::<f32>() / (out.len() - 2048) as f32).sqrt();
|
||
assert!(rms > 1e-4, "{rms}");
|
||
}
|
||
|
||
/// A program config that changes the layout restarts the SBR decoders, even where the
|
||
/// channel counts match, and one in an access unit that fails leaves their history alone: the
|
||
/// access units after it decode as if it had not been there.
|
||
#[test]
|
||
fn a_new_layout_restarts_sbr_and_a_failed_one_keeps_it() {
|
||
let au = sbr_au(0);
|
||
let one = ProgramConfig { front: vec![(false, 0)], ..Default::default() };
|
||
let six = ProgramConfig { front: vec![(false, 0), (true, 0)], back: vec![(true, 1)], lfe: vec![0], ..Default::default() };
|
||
let decoder = || {
|
||
let mut d = Decoder::from_config(AudioSpecificConfig { pce: Some(one.clone()), ..AudioSpecificConfig::lc(7, 0) }).unwrap();
|
||
d.set_sbr(true);
|
||
d
|
||
};
|
||
let (mut kept, mut failed) = (decoder(), decoder());
|
||
for _ in 0..3 {
|
||
assert_eq!(kept.decode(&au).unwrap(), failed.decode(&au).unwrap());
|
||
}
|
||
// the new layout's elements are missing from this access unit
|
||
assert!(failed.decode(&silent_au(Some(&six), &[(ID_SCE, 0)])).is_err());
|
||
for _ in 0..2 {
|
||
assert_eq!(kept.decode(&au).unwrap(), failed.decode(&au).unwrap());
|
||
}
|
||
// the same channel count under another tag is a new layout: the SBR decoder starts over,
|
||
// as in a decoder that had that layout from the start
|
||
let tag1 = ProgramConfig { front: vec![(false, 1)], ..Default::default() };
|
||
let mut fresh = Decoder::from_config(AudioSpecificConfig { pce: Some(tag1.clone()), ..AudioSpecificConfig::lc(7, 0) }).unwrap();
|
||
fresh.set_sbr(true);
|
||
kept.set_layout(tag1.layout());
|
||
assert_eq!(kept.decode(&sbr_au(1)).unwrap(), fresh.decode(&sbr_au(1)).unwrap());
|
||
}
|
||
|
||
/// One access unit of SCE `tag` with an SBR fill element of [`sbr::test_payload`] (44.1 kHz
|
||
/// output).
|
||
fn sbr_au(tag: u32) -> Vec<u8> {
|
||
let payload = sbr::test_payload(44_100, |_| 40, |_| 0).unwrap();
|
||
let mut bw = BitWriter::new();
|
||
bw.write_bits(ID_SCE, 3);
|
||
bw.write_bits(tag, 4);
|
||
bw.write_bits(0, 8 + 11 + 3); // global_gain, ics_info (max_sfb 0), no pulse/TNS/gain control
|
||
bw.write_bits(ID_FIL, 3);
|
||
let count = (4 + 8 * payload.len()).div_ceil(8);
|
||
if count < 15 {
|
||
bw.write_bits(count as u32, 4);
|
||
} else {
|
||
bw.write_bits(15, 4);
|
||
bw.write_bits((count - 14) as u32, 8);
|
||
}
|
||
bw.write_bits(EXT_SBR_DATA, 4);
|
||
for &b in &payload {
|
||
bw.write_bits(u32::from(b), 8);
|
||
}
|
||
bw.write_bits(0, 4);
|
||
bw.write_bits(ID_END, 3);
|
||
bw.finish()
|
||
}
|
||
|
||
/// An access unit whose elements are all empty: `elements` are (id, tag), then ID_END.
|
||
fn silent_au(pce: Option<&ProgramConfig>, elements: &[(u32, u32)]) -> Vec<u8> {
|
||
let mut bw = BitWriter::new();
|
||
if let Some(pce) = pce {
|
||
bw.write_bits(ID_PCE, 3);
|
||
pce.write(&mut bw).unwrap();
|
||
}
|
||
for &(id, tag) in elements {
|
||
bw.write_bits(id, 3);
|
||
bw.write_bits(tag, 4);
|
||
if id == ID_CPE {
|
||
bw.write_bits(0, 1); // common_window
|
||
}
|
||
for _ in 0..if id == ID_CPE { 2 } else { 1 } {
|
||
bw.write_bits(0, 8); // global_gain
|
||
bw.write_bits(0, 11); // ics_info: long window, max_sfb 0, no predictor
|
||
bw.write_bits(0, 3); // no pulse, TNS or gain control data
|
||
}
|
||
}
|
||
bw.write_bits(ID_END, 3);
|
||
bw.finish()
|
||
}
|
||
|
||
/// Indexed configurations place elements by arrival order, whatever their tags (as ffmpeg does).
|
||
#[test]
|
||
fn indexed_configurations_ignore_element_tags() {
|
||
let dec = Decoder::from_config(AudioSpecificConfig::lc(4, 5)).unwrap();
|
||
// SCE, CPE, CPE: the first CPE to arrive is L/R even when tagged 1
|
||
assert_eq!(dec.find_slot(ElementType::Cpe, 1, &[true, false, false]), Ok(1));
|
||
assert_eq!(dec.find_slot(ElementType::Cpe, 0, &[true, true, false]), Ok(2));
|
||
}
|
||
|
||
#[test]
|
||
fn program_configs_place_elements_by_tag() {
|
||
let pce = ProgramConfig { front: vec![(true, 3), (true, 5)], ..Default::default() };
|
||
let asc = AudioSpecificConfig { pce: Some(pce), ..AudioSpecificConfig::lc(4, 0) };
|
||
let dec = Decoder::from_config(asc).unwrap();
|
||
assert_eq!(dec.find_slot(ElementType::Cpe, 5, &[false, false]), Ok(1));
|
||
}
|
||
|
||
/// A program config lists its elements: one it does not list has no place.
|
||
#[test]
|
||
fn program_configs_refuse_unlisted_tags() {
|
||
let pce = ProgramConfig { front: vec![(true, 3), (true, 5)], ..Default::default() };
|
||
let asc = AudioSpecificConfig { pce: Some(pce), ..AudioSpecificConfig::lc(4, 0) };
|
||
let dec = Decoder::from_config(asc).unwrap();
|
||
assert!(dec.find_slot(ElementType::Cpe, 9, &[false, false]).is_err());
|
||
assert!(dec.find_slot(ElementType::Sce, 3, &[false, false]).is_err());
|
||
}
|
||
|
||
/// Some encoders code 5.1's LFE as an SCE, as ffmpeg allows. Nothing else changes type: an LFE
|
||
/// is not written to the centre channel.
|
||
#[test]
|
||
fn indexed_configurations_refuse_elements_of_another_type() {
|
||
let dec = Decoder::from_config(AudioSpecificConfig::lc(4, 6)).unwrap();
|
||
// SCE, CPE, CPE, LFE
|
||
assert_eq!(dec.find_slot(ElementType::Sce, 1, &[true, false, false, false]), Ok(3));
|
||
assert!(dec.find_slot(ElementType::Lfe, 1, &[false, false, false, true]).is_err());
|
||
assert!(dec.find_slot(ElementType::Cpe, 2, &[false, true, true, false]).is_err());
|
||
}
|
||
|
||
/// A PCE in an access unit that then fails leaves the layout as it was.
|
||
#[test]
|
||
fn a_damaged_in_band_pce_does_not_change_the_layout() {
|
||
let two = ProgramConfig { front: vec![(true, 0)], ..Default::default() };
|
||
let six = ProgramConfig { front: vec![(false, 0), (true, 0)], back: vec![(true, 1)], lfe: vec![0], ..Default::default() };
|
||
let mut dec = Decoder::from_config(AudioSpecificConfig { pce: Some(two.clone()), ..AudioSpecificConfig::lc(4, 0) }).unwrap();
|
||
let good = silent_au(None, &[(ID_CPE, 0)]);
|
||
assert_eq!(dec.decode(&good).unwrap().len(), 2);
|
||
// the new layout's elements are missing from this access unit
|
||
assert!(dec.decode(&silent_au(Some(&six), &[(ID_CPE, 0)])).is_err());
|
||
assert_eq!((dec.channels(), dec.config().pce.as_ref()), (2, Some(&two)));
|
||
assert_eq!(dec.decode(&good).unwrap().len(), 2);
|
||
// one that decodes does change it
|
||
let full = silent_au(Some(&six), &[(ID_SCE, 0), (ID_CPE, 0), (ID_CPE, 1), (ID_LFE, 0)]);
|
||
assert_eq!(dec.decode(&full).unwrap().len(), 6);
|
||
assert_eq!(dec.channels(), 6);
|
||
}
|
||
|
||
/// A failed access unit leaves no trace: the next one decodes as if it had not been there.
|
||
#[test]
|
||
fn a_failed_access_unit_leaves_the_state_as_it_was() {
|
||
let mut dec = Decoder::from_config(AudioSpecificConfig::lc(4, 1)).unwrap();
|
||
dec.channels[0].overlap.fill(0.5);
|
||
dec.channels[0].prev_shape = WindowShape::Kbd;
|
||
let noise = dec.noise_state;
|
||
// a complete channel element, then the access unit ends without ID_END
|
||
let au = silent_au(None, &[(ID_SCE, 0)]);
|
||
assert!(dec.decode(&au[..au.len() - 1]).is_err());
|
||
assert!(dec.channels[0].overlap.iter().all(|&s| s == 0.5));
|
||
assert_eq!((dec.channels[0].prev_shape, dec.noise_state), (WindowShape::Kbd, noise));
|
||
}
|
||
|
||
#[test]
|
||
fn a_reset_restarts_the_noise_generator() {
|
||
let mut dec = Decoder::from_config(AudioSpecificConfig::lc(4, 1)).unwrap();
|
||
let first = dec.next_random();
|
||
dec.next_random();
|
||
dec.reset();
|
||
assert_eq!(dec.next_random(), first);
|
||
}
|
||
|
||
#[test]
|
||
fn non_finite_spectra_do_not_reach_the_output_or_the_overlap() {
|
||
let mut state = ChannelState { overlap: vec![0.0; 1024], prev_shape: WindowShape::Sine };
|
||
let info = IcsInfo::parse(&mut BitReader::new(&[0; 2]), 4).unwrap();
|
||
let out = synth(&mut state, &mut Scratch::new(), &info, &vec![f32::INFINITY; 1024]);
|
||
assert!(out.iter().chain(&state.overlap).all(|s| s.is_finite()));
|
||
}
|
||
}
|