Pure-Rust Dolby AC-4 audio codec — decoder and encoder per ETSI
TS 103 190-1 V1.4.1. Zero C dependencies, no FFI, no *-sys crates.
Part of the oxideav framework but usable standalone.
AC-4 is a complex, hierarchical codec — multiple presentations, nested substream descriptors, ASF / A-SPX / SSF coefficient streams, A-CPL channel-pair coupling, and an EMDF metadata sidecar. This crate parses the full framing and decodes channel-based streams to PCM; an encoder covers the channel-based layouts.
- Sync framing (
sync) —0xAC40plain /0xAC41CRC-protected, 16-bitframe_size()with 24-bit escape. The Annex G.4.2crc_wordis verified on every 0xAC41 frame (protected payload =frame_size+raw_ac4_frame); the decoder rejects mismatching frames, andwrap_sync_frameemits both framing forms. - Table of contents (
toc) — the fullac4_toc()walker: bitstream_version, sequence_counter, fs_index, frame_rate_index,b_iframe_global, payload_base, per-presentationac4_presentation_info()(single / multi-substream, configs 0..=5 plus extension escape, HSF extension, pre-virtualised flag, extra EMDF substreams), per-substreamac4_substream_info()(channel-mode prefix decoder, sf_multiplier, bitrate_indicator, content_type with language tag, b_iframe), the substream index table, and thevariable_bits(n)codec. Surfaced on a parsedAc4FrameInfo(including the part-2 A-JOC / object / OAMD substream descriptors). The v2b_pres_ndot/b_audio_ndot/b_oamd_ndotflags follow the §6.3.2.11.2 "no dependency over time" polarity — ndot is the I-frame flag (a long-standing inversion on both the parse and write sides was fixed in r411). - Presentation substream (
pres_data) — the complete part-2ac4_presentation_substream()(§6.2.2.3): alternative-presentation names + targets with per-substream activation maps, the additional-data envelope, presentation dialnorm +further_loudness_info(1, 1), thedrc_metadata_sizeenvelope arounddrc_frame(b_pres_ndot), substream-group gains, the associated-audio scale block, and the §6.2.9 presentation data:custom_dmx_data()(bs_ch_config decision tree,cdmx_parameters()with all sixtool_*()elements) andloud_corr()(the full gate ladder incl. the object corrections) — every parser with an exact writer inverse. - Stereo downmix coefficients (
dmx_coeff) —stereo_dmx_coeff()(the factored-outcustom_dmx_data()block, also invoked frombed_render_info()) with the TS 103 190-1 §4.3.12.2 code → gain mappings (Tables 149/149a quarter-power-of-two steps, the LFE5,5 − codedB rule, dmx loudness corrections, Table 150 preferred method). - OAMD substream — the standalone
oamd_substream()(§6.2.2.4): optionaloamd_common_data()/oamd_timing_data()and theb_alternative == 0oamd_dyndata_multi()walk, withoamd_substream_info()surfaced from the TOC.
Ac4Decoder accepts a sync-wrapped packet or a bare MP4-style
raw_ac4_frame payload, parses the TOC, and decodes the substreams to
an S16 AudioFrame:
- ASF coefficient pipeline —
asf_section_data(),asf_spectral_data()(HCB 1..11 + the codebook-11 escape),asf_scalefac_data(), andasf_snf_data()for mono, stereo (split + joint M/S MDCT), and the multichannel layouts (Tables 26–29), with long-frame and grouped short-frame window handling. Dequantise + scale (rec_spec = sign(q)·|q|^(4/3)) feeds the reference KBD-window IMDCT (mdct) with overlap-add. - A-SPX bandwidth extension —
aspx_config()/companding_controlparse, the FIXFIX / FIXVAR / VARFIX / VARVAR ATSG border derivation, envelope / noise / tone payload decode, QMF analysis/synthesis, the §5.7.6.3.5 balance stereo joint decode (anaspx_data_2ch()pair withaspx_balance = 1decodes as the spec's jointly coded sum/pan pair — Pseudocode 84 withPAN_OFFSET = 12plus the Pseudocode 80/81delta = 2accumulation on the balance channel, wired on every 2ch consumer: stereo CPE, the 5_X trailers, the ICE payload rosters, the 22.2 pairs, and the A-JOC A-SPX downmix; every liveaspx_balance = 1writer emits the exact Pseudocode 84 inverse, so encoded pans survive the joint decode — measured ≈ 20 dB preserved on a hard-panned ACPL_3 chain), and the §5.7.6.4.1.2–4 temporal-noise-shaping chirp / order-2 LPC inverse filtering driven byaspx_tna_mode. The encoder now selects a real per-noise-subband-groupaspx_tna_mode(§4.3.10.6.1 None / Light / Moderate / Heavy) from the carrier's QMF low band — a level-independent predictor-strength measure (|alpha0|² + |alpha1|²from the decoder's owncompute_covariance/compute_alphas, aggregated per noise group via the Pseudocode-89 high-band walk) — and wires it into the live 5_X ASPX_ACPL_3 frame path (aspx_tna_select+ the_tnabody writers), replacing the all-zero inverse-filtering scaffold. - A-CPL channel-pair coupling — ASPX_ACPL_1 / _2 (Pseudocode 117) and ASPX_ACPL_3 (Pseudocode 118) synthesis producing 5-channel L/R/C/Ls/Rs PCM, plus the 7.X (7.0 / 7.1) walker.
- A-JOC (Advanced Joint Object Coding, TS 103 190-2 §5.7 + §6.2.5 /
§6.3.6) — the
ajocmodule lands the bit-exact parameter-processing core: the §5.7.3.1 Table 42 QMF-subband → parameter-band mapping (sb_to_pb, all eightajoc_num_bandsconfigs) + Table 100 band-code resolution, the §5.7.3.2 Table 43 differential decoder (DIFF_FREQ / DIFF_TIME / sparse-absent), the §5.7.3.3 Tables 44-47 uniform dequantizers (validated against the documented dry ±5,0048828 / wet ±2,001953125 endpoints), the §5.7.3.4 Table 48 linear-ramp time interpolator, and the §5.7.3.6 Table 49 dry + wet matrix reconstruction (reconstruct). The full §5.7.3.6 spatial reconstruction (ajoc_reconstruct) closes the A-JOC decode chain end-to-end: it accumulates the §5.7.3.6.2 decorrelation-input pre-matrixD[de][ch] = Σ_o |wet[o][de]|·dry[o][ch](pre_matrix_param), walks the(ts, sb)QMF grid interpolating the dry / wet / pre tracks (per-track interpolators carried across frames inAjocReconState), forms the decorrelator inputsu = pre · x, decorrelates them with the §5.7.3.5 cyclic-0,2,1 decorrelator bank (reusing the part-1 §5.7.7.4.2InputSignalModifier), and sums the dry (x · mtx_dry) + wet (y · mtx_wet) contributions into the reconstructed output objectsz[ts][sb][o]. The Huffman-independent §6.2.5 config-layer parsers (ajoc_ctrl_info,ajoc_data_point_info,ajoc_bed_info,ajoc_dmx_de_datawith the Table 106de_dlg_dmx_coeffprefix code) walk the side-information. The §6.2.5.5ajoc_huff_data()codeword layer (ajoc_huffman) decodes over the twelve Annex A.1.1AJOC_HCB_*codebooks (transcribed from the ETSI Part 2 electronic-attachment table file, verified in-tree as complete prefix codes) with the §6.3.6.5.2 Table 104get_ajoc_hcb()selection, andajoc_data::decode_ajoc()joins the halves: a complete §6.2.5.1ajoc()element goes ctrl-info → Huffman rows → Table 43 differential decode (cross-framemtx_*_q_prevstate) → dequantized matrices, straight intoajoc_reconstruct. The A-JOC parameter encoder (encoder_ajoc) quantises real-valued dry / wet matrices (Tables 44-47 inverses), prices every matrix row with the real codeword lengths to pick DIFF_FREQ vs DIFF_TIME, and emits GOP-chainedajoc()elements whose decode is exact on the quantised grid (verified over I/P/P/P GOPs with measured P-frame row-bit savings). The chain is now wired end-to-end into the frame decoder: the v2 TOC parsesac4_substream_info_ajoc()/ac4_substream_info_obj()(§6.2.1.9-11,bed_dyn_obj_assignmentwith all five position-signalling forms and Table 83-86 static-object counts), theajoc_substreammodule walksaudio_data_ajoc()(§6.2.3.4) with itsvar_channel_element()downmix (§6.2.4.4 — SIMPLE + A-SPX modes, all odd-count tails) and OAMD side information, andAjocSubstreamDecoderdrives IMDCT → QMF analysis → Table 49 reconstruction → per-object QMF synthesis with all cross-frame state persistent.Ac4Decoderroutes v2 A-JOC frames to this chain automatically, andencode_ajoc_raw_frameemits complete matching frames (decoder-level packet-to-PCM tests pin per-object energy and a < 0,5 %-of-peak settled reconstruction error). The LFE channel (coded directly in the downmix as a Table 21mono_data(1)body, bypassing the spatial reconstruction) is decoded to PCM and emitted on the leading LFE output slot, and the §6.2.2.2 post-audiometadata(…, sus_ver = 1)element is parsed on the route (withde_config()carried across frames; per §6.2.7.2 an object substream opens no channel-gatedbasic_metadatafield and carries no stereo-dmx block — the bed/custom downmix data is authoritative). All three §6.2.3.4 downmix forms reach object PCM: the dynamic SIMPLE form, the dynamic A-SPX form (each downmix channel is bandwidth-extended in the QMF domain from its capturedaspx_data_2ch()/aspx_data_1ch()payload before the spatial reconstruction, with the I-frameaspx_config+ xover sticky across P-frames), and theb_static_dmx5.X core (theaudio_data_chan(5.0/5.1)SIMPLE coding configs feed the reconstruction in the Table 180[L, R, C, Ls, Rs]order; the 5.1 core's LFE lands on the leading output slot). The write side gains the matchingwrite_audio_data_ajoc_static/write_audio_data_ajoc_aspxbodies andencode_ajoc_raw_frame_static/encode_ajoc_raw_frame_aspxfull-frame writers. - Immersive channel element (TS 103 190-2 §6.2.4.1-2 — channel
modes 7.0.4 / 7.1.4 / 9.0.4 / 9.1.4, Table 78) — the
icemodule parses the fullimmersive_channel_element()family: the Table 95immersive_codec_modeprefix code,immers_cfg()on the shared I-frame-sticky config slots, all four Table 97 core groupings (1+2+2 incl. both2ch_modeforms / 3+2 / 1+4 / 5) with the §5.2.3.2 Table 19 track assignment, the 7CH_STATICb_use_sap_add_chchparam pair + additional pair, the mode-dependent A-SPX payload roster (captured per element),ajcc_data(b_5fronts), the S-CPL pair/chparam section, and the 4/6-elementacpl_data_1ch()section — with P-frame sticky-config support.Ac4Decoderroutes the immersive channel modes and synthesises all five Table 95 codec modes to PCM in full decoding: SCPL (§5.3.3.1 Table 23 time-domain matrix), ASPX_SCPL (Table 23 withc_gain = m_gain = 1, per-channel A-SPX over the Table 8 channel grouping, §4.8.3.11.3 Table 10/11 output gains), ASPX_ACPL_1 / ASPX_ACPL_2 (§5.5.2 Table 27 — four/six parallel ACplModules with D0/D0/D1/D1/D2/D2 decorrelators over the Table 26 mapping; ACPL_1 runs the PARTIAL config'sacpl_qmf_bandM/S split on the S-CPL-section residual tracks, ACPL_2 runs fully parametric with the coded F/G tracks on the Tfl/Tfr carrier positions; per-module cross-frame differential state), and ASPX_AJCC (§5.6.3.5.2: core IMDCT → per-channel QMF A-SPX extension → §4.8.3.10.3 companding on L/R/C/Ls/Rs → A-JCC full decode → 11/13 outputs; LFE first). The §5.2.3.2 SAP mixing (steps 3-6) runs on the track spectra for the SCPL / ASPX_SCPL / ASPX_ACPL_1 modes — theb_use_sap_add_chquartets mix (D, F) / (E, G) and the S-CPL-section full-SAPa'_jgains drive the Table 20 additive rows. Writers emit complete v2 frames for all five codec modes (write_ice_body_scpl[_with_sap]/write_ice_body_aspx_scpl/write_ice_body_acpl/write_ice_body_ajcc[_with_companding]/encode_ice_raw_frame). - Core decoding mode (TS 103 190-2 §4.7.3, r443) —
Ac4Decoder::set_decoding_mode(DecodingMode::Core)decodes animmersive_channel_elementsubstream to the seven-channel core operating point[L, R, C, Ls, Rs, Tsl, Tsr](+ unity LFE) across all five Table 95 codec modes: SCPL per §5.3.3.2 Table 24 (c_gain × A''..G''), ASPX_SCPL per §4.8.3.11.2 (the Table 8 core payload roster with first-of-pair bracket channels, the §5.4 A-SPX postprocessing tool at −1,5 dB abovesbxon the Table 9 channels,g = 2on every output), the ACPL modes per §4.8.3.14 (carriers A-SPX-extended as in full decoding, no A-CPL,g = 2per present channel), and ASPX_AJCC per §5.6.3.5.3 (Table 39ajcc_core_decodeon the shared §5.6.3.5.1 front end). Thecore_rendermodule implements the §5.10.2.6/§5.10.2.7 channel-based renderer for core decoding (Table 129 gain codes, Table 130 defaults, Table 45 → 5.X.2 and Table 46 → 5.X.0 folds). Validated by PCM-level core-vs-full relationships on the same streams (L/R/C identical; each core surround/top = the ÷√2 fold of its full-decode pair;render_core_to_5_x_2(core)reproduces the full decode folded to 5.X.2 with the same customized gains). The whole 7CH_STATIC track-role reading (Table 23 fold pairs(D, H)/(E, I)/(F, J)/(G, K), Table 8 payload associations) was reconciled to the V1.3.1 edition in the same round — the staged errata notes record the superseded V1.2.1 rows. A-JOC object substreams follow §4.8.2/§4.8.3.13 in core mode: the output is the downmix signal set (A-SPX-extended + companded on the dynamic form) + LFE, without the spatial reconstruction — the first OAMD portion describes this presentation (§4.8.3.4). - 22.2 channel element (TS 103 190-2 §6.2.4.3 + §5.2.4 — Table 78
channel mode 15, 24 channels) —
22_2_channel_element()parses and decodes in both Table 98 codec modes: the two LFEmono_data(1)bodies route directly through the IMDCT, the eleventwo_channel_data()pairs map to channels per Table 21, and the A-SPX mode bandwidth-extends every pair from itsaspx_data_2ch()payload (I-frame-sticky config). Output is 24-channel PCM (both LFEs first, then the Table 21 order);write_22_2_body+encode_22_2_raw_frameemit complete frames for both modes. - OAMD (object audio metadata, TS 103 190-2 §6.2.8 + §6.3.9) —
the
oamdmodule parses and re-emits (exact writer inverses)oamd_timing_data(),object_info_block()with basic / render info and the full property groups,add_per_object_md()/ext_prec_pos()extended-precision refinements,oamd_dyndata_single()(incl. theb_alternativedata-set tail) /oamd_dyndata_multi(), andoamd_common_data()(trim, bed-render-info, tool elements) with all size-announced envelopes reconciled. - A-JCC (Advanced Joint Channel Coding, TS 103 190-2 §5.6 + §6.2.6 /
§6.3.7) — the complete parameter + synthesis chain for both layouts
(
b_5frontsand the 5-channel core layout): the twelve Annex A.1.2AJCC_HCB_*codebooks plus the §6.3.7.3.2 Table 116get_ajcc_hcb()selection (ALPHA / BETA delegate to the Part 1 A-CPL books),ajcc_huff_data()/ajcc_framing_data()/ajced()/ajcc_data()parsers with exact writer inverses, the §5.6.3.2 Table 29 differential decode (plain running sums,ajcc_<SET>_q_prevchained across parameter sets and frames) and Table 30/31 dry (q·Δ−0,6) / wet (q·Δ−2,0) dequantizers with alpha / beta through the Part 1 Tables 202-205ibeta-coupled machinery.ajcc_synthlands the §5.6.3.3 Table 32 smooth / steep interpolator (Table 33 tails), the Table 36 core-mode crossfade, all four reconstruction modules (Tables 37/38 full-mode, Tables 40/41 core-mode) and both drivers:ajcc_full_decode()(Table 35 — 13/11 output channels, per-instance D0/D1/D2 decorrelators + Part 1 transient ducking, √2 output gains) andajcc_core_decode()(Table 39 — 7 channels), with a bitstream-to-QMF end-to-end GOP test throughAjccOwnedParams. - P-frames (
b_iframe = 0) — full inter-frame decode per §4.2.6.x: a per-substream sticky-config state (asf::StickyConfig) carries the I-frame-gatedaspx_config()/acpl_config_*()elements and the Tables 51/52aspx_xover_subband_offsetacross frames, so non-I-frame substreams parse and synthesise their full A-SPX + A-CPL layer on the mono / stereo / 5_X / 7_X paths. The envelope delta decode is the full §5.7.6.3.4 Pseudocode 80/81 — per-envelope frequency resolution with thehigh2low/low2highsubband-group index maps and the cross-intervalqscf_*_prevreference carried per channel (aspx::AspxEnvPrev); the A-CPL DIFF_TIME chain accumulates across frames via the per-element Pseudocode-121AcplDiffStaterows. - SSF front-end — the §5.2.8 arithmetic decoder + Annex C scalar inventory + 37 prediction-coefficient matrices, the bitstream walker, the §5.2.3–5.2.7 PCM synthesis chain, and §5.2.5.2.2 heuristic scaling, with envelope / dither / noise RNG state threaded across granules.
- Metadata — the
metadata()walker, the EMDF payloads substream (emdf_payloads_substream()Table 18 +emdf_payload_config()Table 79, capturing each payload's bytes verbatim), DRC gain application (drc_raw_to_linear+ dialnorm correction applied to planar PCM), and the DE (dialogue enhancement) walker. - Metadata write-side (encoder symmetry) — every metadata parser now
has a bit-exact inverse, so a decoded
Metadataround-trips back to a parse-equivalent bitstream.write_metadata(Table 66) driveswrite_basic_metadata+write_further_loudness_info(Table 67/68, incl. theprgmbndyunary code and the loudness-version escape),write_extended_metadata(Table 69, with an explicitb_channels_classifierflag for layouts that carry no classifiable channels),write_drc_frame/write_drc_data/write_drc_gains(Table 70/74/75, re-deriving the DRC_HCB gain deltas viawrite_drc_huff_diff) andwrite_drc_config/write_drc_compression_curve(Table 71/72/73),write_dialog_enhancement/write_de_config/write_de_data(Table 76/77/78, re-encodingde_parthrough the Annex A.4write_de_abs_huffman/write_de_diff_huffmanhelpers), andwrite_emdf_payloads_substream/write_emdf_payload_config(Table 18/79), all over the canonicalwrite_variable_bitscodec (proven bit-exact against the §4.2.2 decoder for everyu32).
Ac4ImsEncoder emits IMS v2 frames for the channel-based layouts:
- Mono / stereo (SIMPLE/ASF split-MDCT and joint M/S CPE).
- 5.0 / 5.1 and 7.0 / 7.1 SIMPLE/Cfg3Five (per-channel forward MDCT +
DP-optimal sectioning + HCB selection + SNF, with an LFE element for
the
.1layouts). - 5.X / 7.X ASPX_ACPL_1 / _2 / _3 paths with real per-parameter-band α / β extraction from the input channels' MDCT energy / correlation.
- P-frames (
b_iframe = 0) on every live A-SPX path (5_X ACPL_3 single + multi-envelope, 5_X / 7_X ACPL_2, 7_X / 5_X-SAP ACPL_1, 7.0 pure-ASPX): settingb_iframe_global = falseemits the correct Table 25 / Table 33 P-frame body — data elements present, configs + per-element xover omitted — and signals it through the v0/v1/v2 TOC (b_iframe/b_pres_ndot/b_audio_ndot). On the flagship 5_X ACPL_3 path the encoder additionally keeps the previous frame's envelope + parameter rows and switches each of the four A-SPX envelopes (L/R × SIGNAL/NOISE) to TIME-direction DPCM (Pseudocodes 80/81) and each of the 11 A-CPL Table 62 elements to DIFF_TIME (Table 65) whenever strictly cheaper — a stationaryaspx_data_2ch()element shrinks 302 → 55 bits (−81%). Chain consistency over I+5×P GOPs is pinned against an all-I reference. - 5.X ASPX_ACPL_3 with a real ASPX SIGNAL / NOISE envelope on the
L / R carriers (
encode_frame_pcm_5_{0,1}_acpl3_real_aspx): the encoder QMF-analyses the input PCM, aggregates the HF energy across the A-SPX subband-group borders (Pseudocodes 90/91), quantises + FREQ-DPCM packs it (Pseudocodes 80–83), and emits a real-envelopeaspx_data_2ch()instead of the minimum-bit-cost scaffold. - Immersive channel element (ICE) synthesis routes (TS 103 190-2
§6.2.4.1, r419):
encode_frame_pcm_7_{0,1}_4_ice_aspx_scpl(exact Table 23 + §4.8.3.11.3 matrix inverse to the eleven SMP tracks + real per-Table-8-group A-SPX synthesis; ≤ 6,2 % settled relative RMS on all 11 channels, LFE 3,6 %, regenerated HF within 3 dB) andencode_frame_pcm_7_{0,1}_4_ice_acpl{1,2}(§5.5.2 Table 27 module mid carriers with per-band(α, β)from the pair mid/side statistics; ACPL_1 codes the sides belowacpl_qmf_bandas exact M/S residual tracks — ≤ 5,2 % settled RMS; ACPL_2 dry-exact on correlated pairs with β decorrelator fill on independent content). The A-SPX synthesis stack behind them: streaming §5.7.6.2/§5.7.6.5 QMF banks carried across frames, integer-PCM scale anchors (ASPX_QMF_PCM_SCALE— the Pseudocode 82/95 absolute anchors sit at their intended magnitudes, so HF-silence is representable), ratio-coded NOISE envelopes (Pseudocode 94 semantics) and a patch-delivery model (Pseudocode 71 tile map + Pseudocode 86-89 TNS whitening replicated on the encoder's own low band) driving both the noise ratio and an inverse-delivery SIGNAL boost. - 22.2 encode (
encode_frame_pcm_22_2_{simple,aspx}, §6.2.4.3): both Table 98 codec modes from PCM — Simple ≤ 6,0 % settled RMS on all 24 channels, A-SPX with real per-pair synthesis rows. - Encoder companding decision (
select_compand_on_from_qmf): §5.7.5.2 level-crest transient detection feeding the immersivecompanding_control(5)writers. - 9.0.4 / 9.1.4 (
b_5fronts) ICE encode arms (r440): the full encode-side Table 23b_5frontsmatrix inverse (A = (L + Lscr)/2,L″ = (L − Lscr)/2on the fixed ×2 front rows plus the shared half-sum/half-difference surround / top rows) drivesencode_frame_pcm_9_{0,1}_4_ice_aspx_scpl(6×aspx_data_2ch()+ 1×aspx_data_1ch()real-synthesis payloads on theb_5frontsTable 8 groups, three S-CPL pairs; ≤ 6,3 % settled relative RMS on all 13 channels, LFE 3,6 %) andencode_frame_pcm_9_{0,1}_4_ice_acpl{1,2}(the six-module §5.5.2 Table 27 roster — the two front modules(L, Lscr)/(R, Rscr)ride the A / B track positions plain, ACPL_1 codes the front sides on the third S-CPL residual pairL″ / M″; ACPL_1 M/S band ≤ 7,1 % settled RMS on all 13 channels). - A-JCC parameter extractor + ASPX_AJCC encode from PCM (r440,
encoder_ajcc+encode_frame_pcm_{7,9}_{0,1}_4_ice_ajcc): exact Table 30/31 dry / wet quantiser inverses plus alpha (raw F0 lane) / beta lanes; per-parameter-band least-squares dry projections⟨z, x⟩/⟨x, x⟩against the exact per-module output sums (the Table 35/37/38 dry gains sum to 1 and the wet rows cancel, sox = Σ outputsis the natural core), wet gains filling the projection residual through the decorrelator model (wet3 = 0— an informative encoder choice), and alpha / beta from the pair mid/side statistics.build_ajcc_data()assembles smooth-framing single-set elements — FREQ rows on I-frames, per-SET FREQ-vs-TIME rows priced by the real Annex A.1.2 codeword lengths on P-frames — in decoder lockstep via an encoder-heldAjccStatemirror. The encode arms derive the five-channel core as the per-module output sums ÷ (2 + 1/√2) for both layouts; per-band-separated content reconstructs at 0,90..1,08 settled energy ratios on all 11 / 13 channels and the emittedajcc_data()differential-decodes to exactly the extractor's quantised grid across I+P GOPs. - SAP encode decisions (r440, §5.2.3.2 steps 3-6):
encode_frame_pcm_{7,9}_{0,1}_4_ice_scpl_sapmake both immersive SAP decisions automatically — the step-3/4b_use_sap_add_chquartets M/S + prediction code(D, F)/(E, G)per sfb pair (wire = (mid, side − g·mid), the exact Pseudocode 59 quartet inverse), and the step-5/6 full-SAPchparam_info()elements predict each S-CPL track from its Table 20 source carrier with per-pair least-squares gains on thealpha_q · 0,1grid (the wire track carries the residual). Correlated vertical content codes measurably smaller than an identity encode and decodes back within the ASF quantisation floor.
- Immersive remainders — the A-SPX / A-CPL codec modes of
the A-JOC
b_static_dmxcore parse but their carrier synthesis into the object path is pending (needs the 5_X carrier pipeline shared into the object decoder). Core-mode dialogue enhancement (§5.8.2.1-2/4) is not applied (the DE walker parses the payloads; no ICE-route DE application exists in either decoding mode yet). - Remaining TS 103 190-2 multi-stream / immersive / object-based (IFM) extensions beyond the parsed presentation / OAMD / object substream surfaces.
- P-frame refinements: the sticky state carries one xover offset per
substream, so P-frames assume the I-frame used a single
aspx_xover_subband_offsetacross all A-SPX elements of the element (always true for this encoder; per-element sticky xovers would need a per-trailer table). Multi-envelope (num_env > 1) P-frame bodies emit FREQ-direction envelopes only (the encoder clears its cross-frame rows after a multi-envelope frame rather than tracking the last envelope). Cross-frame TIME/DIFF_TIME emission is wired on the 5_X ACPL_3 path; the other live paths emit correct P-frame bodies with FREQ rows. - Per-
emdf_payload_idsemantic interpretation of EMDF payload bodies (captured as raw bytes). - Some advanced A-CPL parameters (β3 / γ on certain encoder paths) remain scaffolded at minimum-bit-cost defaults.
- A-SPX
aspx_hfgen_iwcsub-fields: the live 5_X ASPX_ACPL_3, the 5_X / 7_X ASPX_ACPL_2, the 7.0 pure-ASPX, and the 7_X ASPX_ACPL_1 paths now emit a realaspx_tna_mode(inverse filtering) on every A-SPX carrier — each body derives an independentaspx_tna_modeper carrier from that carrier's own QMF low band (front pair from L, surround pair from Ls, centre from C, and the 7.0 pure-ASPX back pair from Lb). The 7_X ASPX_ACPL_1 path additionally now emits real per-sbg SIGNAL/NOISE ASPX envelopes on all three carriers (replacing the round-118write_aspx_data_*_minimalscaffold). Every live A-SPX path now also emits a realaspx_add_harmonicdecision: theaspx_ah_selectmodule measures each carrier's per-high-res-signal- subband-group HF QMF spectral crest (the group's loudest subband energy ÷ its mean per-subband energy) and requests a restored missing harmonic (§4.2.12.6) where a dominant tonal partial is present (the decoder places the §5.7.6.4.2.1 Pseudocode 92 sinusoid at the group'ssb_mid). This is wired per-channel into the live 5_X ASPX_ACPL_3 (single- and multi-envelope), 5_X / 7_X ASPX_ACPL_2 (single- and centre-multi-envelope), 7.0 pure-ASPX, and 7_X ASPX_ACPL_1 paths via newwrite_aspx_data_{1,2}ch_real_envelope_tna_ah+write_aspx_data_{1,2}ch_multi_envelope_tna_ahwriters and anextract_aspx_add_harmonicper-carrier analysis. The decoder fully consumesaspx_add_harmonic(§5.7.6.4.4 tone generator → HF QMF injection), so the decision changes the decoded PCM, not just the wire bytes. Every live A-SPX path additionally emits a realaspx_preflatdecision (Table 121): theaspx_preflat_selectmodule reuses the decoder's own §5.7.6.4.1.2 Pseudocode 85 gain fit (compute_preflat_gains) over the carrier's QMF low band — the HF-generation source range — and signals spectral pre-flattening when the fitted-slope dB dynamic range (20·log10(max gain ÷ min gain), a level-independent measure of the source range's overall tilt) clears a threshold. A spectrally flat source range yields ~unity gains and is left alone; a steeply tilted one flips the per-aspx_configflag so the decoder applies the §5.7.6.4.1.4 Pseudocode 89 inverse pre-flatten gain to the patched tile (re-shaping the spectrum within each subband group while the SIGNAL envelope, restored after pre-flattening, pins each group's energy). Wired into every live path (5_X ASPX_ACPL_3 single + multi-env, 5_X / 7_X ASPX_ACPL_2, 7.0 pure-ASPX, 7_X ASPX_ACPL_1) via anextract_aspx_preflatper-carrier analysis. Still pending:fic_used_in_sfb/tic_used_in_slotremain at the all-zero scaffold on every live path — they are parsed but not yet driven through the decoder's HF synthesis, so an encoder decision for them would be informative-only (a docs gap on their §5.7.6.4 synthesis semantics blocks a real round-trip). The 7.X ASPX_ACPL_3 path does not yet exist. Theaspx_tna_mode/aspx_add_harmonicthreshold mappings are encoder tuning choices (the spec leaves the selection informative); they are calibrated to the live QMF pipeline but not yet tuned against a perceptual reference. - The live 5_X ASPX_ACPL_3 real-ASPX frame path now selects between a
single FIXFIX envelope and a
num_env = 2multi-envelope body per frame (encode_frame_pcm_5_{0,1}_acpl3_real_aspx_multi_env— the encoder probes the L/R HF QMF energy for a transient and emits the multi-envelopeaspx_data_2ch()with per-envelope FREQ/TIME DPCM when one is present, else falls back to the single-envelope path). The ASPX_ACPL_2 5.X live frame path now also emits a real single-envelopeaspx_data_1ch()for the centre carrier (encode_frame_pcm_5_{0,1}_acpl2_real_aspx: QMF-analyses L/R and C, emitting real SIGNAL/NOISE envelopes on all three carriers viawrite_aspx_data_1ch_real_envelope+write_aspx_data_2ch_real_envelope). The 7.X ASPX_ACPL_2 live frame path now also emits real single-envelope ASPX on all three carriers — both carrier-pairaspx_data_2ch()elements (L/R front, Ls/Rs surround) and the centreaspx_data_1ch()(encode_frame_pcm_7_{0,1}_acpl2_real_aspx→build_7_x_acpl2_body_from_pcm_spectra_real_alpha_beta_real_aspx). The 7.0 pure-ASPX path (encode_frame_pcm_7_0_aspx_real_aspx→build_7_0_aspx_asf_body_from_pcm_spectra_real_aspx_tna) and the 7_X ASPX_ACPL_1 path (encode_frame_pcm_7_{0,1}_acpl1_real_alpha_beta→build_7_x_acpl1_body_from_pcm_spectra_real_alpha_beta_real_aspx_tna) now also emit real single-envelope ASPX + realaspx_tna_modeon every carrier. The liveaspx_data_1ch()path remains single-envelope (num_env = 1), and multi-envelope (num_env > 1) is wired only on the 5.X ASPX_ACPL_3 live path;num_env > 2(requiring a widernum_env_bits_fixfix) is not yet selected.
- ETSI TS 103 190-1 — channel-based coding + bitstream syntax.
- ETSI TS 103 190-2 — multi-stream / immersive / object-based (IFM).
[dependencies]
oxideav-core = "0.1"
oxideav-codec = "0.1"
oxideav-ac4 = "0.0""ac4". Also registers the ISO BMFF fourcc ac-4 so MP4 tracks tagged
with the AC-4 sample entry resolve cleanly.
MIT — see LICENSE.