Skip to content

Unfold Auro-3D carriers into their bed and height layer - #56

Merged
harletty merged 4 commits into
mainfrom
feat/auro-detect
Sep 12, 2026
Merged

harletty merged 4 commits into
mainfrom
feat/auro-detect

Conversation

@harletty

@harletty harletty commented Sep 12, 2026 •

Copy link
Copy Markdown
Owner

What

Auro-3D ships its height layer inside an ordinary 24-bit 5.1/7.1 carrier: the low bits of every carrier sample hold a serial side channel that names the fold and carries the residuals a decoder needs to undo it. This PR reads that side channel in full and unfolds the carrier back into the layout it was encoded from, offline and in realtime.

Three commits:

  1. Detection — a dependency-free auro crate: block sync, CRC-16/CCITT, the ADOL bytecode that announces the channel-input configuration, the public layout tables, and a streaming Detector with fixed per-channel storage. dca::HdDecoder::lossless_samples exposes the integer XLL output the side channel needs.
  2. Unfold — the stream layer (header, seeds, gains, codebook, Golomb-Rice residuals) and the unfold arithmetic (two-stream linear and three-stream three-phase extrapolation, gain scaling, clamping), an Unfolder that joins the carriers with the one-block latency the scheme imposes, and the CLI: harletty decode holds DTS-HD frames back until the carrier is confirmed, then writes a master set of the original layout — bed and corner heights as bed channels, centre height and top as static objects — labelled Auro-3D-13.1 / -11.1 / -10.1 / -9.1.
  3. Bridge — the realtime DTS-HD path does the same: frames held back until the verdict (two of the largest blocks when no block validates, four when blocks validate but the layout has not latched), replayed into the unfolder from their own PCM, then every frame is the unfolded layout as labelled fixed channels (Tfl/Tfr/Tbl/Tbr, Tfc for the centre height, Tc for the top). Output lags input by one block, 21 ms at 48 kHz for 1000-sample blocks. A track that is not a carrier, or a DTS:X one, gets its held frames back unchanged.

docs/pcm-aux-layer-research.md documents the format as found; the earlier draft reasoned from a patent example the streams do not follow.

Verification

  • cargo test -p auro: a synthetic writer mirrors the reader's bit map; an encoder round trip checks the two-stream unfold; the three-stream sum invariant; Rice/codebook decoding on hand-built bit strings.
  • auro/tests/pair.rs against the published original/encoded pair (HARLETTY_AURO_PAIR_*): 1880 blocks, 0 decode errors, correlation 0.999996–0.999998 and gain −0.002 dB on L, R, C, Ls, Rs.
  • auro/tests/corpus.rs on real 13.1 and 11.1 extracts: every block of every channel validates.
  • CLI on 13.1 and 11.1 carriers (30 s extracts and a full 100 s demo): 14- and 12-channel master sets, 0 failed blocks, output length equal to the input, ~2.5 s for 100 s of audio; a DTS:X track still decodes as before.
  • Bridge: a synthetic carrier whose L blocks announce a height stream comes out on Tfl with L silent and the other channels as themselves; plain PCM is released unchanged once the stage gives up; a real 13.1 extract through push_packet (HARLETTY_AURO_DTS_CORPUS) yields 2923 frames of 14 channels with exactly one block held back at the end.
  • cargo test --workspace green; scripts/check-crate-isolation.sh ok.

Not in this PR

  • The bridge has no end-of-stream flush, so the last block of a stream stays in the unfolder (as the PBR pending frames already do).
  • The unfold is the codec's own arithmetic: outputs are "virtually lossless" (about −50 dB relative error on real material), not bit-exact masters.
  • Not yet listened to through mpv.

Sources for the layout layer: almirus, Habr (2026-08); almirus/Orua-D3 (MIT); MediaArea/MediaInfoLib#2531. The stream layer was worked out here by analysis of the carriers and is checked against the published pair.

🤖 Generated with Claude Code

harletty and others added 2 commits September 12, 2026 15:35
Auro-3D ships its height layer inside an ordinary 24-bit carrier: the
low bits of every carrier sample hold a serial side channel that names
the fold (original and carrier layouts) and carries the residuals a
decoder needs to undo it. Only the first part is publicly described, and
that is what this reads.

A new dependency-free `auro` crate parses the block sync, the CRC-16
that guards it, and the ADOL bytecode that announces the channel-input
configuration; a streaming detector with fixed per-channel storage
latches once three consecutive blocks agree. Its bit map is checked by a
synthetic writer in the tests and by an optional corpus test on a real
extract, where every block of every channel validates.

`dca::HdDecoder` gains `lossless_samples`, the integer XLL output before
the float conversion, because the side channel does not survive the
round trip. `harletty decode` feeds it to the detector and logs what the
carrier holds; the output is unchanged, the carrier bed, since the
residual layer is not specified anywhere public.

The research note that guessed a patent scheme is replaced with what the
streams actually do.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The side channel's stream layer is now read in full and undone. Per
block and per carrier: a 112-bit header (Rice parameter, codebook size
and width, ADOL block count, stream-id slots), the predictor seeds, the
ADOL bytecode (gain codes in tenths of a dB via 0x40, an offset via
0x41), a codebook of sign-magnitude residual values, and a Golomb-Rice
stream of codebook indices, one per sample. The unfold treats the
carrier as the exact sum of the folded streams: each sample one stream
is extrapolated from its history (linear for two streams, three-phase
for three) and the others derived from the sum, the residual correcting
the derived value before it feeds the next prediction; outputs are
scaled by their gain codes and clamped to 24 bits.

Checked against the published original/encoded pair (a 5.1 mix and its
Auro-2D carrier): every restored channel correlates 0.99999 with the
original, within 0.01 dB. On the demo carriers every block decodes and
the outputs sum back to the carrier.

`auro::Unfolder` joins the carriers of one stream with the one-block
latency the scheme imposes, averaging a stream folded into two
carriers and playing the carrier as authored where no block claims a
sample. `harletty decode` holds its DTS-HD frames back until the
carrier is confirmed, then writes a master set of the original layout:
bed and corner heights as bed channels, centre height and top as
static objects, labelled Auro-3D-13.1 / -11.1 / -10.1 / -9.1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@harletty harletty changed the title Detect the Auro-Codec side channel in lossless DTS-HD output Unfold Auro-3D carriers into their bed and height layer Sep 12, 2026
harletty and others added 2 commits September 12, 2026 16:50
The bridge now does what the CLI does: a lossless DTS-HD track that
turns out to be an Auro-Codec carrier plays as the layout it folds, with
the bed and the height layer as labelled fixed channels (corner heights
as Tfl/Tfr/Tbl/Tbr, the centre height as Tfc, the top as Tc).

Frames are held back until the carrier question is settled — two of the
largest blocks when no block validates, four when blocks validate but
the layout has not latched — then replayed into the unfolder from their
own PCM, which is the lossless bed exactly. From then on output lags
input by one block, the unfolder's latency now following the detected
block size instead of the largest possible one (the CLI gets the same).
A track that is not a carrier, or that carries a DTS:X presentation,
gets its held frames back unchanged and the stage steps aside.

Checked by a synthetic carrier whose blocks announce a height stream on
the L channel (it comes out on Tfl with L silent, the other channels
play as themselves) and, when the corpus is at hand, by a real 13.1
extract through push_packet: every frame out is the 14-channel layout,
one block held back at the end.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The channel-count label a decoded stream is stored under (Auro-3D-13.1
and friends) now comes from auro::Layout::source_codec_label, so the
DAMF sourceCodec and the atmos-ranker probe cannot drift apart.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@harletty
harletty merged commit cdd3986 into main Sep 12, 2026
1 check passed
@harletty
harletty deleted the feat/auro-detect branch September 12, 2026 17:39
@almirus

almirus commented Sep 18, 2026

Copy link
Copy Markdown

If possible, please add a link to my GitHub account @almirus Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants