v0.0.5
Other
- ac4 round 50: section-boundary DP optimiser + SNF emission
- ac4 round 49: HCB1..11 codebook-selection optimiser + parameterised max_sfb
- ac4 round 48: forward MDCT + ASF entropy encoder for arbitrary PCM
- ac4 round 47: IMS bitstream_version=2 TOC parser + mono SIMPLE/ASF tone encoder
- round 46: AC-4 IMS encoder scaffold + ACPL_1 surround Ls/Rs spec audit
- ac4 round 45: stereo-CPE M=2 synced companding for ACPL_3 surround pair
- ac4 round 44: companding sync_flag=1 cross-channel exact synchronisation
- ac4 round 43: companding sync_flag=1/avg branches + ACPL_1 sb0 hookup
- ac4 round 42: cfg0/cfg1/cfg3 trailer-aware ASPX + §5.7.5 companding
- ac4 round 41: 5_X cfg2 ASPX trailers + Table 181 SAP for ACPL_1
- ac4 round 40: SAP a/b/c/d (Pseudocode 59) + Table 183 + Ls/Rs walker
- ac4 round 39: 5_X cfg0/cfg1/cfg3 dispatch + 7_X additional-channel pair
- ac4 round 38: LFE body decoder + cfg2_back_mono end-to-end + ACPL_3 centre
- ac4 round 37: wire 7_X ACPL_1/_2 dispatch + cfg0 centre end-to-end decode
- ac4 round 36: wire 5_X ASPX_ACPL_1 / ACPL_2 Pseudocode 117 into decoder
- Round 35: extend ETSI validation suite to float reference tables
- drop dead
linkmedep - round 35 — EMDF payloads_substream parser + DRC PCM gain application
- cargo fmt pass after round 34
- update round 34 status (SNF + FIXVAR/VARFIX/VARVAR atsg + ACPL_3)
- ac4 round 34: FIXVAR/VARFIX/VARVAR atsg + SNF inject + 5_X ACPL_3 wiring
- auto-register via oxideav_core::register! macro (linkme distributed slice)
- unify entry point on register(&mut RuntimeContext) (#502)
Added
-
Round 50 — Section-boundary DP optimiser + Spectral Noise Fill
(SNF) emission (TS 103 190-1 §5.7.4 section_data + §5.7.6 SNF +
Pseudocodes 100 + 105 + Table 39/42 + Table SCFB):encoder_asf::dp_optimise_sections(cost_band_cb, max_sections)—
dynamic-program over scale-factor bands that finds the globally
cheapest sequence of(start, end, cb)sections, paying the
per-section header cost (4 + 3 * (floor((L-1)/7)+1)bits per
Table 39) against each band's per-codebook bit cost. Section
count capped at 16 per ETSI Table SCFB. Supersedes the round-49
greedy run-length codebook-merge optimiser.encoder_asf::section_overhead_bits(len)— closed-form per-section
overhead in bits matching the spec'sn_sect_bits=3 / esc=7
long-frame layout: 7 bits forL ∈ 1..=7, 10 bits forL ∈ 8..=14,
13 bits forL ∈ 15..=21, etc.encoder_asf::build_band_codebook_cost_table(natural_q_per_band)
— precomputed per-band per-codebook bit cost (rows of length 12;
cb=0 cost 0 only for all-zero bands; HCB1..11 costs from
bit_cost_for_band;u32::MAXfor codebooks that can't represent
the band's natural quant magnitudes). Drives the DP via O(1)
prefix sums where every band is feasible for the codebook.encoder_asf::build_sections_from_dp(sections, max_sfb)— lowers
the DP-derived(start, end, cb)triples into anAsfSections
suitable for the existingwrite_section_data/
write_spectral_data_sectionsemitters.encoder_asf::compute_snf_dpcm_for_zero_quant_bands(coeffs, sfb_offset, max_sfb, sfb_cb, max_quant_idx)— for each band that
quantises to all-zero (cb == 0 || mqi == 0), estimates the band's
RMS energy from the original MDCT coefficients and picks the
HCB_SNF index whosesnf_gain = 2^((idx*1.5 - 84)/4)best matches.
ReturnsSome(per_band_idx)when at least one zero-quant band
has measurable energy;Nonefor fully-silent input.encoder_asf::write_snf_data(bw, snf, sfb_cb, max_quant_idx, max_sfb)
— emitsb_snf_data_exists(1 bit) plus per-zero-quant-band
HCB_SNF Huffman codewords per Table 42 / Pseudocode 105. Round-trips
cleanly through the existingparse_asf_snf_datadecoder path
(round 36+).encoder_asf::measure_greedy_vs_dp_bits(transform_length, max_sfb, coeffs)— diagnostic helper returning(greedy_bits, dp_bits)
for any input spectrum so callers can quantify the section-boundary
optimiser's contribution to total frame size.Ac4ImsEncoder::encode_frame_pcm_with_max_sfbnow drives the DP
optimiser + SNF emission internally; existing call sites get the
new path automatically with no API change. White-noise spectral
SNR holds at 27.5 dB (round-49 baseline) with section overhead
reduced and SNF emission turned on for high-frequency zero-quant
content.
-
Round 49 — Codebook-selection optimiser (HCB1..11) + parameterised
max_sfb(TS 103 190-1 §5.7 + Pseudocodes 17 + 19 + 20 + Annex A.0
huff_codes + Table SCFB):encoder_asf::pick_best_codebook_for_band— per-band codebook
optimiser sweeping HCB1..11 and choosing the lowest-bit-cost
codebook whoseq_maxcovers the band's natural quantised range.
Anchor scalefactor targets peak quant ≈ 12 (HCB9/10's q_max → 3×
more quantisation levels per band than the round-48 HCB5-only
baseline). HCB11 always qualifies via its Pseudocode 20 escape so
very-high-energy bands don't clip.encoder_asf::bit_cost_for_band— precise bit-counter modelling
HCB1..11 codeword length + sign bits for unsigned codebooks +
per-Pseudocode-20n_extextension bits for HCB11 escapes.
Mirrors the encoder emitter's exact bit shape (inline magnitude
saturates at 16 for HCB11, sign bit per non-zero post-saturation
line for unsigned codebooks).encoder_asf::build_sections_from_per_band_cb— collapses runs
of consecutive same-codebook bands into a singleAsfSections
entry so the emittedasf_section_data()honours the spec's
grouping pseudocode without spurious cb-switch overhead.encoder_asf::write_section_data+write_spectral_data_sections
— multi-section asf_section_data + asf_spectral_data emitters.
Per-section emission walkssect_start..sect_endbins with the
section's codebook, handlescb == 0silent bands, and writes
Pseudocode 20 escape bits for HCB11 outliers.Ac4ImsEncoder::encode_frame_pcm_with_max_sfb(frame, max_sfb)—
new public entry point parameterisingmax_sfb(round-48 default
was hard-coded to 40 → ~6.4 kHz at tl=1920 / 48 kHz). Pad target
scales with max_sfb (2KB / 4KB / 8KB tiers).encode_frame_pcm
keeps the round-48 default ofmax_sfb=40for backwards
compatibility.- White-noise round-trip SNR jumps from 13.6 dB (round-48 HCB5-only
baseline) to 27.5 dB (round-49 HCB1..11 optimiser, q_target=12,
max_sfb=50) — measured spectrally against the encoder's own MDCT
coefficients pre/post quantisation. 1 kHz tone reconstruction at
max_sfb=55preserves >100% of input energy (vs ~40% at the
round-48 max_sfb=40 default).
-
Round 48 — Forward MDCT analysis + ASF entropy encoder for arbitrary
PCM input (TS 103 190-1 §5.5 MDCT + §5.7 SIMPLE + §5.8 ASF +
Pseudocodes 17-19 + Annex A.0 huff_codes):encoder_mdct::mdct_naive+EncoderMdctState— forward MDCT
direction complementing the decoder'smdct::imdct. Naive
O(N²) direct-summation cosine basis (correctness-first; encoder
isn't on a hot path). Sign convention + scaling matched against
the decoder's IMDCT through a Princen-Bradley TDAC round-trip
test (constant-signal recovery in steady-state middle frame
within 1% error).EncoderMdctStatecarries the previous-frame
NPCM samples for cross-frame 50% TDAC overlap.encoder_asf::quantise_coeff+pick_scalefactor_for_band+
encode_pair+write_sect_len_incr+write_scalefac_data+
build_mono_simple_asf_body_from_pcm_spectrum— closed-form
forward ASF entropy encoder for the long-frame, single-window-
group, mono SIMPLE channel case. Per-band scalefactor selection
via the closed-form solvesf_min = ceil(100 + 4*log2(max_abs/q_max^(4/3)))
keeping every quantised line within HCB5's ±4 magnitude bound
afterq = round(sign(c)*|c/sf_gain|^(3/4)). Single-section
HCB5 emission across0..max_sfb; reference scalefactor- DPCM-coded per-band deltas via HCB_SCALEFAC;
b_snf_data_exists = 0.
- DPCM-coded per-band deltas via HCB_SCALEFAC;
Ac4ImsEncoder::encode_frame_pcm(input: &[f32])— public entry
point taking arbitrary float PCM input (range[-1.0, 1.0])
and emitting a structurally-valid IMS v2 frame end-to-end:
forward MDCT analysis → per-band scalefactor + quantisation →
HCB5 entropy coding → wrap in v2 IMS TOC + audio_size header.
Lazily initialises a per-encoderEncoderMdctStateon first
call; bumpssequence_countermodulo 1024. Mono / 48 kHz / 24 fps
by default (frame_len = 1920 samples, max_sfb = 40 covering
bins 0..600 ≈ 7.5 kHz).- 13 new unit tests across the three modules cover: forward MDCT
zero-in-zero-out + linearity + Princen-Bradley constant-signal- sine-wave SNR > 40 dB; quantise/dequantise round-trip;
pick_scalefactor q_max bound; encode_pair signed/unsigned
round-trip viahuff_decode+split_qspec;
build_mono_simple_asf_body_from_pcm_spectrumend-to-end parse
via the existing ASF decoder; full encode → decode round-trip
for 1 kHz tone (peak amplitude > 1000 i16), multi-tone
250+500+1000 Hz (SNR > 10 dB on steady-state frame), and
silence (peak < 50 i16);encode_frame_pcmbumps
sequence_counterper call.
- sine-wave SNR > 40 dB; quantise/dequantise round-trip;
- Codebook-selection optimiser (try HCB1..11 per section, pick
min-bits), section-boundary optimiser (split bands by
codebook), spectral noise fill, and stereo / multichannel
forward analysis remain deferred for round 49+.
-
Round 46 — AC-4 IMS encoder scaffold + ACPL_1 surround Ls/Rs
ASPX-extension spec audit (TS 103 190-2 §6.2.1.1 / §6.3.2.5,
TS 103 190-1 §4.2.6.6 Table 25):encoder_ims::Ac4ImsEncoder— Auditor-mode AC-4 IMS encoder
skeleton. Emits a structurally-validraw_ac4_frame()payload
with the IMS-flavouredac4_toc()(bitstream_version = 2+
ac4_presentation_v1_info()+ac4_substream_group_info()) per
TS 103 190-2 §6.2.1.1. Public API:Ac4ImsEncoder::new()(defaults
to mono 48 kHz 24 fps single-presentation single-substream-group
iframe),with_v0()/with_stereo()/with_5_1()builders,
encode_frame(body_padding_bytes)(bumps the 10-bit
sequence_counter modulo 1024), andencode_frame_v0(...)that
forces the TS 103 190-1 v0 layout for round-trip-with-decoder
tests. Audio body is zero-byte placeholder bits — full encoder
pipeline (MDCT analysis, scalefactor selection, entropy coding,
A-SPX envelope coding, A-CPL parameter extraction) deferred to
future rounds. Eight new unit tests cover sequence_counter wrap,
parse_ac4_tocround-trip for mono / stereo / 5.1, full
Ac4Decoderround-trip emitting silent audio, and the leading
bitstream_versionbit-pattern invariant for both v0 (0b00)
and v2 (0b10) frames.decoder.rs::dispatch_acpl_5x_pair-driving block: spec-confirms
NOT-ASPX finding for the ACPL_1 surround Ls/Rs carriers per ETSI
TS 103 190-1 §4.2.6.6 Table 25 rowcase ASPX_ACPL_1:. The
trailer order isaspx_data_2ch()(L/R primary pair) +
aspx_data_1ch()(centre mono) + twoacpl_data_1ch()parameter
sets — there is NO third ASPX trailer for the surround pair. The
Ls/Rs sSMP,3 / sSMP,4 carriers are joint-MDCT residuals that feed
Pseudocode 117 raw, with the post-Pseudocode-117 surround-output
bandwidth shape coming from the L/R-carrier ACPL synthesis
(alpha/beta/decorrelator), not from independent surround-pair
extension. Same finding for the M=2 surround-pair synced
companding cohort: with no surround carriers, no companding to
sync. The existing raw-PCM dispatch path is correct per spec —
no behavioural change in this round, only a documentation note
closing out the round-45 follow-up flagged indispatch_acpl_5x_pair.
-
Round 44 — companding
sync_flag == 1cross-channel synchronisation
(Pseudocode 121's exactg_synch(ts) = (∏ g_ch)^(1/M)) (TS 103
190-1 §5.7.5.2 + Pseudocode 121):aspx::compute_companding_levels(q, sb0, sb1)exposes the
per-slot energyL_ch(ts) = 0.9105 * mean E_ch(sb,ts)without
applying gain — the building block the synced path collects
across channels.aspx::levels_to_scales_per_slot(levels)and
aspx::levels_to_scale_averaged(levels)produce the per-slot
array (PerSlot / SyncPerSlot) and single constant (Averaged /
SyncAveraged) scalesg(ts) * Gfrom a level vector.aspx::apply_companding_scales_on_qmf(q, sb0, sb1, scales)
applies pre-computed scales — used by both the single-channel
legacy path and the synced multi-channel path.aspx::apply_synchronised_companding_across_channels(channels, mode)
implements Pseudocode 121'ssync_flag == 1branch directly:
collects every channel'sL_ch(ts), computesg_synch(ts)as
the geometric mean acrossMchannels (SyncPerSlot) or
g_avg,synchfrom per-channel averaged levels (SyncAveraged),
then applies the synced gaing_synch(ts) * GUNIFORMLY to
every contributing channel. Geometric mean is computed via
log-sum / exp for numerical stability across many channels.Ac4Decoder::aspx_extend_to_qmf(...)is the new phase-1 split:
runs the QMF analysis + HF generation + envelope adjustment +
noise / tone injection, returns(qmf_matrix, sbx, sbz)BEFORE
companding + synthesis. ReturnsNonewhen the QMF
preconditions trip (length / table / patch derivation).Ac4Decoder::qmf_synthesise_pcm(q, out_len)is the phase-2
split: runs the inverse QMF synthesis and returns PCM. Caller
is responsible for having applied any §5.7.5 gain.Ac4Decoder::extend_5x_channels_with_sync_companding(...)is
the integration glue: drives every entry through phase-1,
collects QMF matrices, calls the synced apply, drives every
entry through phase-2. Channels whose phase-1 returnedNone
fall through to the unmodified PCM (matching the per-channel
aspx_extend_pcmcontract).Ac4Decoder::extend_5x_entries(...)is the shared front-end
used bydispatch_5x_cfg{0,1,2,3}_simple_aspx: routes through
the synced cross-channel path whencompanding.sync_flag == 1,
otherwise through the per-channel path.Ac4Decoder::five_x_synced_mode(cc)resolves the synced mode
once for the whole 5_X frame (Pseudocode 121 broadcasts
compand_on[0]to every channel undersync_flag == 1).- All four
dispatch_5x_cfg{0,1,2,3}_simple_aspxroutes
refactored to build a per-channel entries list then route
throughextend_5x_entries. Cfg2 now also folds the centre
channel into the synced cohort whenback_monois present, so
M == 5for the geometric mean across all five 5_X channels
(wasM == 4in the round-43 approximation). - The round-43 single-channel approximation (
SyncPerSlot/
SyncAveragedcollapsing to per-channelg_ch/g_avg,ch)
is RETIRED — every 5_X SIMPLE/ASPX dispatcher now applies the
exact synchronised gain across all contributing channels.
-
Round 43 — companding
b_compand_avg+sync_flag == 1branches +
ASPX_ACPL_1 sb0 =acpl_qmf_bandhookup (TS 103 190-1 §5.7.5.2 +
Pseudocode 121):aspx::CompandingModeenum captures the (sync_flag,
b_compand_on[ch],b_compand_avg) product per Pseudocode 121:
Off,PerSlot,Averaged,SyncPerSlot,SyncAveraged.aspx::CompandingMode::from_control(cc, slot)resolves the
active branch for a single channel from a parsed
CompandingControl, honouringsync_flag == true(slot 0
broadcasts) and the b_compand_avg presence-rule.aspx::apply_companding_on_qmf_with_mode(q, sb0, sb1, mode)
extends the round-42 single-channel path to all four active
sub-branches:PerSlot/SyncPerSlotapplyg_ch(ts) * G
per timeslot;Averaged/SyncAveragedaverageL_ch(ts)
over the full A-SPX interval, derive a single constant
g_avg,ch * Gand broadcast it across all timeslots. (Off
is a strict no-op.)apply_companding_on_qmfretained as a
PerSlotthin wrapper for backward compatibility.Ac4Decoder::aspx_extend_pcmsignature now takes
(compand_mode: CompandingMode, compand_sb0_override: Option<u32>)
instead of a rawcompand_on: bool. The override implements
§5.7.5.2's sb0 selection rule: for theASPX_ACPL_1codec mode
sb0 =acpl_qmf_band(fromacpl_config_1ch_partial.qmf_band);
otherwise sb0 falls back totables.sbx(=aspx_xover_band).- Stereo CPE path (
receive_frame) lifts the override from
tools.acpl_config_1ch_partial.qmf_bandwhenever the active
stereo or 5_X mode isAspxAcpl1. Cfg0/Cfg1/Cfg2/Cfg3 SIMPLE/ASPX
dispatchers passNone(those paths never run on ACPL_1). Ac4Decoder::five_x_compand_mode_for_slotresolves a
CompandingModeper output slot for the 5_X dispatchers; the
legacyfive_x_compand_on_for_slotis retained as a thin
mode != Offwrapper for round-42 unit-test compatibility.- sync_flag == 1 cross-channel synchronisation: the per-channel
pipeline computesg_ch(ts)per channel; forM = 1(the
dominant case in our pipeline) the geometric mean across
channels reduces to the local gain (exact). ForM > 1channels
processed independently the synchronisation is approximated —
documented as a known limitation in
apply_companding_on_qmf_with_mode. - 5_X ACPL_3 path companding wiring: already in place from round
42 via the stereo CPE path'scompand_mode_pri/
compand_mode_sec(the ACPL_3 walker populatestools.companding
fromcompanding_control(2); the L/R carriers go through the
standard stereo CPE primary/secondary path which now lifts
CompandingModeand applies it before the §5.7.7.6.2
Pseudocode 118 multichannel synthesis). - 7 new tests cover:
CompandingMode::from_controlresolves all
six sync/on/avg product states;apply_companding_on_qmf_with_mode
Offstrict no-op +Averaged/SyncAveragedconstant-scale
invariant + sb0-override band shift;five_x_compand_mode_for_slot
branch resolution;aspx_extend_pcmwith sb0 override + with
Averagedmode diverges from baseline.
-
Round 42 — 5_X SIMPLE/ASPX cfg0/cfg1/cfg3 trailer-aware ASPX
dispatch + §5.7.5 companding tool (TS 103 190-1 §4.2.6.6 / §5.7.5):asf::SubstreamToolsgainedcfg0_aspx_{lr,ls_rs,centre},
cfg1_aspx_{lr,ls_rs,centre},cfg3_aspx_{lr,ls_rs,centre}—
mirrors of the round-41 cfg2 trailer slots. The 5_X SIMPLE/ASPX
outer walker now stores the captured Table-25case ASPX:
trailer triplet (aspx_data_2ch + aspx_data_2ch + aspx_data_1ch)
into the slot matching the activecoding_configinstead of
discarding round-41's cfg0/1/3 captures.Ac4Decoder::dispatch_5x_cfg{0,1,3}_simple_aspxnow apply the
A-SPX bandwidth-extension per output channel using the captured
trailers, with the canonical Table-25 trailer-to-slot mapping
(1st-2ch -> L/R, 2nd-2ch -> Ls/Rs, 1ch -> C). Independent of
cfg0'sb_2ch_mode(ASPX is applied after the channel-element
decode produces PCM, so the L,Ls / R,Rs inner stereo coding
doesn't shuffle the trailer assignment).Ac4Decoder::maybe_extend_5x_slot— internal helper that picks
the right(trailer, primary/secondary)for an output slot and
runsaspx_extend_with_trailerwith the resolved companding-on
flag for that slot.aspx::apply_companding_on_qmf(q, sbx, sbz)— §5.7.5.2
companding tool decoder side, applied per-channel on the QMF
matrix in[sbx, sbz)for all timeslots. Implements the
sync_flag == 0,b_compand_on == truebranch (the dominant
case): per-slot mean-absolute levelL_ch(ts)per Pseudocode
equations, theng_ch(ts) = L^((1-α)/α)with α = 0.65 and
Q_out = g * G * Q_inwithG = 2^α. Zero-signal slot uses
unit gain (avoids0^negative_exp = inf). The b_compand_avg
averaging and thesync_flag == 1per-channel-product synched
gain branches are scaffolded for a later round.Ac4Decoder::aspx_extend_pcmextended with acompand_on: bool
parameter — appliesapply_companding_on_qmfbetween envelope
adjustment and inverse QMF synthesis when set. All call sites
updated: stereo CPE primary/secondary (uses
tools.companding[0]/[1]), and every 5_X cfg0/1/2/3 slot
(uses thecompanding_control(5)slot lookup via
five_x_compand_on_for_slot).Ac4Decoder::five_x_compand_on_for_slot(cc, slot)— resolves
b_compand_on[slot]from aCompandingControl, honouring
sync_flag == true(slot 0 broadcasts), per-channel, and absent
flags (returnsfalse).- 5 new tests cover: per-cfg trailer-aware dispatch produces
output that diverges from the round-39 low-band-only path;
companding flag resolver across mono / per-channel / sync
branches;aspx_extend_pcmwith companding diverges from
baseline; and edge-case no-ops forapply_companding_on_qmf
(degenerate band + zero signal).
-
Round 41 — 5_X SIMPLE/ASPX cfg2 ASPX bandwidth-extension trailers +
Table 181 first-stage SAP matrix for 5_X / 7_XASPX_ACPL_1(TS
103 190-1 §4.2.6.6 / §5.3.4.3.2):aspx::FiveXAspxTrailer+aspx::FiveXAspxChannelTrailer—
captured per-trailer bitstream state (xover, frequency tables,
framing, qmode, delta-dir, sig/noise envelopes, hfgen
add_harmonic / tna_mode) for oneaspx_data_2ch()or
aspx_data_1ch()payload. Wraps everythingaspx_extend_pcm
needs for one or two channels.asf::capture_aspx_data_2ch_trailer/
asf::capture_aspx_data_1ch_trailer— wrapparse_aspx_data_*_body
with snapshot/restore over the per-substream ASPX-trailer slots
so multiple sequential trailers can be parsed without corrupting
each other's state.parse_5x_audio_data_outerSIMPLE/ASPX branch now walks the
Table 25 rowcase ASPX:trailer triplet (aspx_data_2ch + aspx_data_2ch + aspx_data_1ch) and stores the captured trailers
ontools.cfg2_aspx_lr / cfg2_aspx_ls_rs / cfg2_aspx_centre.
Cfg0/Cfg1/Cfg3 still parse the bits (so the bitreader lands at
the right offset) but don't yet wire to dispatch.Ac4Decoder::dispatch_5x_cfg2_simple_aspxextended to apply
A-SPX bandwidth-extension per-channel: L/R useaspx_lr(primary
/ secondary); Ls/Rs useaspx_ls_rs; centre usesaspx_centre.
SIMPLE-mode (no trailers) and trailer-parse-miss paths fall
through to round-38 low-band-only PCM.asf::apply_sap_table_181(a, b, s3, s4, chparam_pair, max_sfb, tl) -> (l, r, ls, rs)— Table 181 first-stage matrix for
5_X_codec_mode == ASPX_ACPL_1. Mixes (sSMP_A, sSMP_B) with
(sSMP_3, sSMP_4) per-sfb using the (a, b, c, d) coefficients
extracted from eachchparam_info()payload (Pseudocode 59) into
preliminary (L, R, Ls, Rs) spectra. Bands pastmax_sfb_master
pull L/R from A/B and zero Ls/Rs.parse_aspx_acpl_1_2_inner_body(5_X) +
parse_7x_audio_data_outer(7_X ASPX_ACPL_1 branch) now persist
the parsedchparam_info()pair on
tools.acpl_1_residual_chparamplusmax_sfb_masteron
tools.acpl_1_residual_max_sfb_master— round 40 already
persisted the residual spectra; round 41 closes the loop with
the SAP coefficients + bound.Ac4Decoder::receive_frame5_X ACPL_1 dispatch now applies
Table 181's SAP matrix in the spectral domain when all four
inputs (sSMP_A, sSMP_B, sSMP_3, sSMP_4 + chparam pair +
max_sfb_master) are available, IMDCTs the resulting (L, R, Ls,
Rs) preliminary spectra, and feeds them into Pseudocode 117
(run_acpl_5x_pair_pcm) as the already-mixed PCM inputs the
spec expects. ACPL_2 mode (no residual pair) falls through to
round-40 silence placeholders.
-
Round 40 — SAP a/b/c/d coefficient extraction (Pseudocode 59) +
Table 183 7_X SIMPLE/ASPX final channel mapping + standalone Ls/Rs
surround mono walker for ACPL_1 Mode 1 (TS 103 190-1 §5.3.2 /
§5.3.4.4.1 / §5.7.7.6.1):asf::SapCoeffs+asf::extract_sap_abcd(info, max_sfb_per_group)
— Pseudocode 59 implementation. Walkschparam_info.sap_mode∈
{0, 1, 3} and emits per-(g, sfb)(a, b, c, d)quartets:sap_mode == 0→ identity (a=d=1, b=c=0).sap_mode == 1, ms_used→ M/S inverse (a=b=c=1, d=-1) per-sfb;
identity where ms_used == false.sap_mode == 3→ alpha-driven SAP. Pair-major DPCM differential
decode ofdpcm_alpha_q→alpha_q[g][sfb](odd sfbs inherit
the even pair-mate; cross-groupdelta_code_timefolds in when
g != 0andmax_sfb_g == max_sfb_prev). `sap_gain = alpha_q- 0.1
drives(a, b, c, d) = (1 + sap_gain, 1, 1 - sap_gain, -1)for SAP-coded bands;(1, 0, 0, 1)` for skipped bands.
- 0.1
Ac4Decoder::dispatch_7x_additional_channel_pairextended:
acceptspartner_pair_spectra+partner_slots+chparam. With
b_use_sap_add_ch == trueAND partner spectra of matching
transform length, applies Table 183's SAP matrix per-sfb in the
spectral domain —[out_high; out_low] = [a b; c d] · [partner; add_ch]— then IMDCTs both halves independently. With identity
SAP the partner slot is left untouched (its independent 5_X-core
IMDCT renders elsewhere) and only F/G land at slots 5/6 per Table
182 — round-39 behaviour preserved.Ac4Decoder::receive_frame7_X branch resolves the partner pair
from the activefive_x_coding_config: cfg2 picks
four_channel_data[2,3](Ls/Rs); cfg3 picks
five_channel_data[3,4]; cfg1 picks the trailing
two_channel_data[0,1]. cfg0 has no natural surround partner
inside the 5_X core — falls through to identity passthrough.- Standalone Ls/Rs surround mono walker for ACPL_1's Mode 1
surround-driven path:parse_aspx_acpl_1_2_inner_body(5_X)
andparse_7x_audio_data_outer(7_X) now persist the joint-MDCT
residual pair (sSMP,3 / sSMP,4 per Table 181) on
tools.acpl_1_residual_pair: [Option<(u32, Vec<f32>)>; 2]. The
decoder's 5_X / 7_X ACPL_1 dispatch IMDCTs them into Ls / Rs PCM
carriers and feeds them as thex3/x4inputs of Pseudocode
117 — replacing the round-37 silence placeholder for slots 3 / 4.
ASPX_ACPL_2 mode never emits a residual pair (no max_sfb_master
in its walker), so the detach isNoneand surround stays at
silence as before. - New tests (+8):
extract_sap_abcd_mode_zero_returns_identity,
extract_sap_abcd_mode_one_swaps_per_sfb_on_ms_used,
extract_sap_abcd_mode_three_pair_dpcm_decode,
extract_sap_abcd_mode_three_unused_bands_pass_through,
extract_sap_abcd_mode_three_delta_code_time_cross_group,
sap_coeffs_identity_helper,
dispatch_7x_additional_pair_sap_identity_routes_partner_and_additional,
dispatch_acpl_5x_pair_with_real_ls_rs_carriers_emits_surround_energy.
The existing
parse_5x_aspx_acpl_1_non_iframe_walks_three_channel_datagrew an
assertion thattools.acpl_1_residual_pair[0/1].is_some()after
the inner walker runs. 568 tests total (was 560).
-
Round 39 — 5_X SIMPLE/ASPX cfg0/cfg1/cfg3 dispatch helpers +
7_X SIMPLE/ASPX additional-channel pair render (TS 103 190-1
§5.3.4.3.1 / Table 180 columns 0/1/3 + §5.3.4.4.1 / Table 182):Ac4Decoder::dispatch_5x_cfg0_simple_aspx— IMDCTs each
two_channel_data.scaled_spec_per_channel[0..2]into PCM slots
per the 1-bitb_2ch_mode:false-> tcd_a→[0,1] (L,R) /
tcd_b→[3,4] (Ls,Rs);true-> tcd_a→[0,3] (L,Ls) /
tcd_b→[1,4] (R,Rs).cfg0_centre_mono.scaled_spec(the trailing
mono_data(0)) lands on slot 2 (C).Ac4Decoder::dispatch_5x_cfg1_simple_aspx— IMDCTs
three_channel_data[0..3]into slots 0/1/2 (L/R/C) and
two_channel_data[0..2]into slots 3/4 (Ls/Rs).Ac4Decoder::dispatch_5x_cfg3_simple_aspx— IMDCTs
five_channel_data[0..5]straight into slots 0..4 (L/R/C/Ls/Rs).Ac4Decoder::dispatch_7x_additional_channel_pair— IMDCTs the
seven_x_additional_channel_data.scaled_spec_per_channel[0..2]
into PCM slots 5 / 6 (the F / G preliminary outputs in Table 182).
SAP companding (Table 183 a,b,c,d) is the identity for now —
b_use_sap_add_ch == falsecollapses the matrix; the explicit
SAP coefficient extraction lands in a future round.Ac4Decoder::receive_frameswitch: round 38's cfg2-only branch
now selects acrossCfg0/Cfg1/Cfg2/Cfg3based on the parsed
five_x_coding_config. Mutually exclusive with the ACPL_3 / pair
paths via the existingfive_x_simple_aspx_activegate. The
7_X SIMPLE/ASPX path additionally runs the additional-channel
pair dispatch on top of the core 5-channel mapping.- New tests (+9):
dispatch_5x_cfg0_populates_l_r_c_ls_rs_default_2ch_mode,
dispatch_5x_cfg0_alternate_2ch_mode_maps_to_l_ls_r_rs,
dispatch_5x_cfg1_populates_l_r_c_ls_rs,
dispatch_5x_cfg3_populates_l_r_c_ls_rs,
dispatch_5x_cfg013_noop_on_length_mismatch,
dispatch_7x_additional_pair_populates_slots_5_and_6,
dispatch_7x_additional_pair_noop_on_length_mismatch,
plus integration tests
five_x_simple_cfg0_walker_populates_two_two_plus_centre,
five_x_simple_cfg3_walker_populates_five_channel_data. Total:
551 -> 560.
-
Round 38 — LFE body decoder + cfg2_back_mono end-to-end decode +
ACPL_3 centre channel via IMDCT (TS 103 190-1 §4.2.7.2 / §4.2.6.6
Cfg2 / §5.7.7.6.2):parse_mono_data(b_lfe=true)now walks the trailingsf_data(ASF)
body via the newdecode_asf_long_lfe_body_with_max_sfb_lfe
helper.sf_info_lfe()(Table 35) forces long-frame / single
window group, so the LFE body shape is the regular ASF long-frame
quartet (`asf_section_data + asf_spectral_data + asf_scalefac_data- asf_snf_data
) — only themax_sfb[0]bit-width changes (n_msfbl_bitsfrom Table 106 column 4 instead ofn_msfb_bits). The dequantised + scaled spectrum lands onMonoLfeData::scaled_spec`,
matching the round-37 non-LFE behaviour.
- asf_snf_data
Ac4Decoder::dispatch_5x_cfg2_simple_aspx— new helper that runs
end-to-end IMDCT + overlap-add for the 5_X SIMPLE/ASPX
coding_config == 2channel layout. Walks the parsed
four_channel_data.scaled_spec_per_channel[0..4]into output
slots 0/1/3/4 (L/R/Ls/Rs per Table 180) and the trailing
cfg2_back_mono.scaled_specinto slot 2 (C). Fires when
five_x_mode in {Simple, Aspx}andfive_x_coding_config == Cfg2FourMono. Cfg0 / Cfg1 / Cfg3 dispatch helpers remain deferred.Ac4Decoder::receive_frameACPL_3 path: replaces the round-37
silence-placeholder for the centre carrier with an IMDCT of
cfg0_centre_mono.scaled_specviaimdct_mono_lfe_data_f32. The
Pseudocode 118 multichannel synthesis now emits a non-silent
centre when the trailingmono_data(0)body decoded successfully;
falls back to silence when the centre body is absent (LFE / SSF /
Huffman miss / non-Cfg0 frame).- New unit + integration tests:
parse_mono_data_lfe_walks_sf_data_body— replaces the round-37
"LFE body deferred" test; verifies the LFE body now decodes into
scaled_specfor an all-zero stream.dispatch_5x_cfg2_populates_l_r_c_ls_rs— cfg2 dispatch IMDCTs
every channel into the right output slot with non-zero energy
from per-channel ramp spectra.dispatch_5x_cfg2_noop_on_length_mismatch— cfg2 dispatch
leaves output slots untouched when the carrier transform length
differs from the requested sample count.five_x_simple_cfg2_walker_populates_four_plus_back_mono—
integration test threadingparse_5x_audio_data_outer→ cfg2
tools layout, asserting the four per-channel scaled spectra +
back-mono body all populate cleanly.
-
Round 37 — 7_X ASPX_ACPL_1 / ASPX_ACPL_2 dispatch + cfg0 centre
end-to-end decode (TS 103 190-1 §5.7.7.6.3 Pseudocode 120 +
§5.7.7.6.1 Pseudocode 117 cfg0 centre-channel wiring):Ac4Decoder::receive_framenow dispatches the §5.7.7.6.3
Pseudocode 120 channel-pair multichannel synthesis when the 7_X
walker resolvedseven_x_modetoAspxAcpl1orAspxAcpl2. The
dispatch reusesdispatch_acpl_5x_pair(the Pseudocode 117
channel-pair core that Pseudocode 120 wraps for 7.X) — for
ASPX_ACPL_{1,2}the additional 2 channels (z6/z7 in Pseudocode
120) live outside the A-CPL pair so the 5-channel core (L/R/C/Ls/Rs)
is identical between 5_X and 7_X. Slots 5..7 stay at silence until
the additional-channel decode path lands.MonoLfeData(mch::parse_mono_data) now decodes the trailing
sf_data(ASF)body for non-LFE, ASF-frontend, long-frame /
single-window-group mono channels — replacing the round-36
silence-placeholder for the centre channel in the 5_X / 7_X
Pseudocode 117 / 120 dispatches with a real IMDCT / overlap-add
centre carrier. The newMonoLfeData::scaled_specfield carries
the dequantised + scaled MDCT spectrum; LFE / SSF-frontend / short
/ grouped / Huffman-error cases still leave thisNoneand
preserve the prior outer-shell-only behaviour.Ac4Decoder::dispatch_acpl_5x_pairnow accepts optional
centre_pcm/ls_pcm/rs_pcmcarrier overrides —None
falls back to the round-36 silence-placeholder;Some(...)threads
real per-channel PCM through Pseudocode 117'sz4 = x2centre
passthrough (and thex3in = 2*x3/x4in = 2*x4Ls/Rs pre-
multiplications for ACPL_1 mode). The Cfg0 centre is wired from
the parsedcfg0_centre_mono.scaled_specvia a new
imdct_mono_lfe_data_f32helper.- New unit + integration tests:
parse_mono_data_non_lfe_walks_sf_data_body— non-LFE long-frame
ASF-frontend mono walks the trailing body intoscaled_spec.parse_mono_data_lfe_skips_body_walk— LFE body decoder remains
deferred (round-36 behaviour preserved).parse_mono_data_non_lfe_ssf_frontend_skips_body_walk—
SSF-frontend mono skips the ASF body walk.dispatch_acpl_5x_pair_centre_pcm_passthrough_emits_centre_energy
— supplying a real centre PCM produces non-silent ch2 output.seven_x_pair_dispatch_resolves_same_mode_as_five_x— the 7_X
dispatch mapsSevenXCodecMode::AspxAcpl{1,2}to the same
Acpl5xPairModeselectors as the 5_X path.imdct_mono_lfe_data_f32_returns_none_when_no_scaled_spec/
imdct_mono_lfe_data_f32_imdcts_when_scaled_spec_present—
cover the new IMDCT helper.seven_x_aspx_acpl_2_walker_to_synthesis_glue— integration
test threadingparse_7x_audio_data_outer→
acpl_data_1ch_pair→run_acpl_5x_pair_pcmfor a non-iframe
7_X ACPL_2 substream.
-
Round 36 — 5_X ASPX_ACPL_1 / ASPX_ACPL_2 decoder dispatch wiring
(TS 103 190-1 §5.7.7.6.1, Pseudocode 117):Ac4Decoder::receive_framenow dispatches the §5.7.7.6.1
Pseudocode 117 channel-pair multichannel synthesis when the 5_X
walker resolvedfive_x_modetoAspxAcpl1orAspxAcpl2and
populated the matchingacpl_config_1ch_*+acpl_data_1ch_pair
tools slots. The dispatch:- Reads L/R carrier PCM from
pcm_per_channel[0]/[1](already
filled by the stereo ASF / ASPX decode path), zero-filling on
absence to keep QMF analysis history consistent. - Resolves the active
acpl_config_1chper mode:
acpl_config_1ch_partialforAspxAcpl1,
acpl_config_1ch_fullforAspxAcpl2(per Tables 25 + 59). - Builds zero-filled centre / Ls / Rs carrier placeholders
(centre matches the existing ACPL_3 wiring; Ls/Rs only for
ACPL_1 mode perAcpl5xPairModemode-vs-surround consistency
check). The carrier-decode paths gain real signal when
cfg0_centre_monoand the surround mono carriers acquire
end-to-end decoders in a future round. - Calls
acpl_synth::run_acpl_5x_pair_pcmand writes the five
output channels (L, R, C, Ls, Rs) intopcm_per_channel[0..5],
growing the slot vector as needed. - Skipped when the substream is already an
AspxAcpl35_X frame
(the two pipelines are mutually exclusive per Table 97).
- Reads L/R carrier PCM from
- The dispatch logic is extracted into a private
Ac4Decoder::dispatch_acpl_5x_pairhelper so the path can be
unit-tested without building a full 5_X TOC + body. - Five new unit tests in
decoder::testscover the synthesis
arithmetic + dispatch behaviour:dispatch_acpl_5x_pair_aspx_acpl_2_emits_five_channels—
ACPL_2 dispatch with ±2000 carrier-PCM tones produces five
channels with non-zero L / R energy.dispatch_acpl_5x_pair_aspx_acpl_1_emits_five_channels—
ACPL_1 dispatch with zero-filled Ls/Rs surround placeholders
still emits five-channel output.dispatch_acpl_5x_pair_rejects_unaligned_sample_count—
dispatch is a no-op when sample count isn't a multiple of
NUM_QMF_SUBBANDS(64).dispatch_acpl_5x_pair_zero_fills_missing_carriers— when L/R
slots areNone, dispatch synthesises silence-grade output
from zero-filled fallbacks.dispatch_acpl_5x_pair_resolves_partial_for_aspx_acpl_1—
regression check thatqmf_banddiffers betweenpartial
(1..8) andfull(always 0) configs per Table 59.
- Test count: 535 → 540 (+5).
- The §5.7.7.6.2 ACPL_3 dispatch (round 34 — Pseudocode 118)
remains unchanged; the new ACPL_1 / ACPL_2 path takes its place
when those modes are signalled.
-
Round 35 — ETSI float-table validation suite (TS 103 190-1
Annex C.1, C.3, C.11 + Annex D.2, D.3):- New
parse_c_float_arrays()parser intests/etsi_table_validation.rs
extracts everyconst float/const float32array (1-D and 2-D) from
the ETSI accompaniment filedocs/audio/ac4/ts_10319001v010401p0-tables.c.
The existing integer parser (round 20) bailed on thefloatkeyword, so
the five float-typed reference tables had been silently un-validated. - New
assert_float_table()/assert_complex_float_table()helpers
compare Rust&[f32](and&[(f32, f32)]) constants against the
parsed reference under a 1 ppm absolute / 10 ppm relative epsilon —
tight enough to catch a single-digit transcription typo in the visible
decimal prefix, loose enough that f32 literal-rounding noise is
invisible. - Four new tests:
etsi_source_parses_floats— sanity-check that the 5 expected float
arrays are parsed with the right lengths (20, 32, 256, 640, 1024).validate_ssf_float_tables—POST_GAIN_LUT[20],
PRED_GAIN_QUANT_TAB[32],RANDOM_NOISE_TABLE[256]against the
Annex C.1 / C.3 / C.11 reference data.validate_qmf_window— the 640-entry Annex D.3QWINprototype
window against the reference.validate_aspx_noise_table— the Annex D.2ASPX_NOISE[512][2]
complex-noise table against the reference (flattened row-major to
compare&[(f32, f32)]against the parsed flatVec<f32>).
- Outcome: all 1,860 published float reference values
(20 + 32 + 256 + 640 + 1,024) cleared the epsilon test on first run,
proving the existing transcriptions inssf_tables.rs,qmf.rs, and
aspx_noise.rsare byte-correct against the ETSI accompaniment data.
Future regressions caught at compile-time of the test target rather
than at decode-time of a fixture.
- New
-
Round 34 — FIXVAR / VARFIX / VARVAR atsg border derivation + SNF injection + 5_X ASPX_ACPL_3 synthesis (TS 103 190-1 §5.7.6.3.3.2 + §5.1.4 + §5.7.7.6.2):
- New
derive_fixvar_atsg()(§5.7.6.3.3.2 Pseudocode 77, FIXVAR arm): builds
the signal-envelope border vector right-to-left fromT - var_bord_rightusing
rel_bord_right[]deltas, then reverses to ascending order. - New
derive_varfix_atsg()(Pseudocode 77, VARFIX arm): builds left-to-right
fromvar_bord_leftusingrel_bord_left[]deltas, appends T. - New
derive_varvar_atsg()(Pseudocode 77, VARVAR arm): left-side anchors +
right-side internal anchors + T, totallingnum_env + 1entries. - New
derive_atsg_borders()dispatcher: routes FIXFIX / FIXVAR / VARFIX /
VARVAR framing to the matching derivation and computes noise borders
([0, T]for 1 noise envelope,[0, mid, T]for 2). - Both the TNS path and envelope-adjustment path in
decoder.rsnow call
derive_atsg_bordersinstead of the FIXFIX-onlyderive_fixfix_atsg, enabling
A-SPX bandwidth extension for all four interval classes. - New
inject_snf_noise()inasf_data.rs(§5.1.4 SNF): injects
shaped noise (gain = 2^((idx * 1.5 - 84) / 4)) into zero-energy MDCT
bins using a 16-bit LCG (multiplier 69069, addend 1). Previously the
parse_asf_snf_data()result was discarded; now it is consumed by the
long-mono ASF decode path. - 5_X
ASPX_ACPL_3synthesis wired intoAc4Decoder::receive_frame:
Ac4Decodergains two new persistent fields (acpl_5x_pair_state,
acpl_5x_mch_state); when the walker populatesacpl_config_2ch+
acpl_data_2ch+ stereo carrier spectra,run_acpl_5x_mch_pcm
(Pseudocode 118) runs and fillspcm_per_channel[0..5]with L/R/C/Ls/Rs. - Unit tests:
derive_fixvar_atsg(2 cases + reject),derive_varfix_atsg
(2 cases + reject),derive_varvar_atsg(2 cases),derive_atsg_borders
dispatch (FIXFIX + FIXVAR),inject_snf_noise(fill, gain formula, LCG).
- New
Changed
registerentry point unified onRuntimeContext(task #502).
The legacypub fn register(reg: &mut CodecRegistry)is renamed to
register_codecsand a newpub fn register(ctx: &mut oxideav_core::RuntimeContext)calls it internally. Breaking change
for direct callers passing aCodecRegistry; switch to either the
newRuntimeContextentry or the explicitregister_codecsname.