use crate::adts::{AdtsHeader, ADTS_HEADER_BYTES_NO_CRC, ADTS_SAMPLE_RATES_HZ};
use crate::encoder_tns::detect_and_apply_tns;
use crate::filterbank::{
forward_mdct, long_sequence_window, short_window_j, SHORT_SEQ_HOP, SHORT_SEQ_START,
};
use crate::ics_body::IcsBody;
use crate::ics_info::{
IcsInfo, WindowSequence, WindowShape, NUM_SWB_LONG_WINDOW, NUM_SWB_SHORT_WINDOW,
};
use crate::pulse_data::{Pulse, PulseData, MAX_PULSES};
use crate::raw_data_block::{FrameAssembler, IdSynEle};
use crate::scale_factor_data::{
differentiate, AbsoluteScaleFactorEntry, AbsoluteScaleFactors, NOISE_OFFSET,
};
use crate::section_data::{
Section, SectionData, INTENSITY_HCB, INTENSITY_HCB2, NOISE_HCB, ZERO_HCB,
};
use crate::spectral_codebook::MAX_QUANT;
use crate::spectral_data::SpectralData;
use crate::swb_offset::{
long_window_offsets, short_window_offsets, LONG_WINDOW_LEN, SHORT_WINDOW_LEN,
};
use crate::tns_data::TnsData;
use crate::{Error, Result};
use oxideav_core::bits::BitWriter;
pub use crate::codec_encoder::make_encoder;
pub const FRAME_LEN: usize = LONG_WINDOW_LEN as usize;
const LONG_TRANSFORM_LEN: usize = 2 * FRAME_LEN;
const SF_OFFSET: i32 = 100;
const MAX_SF_DELTA: i32 = 60;
const TARGET_PEAK_MAG: f64 = 42.0;
const SPREAD: f64 = 0.5;
const MIN_TARGET_MAG: f64 = 0.7;
const MAX_RATE_ITERATIONS: usize = 48;
const MAX_REFINE_OFFSET: i32 = 32;
const PNS_MIN_WIDTH: usize = 8;
const PNS_DENSITY_MIN: f64 = 0.4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EncoderConfig {
pub sample_rate: u32,
pub channels: u8,
pub bitrate: u32,
}
impl EncoderConfig {
fn fs_index(&self) -> Result<u8> {
ADTS_SAMPLE_RATES_HZ
.iter()
.position(|&r| r == self.sample_rate)
.filter(|&i| i < NUM_SWB_LONG_WINDOW.len())
.map(|i| i as u8)
.ok_or(Error::EncoderInvalidConfig)
}
fn frame_budget_bytes(&self) -> usize {
let bits = (self.bitrate as u64 * FRAME_LEN as u64) / self.sample_rate.max(1) as u64;
let bytes = (bits / 8) as usize;
bytes.saturating_sub(ADTS_HEADER_BYTES_NO_CRC).max(16)
}
fn channel_configuration(&self) -> Result<u8> {
match self.channels {
n @ 1..=6 => Ok(n),
8 => Ok(7),
_ => Err(Error::EncoderInvalidConfig),
}
}
}
#[derive(Debug, Clone, Copy)]
enum ElementPlan {
Sce(usize),
Cpe(usize, usize),
Lfe(usize),
}
fn element_plan(channel_configuration: u8) -> Result<Vec<ElementPlan>> {
use ElementPlan::*;
Ok(match channel_configuration {
1 => vec![Sce(0)],
2 => vec![Cpe(0, 1)],
3 => vec![Sce(0), Cpe(1, 2)],
4 => vec![Sce(0), Cpe(1, 2), Sce(3)],
5 => vec![Sce(0), Cpe(1, 2), Cpe(3, 4)],
6 => vec![Sce(0), Cpe(1, 2), Cpe(3, 4), Lfe(5)],
7 => vec![Sce(0), Cpe(1, 2), Cpe(3, 4), Cpe(5, 6), Lfe(7)],
_ => return Err(Error::EncoderInvalidConfig),
})
}
const LFE_MAX_LINES: usize = 12;
#[derive(Debug, Clone)]
pub struct StreamEncoder {
config: EncoderConfig,
fs_index: u8,
channel_configuration: u8,
element_src: Vec<usize>,
lfe_slot: Vec<bool>,
history: Vec<Vec<f64>>,
prev_seq: WindowSequence,
short_pending: bool,
pns_enabled: bool,
tns_enabled: bool,
is_enabled: bool,
}
impl StreamEncoder {
pub fn new(config: EncoderConfig) -> Result<Self> {
let fs_index = config.fs_index()?;
let channel_configuration = config.channel_configuration()?;
if config.bitrate == 0 {
return Err(Error::EncoderInvalidConfig);
}
let ch = config.channels as usize;
let perm = crate::channel_map::reorder_permutation(channel_configuration)
.ok_or(Error::EncoderInvalidConfig)?;
let mut element_src = vec![0usize; ch];
for (i, &j) in perm.iter().enumerate() {
element_src[j] = i;
}
let mut lfe_slot = vec![false; ch];
for elem in element_plan(channel_configuration)? {
if let ElementPlan::Lfe(slot) = elem {
lfe_slot[slot] = true;
}
}
Ok(Self {
config,
fs_index,
channel_configuration,
element_src,
lfe_slot,
history: vec![vec![0.0; FRAME_LEN]; ch],
prev_seq: WindowSequence::OnlyLong,
short_pending: false,
pns_enabled: false,
tns_enabled: true,
is_enabled: false,
})
}
pub fn set_pns(&mut self, enabled: bool) {
self.pns_enabled = enabled;
}
pub fn set_tns(&mut self, enabled: bool) {
self.tns_enabled = enabled;
}
pub fn set_intensity_stereo(&mut self, enabled: bool) {
self.is_enabled = enabled;
}
pub fn config(&self) -> &EncoderConfig {
&self.config
}
pub fn encode_frame(&mut self, interleaved: &[i16]) -> Result<Vec<u8>> {
let ch = self.config.channels as usize;
if interleaved.len() > FRAME_LEN * ch || interleaved.len() % ch != 0 {
return Err(Error::EncoderInvalidConfig);
}
let mut cur: Vec<Vec<f64>> = vec![vec![0.0; FRAME_LEN]; ch];
for (j, chan) in cur.iter_mut().enumerate() {
let src = self.element_src[j];
for (n, slot) in chan.iter_mut().take(interleaved.len() / ch).enumerate() {
*slot = f64::from(interleaved[n * ch + src]);
}
}
let frame = self.encode_hop(&cur, &[])?;
self.history = cur;
Ok(frame)
}
pub fn finish(&mut self) -> Result<Vec<u8>> {
let ch = self.config.channels as usize;
let zeros: Vec<Vec<f64>> = vec![vec![0.0; FRAME_LEN]; ch];
let frame = self.encode_hop(&zeros, &[])?;
self.history = zeros;
Ok(frame)
}
pub(crate) fn encode_frame_with_fills(
&mut self,
interleaved: &[i16],
fills: &[Vec<u8>],
) -> Result<Vec<u8>> {
let ch = self.config.channels as usize;
if interleaved.len() > FRAME_LEN * ch || interleaved.len() % ch != 0 {
return Err(Error::EncoderInvalidConfig);
}
let mut cur: Vec<Vec<f64>> = vec![vec![0.0; FRAME_LEN]; ch];
for (j, chan) in cur.iter_mut().enumerate() {
let src = self.element_src[j];
for (n, slot) in chan.iter_mut().take(interleaved.len() / ch).enumerate() {
*slot = f64::from(interleaved[n * ch + src]);
}
}
let frame = self.encode_hop(&cur, fills)?;
self.history = cur;
Ok(frame)
}
pub fn encode_all(&mut self, interleaved: &[i16]) -> Result<Vec<u8>> {
let ch = self.config.channels as usize;
if interleaved.len() % ch != 0 {
return Err(Error::EncoderInvalidConfig);
}
let mut out = Vec::new();
let hop = FRAME_LEN * ch;
let mut chunks = interleaved.chunks(hop);
let first = chunks.next().unwrap_or(&[]);
out.extend_from_slice(&self.encode_frame(first)?);
for chunk in chunks {
out.extend_from_slice(&self.encode_frame(chunk)?);
}
out.extend_from_slice(&self.finish()?);
Ok(out)
}
fn encode_hop(&mut self, cur: &[Vec<f64>], fills: &[Vec<u8>]) -> Result<Vec<u8>> {
let ch = self.config.channels as usize;
let transient = self
.history
.iter()
.zip(cur.iter())
.zip(self.lfe_slot.iter())
.any(|((h, c), &lfe)| !lfe && detect_transient(h, c));
let seq = if self.short_pending {
WindowSequence::EightShort
} else if transient && self.prev_seq != WindowSequence::EightShort {
WindowSequence::LongStart
} else if self.prev_seq == WindowSequence::EightShort {
if transient {
WindowSequence::EightShort
} else {
WindowSequence::LongStop
}
} else {
WindowSequence::OnlyLong
};
self.short_pending = transient;
let mut spectra: Vec<Vec<f64>> = Vec::with_capacity(ch);
for ((hist, chan), &lfe) in self
.history
.iter()
.zip(cur.iter())
.zip(self.lfe_slot.iter())
{
let ch_seq = if lfe { WindowSequence::OnlyLong } else { seq };
spectra.push(analyze_channel(hist, chan, ch_seq)?);
}
self.prev_seq = seq;
let max_sfb = if seq == WindowSequence::EightShort {
NUM_SWB_SHORT_WINDOW[self.fs_index as usize]
} else {
NUM_SWB_LONG_WINDOW[self.fs_index as usize]
};
let mut tns: Vec<Option<TnsData>> = vec![None; ch];
if self.tns_enabled {
for (j, (spec, slot)) in spectra.iter_mut().zip(tns.iter_mut()).enumerate() {
if self.lfe_slot[j] {
continue; }
let permit = tns_temporal_permits(&self.history[j], &cur[j], seq);
*slot = detect_and_apply_tns(spec, seq, max_sfb, self.fs_index, &permit)?;
}
}
let fill_bytes: usize = fills
.iter()
.filter(|f| !f.is_empty())
.map(|f| f.len() + if f.len() >= 15 { 2 } else { 1 })
.sum();
let budget = self
.config
.frame_budget_bytes()
.saturating_sub(fill_bytes)
.max(16);
let mut sf_offset = 0i32;
let mut raw_block = self.assemble_raw_block(seq, &spectra, &tns, sf_offset, fills)?;
let mut iterations = 0usize;
if raw_block.len() > budget {
while raw_block.len() > budget && iterations < MAX_RATE_ITERATIONS {
sf_offset += 4;
raw_block = self.assemble_raw_block(seq, &spectra, &tns, sf_offset, fills)?;
iterations += 1;
}
} else {
while sf_offset > -MAX_REFINE_OFFSET && iterations < MAX_RATE_ITERATIONS {
let finer = self.assemble_raw_block(seq, &spectra, &tns, sf_offset - 4, fills)?;
if finer.len() > budget {
break;
}
sf_offset -= 4;
raw_block = finer;
iterations += 1;
}
}
if raw_block.len() <= budget && sf_offset > -MAX_REFINE_OFFSET {
for fine in [3i32, 2, 1] {
let cand = self.assemble_raw_block(seq, &spectra, &tns, sf_offset - fine, fills)?;
if cand.len() <= budget {
raw_block = cand;
break;
}
}
}
let frame_len = ADTS_HEADER_BYTES_NO_CRC + raw_block.len();
if frame_len >= (1 << 13) {
return Err(Error::EncoderFrameOverflow);
}
let header = AdtsHeader {
mpeg_version_mpeg2: false,
protection_absent: true,
profile: 1, sampling_frequency_index: self.fs_index,
channel_configuration: self.channel_configuration,
aac_frame_length: frame_len as u16,
adts_buffer_fullness: 0x7FF, number_of_raw_data_blocks_in_frame: 1,
};
let mut out = Vec::with_capacity(frame_len);
out.extend_from_slice(&header.write()?);
out.extend_from_slice(&raw_block);
Ok(out)
}
fn assemble_raw_block(
&self,
seq: WindowSequence,
spectra: &[Vec<f64>],
tns: &[Option<TnsData>],
sf_offset: i32,
fills: &[Vec<u8>],
) -> Result<Vec<u8>> {
let mut asm = FrameAssembler::new();
let plan = element_plan(self.channel_configuration)?;
let mut sce_tag = 0u8;
let mut cpe_tag = 0u8;
let mut lfe_tag = 0u8;
for (i, elem) in plan.into_iter().enumerate() {
let mut body_bits = BitWriter::new();
match elem {
ElementPlan::Sce(ch) => {
asm.push_channel_header(IdSynEle::Sce, sce_tag)?;
sce_tag += 1;
self.assemble_sce(&mut body_bits, &spectra[ch], &tns[ch], seq, sf_offset)?;
}
ElementPlan::Lfe(ch) => {
asm.push_channel_header(IdSynEle::Lfe, lfe_tag)?;
lfe_tag += 1;
self.assemble_lfe(&mut body_bits, &spectra[ch], sf_offset)?;
}
ElementPlan::Cpe(l, r) => {
asm.push_channel_header(IdSynEle::Cpe, cpe_tag)?;
cpe_tag += 1;
self.assemble_cpe(
&mut body_bits,
&spectra[l],
&spectra[r],
&tns[l],
&tns[r],
seq,
sf_offset,
)?;
}
}
let nbits = body_bits.bit_position();
asm.push_channel_body_bits(&body_bits.finish(), nbits)?;
if let Some(fill) = fills.get(i).filter(|f| !f.is_empty()) {
asm.push_fill(fill)?;
}
}
Ok(asm.push_end())
}
#[allow(clippy::too_many_arguments)]
fn quantize_seq(
&self,
spec: &[f64],
seq: WindowSequence,
sf_offset: i32,
peak: f64,
pns_bands: &[bool],
is_bands: &[IsBand],
) -> Result<QuantizedChannel> {
if seq == WindowSequence::EightShort {
quantize_channel_short(spec, self.fs_index, sf_offset, peak)
} else {
quantize_channel(
spec,
seq,
self.fs_index,
sf_offset,
peak,
pns_bands,
is_bands,
)
}
}
fn assemble_sce(
&self,
body_bits: &mut BitWriter,
spec: &[f64],
tns: &Option<TnsData>,
seq: WindowSequence,
sf_offset: i32,
) -> Result<()> {
let pns_long = self.pns_enabled && seq != WindowSequence::EightShort;
let num_swb_long = NUM_SWB_LONG_WINDOW[self.fs_index as usize] as usize;
let peak = spec.iter().fold(0.0f64, |m, &v| m.max(v.abs()));
let pns_bands = if pns_long {
vec![true; num_swb_long]
} else {
Vec::new()
};
let mut chan = self.quantize_seq(spec, seq, sf_offset, peak, &pns_bands, &[])?;
attach_tns(&mut chan, tns);
chan.body.write(body_bits, 2, self.fs_index, false)?;
chan.spectral.write(
body_bits,
&chan.info,
&chan.body.section_data,
self.fs_index,
)
}
fn assemble_lfe(&self, body_bits: &mut BitWriter, spec: &[f64], sf_offset: i32) -> Result<()> {
let mut lfe_spec = spec.to_vec();
for c in lfe_spec[LFE_MAX_LINES..].iter_mut() {
*c = 0.0;
}
let peak = lfe_spec.iter().fold(0.0f64, |m, &v| m.max(v.abs()));
let chan = self.quantize_seq(
&lfe_spec,
WindowSequence::OnlyLong,
sf_offset,
peak,
&[],
&[],
)?;
chan.body.write(body_bits, 2, self.fs_index, false)?;
chan.spectral.write(
body_bits,
&chan.info,
&chan.body.section_data,
self.fs_index,
)
}
#[allow(clippy::too_many_arguments)]
fn assemble_cpe(
&self,
body_bits: &mut BitWriter,
l_spec: &[f64],
r_spec: &[f64],
tns_l: &Option<TnsData>,
tns_r: &Option<TnsData>,
seq: WindowSequence,
sf_offset: i32,
) -> Result<()> {
let pns_long = self.pns_enabled && seq != WindowSequence::EightShort;
let is_bands: Vec<IsBand> = if self.is_enabled && seq != WindowSequence::EightShort {
is_decide(l_spec, r_spec, self.fs_index)?
} else {
Vec::new()
};
let pns = if pns_long {
pns_decide_pair(l_spec, r_spec, &is_bands, self.fs_index)?
} else {
PairPns::default()
};
let (ms_rows, mut left, mut right);
if seq == WindowSequence::EightShort {
let combined: Vec<f64> = l_spec
.iter()
.zip(r_spec.iter())
.map(|(&l, &r)| (l * l + r * r).sqrt())
.collect();
let offsets = short_window_offsets(self.fs_index)?;
let num_swb = NUM_SWB_SHORT_WINDOW[self.fs_index as usize] as usize;
let wgl = decide_short_grouping(&combined, offsets, num_swb);
let rows = ms_decide_short(l_spec, r_spec, offsets, num_swb, &wgl);
let (code_l, code_r) = if rows.iter().flatten().any(|&b| b) {
apply_ms_short(l_spec, r_spec, &rows, offsets, &wgl)
} else {
(l_spec.to_vec(), r_spec.to_vec())
};
let pair_peak = code_l
.iter()
.chain(code_r.iter())
.fold(0.0f64, |m, &v| m.max(v.abs()));
left = quantize_channel_short_grouped(
&code_l,
self.fs_index,
sf_offset,
pair_peak,
wgl.clone(),
)?;
right =
quantize_channel_short_grouped(&code_r, self.fs_index, sf_offset, pair_peak, wgl)?;
ms_rows = rows;
} else {
let mut ms_used = ms_decide(l_spec, r_spec, self.fs_index)?;
for (sfb, band) in is_bands.iter().enumerate() {
if band.is_some() {
if let Some(m) = ms_used.get_mut(sfb) {
*m = false;
}
}
}
for (sfb, m) in ms_used.iter_mut().enumerate() {
let l_n = pns.l_noise.get(sfb).copied().unwrap_or(false);
let r_n = pns.r_noise.get(sfb).copied().unwrap_or(false);
if l_n || r_n {
*m = pns.shared.get(sfb).copied().unwrap_or(false);
}
}
let ms_transform: Vec<bool> = ms_used
.iter()
.enumerate()
.map(|(sfb, &m)| {
m && !pns.l_noise.get(sfb).copied().unwrap_or(false)
&& !pns.r_noise.get(sfb).copied().unwrap_or(false)
})
.collect();
let (code_l, code_r) = if ms_transform.iter().any(|&b| b) {
apply_ms(l_spec, r_spec, &ms_transform, self.fs_index)?
} else {
(l_spec.to_vec(), r_spec.to_vec())
};
let pair_peak = code_l
.iter()
.chain(code_r.iter())
.fold(0.0f64, |m, &v| m.max(v.abs()));
left = self.quantize_seq(&code_l, seq, sf_offset, pair_peak, &pns.l_noise, &[])?;
right =
self.quantize_seq(&code_r, seq, sf_offset, pair_peak, &pns.r_noise, &is_bands)?;
ms_rows = vec![ms_used];
}
attach_tns(&mut left, tns_l);
attach_tns(&mut right, tns_r);
body_bits.write_bit(true);
left.info.write(body_bits, 2, self.fs_index, true)?;
let any_ms = ms_rows.iter().flatten().any(|&b| b);
let all_ms = !ms_rows.is_empty()
&& ms_rows.iter().all(|row| !row.is_empty())
&& ms_rows.iter().flatten().all(|&b| b);
if all_ms {
body_bits.write_u32(2, 2); } else if any_ms {
body_bits.write_u32(1, 2);
for &b in ms_rows.iter().flatten() {
body_bits.write_bit(b);
}
} else {
body_bits.write_u32(0, 2);
}
for chan in [&left, &right] {
chan.body
.write_with_ics_info(body_bits, &chan.info, 2, false)?;
chan.spectral.write(
body_bits,
&chan.info,
&chan.body.section_data,
self.fs_index,
)?;
}
Ok(())
}
}
fn attach_tns(chan: &mut QuantizedChannel, slot: &Option<TnsData>) {
if let Some(t) = slot {
chan.body.tns_data_present = true;
chan.body.tns_data = Some(t.clone());
}
}
const TRANSIENT_SUBBLOCK: usize = SHORT_SEQ_HOP;
const TRANSIENT_RATIO: f64 = 12.0;
const TRANSIENT_FLOOR: f64 = 128.0 * 180.0 * 180.0;
const TRANSIENT_ARM: f64 = TRANSIENT_FLOOR / TRANSIENT_RATIO;
fn detect_transient(hist: &[f64], cur: &[f64]) -> bool {
let energies: Vec<f64> = hist
.chunks(TRANSIENT_SUBBLOCK)
.chain(cur.chunks(TRANSIENT_SUBBLOCK))
.map(|b| b.iter().map(|&v| v * v).sum())
.collect();
let hist_blocks = hist.len() / TRANSIENT_SUBBLOCK;
for (j, &e) in energies.iter().enumerate().skip(hist_blocks) {
let ctx = &energies[j.saturating_sub(hist_blocks.max(1))..j];
let reference = ctx.iter().fold(0.0f64, |m, &v| m.max(v));
if e > TRANSIENT_FLOOR && reference > TRANSIENT_ARM && e > TRANSIENT_RATIO * reference {
return true;
}
}
false
}
const TNS_TEMPORAL_RATIO: f64 = 3.0;
const TNS_TEMPORAL_FLOOR: f64 = 128.0 * 90.0 * 90.0;
fn tns_temporal_permits(hist: &[f64], cur: &[f64], seq: WindowSequence) -> Vec<bool> {
let region = |i: usize| -> f64 {
if i < FRAME_LEN {
hist[i]
} else {
cur[i - FRAME_LEN]
}
};
let flatness_permits = |base: usize, len: usize| -> bool {
let sub = len / 16;
let energies: Vec<f64> = (0..16)
.map(|j| {
(0..sub)
.map(|m| {
let v = region(base + j * sub + m);
v * v
})
.sum()
})
.collect();
let mean = energies.iter().sum::<f64>() / 16.0;
let max = energies.iter().fold(0.0f64, |a, &b| a.max(b));
let floor = TNS_TEMPORAL_FLOOR * sub as f64 / 128.0;
mean > floor && max > TNS_TEMPORAL_RATIO * mean
};
if seq == WindowSequence::EightShort {
(0..8)
.map(|j| {
flatness_permits(
SHORT_SEQ_START + j * SHORT_SEQ_HOP,
2 * SHORT_WINDOW_LEN as usize,
)
})
.collect()
} else {
vec![flatness_permits(0, LONG_TRANSFORM_LEN)]
}
}
fn analyze_channel(hist: &[f64], cur: &[f64], seq: WindowSequence) -> Result<Vec<f64>> {
debug_assert_eq!(hist.len(), FRAME_LEN);
debug_assert_eq!(cur.len(), FRAME_LEN);
let region = |i: usize| -> f64 {
if i < FRAME_LEN {
hist[i]
} else {
cur[i - FRAME_LEN]
}
};
if seq == WindowSequence::EightShort {
let short_len = SHORT_WINDOW_LEN as usize; let n_s = 2 * short_len; let mut out = Vec::with_capacity(8 * short_len);
for j in 0..8 {
let w = short_window_j(j, WindowShape::Sine, WindowShape::Sine);
let base = SHORT_SEQ_START + j * SHORT_SEQ_HOP;
let seg: Vec<f64> = (0..n_s).map(|m| region(base + m) * w[m]).collect();
out.extend_from_slice(&forward_mdct(&seg, n_s));
}
Ok(out)
} else {
let w = long_sequence_window(seq, WindowShape::Sine, WindowShape::Sine)?;
let z: Vec<f64> = (0..LONG_TRANSFORM_LEN).map(|m| region(m) * w[m]).collect();
Ok(forward_mdct(&z, LONG_TRANSFORM_LEN))
}
}
struct QuantizedChannel {
info: IcsInfo,
body: IcsBody,
spectral: SpectralData,
}
type IsBand = Option<(u8, i32)>;
const IS_MIN_SPECTRAL_LINE: usize = FRAME_LEN / 4;
pub const IS_CORR_MIN: f64 = 0.95;
const IS_PEAK_FLOOR_RATIO: f64 = 3e-3;
fn is_decide(l_spec: &[f64], r_spec: &[f64], fs_index: u8) -> Result<Vec<IsBand>> {
let offsets = long_window_offsets(fs_index)?;
let num_swb = NUM_SWB_LONG_WINDOW[fs_index as usize] as usize;
let frame_peak = l_spec
.iter()
.chain(r_spec.iter())
.fold(0.0f64, |m, &v| m.max(v.abs()));
let peak_floor = frame_peak * IS_PEAK_FLOOR_RATIO;
let mut out: Vec<IsBand> = vec![None; num_swb];
for (sfb, slot) in out.iter_mut().enumerate() {
let start = offsets[sfb] as usize;
let end = offsets[sfb + 1] as usize;
if start < IS_MIN_SPECTRAL_LINE {
continue;
}
let mut e_l = 0.0f64;
let mut e_r = 0.0f64;
let mut dot = 0.0f64;
let mut band_peak = 0.0f64;
for k in start..end {
e_l += l_spec[k] * l_spec[k];
e_r += r_spec[k] * r_spec[k];
dot += l_spec[k] * r_spec[k];
band_peak = band_peak.max(l_spec[k].abs()).max(r_spec[k].abs());
}
if e_l <= 0.0 || e_r <= 0.0 || band_peak < peak_floor {
continue;
}
let corr = dot.abs() / (e_l * e_r).sqrt();
if corr < IS_CORR_MIN {
continue;
}
let pos = (2.0 * (e_l / e_r).log2()).round();
if !(-80.0..=80.0).contains(&pos) {
continue;
}
let cb = if dot >= 0.0 {
INTENSITY_HCB
} else {
INTENSITY_HCB2
};
*slot = Some((cb, pos as i32));
}
Ok(out)
}
pub const PNS_CORR_MIN: f64 = 0.5;
#[derive(Debug, Default)]
struct PairPns {
l_noise: Vec<bool>,
r_noise: Vec<bool>,
shared: Vec<bool>,
}
fn pns_decide_pair(
l_spec: &[f64],
r_spec: &[f64],
is_bands: &[IsBand],
fs_index: u8,
) -> Result<PairPns> {
let offsets = long_window_offsets(fs_index)?;
let num_swb = NUM_SWB_LONG_WINDOW[fs_index as usize] as usize;
let mut out = PairPns {
l_noise: vec![false; num_swb],
r_noise: vec![false; num_swb],
shared: vec![false; num_swb],
};
for sfb in 0..num_swb {
if is_bands.get(sfb).copied().flatten().is_some() {
continue; }
let start = offsets[sfb] as usize;
let end = offsets[sfb + 1] as usize;
let l_band = &l_spec[start..end];
let r_band = &r_spec[start..end];
let l_n = is_noise_like(l_band);
let r_n = is_noise_like(r_band);
out.l_noise[sfb] = l_n;
out.r_noise[sfb] = r_n;
if l_n && r_n {
let e_l: f64 = l_band.iter().map(|&v| v * v).sum();
let e_r: f64 = r_band.iter().map(|&v| v * v).sum();
let dot: f64 = l_band.iter().zip(r_band).map(|(&a, &b)| a * b).sum();
if e_l > 0.0 && e_r > 0.0 && dot / (e_l * e_r).sqrt() >= PNS_CORR_MIN {
out.shared[sfb] = true;
}
}
}
Ok(out)
}
fn ms_decide(l_spec: &[f64], r_spec: &[f64], fs_index: u8) -> Result<Vec<bool>> {
let offsets = long_window_offsets(fs_index)?;
let num_swb = NUM_SWB_LONG_WINDOW[fs_index as usize] as usize;
let mut used = Vec::with_capacity(num_swb);
for sfb in 0..num_swb {
let start = offsets[sfb] as usize;
let end = offsets[sfb + 1] as usize;
let mut e_lr = 0.0f64;
let mut e_m = 0.0f64;
let mut e_s = 0.0f64;
for k in start..end {
let (l, r) = (l_spec[k], r_spec[k]);
e_lr += l * l + r * r;
let m = 0.5 * (l + r);
let s = 0.5 * (l - r);
e_m += m * m;
e_s += s * s;
}
used.push(e_lr > 0.0 && e_m.min(e_s) <= e_lr / 8.0);
}
Ok(used)
}
fn apply_ms(
l_spec: &[f64],
r_spec: &[f64],
ms_used: &[bool],
fs_index: u8,
) -> Result<(Vec<f64>, Vec<f64>)> {
let offsets = long_window_offsets(fs_index)?;
let mut code_l = l_spec.to_vec();
let mut code_r = r_spec.to_vec();
for (sfb, &used) in ms_used.iter().enumerate() {
if !used {
continue;
}
let start = offsets[sfb] as usize;
let end = offsets[sfb + 1] as usize;
for k in start..end {
let m = 0.5 * (l_spec[k] + r_spec[k]);
let s = 0.5 * (l_spec[k] - r_spec[k]);
code_l[k] = m;
code_r[k] = s;
}
}
Ok((code_l, code_r))
}
fn ms_decide_short(
l_spec: &[f64],
r_spec: &[f64],
offsets: &[u16],
num_swb: usize,
wgl: &[u8],
) -> Vec<Vec<bool>> {
let short_len = SHORT_WINDOW_LEN as usize;
let mut rows = Vec::with_capacity(wgl.len());
let mut win_base = 0usize;
for &len in wgl {
let mut row = Vec::with_capacity(num_swb);
for sfb in 0..num_swb {
let mut e_lr = 0.0f64;
let mut e_m = 0.0f64;
let mut e_s = 0.0f64;
for w in win_base..win_base + len as usize {
for k in offsets[sfb] as usize..offsets[sfb + 1] as usize {
let (l, r) = (l_spec[w * short_len + k], r_spec[w * short_len + k]);
e_lr += l * l + r * r;
let m = 0.5 * (l + r);
let s = 0.5 * (l - r);
e_m += m * m;
e_s += s * s;
}
}
row.push(e_lr > 0.0 && e_m.min(e_s) <= e_lr / 8.0);
}
rows.push(row);
win_base += len as usize;
}
rows
}
fn apply_ms_short(
l_spec: &[f64],
r_spec: &[f64],
rows: &[Vec<bool>],
offsets: &[u16],
wgl: &[u8],
) -> (Vec<f64>, Vec<f64>) {
let short_len = SHORT_WINDOW_LEN as usize;
let mut code_l = l_spec.to_vec();
let mut code_r = r_spec.to_vec();
let mut win_base = 0usize;
for (row, &len) in rows.iter().zip(wgl) {
for (sfb, &used) in row.iter().enumerate() {
if !used {
continue;
}
for w in win_base..win_base + len as usize {
for k in offsets[sfb] as usize..offsets[sfb + 1] as usize {
let i = w * short_len + k;
let m = 0.5 * (l_spec[i] + r_spec[i]);
let s = 0.5 * (l_spec[i] - r_spec[i]);
code_l[i] = m;
code_r[i] = s;
}
}
}
win_base += len as usize;
}
(code_l, code_r)
}
fn quantize_coef(x: f64, sf: i32) -> i32 {
let gain = (0.25 * f64::from(sf - SF_OFFSET)).exp2();
let mag = (x.abs() / gain).powf(0.75).round();
let mag = mag.min(f64::from(MAX_QUANT)) as i32;
if x < 0.0 {
-mag
} else {
mag
}
}
fn band_scalefactor(peak: f64, frame_peak: f64, sf_offset: i32) -> Option<i32> {
if peak <= 0.0 || frame_peak <= 0.0 {
return None;
}
let target = TARGET_PEAK_MAG * (peak / frame_peak).powf(SPREAD);
if target < MIN_TARGET_MAG {
return None;
}
let sf = f64::from(SF_OFFSET) + 4.0 * peak.log2() - (16.0 / 3.0) * target.log2();
Some((sf.round() as i32 + sf_offset).clamp(0, 255))
}
fn codebook_for(qmax: i32) -> u8 {
match qmax {
0 => ZERO_HCB,
1 => 1,
2 => 3,
3..=4 => 5,
5..=7 => 7,
8..=12 => 9,
_ => 11,
}
}
struct GroupQuant {
x_quant: Vec<i32>,
sfb_cb: Vec<u8>,
sfs: Vec<Option<i32>>,
noise: Vec<Option<i32>>,
is_pos: Vec<Option<i32>>,
}
#[allow(clippy::too_many_arguments)]
fn quantize_group(
spec: &[f64],
offsets: &[u16],
num_swb: usize,
sf_offset: i32,
frame_peak: f64,
prev_sf: &mut Option<i32>,
pns_bands: &[bool],
) -> GroupQuant {
let mut x_quant = vec![0i32; spec.len()];
let mut sfb_cb = vec![ZERO_HCB; num_swb];
let mut sfs: Vec<Option<i32>> = vec![None; num_swb];
let mut noise: Vec<Option<i32>> = vec![None; num_swb];
for sfb in 0..num_swb {
let start = offsets[sfb] as usize;
let end = (offsets[sfb + 1] as usize).min(spec.len());
let peak = spec[start..end].iter().fold(0.0f64, |m, &v| m.max(v.abs()));
let Some(mut sf) = band_scalefactor(peak, frame_peak, sf_offset) else {
continue; };
if pns_bands.get(sfb).copied().unwrap_or(false) && is_noise_like(&spec[start..end]) {
let nrg: f64 = spec[start..end].iter().map(|&x| x * x).sum();
let noise_nrg = (4.0 * nrg.sqrt().log2()).round() as i32;
sfb_cb[sfb] = NOISE_HCB;
noise[sfb] = Some(noise_nrg);
continue;
}
if let Some(p) = *prev_sf {
sf = sf.clamp(p - MAX_SF_DELTA, p + MAX_SF_DELTA).clamp(0, 255);
}
let mut qmax = quantize_coef(peak, sf).abs();
while qmax >= MAX_QUANT && sf < 255 {
sf = (sf + 4).min(255);
qmax = quantize_coef(peak, sf).abs();
}
if qmax == 0 {
continue; }
let mut band_max = 0i32;
for k in start..end {
let q = quantize_coef(spec[k], sf);
x_quant[k] = q;
band_max = band_max.max(q.abs());
}
if band_max == 0 {
continue;
}
sfb_cb[sfb] = codebook_for(band_max);
sfs[sfb] = Some(sf);
*prev_sf = Some(sf);
}
GroupQuant {
x_quant,
sfb_cb,
sfs,
noise,
is_pos: vec![None; num_swb],
}
}
fn apply_is_overrides(group: &mut GroupQuant, is_bands: &[IsBand], offsets: &[u16]) {
for (sfb, band) in is_bands.iter().enumerate().take(group.sfb_cb.len()) {
let Some((cb, pos)) = band else {
continue;
};
let start = offsets[sfb] as usize;
let end = (offsets[sfb + 1] as usize).min(group.x_quant.len());
for q in &mut group.x_quant[start..end] {
*q = 0;
}
group.sfb_cb[sfb] = *cb;
group.sfs[sfb] = None;
group.noise[sfb] = None;
group.is_pos[sfb] = Some(*pos);
}
}
fn is_noise_like(band: &[f64]) -> bool {
let width = band.len();
if width < PNS_MIN_WIDTH {
return false;
}
let l1: f64 = band.iter().map(|&v| v.abs()).sum();
let l2_sq: f64 = band.iter().map(|&v| v * v).sum();
if l2_sq <= 0.0 {
return false;
}
(l1 * l1) / (width as f64 * l2_sq) > PNS_DENSITY_MIN
}
fn scalefactor_track(groups: &[GroupQuant]) -> (u8, AbsoluteScaleFactors) {
let global_gain = groups
.iter()
.flat_map(|g| g.sfs.iter().copied().flatten())
.next()
.unwrap_or(SF_OFFSET) as u8;
let mut last_nrg = i32::from(global_gain) - NOISE_OFFSET - 256;
let mut first_noise = true;
let mut last_is = 0i32;
let mut entries = Vec::with_capacity(groups.len());
for g in groups {
let mut group_out = Vec::new();
for sfb in 0..g.sfb_cb.len() {
if let Some(sf) = g.sfs[sfb] {
group_out.push(AbsoluteScaleFactorEntry::Sf(sf as u8));
} else if let Some(nrg) = g.noise[sfb] {
let delta = nrg - last_nrg;
let clamped = if first_noise {
delta.clamp(0, 511)
} else {
delta.clamp(-MAX_SF_DELTA, MAX_SF_DELTA)
};
first_noise = false;
last_nrg += clamped;
group_out.push(AbsoluteScaleFactorEntry::NoiseNrg(last_nrg));
} else if let Some(pos) = g.is_pos[sfb] {
let delta = (pos - last_is).clamp(-MAX_SF_DELTA, MAX_SF_DELTA);
last_is += delta;
group_out.push(AbsoluteScaleFactorEntry::IsPos(last_is as i16));
}
}
entries.push(group_out);
}
(global_gain, AbsoluteScaleFactors { entries })
}
fn band_bits(cb: u8, coeffs: &[i32]) -> Option<u32> {
let row = crate::spectral_codebook::table_4_95(cb).ok()?;
let dim = row.dimension? as usize;
let mut bw = BitWriter::new();
let mut k = 0;
while k + dim <= coeffs.len() {
crate::spectral_data::write_tuple(&mut bw, cb, dim, &coeffs[k..k + dim]).ok()?;
k += dim;
}
if k != coeffs.len() {
return None; }
Some(bw.bit_position() as u32)
}
fn section_header_bits(len: u32, long: bool) -> u32 {
let (esc, w) = if long { (31, 5) } else { (7, 3) };
4 + w * (len / esc + 1)
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum BandClass {
Zero,
Fixed(u8),
Spectral,
}
fn optimize_group_sections(
x_quant: &[i32],
ranges: &[(usize, usize)],
sfb_cb: &mut [u8],
long: bool,
) -> Result<Vec<Section>> {
let n = sfb_cb.len();
if n == 0 {
return Ok(Vec::new());
}
let class: Vec<BandClass> = sfb_cb
.iter()
.map(|&cb| match cb {
ZERO_HCB => BandClass::Zero,
NOISE_HCB | INTENSITY_HCB | INTENSITY_HCB2 => BandClass::Fixed(cb),
_ => BandClass::Spectral,
})
.collect();
let books: [u8; 11] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
let cost: Vec<[Option<u32>; 11]> = (0..n)
.map(|b| {
let mut row = [None; 11];
if class[b] == BandClass::Spectral {
let (s, e) = ranges[b];
for (i, &cb) in books.iter().enumerate() {
row[i] = band_bits(cb, &x_quant[s..e]);
}
}
row
})
.collect();
let mut dp: Vec<(u64, usize, u8)> = vec![(u64::MAX, 0, 0); n + 1];
dp[0] = (0, 0, 0);
for b in 1..=n {
for a in (0..b).rev() {
if class[a] != class[b - 1] {
break;
}
let header = u64::from(section_header_bits((b - a) as u32, long));
let run = match class[a] {
BandClass::Zero => Some((0u8, 0u64)),
BandClass::Fixed(cb) => {
if sfb_cb[a..b].iter().any(|&c| c != cb) {
None } else {
Some((cb, 0))
}
}
BandClass::Spectral => {
let mut best: Option<(u8, u64)> = None;
for (i, &cb) in books.iter().enumerate() {
let mut sum = 0u64;
let mut ok = true;
for c in cost[a..b].iter() {
match c[i] {
Some(bits) => sum += u64::from(bits),
None => {
ok = false;
break;
}
}
}
if ok && best.map(|(_, s)| sum < s).unwrap_or(true) {
best = Some((cb, sum));
}
}
best
}
};
let Some((book, run_bits)) = run else {
continue;
};
let total = dp[a].0.saturating_add(header + run_bits);
if total < dp[b].0 {
dp[b] = (total, a, book);
}
}
}
if dp[n].0 == u64::MAX {
return Err(Error::SpectralDataEncodeInvalid);
}
let mut bounds = Vec::new();
let mut b = n;
while b > 0 {
let (_, a, book) = dp[b];
bounds.push((a, b, book));
b = a;
}
bounds.reverse();
let mut sections = Vec::with_capacity(bounds.len());
for (a, b, book) in bounds {
for cb in sfb_cb[a..b].iter_mut() {
*cb = book;
}
sections.push(Section {
codebook: book,
start: a as u8,
end: b as u8,
});
}
Ok(sections)
}
fn finish_channel(
info: IcsInfo,
groups: Vec<GroupQuant>,
fs_index: u8,
pulse_data: Option<PulseData>,
) -> Result<QuantizedChannel> {
let (global_gain, abs) = scalefactor_track(&groups);
let long = info.window_sequence != WindowSequence::EightShort;
let per_group_offsets = crate::spectral_data::sect_sfb_offset(&info, fs_index)?;
let mut sections = Vec::with_capacity(groups.len());
let mut sfb_cb = Vec::with_capacity(groups.len());
let mut x_quant = Vec::with_capacity(groups.len());
for (mut g, offsets) in groups.into_iter().zip(per_group_offsets.iter()) {
let ranges: Vec<(usize, usize)> = (0..g.sfb_cb.len())
.map(|sfb| {
(
(offsets[sfb] as usize).min(g.x_quant.len()),
(offsets[sfb + 1] as usize).min(g.x_quant.len()),
)
})
.collect();
sections.push(optimize_group_sections(
&g.x_quant,
&ranges,
&mut g.sfb_cb,
long,
)?);
sfb_cb.push(g.sfb_cb);
x_quant.push(g.x_quant);
}
let scale_factor_data = differentiate(&abs, &sfb_cb, global_gain)?;
let body = IcsBody {
global_gain,
ics_info: Some(info.clone()),
section_data: SectionData { sections, sfb_cb },
scale_factor_data,
pulse_data_present: pulse_data.is_some(),
pulse_data,
tns_data_present: false,
tns_data: None,
gain_control_data_present: false,
gain_control_data: None,
spectral_data_bit_offset: 0,
er_scale_factor_data: None,
reordered_spectral_lengths: None,
};
let spectral = SpectralData { x_quant };
Ok(QuantizedChannel {
info,
body,
spectral,
})
}
fn channel_wire_bits(chan: &QuantizedChannel, fs_index: u8) -> Result<u64> {
let mut bw = BitWriter::new();
chan.body.write(&mut bw, 2, fs_index, false)?;
chan.spectral
.write(&mut bw, &chan.info, &chan.body.section_data, fs_index)?;
Ok(bw.bit_position())
}
fn pulse_record_bits(n: usize) -> u64 {
8 + 9 * n as u64
}
fn extract_pulse_candidate(
x_quant: &[i32],
sfb_cb: &[u8],
offsets: &[u16],
) -> Option<(PulseData, Vec<i32>)> {
#[allow(clippy::type_complexity)]
let mut best: Option<(i64, usize, Vec<(usize, i32)>)> = None;
for sfb in 0..sfb_cb.len().min(offsets.len().saturating_sub(1)).min(64) {
match sfb_cb[sfb] {
ZERO_HCB | NOISE_HCB | INTENSITY_HCB | INTENSITY_HCB2 => continue,
_ => {}
}
let (s, e) = (
offsets[sfb] as usize,
(offsets[sfb + 1] as usize).min(x_quant.len()),
);
if e <= s {
continue;
}
let band = &x_quant[s..e];
let Some(base_cost) = cheapest_band_bits(band) else {
continue;
};
let mut by_mag: Vec<usize> = (0..band.len()).collect();
by_mag.sort_by_key(|&i| std::cmp::Reverse(band[i].abs()));
for j in 1..=MAX_PULSES.min(band.len().saturating_sub(1)) {
let floor = band[by_mag[j]].abs().max(1);
let mut lines: Vec<(usize, i32)> = by_mag[..j]
.iter()
.map(|&i| (i, (band[i].abs() - floor).min(15)))
.filter(|&(_, amp)| amp >= 1)
.collect();
if lines.len() < j {
continue; }
lines.sort_by_key(|&(i, _)| i);
if lines[0].0 > 0x1f {
continue;
}
if lines.windows(2).any(|w| w[1].0 - w[0].0 > 0x1f) {
continue;
}
let mut reduced = band.to_vec();
for &(i, amp) in &lines {
if reduced[i] > 0 {
reduced[i] -= amp;
} else {
reduced[i] += amp;
}
}
let Some(cost) = cheapest_band_bits(&reduced) else {
continue;
};
let saving = i64::from(base_cost) - i64::from(cost) - pulse_record_bits(j) as i64;
if saving > 0 && best.as_ref().map(|(bs, _, _)| saving > *bs).unwrap_or(true) {
best = Some((
saving,
sfb,
lines.iter().map(|&(i, amp)| (s + i, amp)).collect(),
));
}
}
}
let (_, start_sfb, lines) = best?;
let mut reduced = x_quant.to_vec();
let mut pulses = Vec::with_capacity(lines.len());
let mut prev_k = offsets[start_sfb] as usize;
for &(k, amp) in &lines {
pulses.push(Pulse {
offset: (k - prev_k) as u8,
amp: amp as u8,
});
prev_k = k;
if reduced[k] > 0 {
reduced[k] -= amp;
} else {
reduced[k] += amp;
}
}
Some((
PulseData {
pulse_start_sfb: start_sfb as u8,
pulses,
},
reduced,
))
}
fn cheapest_band_bits(coeffs: &[i32]) -> Option<u32> {
(1u8..=11).filter_map(|cb| band_bits(cb, coeffs)).min()
}
#[allow(clippy::too_many_arguments)]
fn quantize_channel(
spec: &[f64],
seq: WindowSequence,
fs_index: u8,
sf_offset: i32,
frame_peak: f64,
pns_bands: &[bool],
is_bands: &[IsBand],
) -> Result<QuantizedChannel> {
debug_assert_eq!(spec.len(), FRAME_LEN);
debug_assert!(seq != WindowSequence::EightShort);
let offsets = long_window_offsets(fs_index)?;
let num_swb = NUM_SWB_LONG_WINDOW[fs_index as usize] as usize;
let mut prev_sf: Option<i32> = None;
let mut group = quantize_group(
spec,
offsets,
num_swb,
sf_offset,
frame_peak,
&mut prev_sf,
pns_bands,
);
if !is_bands.is_empty() {
apply_is_overrides(&mut group, is_bands, offsets);
}
let pulse_candidate = extract_pulse_candidate(&group.x_quant, &group.sfb_cb, offsets);
let info = IcsInfo {
family: crate::swb_offset::FrameFamily::Lc1024,
ics_reserved_bit: false,
window_sequence: seq,
window_shape: WindowShape::Sine,
max_sfb: num_swb as u8,
scale_factor_grouping: None,
predictor_data_present: false,
predictor_data: None,
ltp_data_present: false,
ltp_data: None,
ltp_data_present_pair: None,
ltp_data_pair: None,
num_windows: 1,
num_window_groups: 1,
window_group_length: vec![1],
num_swb: num_swb as u8,
};
if let Some((pd, reduced)) = pulse_candidate {
let mut pulsed_group = GroupQuant {
x_quant: reduced,
sfb_cb: group.sfb_cb.clone(),
sfs: group.sfs.clone(),
noise: group.noise.clone(),
is_pos: group.is_pos.clone(),
};
for sfb in 0..pulsed_group.sfb_cb.len() {
match pulsed_group.sfb_cb[sfb] {
ZERO_HCB | NOISE_HCB | INTENSITY_HCB | INTENSITY_HCB2 => continue,
_ => {}
}
let (s, e) = (
offsets[sfb] as usize,
(offsets[sfb + 1] as usize).min(pulsed_group.x_quant.len()),
);
let band_max = pulsed_group.x_quant[s..e]
.iter()
.map(|q| q.abs())
.max()
.unwrap_or(0);
if band_max > 0 {
pulsed_group.sfb_cb[sfb] = codebook_for(band_max);
}
}
let plain = finish_channel(info.clone(), vec![group], fs_index, None)?;
let pulsed = finish_channel(info, vec![pulsed_group], fs_index, Some(pd))?;
return if channel_wire_bits(&pulsed, fs_index)? < channel_wire_bits(&plain, fs_index)? {
Ok(pulsed)
} else {
Ok(plain)
};
}
finish_channel(info, vec![group], fs_index, None)
}
const GROUP_MERGE_LOG_DIST: f64 = 1.386;
const GROUP_MERGE_EPS: f64 = 1.0;
fn decide_short_grouping(spec: &[f64], offsets: &[u16], num_swb: usize) -> Vec<u8> {
let short_len = SHORT_WINDOW_LEN as usize;
let band_energy = |w: usize, sfb: usize| -> f64 {
let base = w * short_len;
spec[base + offsets[sfb] as usize..base + offsets[sfb + 1] as usize]
.iter()
.map(|&v| v * v)
.sum()
};
let mut lengths: Vec<u8> = vec![1];
for w in 1..8 {
let dist: f64 = (0..num_swb)
.map(|sfb| {
let a = band_energy(w - 1, sfb) + GROUP_MERGE_EPS;
let b = band_energy(w, sfb) + GROUP_MERGE_EPS;
(a / b).ln().abs()
})
.sum::<f64>()
/ num_swb.max(1) as f64;
if dist <= GROUP_MERGE_LOG_DIST {
*lengths.last_mut().expect("non-empty") += 1;
} else {
lengths.push(1);
}
}
lengths
}
fn grouping_mask(window_group_length: &[u8]) -> u8 {
let mut mask = 0u8;
let mut w = 0usize;
for &len in window_group_length {
for j in 0..len as usize {
if j > 0 {
mask |= 1 << (6 - (w - 1));
}
w += 1;
}
}
mask
}
fn quantize_channel_short(
spec: &[f64],
fs_index: u8,
sf_offset: i32,
frame_peak: f64,
) -> Result<QuantizedChannel> {
let offsets = short_window_offsets(fs_index)?;
let num_swb = NUM_SWB_SHORT_WINDOW[fs_index as usize] as usize;
let window_group_length = decide_short_grouping(spec, offsets, num_swb);
quantize_channel_short_grouped(spec, fs_index, sf_offset, frame_peak, window_group_length)
}
fn quantize_channel_short_grouped(
spec: &[f64],
fs_index: u8,
sf_offset: i32,
frame_peak: f64,
window_group_length: Vec<u8>,
) -> Result<QuantizedChannel> {
let short_len = SHORT_WINDOW_LEN as usize;
debug_assert_eq!(spec.len(), 8 * short_len);
let offsets = short_window_offsets(fs_index)?;
let num_swb = NUM_SWB_SHORT_WINDOW[fs_index as usize] as usize;
let mask = grouping_mask(&window_group_length);
let mut prev_sf: Option<i32> = None;
let mut groups = Vec::with_capacity(window_group_length.len());
let mut win_base = 0usize;
for &len in &window_group_length {
let wgl = len as usize;
let mut buf = Vec::with_capacity(wgl * short_len);
for sfb in 0..num_swb {
let (s, e) = (offsets[sfb] as usize, offsets[sfb + 1] as usize);
for w in 0..wgl {
let base = (win_base + w) * short_len;
buf.extend_from_slice(&spec[base + s..base + e]);
}
}
let mut scaled = Vec::with_capacity(num_swb + 1);
let mut acc = 0u16;
scaled.push(0u16);
for sfb in 0..num_swb {
acc += (offsets[sfb + 1] - offsets[sfb]) * len as u16;
scaled.push(acc);
}
groups.push(quantize_group(
&buf,
&scaled,
num_swb,
sf_offset,
frame_peak,
&mut prev_sf,
&[], ));
win_base += wgl;
}
let info = IcsInfo {
family: crate::swb_offset::FrameFamily::Lc1024,
ics_reserved_bit: false,
window_sequence: WindowSequence::EightShort,
window_shape: WindowShape::Sine,
max_sfb: num_swb as u8,
scale_factor_grouping: Some(mask),
predictor_data_present: false,
predictor_data: None,
ltp_data_present: false,
ltp_data: None,
ltp_data_present_pair: None,
ltp_data_pair: None,
num_windows: 8,
num_window_groups: window_group_length.len() as u8,
window_group_length,
num_swb: num_swb as u8,
};
finish_channel(info, groups, fs_index, None)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dequant::{inverse_quantize, scale_factor_gain};
#[test]
fn quantize_coef_inverts_dequant_within_rounding() {
for sf in [40i32, 100, 156, 200] {
for q in [-8190i32, -1000, -12, -1, 0, 1, 7, 40, 999, 8190] {
let x = inverse_quantize(q) * scale_factor_gain(sf as u8);
assert_eq!(quantize_coef(x, sf), q, "sf={sf} q={q}");
}
}
}
#[test]
fn band_scalefactor_hits_target_magnitude() {
for peak in [1.0f64, 100.0, 3.2e4, 6.7e7] {
let sf = band_scalefactor(peak, peak, 0).expect("loudest band never culls");
let q = quantize_coef(peak, sf).abs();
let lo = (TARGET_PEAK_MAG / 2.0_f64.powf(0.375)).floor() as i32;
let hi = (TARGET_PEAK_MAG * 2.0_f64.powf(0.375)).ceil() as i32;
assert!(
(lo..=hi).contains(&q),
"peak={peak} sf={sf} q={q} not in [{lo},{hi}]"
);
}
}
#[test]
fn band_scalefactor_spreads_and_culls() {
let frame_peak = 1.0e6f64;
let sf = band_scalefactor(frame_peak * 1e-2, frame_peak, 0).unwrap();
let q = quantize_coef(frame_peak * 1e-2, sf).abs();
assert!((2..=8).contains(&q), "spread target off: q={q}");
assert_eq!(band_scalefactor(frame_peak * 3.2e-5, frame_peak, 0), None);
assert_eq!(band_scalefactor(0.0, frame_peak, 0), None);
}
#[test]
fn codebook_selection_covers_table_4_95_lavs() {
assert_eq!(codebook_for(0), ZERO_HCB);
assert_eq!(codebook_for(1), 1);
assert_eq!(codebook_for(2), 3);
assert_eq!(codebook_for(4), 5);
assert_eq!(codebook_for(7), 7);
assert_eq!(codebook_for(12), 9);
assert_eq!(codebook_for(13), 11);
assert_eq!(codebook_for(8191), 11);
}
#[test]
fn config_rejects_bad_parameters() {
assert!(StreamEncoder::new(EncoderConfig {
sample_rate: 44_100,
channels: 1,
bitrate: 64_000,
})
.is_ok());
assert!(matches!(
StreamEncoder::new(EncoderConfig {
sample_rate: 44_056, channels: 1,
bitrate: 64_000,
}),
Err(Error::EncoderInvalidConfig)
));
for ok in [3u8, 4, 5, 6, 8] {
assert!(
StreamEncoder::new(EncoderConfig {
sample_rate: 44_100,
channels: ok,
bitrate: 64_000 * u32::from(ok),
})
.is_ok(),
"channels {ok}"
);
}
for bad in [0u8, 7, 9] {
assert!(
matches!(
StreamEncoder::new(EncoderConfig {
sample_rate: 44_100,
channels: bad,
bitrate: 64_000,
}),
Err(Error::EncoderInvalidConfig)
),
"channels {bad}"
);
}
assert!(matches!(
StreamEncoder::new(EncoderConfig {
sample_rate: 44_100,
channels: 1,
bitrate: 0,
}),
Err(Error::EncoderInvalidConfig)
));
}
fn fill_band(buf: &mut [i32], range: (usize, usize), max: i32, seed: &mut u32) {
for slot in buf[range.0..range.1].iter_mut() {
*seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
*slot = ((*seed >> 8) % (2 * max + 1) as u32) as i32 - max;
}
}
fn measure_group(
sections: Vec<Section>,
sfb_cb: Vec<u8>,
x_quant: Vec<i32>,
num_swb: usize,
fs_index: u8,
) -> u64 {
let info = IcsInfo {
family: crate::swb_offset::FrameFamily::Lc1024,
ics_reserved_bit: false,
window_sequence: WindowSequence::OnlyLong,
window_shape: WindowShape::Sine,
max_sfb: num_swb as u8,
scale_factor_grouping: None,
predictor_data_present: false,
predictor_data: None,
ltp_data_present: false,
ltp_data: None,
ltp_data_present_pair: None,
ltp_data_pair: None,
num_windows: 1,
num_window_groups: 1,
window_group_length: vec![1],
num_swb: num_swb as u8,
};
let sd = SectionData {
sections: vec![sections],
sfb_cb: vec![sfb_cb],
};
let spectral = SpectralData {
x_quant: vec![x_quant],
};
let mut bw = BitWriter::new();
sd.write(&mut bw, WindowSequence::OnlyLong, num_swb as u8)
.unwrap();
spectral.write(&mut bw, &info, &sd, fs_index).unwrap();
bw.bit_position()
}
#[test]
fn band_bits_matches_wire_writer() {
let fs_index = 4u8;
let offsets = long_window_offsets(fs_index).unwrap();
let mut buf = vec![0i32; FRAME_LEN];
let mut seed = 0xB17u32;
for sfb in 0..6 {
fill_band(
&mut buf,
(offsets[sfb] as usize, offsets[sfb + 1] as usize),
7,
&mut seed,
);
}
for cb in [7u8, 8, 9, 10, 11] {
let per_band: u32 = (0..6)
.map(|sfb| {
band_bits(cb, &buf[offsets[sfb] as usize..offsets[sfb + 1] as usize]).unwrap()
})
.sum();
let sections = vec![Section {
codebook: cb,
start: 0,
end: 6,
}];
let wire = measure_group(sections.clone(), vec![cb; 6], buf.clone(), 6, fs_index);
let header = u64::from(section_header_bits(6, true));
assert_eq!(wire, header + u64::from(per_band), "cb {cb}");
}
assert!(band_bits(1, &[2, 0, 0, 0]).is_none());
assert!(band_bits(3, &[3, 0, 0, 0]).is_none());
}
#[test]
fn optimizer_never_loses_to_naive_sections() {
let fs_index = 4u8;
let offsets = long_window_offsets(fs_index).unwrap();
let num_swb = NUM_SWB_LONG_WINDOW[fs_index as usize] as usize;
for (case, seed0) in [(0u32, 1u32), (1, 0xACE), (2, 0x5EED), (3, 77)] {
let mut buf = vec![0i32; FRAME_LEN];
let mut seed = seed0;
for sfb in 0..num_swb {
let range = (offsets[sfb] as usize, offsets[sfb + 1] as usize);
let max = match case {
0 => [0, 1, 1, 2, 0, 0, 4, 7, 1][sfb % 9],
1 => [1, 12, 1, 30, 0, 2][sfb % 6],
2 => (sfb as i32) % 5,
_ => [7, 7, 0, 0, 0, 12, 1, 1][sfb % 8],
};
if max > 0 {
fill_band(&mut buf, range, max, &mut seed);
}
}
let provisional: Vec<u8> = (0..num_swb)
.map(|sfb| {
let band = &buf[offsets[sfb] as usize..offsets[sfb + 1] as usize];
codebook_for(band.iter().map(|&v| v.abs()).max().unwrap_or(0))
})
.collect();
let mut naive_sections: Vec<Section> = Vec::new();
for (sfb, &cb) in provisional.iter().enumerate() {
match naive_sections.last_mut() {
Some(s) if s.codebook == cb => s.end = (sfb + 1) as u8,
_ => naive_sections.push(Section {
codebook: cb,
start: sfb as u8,
end: (sfb + 1) as u8,
}),
}
}
let naive = measure_group(
naive_sections,
provisional.clone(),
buf.clone(),
num_swb,
fs_index,
);
let ranges: Vec<(usize, usize)> = (0..num_swb)
.map(|sfb| (offsets[sfb] as usize, offsets[sfb + 1] as usize))
.collect();
let mut books = provisional;
let sections = optimize_group_sections(&buf, &ranges, &mut books, true).unwrap();
let opt = measure_group(sections, books, buf, num_swb, fs_index);
assert!(opt <= naive, "case {case}: opt {opt} > naive {naive}");
}
}
#[test]
fn short_grouping_decision_and_mask() {
let fs_index = 4u8;
let offsets = short_window_offsets(fs_index).unwrap();
let num_swb = NUM_SWB_SHORT_WINDOW[fs_index as usize] as usize;
let short_len = SHORT_WINDOW_LEN as usize;
let mut spec = vec![0.0f64; 8 * short_len];
for w in 0..8 {
for k in 0..short_len {
spec[w * short_len + k] = 1000.0 * ((k as f64) * 0.37).sin();
}
}
assert_eq!(decide_short_grouping(&spec, offsets, num_swb), vec![8]);
assert_eq!(grouping_mask(&[8]), 0x7F);
for k in 0..short_len {
for w in 3..8 {
spec[w * short_len + k] *= 1000.0;
}
}
let lengths = decide_short_grouping(&spec, offsets, num_swb);
assert_eq!(lengths, vec![3, 5]);
assert_eq!(grouping_mask(&lengths), 0b110_1111);
assert_eq!(grouping_mask(&[1; 8]), 0);
for lengths in [vec![8u8], vec![3, 5], vec![1; 8], vec![2, 1, 4, 1]] {
let mask = grouping_mask(&lengths);
let (_, n, derived, _) = crate::ics_info::derive_window_grouping(
WindowSequence::EightShort,
Some(mask),
fs_index as usize,
);
assert_eq!(derived, lengths);
assert_eq!(n as usize, lengths.len());
}
}
#[test]
fn short_grouping_wire_roundtrip() {
use oxideav_core::bits::BitReader;
let fs_index = 4u8;
let short_len = SHORT_WINDOW_LEN as usize;
let mut spec = vec![0.0f64; 8 * short_len];
for w in 0..8 {
let (gain, phase) = if w < 3 {
(300.0, 0.31)
} else {
(30000.0, 0.11)
};
for k in 0..short_len {
spec[w * short_len + k] = gain * ((k as f64) * phase).sin();
}
}
let frame_peak = spec.iter().fold(0.0f64, |m, &v| m.max(v.abs()));
let chan = quantize_channel_short(&spec, fs_index, 0, frame_peak).unwrap();
assert_eq!(chan.info.window_group_length, vec![3, 5]);
let mut bw = BitWriter::new();
chan.body.write(&mut bw, 2, fs_index, false).unwrap();
chan.spectral
.write(&mut bw, &chan.info, &chan.body.section_data, fs_index)
.unwrap();
let bytes = bw.finish();
let mut reader = BitReader::new(&bytes);
let body = IcsBody::parse(&mut reader, 2, fs_index, false).unwrap();
let ics = body.ics_info.as_ref().unwrap();
assert_eq!(ics.scale_factor_grouping, Some(0b110_1111));
assert_eq!(ics.num_window_groups, 2);
assert_eq!(ics.window_group_length, vec![3, 5]);
let spectral = SpectralData::parse(&mut reader, ics, &body.section_data, fs_index).unwrap();
assert_eq!(spectral, chan.spectral);
assert_eq!(body.section_data, chan.body.section_data);
assert_eq!(body.scale_factor_data, chan.body.scale_factor_data);
}
#[test]
fn cpe_short_grouping_is_joint() {
let fs_index = 4u8;
let short_len = SHORT_WINDOW_LEN as usize;
let offsets = short_window_offsets(fs_index).unwrap();
let num_swb = NUM_SWB_SHORT_WINDOW[fs_index as usize] as usize;
let mut l_spec = vec![0.0f64; 8 * short_len];
let mut r_spec = vec![0.0f64; 8 * short_len];
for w in 0..8 {
for k in 0..short_len {
let l_gain = if w < 2 { 30.0 } else { 30000.0 };
let r_gain = if w < 5 { 30.0 } else { 30000.0 };
l_spec[w * short_len + k] = l_gain * ((k as f64) * 0.23).sin();
r_spec[w * short_len + k] = r_gain * ((k as f64) * 0.19).sin();
}
}
let gl = decide_short_grouping(&l_spec, offsets, num_swb);
let gr = decide_short_grouping(&r_spec, offsets, num_swb);
assert_ne!(gl, gr, "premise: independent groupings diverge");
let n = 4 * FRAME_LEN;
let mut pcm = Vec::with_capacity(n * 2);
for i in 0..n {
let t = i as f64;
let in_l = (FRAME_LEN + 256..FRAME_LEN + 640).contains(&i);
let in_r = (FRAME_LEN + 640..FRAME_LEN + 1024).contains(&i);
let base = 400.0 * (0.09 * t).sin();
let l = base + if in_l { 20000.0 * (0.5 * t).sin() } else { 0.0 };
let r = base
+ if in_r {
20000.0 * (0.43 * t).sin()
} else {
0.0
};
pcm.push(l.round() as i16);
pcm.push(r.round() as i16);
}
let mut enc = StreamEncoder::new(EncoderConfig {
sample_rate: 44_100,
channels: 2,
bitrate: 192_000,
})
.unwrap();
let stream = enc.encode_all(&pcm).unwrap();
let mut dec = crate::decode::StreamDecoder::new();
let frames = dec.decode_all(&stream).unwrap();
assert!(frames.len() >= 4);
let mut decoded = Vec::new();
for f in &frames {
decoded.extend_from_slice(&f.pcm);
}
let aligned = &decoded[FRAME_LEN * 2..];
let window_energy = |c: usize, range: core::ops::Range<usize>| -> f64 {
range.map(|i| f64::from(aligned[i * 2 + c]).powi(2)).sum()
};
let l_burst = window_energy(0, FRAME_LEN + 256..FRAME_LEN + 640);
let r_quiet = window_energy(1, FRAME_LEN + 256..FRAME_LEN + 640);
assert!(
l_burst > 20.0 * r_quiet,
"left burst region not reconstructed: {l_burst:.0} vs {r_quiet:.0}"
);
}
#[test]
fn cpe_short_frame_ms_coding() {
let fs_index = 4u8;
let short_len = SHORT_WINDOW_LEN as usize;
let offsets = short_window_offsets(fs_index).unwrap();
let num_swb = NUM_SWB_SHORT_WINDOW[fs_index as usize] as usize;
let mut spec = vec![0.0f64; 8 * short_len];
let mut seed = 0x515u32;
for v in spec.iter_mut() {
seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
*v = f64::from(seed >> 16) - 32768.0;
}
let inverted: Vec<f64> = spec.iter().map(|&v| -v).collect();
let wgl = vec![2u8, 3, 3];
let same = ms_decide_short(&spec, &spec, offsets, num_swb, &wgl);
assert!(same.iter().flatten().all(|&b| b), "identical pair all-M/S");
let anti = ms_decide_short(&spec, &inverted, offsets, num_swb, &wgl);
assert!(anti.iter().flatten().all(|&b| b), "inverted pair all-M/S");
let mut other = vec![0.0f64; 8 * short_len];
for v in other.iter_mut() {
seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
*v = f64::from(seed >> 16) - 32768.0;
}
let indep = ms_decide_short(&spec, &other, offsets, num_swb, &wgl);
let flagged = indep.iter().flatten().filter(|&&b| b).count();
let total = indep.iter().flatten().count();
assert!(
flagged * 4 < total,
"independent pair flagged {flagged}/{total}"
);
let (code_l, code_r) = apply_ms_short(&spec, &spec, &same, offsets, &wgl);
assert_eq!(code_l, spec);
assert!(code_r.iter().all(|&v| v == 0.0));
let n = 4 * FRAME_LEN;
let mut pcm = Vec::with_capacity(n * 2);
for i in 0..n {
let t = i as f64;
let burst = (FRAME_LEN + 256..FRAME_LEN + 640).contains(&i);
let v = 500.0 * (0.07 * t).sin()
+ if burst {
18000.0 * (0.6 * t).sin()
} else {
0.0
};
let s = v.round() as i16;
pcm.push(s);
pcm.push(s);
}
let mut enc = StreamEncoder::new(EncoderConfig {
sample_rate: 44_100,
channels: 2,
bitrate: 128_000,
})
.unwrap();
let stream = enc.encode_all(&pcm).unwrap();
let mut dec = crate::decode::StreamDecoder::new();
let frames = dec.decode_all(&stream).unwrap();
for (f, frame) in frames.iter().enumerate() {
for i in 0..(frame.pcm.len() / 2) {
assert_eq!(
frame.pcm[2 * i],
frame.pcm[2 * i + 1],
"frame {f} sample {i}: identical channels must decode identical"
);
}
}
}
#[test]
fn silent_input_yields_valid_minimal_frames() {
let mut enc = StreamEncoder::new(EncoderConfig {
sample_rate: 44_100,
channels: 1,
bitrate: 64_000,
})
.unwrap();
let stream = enc.encode_all(&[0i16; FRAME_LEN]).unwrap();
let (h0, off) = AdtsHeader::parse(&stream).unwrap();
assert_eq!(h0.channel_configuration, 1);
assert_eq!(off, ADTS_HEADER_BYTES_NO_CRC);
let second = &stream[h0.aac_frame_length as usize..];
let (h1, _) = AdtsHeader::parse(second).unwrap();
assert_eq!(
h0.aac_frame_length as usize + h1.aac_frame_length as usize,
stream.len()
);
}
#[test]
fn pulse_candidate_reduction_is_exactly_invertible() {
let fs_index = 3u8; let offsets = long_window_offsets(fs_index).unwrap();
let num_swb = NUM_SWB_LONG_WINDOW[fs_index as usize] as usize;
let mut x_quant = vec![0i32; FRAME_LEN];
let mut sfb_cb = vec![ZERO_HCB; num_swb];
let (s, e) = (offsets[29] as usize, offsets[30] as usize);
for (k, slot) in x_quant.iter_mut().enumerate().take(e).skip(s) {
*slot = 1 - ((k as i32) & 2);
}
x_quant[s + 1] = 17;
x_quant[s + 5] = -19;
sfb_cb[29] = codebook_for(19);
let (pd, reduced) =
extract_pulse_candidate(&x_quant, &sfb_cb, offsets).expect("outliers must qualify");
assert_eq!(pd.pulse_start_sfb, 29);
assert_eq!(pd.pulses.len(), 2);
assert_eq!(reduced[s + 1], 2);
assert_eq!(reduced[s + 5], -4);
let mut restored = reduced.clone();
crate::swb_offset::apply_pulse_data(&mut restored, fs_index, &pd).unwrap();
assert_eq!(restored, x_quant);
let plain_bits = cheapest_band_bits(&x_quant[s..e]).unwrap() as u64;
let pulsed_bits =
cheapest_band_bits(&reduced[s..e]).unwrap() as u64 + pulse_record_bits(pd.pulses.len());
assert!(
pulsed_bits < plain_bits,
"pulsed {pulsed_bits} vs plain {plain_bits}"
);
}
#[test]
fn pulse_candidate_requires_a_measured_win() {
let fs_index = 3u8;
let offsets = long_window_offsets(fs_index).unwrap();
let num_swb = NUM_SWB_LONG_WINDOW[fs_index as usize] as usize;
let mut x_quant = vec![0i32; FRAME_LEN];
let mut sfb_cb = vec![ZERO_HCB; num_swb];
let (s, e) = (offsets[10] as usize, offsets[11] as usize);
x_quant[s..e].fill(3);
sfb_cb[10] = codebook_for(3);
assert!(extract_pulse_candidate(&x_quant, &sfb_cb, offsets).is_none());
}
#[test]
fn quantize_channel_emits_measured_pulse_data() {
let fs_index = 3u8; let offsets = long_window_offsets(fs_index).unwrap();
let mut spec = vec![0.0f64; FRAME_LEN];
let (s29, e29) = (offsets[29] as usize, offsets[30] as usize);
spec[offsets[20] as usize] = 1.0; spec[s29 + 3] = 0.17; for (k, slot) in spec.iter_mut().enumerate().take(e29).skip(s29) {
if k != s29 + 3 {
*slot = 0.0095; }
}
let chan =
quantize_channel(&spec, WindowSequence::OnlyLong, fs_index, 0, 1.0, &[], &[]).unwrap();
let plain = quantize_group(
&spec,
offsets,
NUM_SWB_LONG_WINDOW[fs_index as usize] as usize,
0,
1.0,
&mut None,
&[],
);
assert!(
chan.body.pulse_data_present,
"outlier-over-floor band must select the pulse variant"
);
let pd = chan.body.pulse_data.as_ref().unwrap();
assert!((1..=MAX_PULSES).contains(&pd.pulses.len()));
let mut restored = chan.spectral.x_quant[0].clone();
crate::swb_offset::apply_pulse_data(&mut restored, fs_index, pd).unwrap();
assert_eq!(restored, plain.x_quant);
let plain_chan = {
let info = chan.info.clone();
finish_channel(info, vec![plain], fs_index, None).unwrap()
};
assert!(
channel_wire_bits(&chan, fs_index).unwrap()
< channel_wire_bits(&plain_chan, fs_index).unwrap()
);
}
}