v0.0.6
Other
- ac4 r190: close ASPX_ACPL_1 desync — fix aspx_framing() FIXFIX prefix
- ac4 round 187 — pin ACPL_1 residual / α-β desync follow-up
- round 181 — close r128 alpha_q desync at parser indexing + aspx_data_2ch SIGNAL band count
- round 174 — fix ALPHA / BETA3 F0 cb_off (latent #1121 desync)
- round 144 — real per-band α + β extraction for 5_X ASPX_ACPL_2
- round 139 — 7.1-with-LFE ACPL_1 real per-band α + β
- round 135 — real per-band α + β extraction for 7_X ASPX_ACPL_1
- round 132 — real per-band β extraction in ACPL_1 5.0 encoder
- round 128 — real per-band α extraction in ACPL_1 5.0 encoder
- round 125 — 7.0 (3/4/0) SIMPLE/Cfg3Five multichannel encoder
- round 118 — 7.0/7.1 SIMPLE/ASPX_ACPL_1 multichannel encoder
- Round 114: 7.1 (3/4/0.1) SIMPLE/ASPX_ACPL_2 multichannel encoder (LFE)
- Round 107: 7.0 SIMPLE/ASPX_ACPL_2 multichannel encoder
- round 103 — 5_X SIMPLE/ASPX_ACPL_1 multichannel encoder path
- round 100 — 5_X SIMPLE/ASPX_ACPL_2 multichannel encoder path
- ac4 round 95: 5_X SIMPLE/ASPX_ACPL_3 multichannel encoder path
- ac4 round 91: 7.1 (3/4/0.1) SIMPLE/Cfg3Five encoder (7 SCE + LFE)
- ac4 round 80: 5.1 SIMPLE/Cfg3Five encoder (5 SCE + LFE) + decoder LFE PCM render
- ac4 round 74: 5.0 SIMPLE/Cfg3Five multichannel forward analysis (5 SCE)
- ac4 round 52: joint M/S CPE (Path B, b_enable_mdct_stereo_proc=1)
- ac4 round 51: stereo SIMPLE/ASF split-MDCT (Path A: 2x SCE) encoder
Fixed
- Round 190 — close the 5_X ASPX_ACPL_1 desync the r187 tests
pinned. Two minimal A-SPX writers
([crate::encoder_acpl3::write_aspx_data_2ch_minimal] and
[write_aspx_data_1ch_minimal]) emittedaspx_int_class = FIXFIX
as the wrong prefix code:0b11(2 bits) instead of0b0(1 bit)
per ETSI TS 103 190-1 Table 126. The decoder's
[crate::aspx::AspxIntClass::read] correctly walks the prefix —
0→ FixFix,10→ FixVar,110/111→ VarFix / VarVar — so
the writer's11start signalled the parser to read the VarFix /
VarVar branch instead. For our config that put the parser in the
VarFix branch withb_iframe = 1: it then read
var_bord_left(2 b),num_rel_left(2 b —num_aspx_timeslots = 15 > 8makes Note-1 fields 2-bit wide), andtsg_ptr(2 b).
Net: parser consumed 9 bits in the framing where the writer
only emitted 3 bits, a 6-bit upstream drift that the
silence / L-only / Ls-only test paths masked (α / β quantised to 0
⇒ theacpl_data_1chbody shape was constant minimum-cost on each
side and thenum_param_sets_codbit positions on both sides
sampled0within the long run of zero codewords). With non-zero
α / β the codewords shift, the pair-1num_param_sets_codbit
position lands on a1, and pair1 readsnum_param_sets = 2—
the r187 symptom. Fix is one line per writer: emit
bw.write_bit(false)for the FIXFIX prefix, matching Table 126.- The r187 test #4 (
acpl1_combined_l_and_ls_pair1_currently_misaligns)
was renamed toacpl1_full_round_trips_with_aligned_pair_lengths
and its assertion flipped fromassert_eq!(n1, 2)(pinned
misalignment) toassert_eq!(n1, 1)(post-fix). All four
combinations now round-trip with
pair0.num_param_sets = pair1.num_param_sets = 1. - Total tests 784 (unchanged from r187 — r190 fixed the third pin
in place rather than adding new ones; the bit-level diagnosis is
carried in the test file's module doc-comment instead).
- The r187 test #4 (
Added
- Round 187 — characterisation tests pinning the remaining
5_X ASPX_ACPL_1 residual / α-β-writer desync the r181 follow-up
flagged. Four end-to-end pinning tests in
tests/round187_acpl1_residual_desync_characterization.rssweep
the encoder'sencode_frame_pcm_5_0_acpl1_real_alpha_betaacross
four input combinations and assert the decoder's recovered
acpl_data_1ch_pair[0/1].framing.num_param_setsso the next round
can iterate on the residual-layer / α-β writers without regressing
the aligned silence / L-only / Ls-only paths.- Silence (all-zero PCM) → both pair slots resolve
num_param_sets = 1. - L-carrier-only (
Ls = Rs = 0) → both pair slots resolve
num_param_sets = 1; thewrite_two_channel_datacarrier writer
is exercised non-trivially while α / β stay quantised to 0
(correlationΣ L · Ls = 0⇒ α extractor returns 0; surround
energyE[Ls²] = 0⇒ β extractor returns 0). - Ls-residual-only (
L = R = 0) → both pair slots still resolve
num_param_sets = 1; thewrite_acpl_1_residual_layerjoint-MDCT
residual writer is exercised non-trivially withmax_sfb_master
non-zero band budget but α / β stay 0 because carrier energy is 0. - Combined L-carrier + Ls-residual (
L = 0.5,Ls = 0.05) →
pair0 still resolvesnum_param_sets = 1, but pair1 drifts to
num_param_sets = 2. The pin captures this as the currently
expected behaviour so the next round's residual-layer fix can
flip the assertion back to 1 once aligned. - Diagnostic narrative in the test file's module doc-comment
triangulates the bug surface: the writer→parser pairs for
write_acpl_data_1ch_real_alpha_beta↔parse_acpl_data_1ch
are bit-exact in isolation (pinned by
round181_alpha_desync_fix::standalone_*); back-to-back
invocations into the sameBitWriterwithout byte alignment
between them also round-trip cleanly. The drift therefore sits
upstream of pair0 — either in the joint-MDCT residual writer
(write_acpl_1_residual_layer) vs the inline residual walk
insideparse_aspx_acpl_1_2_inner_body's ASPX_ACPL_1 branch, or
in thetwo_channel_data()L/R carrier writer vs
parse_two_channel_data— when L and Ls are simultaneously
non-trivial. Total tests 784 (was 780).
- Silence (all-zero PCM) → both pair slots resolve
Fixed
-
Round 181 — A-CPL
acpl_huff_data()Pseudocode-121 indexing +
aspx_data_2ch()SIGNAL band count. Closes the user's "alpha_q
desync" follow-up the round-174 ALPHA / BETA3 F0cb_offfix
deferred. Two distinct layers were involved.- Layer 1 — §4.2.13.7 Table 65 / §5.7.7.7 Pseudocode 121 parser
indexing. Pre-r181 [crate::acpl::parse_acpl_huff_data] packed
the(num_param_bands - start_band)Huffman-decoded values into
a vector starting at index 0. Per the spec the same array is the
input to Pseudocode 121'sfor (i = 0; i < num_bands; i++)
accumulation — so positions[0..start_band)are zero (the
encoder did not transmit those bands) and the F0 codeword lands at
values[start_band]. The packed-from-0 layout silently shifted
the §5.7.7.7 DIFF_FREQ accumulation bystart_bandparameter
bands for the 5_X SIMPLE/ASPX_ACPL_1 PARTIAL path
(acpl_qmf_band > 0,start_band > 0). The r181 fix rewrites
[crate::acpl::parse_acpl_huff_data] to return a length-
num_param_bandsvector indexed by full param-band number — the
spec-alignedacpl_<SET>[ps][i]shape Pseudocode 121 reads. The
[AcplHuffParam`] doc comment now spells out the new indexing
contract. - Layer 2 — §4.2.12.4 Table 52
aspx_data_2ch()SIGNAL band
count. Per ETSI TS 103 190-1 §4.3.10.4.9 (Table 124 NOTE 3)
aspx_ec_data(SIGNAL, …)readsnum_sbg_sig_lowresSIGNAL bands
when the correspondingaspx_freq_res[env]bit was emitted as 0
andnum_sbg_sig_highreswhen it was 1 or absent (when
freq_res_mode != Signalledthe encoder writes no in-band
aspx_freq_resbit and the decoder's
freq_res.get(env).copied().unwrap_or(true)fallback selects
high-res). Pre-r181 [crate::encoder_acpl3::write_aspx_data_2ch_minimal]
hard-codednum_sbg_sig_lowresregardless — so for the
encoder's defaultfreq_res_mode = DurationDependentconfig
(20-band high-res vs 10-band low-res, no in-band freq_res bit)
the writer emitted 10 SIGNAL F0+DF codewords per channel while
the parser read 20. The 20-vs-10 mismatch buried every
subsequentacpl_data_1ch()α / β codeword in trailing
zero-padding, recovered as length-num_param_bandsall-zero
rows. r181 keys the SIGNAL band count offcfg.signals_freq_res()
matching the writer's own freq_res-bit gate. - 4 new round-181 unit / integration tests in
tests/round181_alpha_desync_fix.rs:
[standalone_alpha_writer_round_trips_through_parser] (Layer 1,
standalone writer→parser→differential_decode),
[parser_values_are_indexed_by_full_param_band_number] (Layer 1,
structural — confirmsvalues[0..start_band) == 0and
values[start_band..]carries the F0 + DF accumulation),
[end_to_end_acpl2_asymmetric_surround_recovers_nonzero_alpha]
(Layer 2 — encode 5.0 ASPX_ACPL_2 with asymmetric L/Ls energy,
decode, assertacpl_data_1ch_pair[0/1].alpha1[0].values
carries non-zero entries),
[end_to_end_acpl2_silence_still_round_trips] (Layer 2 regression
guard — silence input still yields all-zero α/β). Total tests
780 (was 776). - The r132
acpl_data_1ch_real_alpha_beta_round_trips_byte_exact
test is re-shaped to apply the spec-aligned Pseudocode 121 DIFF_FREQ
accumulation directly on the parser's length-num_param_bands
output (instead of cumulating the pre-r181 packed
(num_bands - start_band)-length slice). - The 5_X SIMPLE/ASPX_ACPL_1 PARTIAL end-to-end path retains a
separate joint-MDCT residual-layer alignment issue (the residual
sf_data writer and the decoder'sdecode_asf_long_mono_body_with_max_sfb
appear to read different total bit counts on non-trivial inputs)
that the r181 ACPL_2 end-to-end tests do not exercise. Tracking
as the remaining "alpha_q desync" follow-up — the structural
Layer 1 + Layer 2 fixes are independent of it and land here.
- Layer 1 — §4.2.13.7 Table 65 / §5.7.7.7 Pseudocode 121 parser
-
Round 174 — ALPHA / BETA3 F0 codebook
cb_offcorrected per ETSI
TS 103 190-1 §A.3 Tables A.34 / A.35 (ALPHA) and A.46 / A.47 (BETA3).- Pre-fix
cb_off = 0for ALPHA / BETA3 F0 conflicted with the §5.7.7.7
Pseudocode 121 differential-decoder contract —dequantize_alpha_index
re-adds the signed-lane offset (+8Coarse /+16Fine for ALPHA),
expecting the Huffman pipeline to deliver the signed quantised lane
alpha_q ∈ [-N/2, +N/2]. Withcb_off = 0the F0 codeword
symbol_indexwas returned raw (unsigned 0..N), shifting every
dequant lookup byN/2lanes. - Symptom: the round-128 / 132 / 135 / 139 / 144 real-α / α+β encoder
paths populatedalpha_q_per_bandcorrectly viaquantise_alpha
(which already returned signed lanes via its owncb_off = 8 / 16)
butwrite_acpl_alpha_f0_valuethen wrotesymbol_index = alpha_q
instead ofsymbol_index = alpha_q + 8 / 16. Foralpha_q = 0the
writer picked the most expensive lane (10 / 12 bits) instead of
the 1-bit symmetric peak; for negativealpha_qthe writer clamped
to lane 0; positivealpha_qround-tripped accidentally for
sufficiently small magnitudes via the rawsymbol_indexlookup. The
decoder dequant lane shifted bycb_off, producing wrong
dequantised α magnitudes. - Fix: set
cb_off = 8(Coarse) /16(Fine) for ALPHA F0 in both
[crate::acpl::get_acpl_hcb] (decoder) and the matching encoder-
localacpl_hcb_arraysin [crate::encoder_acpl3]. Same shape
applied to BETA3 F0 (cb_off = 4Coarse /8Fine —dequantize_beta3
multiplies the signed lane bybeta3_deltadirectly). BETA F0 stays
atcb_off = 0(unsigned magnitude —dequantize_beta_indextakes
unsigned_absand re-applies the sign from the differential
accumulator). Companion comment edits document the asymmetry. - 3 new round-174 unit tests:
[alpha_f0_signed_lanes_round_trip_fine_and_coarse] sweeps every
signed lane in both ALPHA F0 codebooks through encode → decode →
decode value; [beta3_f0_signed_lanes_round_trip_fine_and_coarse]
does the same for BETA3 F0; [alpha_f0_zero_alpha_picks_one_bit_peak]
confirms the writer now picks the 1-bit symmetric peak for
alpha_q = 0(down from the pre-fix 10 / 12 bits). Total tests
776 (was 773). - Round-128 family tests
(encode_5_0_acpl1_real_alpha_emits_nonzero_alpha_when_surround_differs..._symmetric_scaling_yields_matching_alpha) re-shaped to assert
on encoder byte-stream divergence rather than the decoder's
recoveredalpha_q— bit-position drift through the full 5_X
SIMPLE/ASPX_ACPL_1 walker on non-silence input is independent of
the F0 cb_off bug and still pending separate investigation (the
user's "alpha_q desync" followup tracks it).
- Pre-fix
Added
-
Round 144 — 5_X SIMPLE/ASPX_ACPL_2 encoder with real per-parameter-
band α + β extraction per ETSI TS 103 190-1 §4.2.6.6 Table 25 row
case ASPX_ACPL_2:+ §5.7.7.5 Pseudocode 116 + §5.7.7.6.1 Pseudocode
117. The ACPL_2 counterpart to the round-132 5_X ACPL_1 real α+β path.- New
Ac4ImsEncoder::encode_frame_pcm_5_0_acpl2_real_alpha_beta
(+..._with_max_sfb) accepts a 5-channel[L, R, C, Ls, Rs]input
and produces a 5_X ASPX_ACPL_2 frame whose two trailing
acpl_data_1ch()elements carry per-parameter-band α + β indices
extracted from the (L, Ls) and (R, Rs) MDCT energy ratios. - The on-wire body layout is the round-100
build_5_x_acpl2_body_from_pcm_spectralayout (no joint-MDCT
residual layer — ACPL_2 reconstructs the surround from L/R + the two
acpl_data_1ch()parameter sets at decode time); the Ls/Rs spectra
are consumed only by the α + β extractors and are not transmitted. - New
encoder_acpl3::build_5_x_acpl2_body_from_pcm_spectra_real_alpha_beta
builder reuses the round-128 / 132 shared α + β analytic primitives
(compute_per_band_correlations/analytic_alpha_per_band/
compute_per_band_energies/analytic_beta_per_band/
quantise_alpha/quantise_beta_magnitude) and the
write_acpl_data_1ch_real_alpha_betawriter withstart_band = 0
(acpl_config_1ch(FULL) carries no qmf_band) so every parameter band
participates in the α + β coding. - β analytic derivation per Pseudocode 116 with
y⊥x0and
E[y²] ≈ E[x0²]:E[Ls²] = 0.5 · E[L²] · ((1 − α)² + β²)⇒
β = √max(0, 2·E[Ls²]/E[L²] − (1 − α_dq)²). - Total tests 773 (was 766): 7 new round-144 tests covering 5-channel
AudioFrame round-trip, decoder mode resolution, on-wire body
divergence from the round-100 scaffold for non-trivial surround,
directextract_beta_q_per_bandnon-zero gate, silence round-trip,
encoder determinism, and structural pair0/pair1 population. - Deferred: real β extraction for ACPL_3 paths; real ASPX envelope
coding; the round-128 ALPHA F0 writer-sidealpha_qdesync
(deferred since r132) which currently obscures per-band on-wire α/β
recovery through the full PCM→MDCT→writer→parser→synth chain when
the analytic α quantises to a non-center lane.
- New
-
Round 139 — 7.1-with-LFE (3/4/0.1) SIMPLE/ASPX_ACPL_1 encoder
with real per-parameter-band α + β extraction per ETSI TS 103 190-1
§4.2.6.14 Table 33 rowcase ASPX_ACPL_1:withb_has_lfe = 1+
§5.7.7.5 Pseudocode 116 + §5.7.7.6.1 Pseudocode 117. The LFE
counterpart of the round-135 7.0 immersive real-α+β path.- New
Ac4ImsEncoder::encode_frame_pcm_7_1_acpl1_real_alpha_beta
(+..._with_max_sfb) reuses the round-135
encoder_acpl3::build_7_x_acpl1_body_from_pcm_spectra_real_alpha_beta
builder with the LFEcoeffs_lfe+max_sfb_lfeslots populated,
emitting a leadingmono_data(b_lfe = 1)element (Table 21 +
sf_info_lfe()Table 35) between the I-frame config block and
companding_control(5)— exactly where the decoder's
parse_7x_audio_data_outer(b_has_lfe = true)reads
if (b_has_lfe) mono_data(1);. - The on-wire body structure matches the existing round-118 7.1
ACPL_1 path. Decoder resolvesSevenXCodecMode::AspxAcpl1with
b_has_lfe = true, bothacpl_data_1ch_pair[0/1]populated (now
carrying real α + β), joint-MDCT residual layer walked, LFE
IMDCT'd into slot 7. A 60 Hz LFE tone round-trips to a non-silent
reconstructed LFE channel. - +6 tests (total 766, was 760).
- New
-
Round 132 — 5.0 SIMPLE/ASPX_ACPL_1 encoder with real per-parameter-
band β extraction per ETSI TS 103 190-1 §5.7.7.5 Pseudocode 116 +
§5.7.7.6.1 Pseudocode 117. Extends the round-128 real-α path: β was
pinned to 0 (pure level-only surround image); round 132 derives a real
per-band β magnitude from the surround/carrier energy residual that
remains after α removes the level component.- Per Pseudocode 116 with the decorrelator output
y⊥x0and
E[y²] ≈ E[x0²]:E[Ls²] = 0.5·E[x0²]·((1-α)² + β²). Solving for
the magnitude givesβ = √max(0, 2·E[Ls²]/E[x0²] − (1-α)²), where
αis the dequantised value the decoder reconstructs (so β closes
the balance against the actual(1 − α_dq)). - New
encoder_acpl3helpers:compute_per_band_energies(per-band
Σ x²for carrier + surround),analytic_beta_per_band(the
energy-residual β estimator),quantise_beta_magnitude(nearest
beta_qindex against the Table 204 / 206 column-0 grid),
write_acpl_beta_f0_value/write_acpl_beta_df_value(ACPL BETA
F0 + DF codebook emitters, Tables A.40 / A.41), and the optional-β
write_acpl_data_1ch_real_alpha_betabody writer. - New build function
build_5_x_acpl1_body_from_pcm_spectra_real_alpha_beta+ encoder
entry pointsAc4ImsEncoder::encode_frame_pcm_5_0_acpl1_real_alpha_beta
(+..._with_max_sfb). The on-wire body structure is unchanged from
the round-128 path — the decoder resolvesFiveXCodecMode::AspxAcpl1,
bothacpl_data_1ch_pair[0/1]populated, and the β layer now carries
real magnitudes. - Public extractor/validator entry points
extract_alpha_q_per_band,
extract_beta_q_per_band, andwrite_acpl_data_1ch_real_alpha_beta_bytes
for round-trip testing. - β / β3 / γ otherwise stay at the round-95 / 100 / 103 / 128 scaffold
for non-ACPL_1 paths. Total tests 755 (was 743). - Followup (round 128 latent bug, not introduced here): the ACPL
ALPHA F0/DF writer (write_acpl_alpha_f0_value/_df_value) clamps a
negativealpha_qto lane 0, writing the wrong codeword and
desyncing the rest of theacpl_data_1chelement. It only round-trips
correctly foralpha_q ≥ 0. The round-132 β coding contract is
verified byte-exact via the isolatedacpl_data_1chround-trip test;
the full-substream PCM path inherits the round-128 α-writer's
in-range limitation. Both the α-writer sign/offset fix and real β
extraction for the 7_X / ACPL_2 / ACPL_3 paths remain deferred.
- Per Pseudocode 116 with the decorrelator output
-
Round 128 — 5.0 SIMPLE/ASPX_ACPL_1 encoder with real per-parameter-
band α extraction per ETSI TS 103 190-1 §5.7.7.5 Pseudocode 116 +
§5.7.7.6.1 Pseudocode 117. Replaces the round-103 zero-delta scaffold
for the α coefficient family in the ACPL_1 path (β / β3 / γ stay at
the round-95 / 100 / 103 zero-delta scaffold — β3 / γ only fire in
ASPX_ACPL_3 anyway).- Per Pseudocode 116, above
acpl_qmf_band:z0 = 0.5·(x0·(1+α) + y·β),z1 = 0.5·(x0·(1-α) - y·β)(thenz1 *= √2per
Pseudocode 117). With β = 0 the surround reconstruction is a pure
level-only image:Ls_recon = 0.5/√2 · L · (1 − α). Solving for α
that minimises(Ls − 0.5/√2·L·(1−α))²per parameter band gives
α = 1 − 2·√2 · ⟨L, Ls⟩ / ⟨L, L⟩. - New
encoder_acpl3::build_5_x_acpl1_body_from_pcm_spectra_real_alpha
— mirrorsbuild_5_x_acpl1_body_from_pcm_spectra(round 103) but
with real α emitted via the ACPL ALPHA F0 + DF codebooks (Tables
A.35 / A.34). Helper functions:mdct_bin_to_param_band— maps MDCT bin → QMF subbandsb = bin · 64 / transform_length→ parameter band via
acpl::sb_to_pb(§5.7.7.2 Table 197).compute_per_band_correlations— computes(Σ x·y, Σ x²)per
parameter band over the MDCT carrier vs. surround spectra,
skipping bands belowstart_pb(the PARTIALacpl_qmf_band
maps to astart_pb > 0; bands below are M/S-recovered by the
synth and α has no effect there).analytic_alpha_per_band— closed-form α with clamp to ±2.0.quantise_alpha— nearest-neighbour toALPHA_DQ_FINE(Table
203) /ALPHA_DQ_COARSE(Table 205); returns the signed
alpha_qin-N/2..=+N/2.write_acpl_alpha_f0_value/write_acpl_alpha_df_value—
emit ALPHA F0 (first band) + ALPHA DF (subsequent bands using
delta_q = alpha_q[pb] - alpha_q[pb-1]) codewords per the
acpl_hcb_arraystable family.write_acpl_data_1ch_real_alpha— fullacpl_data_1ch()body
with real α + zero-delta β (β / β3 / γ fall back to
write_acpl_*_zerofrom round 95).
- New encoder entry points:
Ac4ImsEncoder::encode_frame_pcm_5_0_acpl1_real_alpha(&[L, R, C, Ls, Rs])Ac4ImsEncoder::encode_frame_pcm_5_0_acpl1_real_alpha_with_max_sfb(.., max_sfb, max_sfb_master)
- The on-wire body structure is identical to the round-103 path: the
decoder resolvesFiveXCodecMode::AspxAcpl1, parses both
acpl_data_1ch_pair[0/1]slots, walks the joint-MDCT residual
layer, and synthesises[L, R, C, Ls, Rs]via
acpl_synth::run_acpl_5x_pair_pcm. The only difference is that
the α huffman values now carry per-band non-zero deltas chosen by
the encoder rather than the structural zero scaffold. - 6 new integration tests in
tests/round128_5_x_acpl1_real_alpha.rs: end-to-end round-trip,
decoder mode resolution, non-zero α emission when surround
differs from carrier, silence round-trip, symmetric scaling
yields a positive α in both pairs, encoder determinism. - Total test count: 743 (was 737) — 0 ignored, 0 failed.
- Follow-ups (deferred): real per-band β / β3 / γ extraction
(β = 0 simplification is spec-defensible for "level-only"
encoding); real ASPX envelope coding; real Table-181 SAP-derived
residual content; same real-α uplift for the ACPL_2 / ACPL_3 5_X
paths and the 7_X ACPL_1 / ACPL_2 paths; back-pair Lb/Rb
carriage on the ACPL paths; DT-mode (DIFF_TIME) coding using
cross-frame state.
- Per Pseudocode 116, above
-
Round 125 — 7.0 (3/4/0) SIMPLE/Cfg3Five multichannel encoder
path per ETSI TS 103 190-1 §4.2.6.14 Table 33 + §4.2.7.5 Table 29
(five_channel_data()) + §4.2.7.4 Table 26 (additional-channel
two_channel_data()). The non-LFE immersive counterpart of
round-91's 7.1 SIMPLE encoder (the 7_X analogue of round 74's 5.0 vs
round 80's 5.1).Ac4ImsEncoder::with_7_0()— flips the TOC channel_mode prefix to
0b1111000(7 b — Table 85 channel_mode 5, 7.0 (3/4/0) → 7
channels). The decoder'swalk_ac4_substreamthen dispatches
channels == 7throughparse_7x_audio_data_outer(b_has_lfe = false).Ac4ImsEncoder::encode_frame_pcm_7_0(&[L, R, C, Ls, Rs, Lb, Rb])..._with_max_sfb(&[..], max_sfb, max_sfb_add)— emit IMS v2
frames in7_X_codec_mode = SIMPLE (0)+coding_config = Cfg3Five (3). The five front/surround channels share the
Cfg3Fivefive_channel_data()body (the same shape as round-74
5.0 / round-80 5.1 / round-91 7.1); the immersive back pair
Lb/Rb rides a trailing identity-SAPtwo_channel_data()
(b_use_sap_add_ch = 0,sap_mode = 0on the shared
chparam_info) so the decoder's
dispatch_7x_additional_channel_pairroutes Lb/Rb directly into
output slots 5/6 (Table 183 row "3/4/0.x" identity path).
max_sfbdefaults to 40;max_sfb_adddefaults to 40.
- New
encoder_asf::build_7_0_simple_asf_body_from_pcm_spectra—
emits the substream body bytes. Body layout:7_X_codec_mode = SIMPLE (0)(2 b) +coding_config = 3(2 b) +
five_channel_data()(shared sf_info + 5x sf_data per Table 29)b_use_sap_add_ch = 0(1 b) +two_channel_data()(Lb/Rb per
Table 26). The body is structurally the round-91 7.1 body with
the leadingmono_data(b_lfe = 1)element omitted (the walker's
if (b_has_lfe) mono_data(1);branch is gated off for
channel_mode 5). No companding (SIMPLE), no ASPX trailers
(SIMPLE), no ACPL pair (SIMPLE), no trailingmono_data(0)
(Cfg3Five — the 7.X trailing-mono gate iscoding_config in {0, 2}only).
- Decoder round-trip verified: 7.0 → 7-channel S16 interleaved PCM
(1920 × 7 × 2). The 7.0 walker resolvesseven_x_mode == Simple,
seven_x_b_has_lfe == false,lfe_mono_data == None,
five_channel_datapopulated with five non-empty
scaled_spec_per_channelentries, identity-SAP
seven_x_additional_channel_datapopulated with two non-empty
scaled_spec_per_channelentries for the Lb/Rb pair. Slots 0..4
synthesise viadispatch_5x_cfg3_simple_aspx(round 39); slots
5/6 viadispatch_7x_additional_channel_pair(round 39/40
identity-SAP path). - Per-channel spectral SNR on the 220/440/660/880/1100/1320/1540 Hz
independent-tone 7.0 fixture: L=24.5 / R=24.8 / C=25.0 / Ls=23.4 /
Rs=27.4 / Lb=25.4 / Rb=26.0 dB — all above the ≥ 20 dB floor,
matching the round-74 / 80 / 91 SNR numbers exactly (the encoder
reuses the same per-channel forward pipeline). - 8 new integration tests in
tests/round125_7_0_multichannel.rs
(7-channel layout, TOC declares 7 channels, sequence-counter roll,
with_7_0()builder smoke-test, substream-walker confirms
b_has_lfe = false+ no LFE + additional pair populated,
independent-tones round-trip, silence round-trip, per-channel
spectral SNR ≥ 20 dB). The test helper wraps the encoder's
raw_ac4_frame()payload in an Annex G0xAC40 + frame_size
sync header so the decoder'sfind_sync_framelatches onto the
genuine sync word rather than an incidental0xAC40byte pair in
the body (a hazard round 91's fixture happened to dodge by data
luck). - Total test count: 737 (was 729) — 0 ignored, 0 failed.
- Follow-ups (deferred, unchanged from round 118): real per-band
(alpha, beta)extraction replacing the zero-delta scaffold;
real ASPX envelope coding; real Table-181 SAP-derived residual
content; back-pair Lb/Rb carriage on the ACPL paths (currently
silent on the 7_X ACPL_1 / ACPL_2 paths since those modes carry
no SIMPLE/ASPX additional-channel block).
-
Round 118 — 7.0 / 7.1 (3/4/0(.1)) SIMPLE/ASPX_ACPL_1 multichannel
encoder path per ETSI TS 103 190-1 §4.2.6.14 Table 33 row
case ASPX_ACPL_1:(+b_has_lfe = 1for 7.1 — §4.2.6.5 Table 21
mono_data(b_lfe)+ §4.2.8 Table 35sf_info_lfe()). The 7_X
(immersive) counterpart to the round-103 5_X ASPX_ACPL_1 encoder and the
encoder side of the decoder's round-27parse_7x_audio_data_outer
ASPX_ACPL_1 branch (which already reads the joint-MDCT residual layer).
Closes the first deferred follow-up from round 114.Ac4ImsEncoder::encode_frame_pcm_7_0_acpl1(&[L, R, C, Ls, Rs, Lb, Rb])..._with_max_sfb(&[..], max_sfb, max_sfb_master)— emit IMS v2
frames in7_X_codec_mode = ASPX_ACPL_1 (2). Channel_mode prefix
forced to0b1111000(7 b — Table 85 channel_mode 5, 7.0 (3/4/0)) so
the decoder dispatcheschannels == 7through
parse_7x_audio_data_outer(b_has_lfe = false).max_sfbdefaults to
40;max_sfb_master(residual band bound) defaults to 20.
Ac4ImsEncoder::encode_frame_pcm_7_1_acpl1(&[L, R, C, Ls, Rs, Lb, Rb, LFE])+..._with_max_sfb(&[..], max_sfb, max_sfb_master, max_sfb_lfe)
— the LFE counterpart: identical body plus a leading
mono_data(b_lfe = 1)between the I-frame config block and
companding_control(5), exactly where
parse_7x_audio_data_outer(b_has_lfe = true)reads
if (b_has_lfe) mono_data(1);. Channel_mode prefix forced to
0b1111001(7 b — Table 88 channel_mode 6, 7.1) →channels == 8.
max_sfb_lfedefaults to 7 (LFE-spec cap attl = 1920,
n_msfbl_bits = 3).- New
encoder_acpl3::build_7_x_acpl1_body_from_pcm_spectra— the 7_X
ASPX_ACPL_2 body (round 107/114) with three structural differences,
the same three that separate the 5_X ACPL_1 path from the 5_X ACPL_2
path: (1)7_X_codec_mode = 2(vs 3); (2)acpl_config_1chis
PARTIAL via the sharedwrite_acpl_config_1ch_partialemitter (6 b —
carriesacpl_qmf_band_minus1, soacpl_data_1ch()start_band
resolves fromqmf_bandviasb_to_pb); (3) an explicit joint-MDCT
residual layer via the sharedwrite_acpl_1_residual_layeremitter
(max_sfb_master + 2× chparam_info + 2× sf_data(ASF)) carrying the
Ls/Rs surround pair (sSMP,3 / sSMP,4 per Table 181) after the two
two_channel_data()pairs and before the trailing Cfg0 centre
mono_data(0). The SIMPLE/ASPX additional-channel block is skipped
(the decoder only walks it for SIMPLE/Aspx modes). Reuses the
round-80write_lfe_mono_dataemitter for the 7.1 LFE element. - Decoder round-trip verified: 7.0 ACPL_1 → 7-channel S16 interleaved
PCM (1920 × 7 × 2); 7.1 ACPL_1 → 8-channel S16 (with LFE slot 7). The
decoder resolvesseven_x_mode == AspxAcpl1, the PARTIAL config
(non-zeroqmf_band), bothtwo_channel_datapairs, the residual
pair +max_sfb_master, the Cfg0 centre, and both
acpl_data_1ch_pair[0/1]. The LFE spectrum IMDCT's into slot 7 (round
80 render);[L, R, C, Ls, Rs]slots 0..4 synthesise via
acpl_synth::run_acpl_5x_pair_pcm; the back pair Lb/Rb (slots 5/6)
stays silent per the Table 202 mapping. - 8 new integration tests in
tests/round118_7_x_acpl1_encoder.rs
(7-channel + 8-channel layout, sequence-counter roll, 7.0 + 7.1
full-body decoder resolution, LFE-slot-non-silent for a 60 Hz LFE
tone, silence round-trip, small-residual-budget round-trip recovering
the clampedmax_sfb_master). - Total test count: 729 (was 721) — 0 ignored, 0 failed.
- Follow-ups (deferred): real per-band
(alpha, beta)extraction
replacing the zero-delta scaffold; real ASPX envelope coding; real
Table-181 SAP-derived residual content (the residualsf_data
currently codes the raw Ls/Rs spectra); back-pair Lb/Rb carriage
(currently silent on the ACPL paths).
-
Round 114 — 7.1 (3/4/0.1) SIMPLE/ASPX_ACPL_2 multichannel encoder
path per ETSI TS 103 190-1 §4.2.6.14 Table 33 rowcase ASPX_ACPL_2:
withb_has_lfe = 1+ §4.2.6.5 Table 21 (mono_data(b_lfe)) + §4.2.8
Table 35 (sf_info_lfe()) + Table 106 column 4 (n_msfbl_bits). The
LFE counterpart of the round-107 7.0 ASPX_ACPL_2 encoder — the body is
identical except a leadingmono_data(b_lfe = 1)element is emitted
between the I-frame config block andcompanding_control(5), exactly
where the decoder'sparse_7x_audio_data_outer(b_has_lfe = true)reads
if (b_has_lfe) mono_data(1);(§4.2.6.14 Table 33). Closes the first
deferred follow-up from round 107.Ac4ImsEncoder::encode_frame_pcm_7_1_acpl2(&[L, R, C, Ls, Rs, Lb, Rb, LFE])+..._with_max_sfb(&[..], max_sfb, max_sfb_lfe)— emit IMS v2
frames in7_X_codec_mode = ASPX_ACPL_2 (3)with the LFE element.
Channel_mode prefix forced to0b1111001(7 b — Table 88 channel_mode
6, 7.1 (3/4/0.1)) so the decoder dispatcheschannels == 8through
parse_7x_audio_data_outer(b_has_lfe = true).max_sfbdefaults to
40;max_sfb_lfedefaults to 7 (the LFE-spec cap attl = 1920,
n_msfbl_bits = 3).encoder_acpl3::build_7_x_acpl2_body_from_pcm_spectragained two
parameters —max_sfb_lfe: Option<u32>andcoeffs_lfe: Option<&[f32]>. When both areSomethe shared
write_lfe_mono_dataemitter (round 80) prepends the LFE
mono_data(b_lfe = 1)element at the spec-correct position; with both
Nonethe body is the unchanged round-107 7.0 form (the round-107
7.0 caller passesNone/None).- Decoder round-trip verified: 7.1 ACPL_2 → 8-channel S16 interleaved
PCM (1920 samples × 8 ch × 2 bytes). The decoder walks the full
Table 33 ASPX_ACPL_2 + LFE body and resolvesseven_x_mode == AspxAcpl2,seven_x_b_has_lfe == true,lfe_mono_data.is_some(),
acpl_config_1ch_full.is_some(),two_channel_data.len() == 2,
cfg0_centre_mono.is_some(), and bothacpl_data_1ch_pair[0/1]. The
LFE spectrum is IMDCT'd into slot 7 via the existing round-80 LFE
render (Ac4Decoder::receive_frame,channels == 8); the round-107
[L, R, C, Ls, Rs]slots 0..4 synthesis (via
acpl_synth::run_acpl_5x_pair_pcm) and the silent back pair Lb/Rb
(slots 5/6, Table 202 ACPL_2 mapping) are unchanged. - 6 new integration tests in
tests/round114_7_1_acpl2_encoder.rs
(8-channel layout, sequence-counter roll, full-body + LFE decoder
resolution, LFE-slot-non-silent for a 60 Hz LFE tone, silence
round-trip, wide-max_sfb round-trip) + 1 new unit test in
encoder_acpl3::tests(build_7_x_acpl2_body_with_lfe_decoder_resolves_lfe). - Total test count: 721 (was 714) — 0 ignored, 0 failed.
- Follow-ups (deferred): the 7_X ASPX_ACPL_1 path (PARTIAL config +
joint-MDCT residual layer, the 7_X analogue of round 103); real
per-band(alpha, beta)extraction replacing the zero-delta scaffold;
real ASPX envelope coding; back-pair Lb/Rb carriage (currently silent
on the ACPL_2 path).
-
Round 107 — 7.0 SIMPLE/ASPX_ACPL_2 multichannel encoder path per
ETSI TS 103 190-1 §4.2.6.14 Table 33 rowcase ASPX_ACPL_2:+
§4.2.12.1 Table 50 (aspx_config) + §4.2.13.1 Table 59
(acpl_config_1ch FULL) + §4.2.7.4 Table 26 (two_channel_data) +
§4.2.6.5 Table 21 (mono_data) + §4.2.12.4 Table 52 (aspx_data_2ch) +
§4.2.12.3 Table 51 (aspx_data_1ch) + §4.2.13.3 Table 61
(acpl_data_1ch) + §4.2.11 Table 49 (companding_control). The 7_X
(immersive) symmetric counterpart to the round-100 5_X ASPX_ACPL_2
encoder and the encoder side of the decoder's round-27
parse_7x_audio_data_outerASPX_ACPL_2 branch. Reuses the same 1ch
ACPL / ASPX parameter shape (Pseudocode 117) as the 5_X path but emits
the 7_X channel element's distinct framing.Ac4ImsEncoder::encode_frame_pcm_7_0_acpl2(&[L, R, C, Ls, Rs, Lb, Rb])..._with_max_sfb(&[..], max_sfb)— emit IMS v2 frames in
7_X_codec_mode = ASPX_ACPL_2 (3). Channel_mode prefix forced to
0b1111000(7 b — Table 85 channel_mode 5, 7.0 (3/4/0)) so the
decoder dispatcheschannels == 7through
parse_7x_audio_data_outer(b_has_lfe = false).
encoder_acpl3::build_7_x_acpl2_body_from_pcm_spectra— shared body
builder. Layout:7_X_codec_mode = 3(2 b, vs the 5_X 3-bit
field) + I-frame block (aspx_config()15 b +acpl_config_1ch(FULL)
3 b) +companding_control(5)(sync=1, on=1 — the 2-bit sync-on wire
shape is identical to companding_control(2/3)) +coding_config = 0
(2 b, Cfg0) +b_2ch_mode+two_channel_data()(L/R carriers) +
two_channel_data()(Ls/Rs carriers) + trailing Cfg0mono_data(0)
(centre) + I-frameaspx_data_2ch() + aspx_data_2ch() + aspx_data_1ch()envelope trailer + twoacpl_data_1ch()parameter
sets (Pseudocode 117 D0 / D1). Structural differences from the 5_X
ACPL_2 path: 2-bit codec_mode, 2-bit coding_config, the centre
mono_data(0)moves out of the coding_config switch to a single
trailing element, the body carries two stereo pairs (the surround pair
rides the secondtwo_channel_data), and the ASPX trailer carries an
extraaspx_data_2ch(). The ASPX_ACPL_1-only joint-MDCT residual
layer and the SIMPLE/ASPX additional-channel block are both skipped
for ASPX_ACPL_2.- Decoder round-trip verified: 7.0 ACPL_2 → 7-channel S16 interleaved
PCM (1920 samples × 7 ch × 2 bytes). The decoder walks the full
Table 33 ASPX_ACPL_2 body and resolves
seven_x_mode == AspxAcpl2,acpl_config_1ch_full.is_some(),
two_channel_data.len() == 2,cfg0_centre_mono.is_some(), and both
acpl_data_1ch_pair[0/1].is_some()— the existing round-37/40 7_X
pair dispatch synthesises[L, R, C, Ls, Rs](slots 0..4) via
acpl_synth::run_acpl_5x_pair_pcm; the back pair Lb/Rb (slots 5/6)
stays silent per the documented Table 202 ACPL_2 channel mapping. - 5 new integration tests in
tests/round107_7_x_acpl2_encoder.rs
(7-channel layout, sequence-counter roll, full-body decoder resolution
including the two-pair Cfg0 + no-residual assertion, silence
round-trip, wide-max_sfb round-trip) + 1 new unit test in
encoder_acpl3::tests(full-body decoder resolution via
parse_7x_audio_data_outer). - Total test count: 714 (was 708) — 0 ignored, 0 failed.
- Follow-ups (deferred): the 7.1 (LFE) ASPX_ACPL_2 path
(b_has_lfe = trueleadingmono_data(1)); the 7_X ASPX_ACPL_1 path
(PARTIAL config + joint-MDCT residual layer, the 7_X analogue of
round 103); real per-band(alpha, beta)extraction replacing the
zero-delta scaffold; real ASPX envelope coding; back-pair Lb/Rb
carriage (currently silent on the ACPL_2 path).
-
Round 103 — 5_X SIMPLE/ASPX_ACPL_1 multichannel encoder path per
ETSI TS 103 190-1 §4.2.6.6 Table 25 rowcase ASPX_ACPL_1:+
§4.2.12.1 Table 50 (aspx_config) + §4.2.13.1 Table 59
(acpl_config_1ch PARTIAL) + §4.2.7.4 Table 26 (two_channel_data) +
§4.2.10.1 Table 47 (chparam_info) + §4.2.6.5 Table 21 (mono_data) +
§4.2.12.4 Table 52 (aspx_data_2ch) + §4.2.12.3 Table 51
(aspx_data_1ch) + §4.2.13.3 Table 61 (acpl_data_1ch) + §4.2.11
Table 49 (companding_control). Completes the round-100 follow-up: the
symmetric encoder for the decoder's round-25
parse_aspx_acpl_1_2_inner_bodyASPX_ACPL_1 branch (Pseudocode 117),
including the joint-MDCT residual layer that ASPX_ACPL_2 omits.
Extends theencoder_acpl3module:Ac4ImsEncoder::encode_frame_pcm_5_0_acpl1(&[L, R, C, Ls, Rs])+
..._with_max_sfb(&[L, R, C, Ls, Rs], max_sfb, max_sfb_master)—
emit IMS v2 frames in5_X_codec_mode = ASPX_ACPL_1 (2).
Channel_mode prefix forced to0b1101(5 ch) per Table 85 so the
decoder dispatcheschannels == 5through
parse_5x_audio_data_outer(b_has_lfe = false).encoder_acpl3::build_5_x_acpl1_body_from_pcm_spectra— shared body
builder. Layout:5_X_codec_mode = 2(3 b) + I-frame block
(aspx_config()15 b +acpl_config_1ch(PARTIAL)6 b) +
companding_control(3)(sync=1, on=1) +coding_config = 0(1 b) +
two_channel_data()(L/R carriers) + ASPX_ACPL_1 joint-MDCT
residual layer (max_sfb_masterin n_side bits + 2×chparam_info- 2×
sf_data(ASF)for the Ls/Rs surround pair sSMP,3 / sSMP,4 per
Table 181) + Cfg0mono_data(0)(centre) + I-frameaspx_data_2ch() aspx_data_1ch()+ twoacpl_data_1ch()parameter sets
(Pseudocode 117 D0 / D1). The residual layer + PARTIAL config are the
two structural differences from the round-100 ACPL_2 path: ASPX_ACPL_1
transmits the surround residual explicitly (so the encoder takes a
full 5-channel input) rather than reconstructing Ls/Rs purely from the
L/R carriers.
- 2×
- New bit-exact emitters:
write_acpl_config_1ch_partial(Table 59,
2-bit id + 1-bit quant_mode + 3-bit acpl_qmf_band_minus1) and
write_acpl_1_residual_layer(max_sfb_master clamped to the n_side
band budget + identity-SAP chparam pair + twosf_data(ASF)bodies
via the sharedprepare_stereo_channelforward pipeline). The
acpl_data_1ch()start_band is resolved from the PARTIAL config's
qmf_bandviacrate::acpl::sb_to_pb(vs ACPL_2's FULL config →
start_band 0). - Decoder round-trip verified: 5.0 ACPL_1 → 5-channel S16 interleaved
PCM (1920 samples × 5 ch × 2 bytes). The decoder walks the full
Table 25 ASPX_ACPL_1 body and resolvesfive_x_mode == AspxAcpl1,
acpl_config_1ch_partial.is_some()(with non-zeroqmf_band),
acpl_1_residual_max_sfb_master.is_some(), both
acpl_1_residual_pair[0/1].is_some()(sSMP,3 / sSMP,4),
cfg0_centre_mono.is_some(), and bothacpl_data_1ch_pair[0/1]—
the 5-channel[L, R, C, Ls, Rs]synthesis runs via
acpl_synth::run_acpl_5x_pair_pcm(Pseudocode 117). - 5 new integration tests in
tests/round103_5_x_acpl1_encoder.rs
(5-channel layout, sequence-counter roll, full-body decoder
resolution including the residual layer, silence round-trip,
small-residual-budget round-trip) + 3 new unit tests in
encoder_acpl3::tests(acpl_config_1ch_partial round-trip,
residual-layer clamp behaviour, full-body decoder resolution). - Total test count: 708 (was 700) — 0 ignored, 0 failed.
- Follow-ups (deferred): real per-band
(alpha, beta)parameter
extraction replacing the zero-delta scaffold; real ASPX envelope
coding; real joint-MDCT residual content (the residual sf_data
currently codes the raw Ls/Rs spectra — a future round should derive
the proper sSMP,3 / sSMP,4 residual from the Table-181 SAP first
stage); matching 7_X ASPX_ACPL_{1,2} encoder paths (the 7_X walker
shares this 1ch acpl/aspx shape).
-
Round 100 — 5_X SIMPLE/ASPX_ACPL_2 multichannel encoder path per
ETSI TS 103 190-1 §4.2.6.6 Table 25 rowcase ASPX_ACPL_2:+
§4.2.12.1 Table 50 (aspx_config) + §4.2.13.1 Table 59
(acpl_config_1ch FULL) + §4.2.7.4 Table 26 (two_channel_data) +
§4.2.6.5 Table 21 (mono_data) + §4.2.12.4 Table 52 (aspx_data_2ch)- §4.2.12.3 Table 51 (aspx_data_1ch) + §4.2.13.3 Table 61
(acpl_data_1ch) + §4.2.11 Table 49 (companding_control). Symmetric
counterpart to the decoder's round-25parse_aspx_acpl_1_2_inner_body
walker (Pseudocode 117). Extends theencoder_acpl3module:
Ac4ImsEncoder::encode_frame_pcm_5_0_acpl2(&[L, R, C])+
..._with_max_sfb(&[L, R, C], max_sfb)— emit IMS v2 frames in
5_X_codec_mode = ASPX_ACPL_2 (3). Channel_mode prefix forced to
0b1101(5 ch) per Table 85 so the decoder dispatcheschannels == 5throughparse_5x_audio_data_outer(b_has_lfe = false).encoder_acpl3::build_5_x_acpl2_body_from_pcm_spectra— shared
body builder. Layout:5_X_codec_mode = 3(3 b) + I-frame block
(aspx_config()15 b +acpl_config_1ch(FULL)3 b) +
companding_control(3)(sync=1, on=1) +coding_config = 0(1 b,
AcplLite2 / two-channel false-branch) +two_channel_data()(L/R
carriers) + Cfg0mono_data(0)(centre carrier) + I-frame
aspx_data_2ch()+aspx_data_1ch()+ twoacpl_data_1ch()
parameter sets (Pseudocode 117 D0 / D1). The ASPX_ACPL_1-only
joint-MDCT residual layer (max_sfb_master + 2× chparam_info + 2× sf_data) is skipped for ACPL_2 — that's the structural
difference that makes the ACPL_2 path the cleanest encoder win.- New bit-exact emitters:
write_acpl_config_1ch_full(Table 59,
2-bit id + 1-bit quant_mode, no qmf_band),write_two_channel_data
(Table 26 sharedsf_info(ASF)+ identity-SAPchparam_info+
2×sf_data),write_mono_data_centre(Table 21 non-LFE:
spec_frontend = 0+sf_info(ASF)+sf_data),
write_aspx_data_1ch_minimal(Table 51 FIXFIX num_env=1 path),
write_acpl_data_1ch_minimal(Table 61:acpl_framing_data+
acpl_ec_data(ALPHA)+acpl_ec_data(BETA), 1 param set, DF
zero-delta). The 1ch ASPX SIGNAL band count uses
num_sbg_sig_highres(matchingparse_aspx_ec_data's empty-
freq_resfallback whenfreq_res_mode != Signalled). - Decoder round-trip verified: 5.0 ACPL_2 → 5-channel S16
interleaved PCM (1920 samples × 5 ch × 2 bytes). The decoder
walks the full Table 25 ASPX_ACPL_2 body and resolves
five_x_mode == AspxAcpl2,acpl_config_1ch_full.is_some(),
two_channel_data.len() == 1,cfg0_centre_mono.is_some(), and
bothacpl_data_1ch_pair[0/1].is_some()— the 5-channel
[L, R, C, Ls, Rs]synthesis runs via
acpl_synth::run_acpl_5x_pair_pcm(Pseudocode 117). With all-zero
ACPL parameter deltas Ls/Rs collapses to ducker-driven
reconstruction from the L/R carriers. - 4 new integration tests in
tests/round100_5_x_acpl2_encoder.rs
(5-channel layout, sequence-counter roll, full-body decoder
resolution, silence round-trip) + 5 new unit tests in
encoder_acpl3::tests(bit-order round-trips for
acpl_config_1ch_full / two_channel_data / mono_data(0) /
acpl_data_1ch + aspx_data_1ch emit). - Total test count: 700 (was 691) — 0 ignored, 0 failed.
- Follow-ups (deferred): the ASPX_ACPL_1 encoder path (adds the
joint-MDCT residual layer + PARTIAL-modeacpl_config_1chwith
acpl_qmf_band); real per-band(alpha, beta)extraction
replacing the zero-delta scaffold; matching 7_X ASPX_ACPL_{1,2}
encoder paths (the 7_X walker shares this 1ch acpl/aspx shape).
- §4.2.12.3 Table 51 (aspx_data_1ch) + §4.2.13.3 Table 61
-
Round 95 — 5_X SIMPLE/ASPX_ACPL_3 multichannel encoder path per
ETSI TS 103 190-1 §4.2.6.6 Table 25 rowcase ASPX_ACPL_3:+
§4.2.12.1 Table 50 (aspx_config) + §4.2.13.2 Table 60 (acpl_config_2ch)- §4.2.13.4 Table 62 (acpl_data_2ch) + §4.2.12.4 Table 52
(aspx_data_2ch) + §4.2.11 Table 49 (companding_control) + §4.2.6.3
Table 22 (stereo_data). Symmetric counterpart to the decoder's round-34
parse_5x_audio_data_outerASPX_ACPL_3 walker (5a58f6a). New
encoder_acpl3module:
Ac4ImsEncoder::encode_frame_pcm_5_0_acpl3(&[L, R, C])and
encode_frame_pcm_5_1_acpl3(&[L, R, C, LFE])— emit IMS v2 frames
in 5_X_codec_mode = ASPX_ACPL_3 (4). Channel_mode prefix forced to
0b1101(5 ch) /0b1110(6 ch) per Table 85.encoder_acpl3::build_5_x_acpl3_body_from_pcm_spectra— shared
body builder. Layout:5_X_codec_mode = 4(3 b) + I-frame block
(aspx_config()15 b +acpl_config_2ch()4 b) + optional LFE
mono_data(b_lfe = 1)+companding_control(2)(sync=1, on=1) +
stereo_data()(split-MDCT path) +aspx_data_2ch()+acpl_data_2ch().encoder_acpl3::write_aspx_config/write_acpl_config_2ch/
write_companding_control_2ch_sync_on— bit-exact emitters for
the small configuration elements. Round-trip-verified against the
matching parsers via unit tests.- ASPX/A-CPL parameter bits emitted as minimum-bit-cost zero-delta
Huffman codewords:pick_zero_delta_cw(len, cw, cb_off)picks the
entry atindex == cb_off(zero delta for DF/DT) and
pick_min_len_cwpicks the smallest-length entry (used for F0
seeds). Covers all 18 ASPX HCBs (Annex A.2 Tables A.16-A.33) and
all 24 ACPL HCBs (Annex A.3 Tables A.34-A.57). write_aspx_data_2ch_minimalemits the FIXFIX + num_env=1 path
withaspx_balance = 1, all-FREQ delta directions, and per-channel
SBG counts derived fromaspx::derive_aspx_frequency_tables.write_acpl_data_2ch_minimalemitsacpl_framing_data()(smooth
interp, num_param_sets = 1) + 11 ×acpl_huff_data()(alpha1/2,
beta1/2/3, gamma1..6) withdiff_type = 0(FREQ) and zero-delta
DF codewords.- Decoder round-trip verified: 5.0 ACPL_3 → 5-channel S16
interleaved PCM (1920 samples × 5 ch × 2 bytes); 5.1 ACPL_3 →
6-channel S16. The decoder walks the full Table 25 body
(parse_stereo_data_body+parse_aspx_data_2ch_body+
parse_acpl_data_2ch) and resolvesfive_x_mode == AspxAcpl3+
acpl_config_2ch.is_some() && acpl_data_2ch.is_some(). The 5-channel
[L, R, C, Ls, Rs]synthesis runs viaacpl_synth::run_acpl_5x_mch_pcm
(Pseudocode 118) with all-zero ACPL parameter deltas — Ls/Rs
collapses to ducker-driven reconstruction from the L/R carriers. - 4 new integration tests in
tests/round95_5_x_acpl3_encoder.rs:
encode_5_0_acpl3_produces_5_channel_audio_frame,
encode_5_1_acpl3_produces_6_channel_audio_frame,
encode_5_0_acpl3_advances_sequence_counter,
encode_5_0_acpl3_decoder_resolves_aspx_acpl_3_mode. - 7 new unit tests in
encoder_acpl3::testscovering bit-order
round-trips for aspx_config / acpl_config_2ch / companding_control /
acpl_data_2ch + minimum-cost / zero-delta HCB picker invariants. - Total test count: 691 (was 680) — 0 ignored, 0 failed.
- Follow-ups (deferred to subsequent rounds): replace zero-delta
ACPL parameter writer with real QMF-domain(alpha, beta, gamma)
parameter extractor (per-band downmix correction estimated from
L/R/Ls/Rs source PCM); replace zero-delta ASPX envelope coder
with real envelope extraction; matching encoder paths for
5_X_codec_mode in{ASPX_ACPL_1, ASPX_ACPL_2}(Pseudocode 117).
- §4.2.13.4 Table 62 (acpl_data_2ch) + §4.2.12.4 Table 52
-
Round 91 — 7.1 (3/4/0.1) SIMPLE/ASF Cfg3Five multichannel forward
analysis (7 SCE + LFE) encoder + decoder 7_X SIMPLE/Cfg3Five core
render per ETSI TS 103 190-1 §4.2.6.14 Table 33 + §4.2.7.5 Table 29- §4.2.7.4 Table 26 + §4.2.8 Table 35 + §4.3.3.7.1 Table 88
(channel_mode 6 = 7.1 (3/4/0.1)):
encoder_asf::build_7_1_simple_asf_body_from_pcm_spectra(transform_length, max_sfb, max_sfb_add, max_sfb_lfe, &[&[f32]; 8], pad_target_bytes)—
emits the full 7.1 multichannelaudio_databody for7_X_codec_mode = SIMPLE,b_has_lfe = 1,coding_config = 3(Cfg3Five). Differs
from the round-80 5.1 builder in three places per Table 33: (1)
7_X_codec_modeis 2 bits (vs 3 for5_X_codec_modeper Table 25);
(2) SIMPLE skips the leadingcompanding_control(5)(5_X SIMPLE
skips it too, but the 7_X-only ASPX path also skips it whereas 5_X
ASPX would emit it); (3) after the innerfive_channel_data()for
L/R/C/Ls/Rs the SIMPLE/ASPX additional-channel block emits
b_use_sap_add_ch (1 b) = 0 + two_channel_data()carrying the
immersive pair Lb/Rb (identity SAP withsap_mode = 0on its
chparam_info) per Table 26. No trailingmono_data(0)for Cfg3
(gated oncoding_config in {0, 2}only); no ASPX trailers / ACPL
data pair for SIMPLE.Ac4ImsEncoder::with_7_1()— channel-mode prefix0b1111001(7 b
— Table 88 channel_mode 6) for the 7.1 (3/4/0.1) layout. Builder
method parity withwith_5_0()/with_5_1().Ac4ImsEncoder::encode_frame_pcm_7_1(&[L, R, C, Ls, Rs, Lb, Rb, LFE])
andencode_frame_pcm_7_1_with_max_sfb(..., max_sfb, max_sfb_add, max_sfb_lfe)— force the 7.1 channel_mode prefix so the decoder's
walk_ac4_substreamdispatcheschannels == 8through
parse_7x_audio_data_outer(b_has_lfe = true), run the round-50
forward MDCT pipeline (KBD-windowed MDCT + DP-optimal sectioning +
HCB1..11 + SNF) independently per channel, and wrap the body in
the v2 IMS TOC.- Decoder 7_X SIMPLE/Cfg3Five core render in
Ac4Decoder::receive_frame: whenseven_x_mode in {SIMPLE, ASPX}
andseven_x_coding_config == Cfg3Five, drive
dispatch_5x_cfg3_simple_aspxon the innerfive_channel_datato
IMDCT slots 0..4 (L/R/C/Ls/Rs). The 7_X walker had been populating
tools.five_channel_datafor ~50+ rounds but the inner 5-channel
PCM was never rendered — only slots 5/6 (the additional-pair F/G)
and slot 7 (LFE, round 80) were touched. This change inherits the
5_X core IMDCT/KBD/overlap-add chain unchanged (the per-channel
body shape is identical to the 5_X Cfg3Five case). ASPX trailer
plumbing (cfg3_aspx_lr / cfg3_aspx_ls_rs / cfg3_aspx_centre) is
passed asNone— the 7_X walker has its own ASPX trailer slots
that need separate wiring (deferred). - Spectral SNR on the 220 / 440 / 660 / 880 / 1100 / 1320 /
1540 Hz independent-tone 7.1 fixture: L=24.5 / R=24.8 / C=25.0 /
Ls=23.4 / Rs=27.4 / Lb=25.4 / Rb=26.0 dB — all above the ≥ 20 dB
floor, identical to round-80 5.1 for the L/R/C/Ls/Rs channels (same
forward pipeline) with Lb/Rb tracking the same SNR-bandwidth
relationship. The 60 Hz LFE tone round-trips to a non-silent
reconstructed LFE channel via the shared round-80 LFE render in
receive_frame. - New test suite
tests/round91_7_1_multichannel.rs(8 tests):
layout (8-channel S16 interleaved PCM), TOC channels=8, sequence
counter roll,with_7_1()builder TOC contract, walker contract
(b_has_lfe == true, populatedlfe_mono_data.scaled_spec,
populatedfive_channel_data+seven_x_additional_channel_data
withb_use_sap_add_ch = false), silence round-trip, independent-
tone round-trip with audible PCM in every non-LFE slot + audible
LFE + verified Lb/Rb separation, and per-channel SNR ≥ 20 dB. - ACPL_3 multichannel ASPX / A-CPL encoder remains deferred (the
5_X ASPX_ACPL_3 / 5_X ASPX_ACPL_{1,2} encoder paths haven't
landed; 7_X ASPX modes are gated on those).
- §4.2.7.4 Table 26 + §4.2.8 Table 35 + §4.3.3.7.1 Table 88
-
**Round 80 — 5.1 SIMPLE/ASF Cfg3Five multichannel forward analysis (5 SCE
- LFE) encoder** per ETSI TS 103 190-1 §4.2.6.6 Table 25 (
if (b_has_lfe) mono_data(1);) + §4.2.7.5 Table 29 + §4.2.8 (sf_info_lfe()Table 35 /
Table 106 column 4n_msfbl_bits):
encoder_asf::build_5_1_simple_asf_body_from_pcm_spectra(transform_length, max_sfb, max_sfb_lfe, &[&[f32]; 6], pad_target_bytes)— emits the full
5.1 multichannelaudio_databody for5_X_codec_mode = SIMPLE,
b_has_lfe = 1,coding_config = 3(Cfg3Five): the round-74 5.0
five_channel_data()payload is preceded by an LFEmono_data(1)
element (no leadingspec_frontendbit per Table 21,
b_long_frame = 1,sf_info_lfe()withmax_sfb_lfein
n_msfbl_bitsbits, then a single
(section + spectral + scalefac + snf)ASF body capped to the LFE
band budget). Attl = 1920n_msfbl_bits = 3, so the LFE channel
spans at most 7 scalefactor bands (≈0–350 Hz) — comfortably more than
the 120 Hz LFE crossover and the 60 Hz tone used by the new tests.Ac4ImsEncoder::encode_frame_pcm_5_1(&[L, R, C, Ls, Rs, LFE])and
encode_frame_pcm_5_1_with_max_sfb(..., max_sfb, max_sfb_lfe)—
forces the 5.1 channel_mode prefix (0b1110, 4 b — Table 85
channel_mode 4) so the decoder'swalk_ac4_substreamdispatches
channels == 6throughparse_5x_audio_data_outer(b_has_lfe = true),
runs the round-74 forward MDCT pipeline (KBD-windowed MDCT +
DP-optimal sectioning + HCB1..11 + SNF) per channel, and wraps the
body in the v2 IMS TOC withbitstream_version = 2.- Decoder LFE PCM render in
Ac4Decoder::receive_frame: when
channels == 6(5.1) orchannels == 8(7.1) and the 5_X / 7_X
walker populatedtools.lfe_mono_data.scaled_spec, the LFE
spectrum is IMDCT'd into the trailing PCM slot (slot 5 for 5.1,
slot 7 for 7.1) using the per-channel overlap-add history.
Pre-r80 the LFE block was parsed but its PCM was silently dropped. - Spectral SNR on the 220 / 440 / 660 / 880 / 1100 Hz independent-tone
fixture matches the round-74 5.0 numbers
(L=24.5 / R=24.8 / C=25.0 / Ls=23.4 / Rs=27.4 dB) and clears the ≥ 20 dB
floor; the 60 Hz LFE tone round-trips to a non-silent reconstructed
LFE channel. 7.0 / 7.1 (immersive add-channel pair) and the ASPX /
A-CPL multichannel modes remain deferred. - New test suite
tests/round80_5_1_multichannel.rs(7 tests) covers
the layout, sequence-counter rolling, walker contract
(b_has_lfe == true+ populatedlfe_mono_data.scaled_spec),
silence round-trip, independent-tone round-trip with audible LFE, and
per-channel SNR ≥ 20 dB.
- LFE) encoder** per ETSI TS 103 190-1 §4.2.6.6 Table 25 (
-
Round 74 — 5.0 SIMPLE/ASF Cfg3Five multichannel forward analysis (5 SCE)
encoder per ETSI TS 103 190-1 §4.2.6.6 Table 25 row
case SIMPLE: coding_config == 3+ §4.2.7.5 Table 29 (five_channel_data())- §4.2.10.1 Table 47 (
chparam_info()):
encoder_asf::build_5_0_simple_asf_body_from_pcm_spectra(transform_length, max_sfb, &[&[f32]; 5], pad_target_bytes)— emits the full 5.0
multichannel audio_data body for5_X_codec_mode = SIMPLE/
coding_config = 3(Cfg3Five):audio_size_value (15 b)+
b_more_bits (1 b)+ byte_align +5_X_codec_mode = SIMPLE (3 b)+
coding_config = 3 (2 b)+five_channel_data()(shared
asf_transform_info+ sharedasf_psy_info+five_channel_infowith
chel_matsel = 0+ 5xchparam_infowithsap_mode = 0for identity
SAP + 5xsf_data(ASF)bodies). No joint-MDCT mixing happens at decode
time — every output channel comes straight from its ownsf_data(ASF)
body. SIMPLE has noaspx_config()/acpl_config_*()(those are
I-frame I/O for ASPX modes only), no companding, no LFE
mono_data(b_lfe=1)(5.0 →b_has_lfe = false).Ac4ImsEncoder::with_5_0()— channel-mode prefix0b1101(4 b — Table
85 channel_mode 3) for the 5.0 surround layout (L, R, C, Ls, Rs)
without LFE.Ac4ImsEncoder::encode_frame_pcm_5_0(&[&[f32]; 5])+
encode_frame_pcm_5_0_with_max_sfb()— accept paired L/R/C/Ls/Rs float
PCM frames at the encoder's configured frame_len (1920 samples for the
default 48 kHz / 24 fps), run the round-50 forward pipeline (KBD-
windowed MDCT + per-band scalefactor + DP-optimal sectioning +
HCB1..11 codebook selection + SNF emission) independently per channel,
then emit abitstream_version = 2IMS TOC (channel_mode prefix
'1101') followed by the 5.0 SIMPLE/Cfg3Five body. The encoder uses
one [encoder_mdct::EncoderMdctState] per channel (new field
mdct_states_multi: Vec<EncoderMdctState>) so 50% TDAC overlap
continuity is preserved per channel.- The decoder's existing
dispatch_5x_cfg3_simple_aspxpath (round 39)
consumes the body, IMDCTs each per-channel spectrum into output slots
0..4 (L/R/C/Ls/Rs per Table 180 rowcoding_config == 3), and emits
5-channel interleaved S16 PCM at the declared sample rate. - Round-trip SNR target met: ≥ 20 dB spectral SNR per channel on the
independent-tone fixture (220 / 440 / 660 / 880 / 1100 Hz on L/R/C/Ls/Rs).
Measured: L=24.5, R=24.8, C=25.0, Ls=23.4, Rs=27.4 dB — comfortably above
the 20 dB floor and in the same band as the round-51 stereo Path A SNR
(24.8 dB on 440 Hz). The decoder's 5-channel S16 interleaved layout
(1920 × 5 × 2 = 19,200 bytes) round-trips cleanly through
Ac4Decoder::receive_frame. - Seven new tests in
tests/round74_5_0_multichannel.rs
(round74_5_0_encoder_produces_5channel_layout_pcm,
round74_5_0_encoder_bumps_sequence_counter,
round74_5_0_encoder_toc_declares_5_channels,
round74_5_0_independent_tones_per_channel_round_trip_with_distinct_audio,
round74_5_0_per_channel_spectral_snr_exceeds_20db,
round74_5_0_substream_parses_via_walk_ac4_substream,
round74_5_0_silence_round_trips_to_silence). - 5.1 (channel_mode
0b1110— adds LFE), 7.0/7.1 (channel_mode
0b11110000/0b11110001— adds front-extension / back-surround pair),
and the ASPX/A-CPL multichannel modes (5_X_codec_mode in {ASPX, ASPX_ACPL_1, ASPX_ACPL_2, ASPX_ACPL_3}) are deferred. 5.0
SIMPLE Cfg3Five is the spec-mandated minimum for 5-channel AC-4
streams and unblocks the encoder's path to LFE / immersive layouts.
- §4.2.10.1 Table 47 (
-
Round 52 — Joint M/S CPE (Path B:
b_enable_mdct_stereo_proc == 1) encoder
per ETSI TS 103 190-1 §5.3 (channel_count > 1) + §4.2.6.3 Table 22
(stereo_data()withb_enable_mdct_stereo_proc == 1) + §7.5
(Pseudocode 77 joint stereo, inverse M/S =L = M+S, R = M-S):encoder_asf::average_per_sfb_correlation(transform_length, max_sfb, coeffs_l, coeffs_r)— energy-weighted per-SFB Pearson correlation
between the L and R MDCT spectra. Bands are weighted by their
geometric-mean energysqrt(s_ll * s_rr)so spectrally-disjoint
tones don't contaminate the metric. Returns a value in[-1.0, 1.0].encoder_asf::build_stereo_simple_asf_joint_body_from_pcm_spectra( transform_length, max_sfb, coeffs_l, coeffs_r, pad_target_bytes)
— emits the full joint stereo audio_data body:
audio_size_value (15 b)+b_more_bits (1 b)+ byte_align +
stereo_codec_mode = SIMPLE (2 b)+b_enable_mdct_stereo_proc = 1 (1 b)+b_long_frame (1 b)+ sharedmax_sfb (n_msfb_bits b)- shared
asf_section_data()+ per-channelasf_spectral_data()
for M and S residuals + sharedasf_scalefac_data()+ per-active-
sfbms_used[sfb](1 b each) + sharedasf_snf_data().
- shared
- Per-SFB M/S vs L/R decision: bit-cost compare between (M, S) and
(L, R) at the baseline q_target=12 picks the cheaper representation
band-by-band. Thems_used[sfb]flag on each active band tells the
decoder whether to apply the inverse-M/S transform. - Frame-level "matched-channels" q_target bump: when the frame's
total S energy is below 15% of M (e.g. for matched / near-matched
stereo content), the M-channel anchor scalefactor is re-picked at a
bumped peak-quant target (up to 16, smoothly tapering down to 12
at the 15% threshold), spending the bits saved on the silent /
near-silent S residual on a finer M quantisation. Per-band bumps
were tried first but interact destructively with the shared joint-
section cost table — when some bands are bumped and others aren't
the joint scalefactor sequence misaligns the decoder's
dequantisation. The frame-level gate keeps the bump self-
consistent across the section partition. Ac4ImsEncoder::encode_frame_pcm_stereonow dispatches between
Path A (round 51, split-MDCT) and Path B (this round, joint M/S)
based on the energy-weighted per-SFB correlation rising above
Ac4ImsEncoder::STEREO_JOINT_MS_CORRELATION_THRESHOLD(0.7). New
encode_frame_pcm_stereo_split_with_max_sfb/
encode_frame_pcm_stereo_joint_with_max_sfbforce a specific path
regardless of correlation — used by tests / fixtures that need a
deterministic on-wire layout.- Round-trip SNR targets met:
- Matched 440 Hz L=R: ≥ 34.5 dB spectral SNR (vs round-51's
24.8 dB on this fixture) — the q_target bump tightens the M
quantisation step by ~half, S quantises to all-zero, and the
decoder reconstructs L = M+0, R = M-0 with the bumped precision. - Independent 440 Hz L + 660 Hz R: routed via Path A (correlation
0.0 by design), preserving round-51's ≥ 24.8 dB SNR floor. - Half-correlated stereo (amplitude-imbalanced 440 Hz at
0.30 / 0.36): routed via Path B, frame-level S/M ratio ≈ 0.003
triggers the q_target bump, output ≥ 26.4 dB / 28.0 dB per
channel — between the pure-matched and fully-independent
regimes.
- Matched 440 Hz L=R: ≥ 34.5 dB spectral SNR (vs round-51's
- Six new tests in
tests/round52_joint_ms_stereo.rs
(round52_matched_stereo_joint_ms_snr_exceeds_28db,
round52_independent_stereo_routes_via_split_path_a,
round52_half_correlated_stereo_joint_ms_snr_exceeds_26db,
round52_joint_ms_full_pcm_roundtrip_through_ac4decoder) plus two
in-module correlation sanity tests
(round52_correlation_identical_channels_is_one,
round52_correlation_independent_tones_below_threshold).
-
Round 51 — Stereo SIMPLE/ASF split-MDCT (Path A: 2× SCE) encoder
per ETSI TS 103 190-1 §5.3 (channel_count > 1) + §4.2.6.3 Table 22
(stereo_data()withb_enable_mdct_stereo_proc == 0):Ac4ImsEncoder::encode_frame_pcm_stereo(frame_l, frame_r)and
encode_frame_pcm_stereo_with_max_sfb()— accept paired L+R float
PCM frames at the encoder's configured frame_len (1920 samples for
the default 48 kHz / 24 fps), run the round-50 forward pipeline
(KBD-windowed MDCT + per-band scalefactor + HCB1..11 codebook
selection + DP-optimal section boundaries + SNF emission)
independently per channel, then emit abitstream_version = 2IMS
TOC (channel_mode prefix'10') followed by the split-MDCT stereo
CPE body. The encoder uses separateEncoderMdctStateper channel
(new fieldmdct_state_r: Option<EncoderMdctState>) so 50% TDAC
overlap continuity is preserved per channel.encoder_asf::build_stereo_simple_asf_split_body_from_pcm_spectra( transform_length, max_sfb, coeffs_l, coeffs_r, pad_target_bytes)
— emits the full stereo audio_data body:audio_size_value (15 b)b_more_bits (1 b)+ byte_align +stereo_codec_mode = SIMPLE (2 b)+b_enable_mdct_stereo_proc = 0 (1 b)+ per-channel
spec_frontend (1 b) + b_long_frame (1 b) + max_sfb (n_msfb_bits b)headers + per-channelsf_data(ASF)payloads (sections +
spectral + scalefac + snf).
- Round-trip SNR target met: ≥24.8 dB spectral SNR on both L and R
for the 440 Hz matched-tone fixture and the 440 Hz L + 660 Hz R
independent-tone fixture (PCM amplitude 0.3, 3 frames to reach
steady state, comparison done in MDCT-spectrum domain to isolate
the encoder quantisation contribution from IMDCT/KBD reconstruction
phase shift). PCM peak ~10 400 i16 (= 0.317 amplitude, matching
input). - Three new SNR / non-silence tests
(encode_frame_pcm_stereo_440hz_both_channels_snr_exceeds_20db,
encode_frame_pcm_stereo_440l_660r_independent_channels_snr_exceeds_20db,
encode_frame_pcm_stereo_440hz_steady_state_nonsilent_both_channels)
plus three structural smoke tests
(encode_frame_pcm_stereo_bumps_sequence_counter,
encode_frame_pcm_stereo_produces_stereo_layout_pcm,
encode_frame_pcm_stereo_substream_parses). - Joint M/S coding (Path B —
b_enable_mdct_stereo_proc == 1) and
multichannel SAP/M-S decisioning are deferred. SIMPLE 2× SCE is
the spec-mandated minimum for stereo AC-4 streams and unblocks the
encoder's path to multichannel.