oxideav-opus
Pure-Rust Opus audio codec (SILK + CELT) for the oxideav framework.
Status
Clean-room rebuild in progress (orphan scaffold). The prior
implementation was retired under the workspace clean-room policy; the
crate is being re-implemented from scratch against the published RFCs
using only material under docs/ and black-box validator binaries.
A top-level OpusDecoder::decode_packet packet → PCM orchestration is
now in place: it parses the §3.1 TOC, splits the §3.2 frame packing
(all four frame-count codes), runs the §4.5 multi-frame loop, routes
each Opus frame by mode, and lays out the interleaved 48 kHz output
buffer (RFC 7845 §5.1) with correct per-frame sample counts. Both mono
and stereo SILK-only packets now decode end-to-end to real PCM: the
§4.2 bitstream decode (the §4.2.3 header bits, the §4.2.5 LBRR / §4.2.6
regular SILK frame loop, each frame decoded in Table-5 order through
gains / LSF chain / LTP / excitation with inter-frame state threaded),
then the §4.2.7.9 LTP / LPC synthesis filters (composed in the
silk_synthesis module with the §4.2.7.9 per-subframe LPC selection and
cross-frame histories), then a §4.2.9 non-normative resample to 48 kHz
and i16 conversion. For stereo, the §4.2.2 mid/side interleave (mid
frame then side frame per 20 ms interval, the §4.2.7.2 mid-only flag
skipping the side frame) is decoded into two independent per-channel
synthesis states and converted from mid/side to left/right by the §4.2.8
silk_stereo unmixer, run per SILK interval with that interval's
§4.2.7.1 weights and the cross-packet StereoUnmixState history. The
§4.5.2 SILK state reset (CELT→SILK transition) and the §4.2.7.1
mono→stereo weight reset are applied across packets. The CELT synthesis
backend is now composed end-to-end: celt_synthesis::CeltSynthState
turns already-decoded per-band shapes + log2 energies into time-domain
PCM through §4.3.6 denormalise → §4.3.7 inverse MDCT → §4.3.7 weighted
overlap-add → §4.3.7.2 de-emphasis, threading the cross-frame overlap and
de-emphasis state (the CELT analogue of silk_synthesis), and emits
interleaved 48 kHz i16 via synthesize_frame_interleaved_i16. A
CELT-only silence frame now decodes end-to-end to real PCM: the
§4.3.7.1 range-coded frame prefix (silence flag + post-filter group +
transient + intra) is decoded from the real range coder by
celt_frame_prefix, and a set silence flag drives the synthesis backend
with all-zero bands, emitting zero PCM while advancing the overlap-add /
de-emphasis state (FrameDecodeStatus::CeltSilence). The §4.3.2.1
coarse-energy front half now decodes for non-silent CELT-only frames:
the Laplace symbol decoder (celt_laplace::ec_laplace_decode, the
15-bit path) feeds the new celt_coarse_energy reconstruction
recurrence, which runs the §4.3.2.1 2-D prediction filter
A(z_l, z_b) in reverse (the frequency accumulator pred_freq[b+1] = pred_freq[b] + (1-beta)*R[b] and E[b][l] = alpha*E[b][l-1] + pred_freq[b] + R[b], derived algebraically from the RFC z-transform),
adds back the §4.3 e_means Q4 baseline, and threads the cross-frame
E[b][l-1] predictor state on OpusDecoder (reset on an intra frame /
SILK→CELT transition). A non-silent CELT-only frame thus consumes its
prefix + coarse energy from the real range coder and advances the
synthesis state, reporting FrameDecodeStatus::CeltCoarseEnergyDecoded.
On top of the coarse energy, a non-silent frame now also decodes the
full run of Table-56 symbols between coarse energy and the §4.3.4
residual, from the same range coder in exact Table-56 order: the §4.3.1
per-band tf_change flags and gated tf_select bit (celt_tf_decode),
the §4.3.4.3 spread symbol (celt_spreading), then the §4.3.3
allocation header — the signalled part of the bit allocation: the band
boosts
(celt_band_boost, walked over the start..end coding window with the
per-band cap[] from celt_cache_caps50 and the per-channel MDCT-bin
counts), the allocation trim (celt_alloc_trim, gated on the running
ec_tell_frac), and the §4.3.3 anti-collapse / skip / intensity-stereo
/ dual-stereo reservations (celt_reservations). This advances the
entropy decoder through everything the bitstream explicitly carries
before the implicit interpolation, leaving the coder positioned exactly
where the §4.3.4 PVQ shape decode resumes, and the frame reports
FrameDecodeStatus::CeltAllocationDecoded. The remaining CELT band-data
stages — the §4.3.3 implicit allocation (interp_bits2pulses, the
per-band pulse / fine-energy split, which is reference-code-only and
absent from the RFC narrative body), §4.3.4 PVQ band shapes, and
§4.3.2.2 fine energy — are still pending, so the band shapes are
all-zero and these frames emit correct-length silence until those land.
The one residual coarse-energy seam is the RFC's "clamped internally"
bound, which is not in the normative body (only in reference code) and
is left as a documented identity in celt_coarse_energy — exact for
every in-range bitstream — pending a clean-room docs trace. The §4.4
packet-loss concealment is also outstanding (the RFC defines PLC as a
non-normative decoder feature with no bitstream algorithm; lost / DTX
frames currently emit the §4.6 silence floor).
The crate now also carries the start of the encode side: the
bit-exact §5.1 range encoder (RangeEncoder — the §5.1.1 symbol
update, §5.1.1.2 carry propagation, the §5.1.2 division-free variants
sharing the decoder's icdf[] tables, §5.1.3 raw bits, §5.1.4
uniform integers, §5.1.5 finalization, §5.1.6 tell/tell_frac
matching the decoder bit-for-bit), write-side mirrors of every
SILK §4.2.7 decode stage (header / gains with a deterministic
quantizer / LSF stage-1 + stage-2 / interpolation index / LTP / seed
/ excitation, each returning the value the decoder will
reconstruct), the whole-frame Table-5 composition
(encode_silk_frame), and SILK-only packet encoders for both mono
and stereo (encode_silk_only_packet_mono /
encode_silk_only_packet_stereo: TOC byte + §4.2.3/§4.2.4 header
bits + 1–3 SILK frames at 10/20/40/60 ms — the stereo entry writing
the §4.2.2 mid/side interleave with the §4.2.7.1 weight quintuple and
gated §4.2.7.2 mid-only flag on each mid frame, and two independent
per-channel carried states) whose packets decode end-to-end through a
fresh OpusDecoder::decode_packet to real SILK PCM, with every
per-frame parameter verified equal to the encoder's prediction. LBRR
(in-band FEC, §4.2.5) emission is included for both channel layouts
and closes the FEC loop: decode_packet_fec recovers real (mono or
two-channel) audio from the encoder's own redundancy. On top of the
packet writers sit the stereo analysis front half — the exact
§4.2.8 algebraic-inverse downmix stereo_lr_to_ms (L/R → mid/side
with the decoder's weight-interpolation ramp; roundtrips to the
input at the §4.2.8 one-sample delay), the least-squares §4.2.7.1
weight estimator estimate_stereo_weights, and the exhaustive
codebook quantizer StereoWeightSymbols::quantize — plus the §3.2
/ Appendix-B framing writers (compose_packet,
compose_packet_code3, compose_self_delimited; all four codes,
CBR/VBR, §3.2.5 padding chains, parser-validated R2/R3/R5/R6) and
the RFC 7845 write side (OpusHead::compose, byte-identical on
reparse, and assemble_multistream_packet, roundtripped against the
splitter and decoded sample-identically through
MultistreamDecoder). What the encoder does not yet have is the
§5.2.3 signal analysis that picks the SILK symbols themselves
(pitch/LTP analysis, LSF fitting, excitation quantization from
residual PCM beyond the gains quantizer) — packets are encoded from
symbol scripts, not yet from raw PCM.
Differential encoder/decoder testing and a restored cargo-fuzz suite
(4 coverage-guided targets, incl. an encoder↔decoder range-coder
roundtrip) have also hardened the decoder: five mis-transcribed rows
in the §4.2.7.8.3 split tables (now verified cell-by-cell against the
RFC across all 64 rows), a dec_bits(32) shift overflow, a
§4.2.7.5.8 recurrence i64 overflow on adversarial input, and the
§4.2.7.8 10 ms-MB 128-vs-120-sample special case (previously every
10 ms MB SILK packet failed to synthesize) are all fixed with
regression tests.
The crate ships a large, individually unit-tested set of SILK and
CELT building blocks plus a complete RFC 7845 multistream /
multichannel decode subsystem (1296 lib tests + SILK-fixture,
multistream, FEC, and CELT synthesis-backend integration suites).
Per-stage progress lives in CHANGELOG.md.
What works
Packet → PCM orchestration (RFC 6716 §3 / §4):
OpusDecoder::decode_packet— the top-level packet → interleaved 48 kHz PCM path: TOC parse, §3.2 frame split, §4.5 multi-frame loop, per-mode routing, the §4.5.2 cross-packet SILK state reset, and the RFC 7845 §5.1 output sample-count layout. Mono SILK-only packets decode end-to-end to real PCM (bitstream → §4.2.7.9 synthesis → §4.2.9 resample); other modes emit correct-length silence flagged viaFrameDecodeStatus.silk_decode::decode_silk_frame— the §4.2.6 / §4.2.7 in-order SILK frame decode that composes the per-stage decoders in exact Table-5 symbol order and runs the LSF → stable-Q12-LPC chain.silk_synthesis::synthesize_silk_frame— the §4.2.7.9 synthesis composition: §4.2.7.9.1 LTP + §4.2.7.9.2 LPC filters with the §4.2.7.9 per-subframe LPC selection and cross-frameSilkSynthStatehistories, producing internal-rate (8/12/16 kHz) time-domain samples.OpusDecoder::decode_silk_only_stereo— the §4.2.2 stereo SILK decode: the §4.2.3 two-channel header bits, the §4.2.5 / §4.2.6 interleaved mid/side SILK frames (the §4.2.7.1 weights + §4.2.7.2 mid-only flag on the mid frame; an uncoded side frame clears its §4.2.7.9 LTP buffer per §4.5.2), two independent per-channel synthesis states, and the §4.2.8silk_stereo::stereo_ms_to_lrmid/side → left/right unmix run per SILK interval into interleaved L/R PCM.
Packet & framing (RFC 6716 §3 / §4.2):
OpusTocByte— the §3.1 TOC parser (config × stereo flag × frame-count code).OpusPacket— the §3.2 frame-packing parser for all four frame-count codes (single, two-equal, two-unequal, signalled with optional VBR lengths + padding); returned frame slices borrow from the input.parse_self_delimited— RFC 6716 Appendix B self-delimiting framing (for chaining inside a multistream demuxer).OpusFrameRouting— §3.1 / §4.2 mode dispatch (SILK-only / Hybrid / CELT-only, SILK-frame count, per-frame LBRR-flag gating, channel multiplier).- A §3.4 R1–R7 malformed-input rejection audit
(
tests/malformed_input.rs). - An end-to-end SILK fixture-decode suite (
tests/silk_fixture_decode.rs) that decodes the in-project NB-mono / WB-stereo / MB-60 ms-mono Opus streams packet-by-packet throughdecode_packetand validates §3.1 TOC routing, whole-stream error-free SILK decode (mono + stereo, NB/MB/WB, 20/60 ms), §3 sample-count accounting, and 440 Hz dominance on the NB sine fixture. Validation is signal- / structure-based, not bit-exact: the §4.2.9 SILK→48 kHz resampler is non-normative, so the decoded envelope differs from the polyphase-resampled reference decoder.
Multistream / multichannel (RFC 7845 §3 / §5.1 / §5.1.1):
OpusHead— the §5.1 identification-header parser: version (with the major-nibble compatibility bound), output channel count, pre-skip, input sample rate, output gain, mapping family, and the §5.1.1 channel-mapping table (stream count N, coupled count M, per-output mapping indices). Enforces every MUST in §5.1 / §5.1.1 (non-zero channel/stream counts, per-family channel ranges,M ≤ N,M + N ≤ 255, and the< M+N/ 255 mapping-index bound). Family 0 synthesizes the table from the RFC-pinned defaults.split_multistream_packet— the §3 N-packet split: the firstN − 1streams via Appendix-B self-delimited framing, the final stream as the undelimited remainder.MultistreamDecoder— the multichannel decode: one stateful sub-decoder per coded stream, decoding each split packet and assembling theCoutput channels by the §5.1.1 index rule (coupled-stream L/R by parity, mono streams, index-255 silence, a decoded channel routed to multiple outputs), with the §3 equal-duration constraint enforced. Validated end-to-end against the real SILK fixtures: anN = 1family-0 decode is byte-identical to a plainOpusDecoder, a coupled-stream L/R split reproduces a plain stereo decode exactly, and mono-pair / swapped / silence / duplicate maps all route correctly.apply_output_gain/PreSkip— the §5.1 post-decode output-gain application (Q7.8 dB, i16-saturating) and the cross-packet pre-skip accumulator.
Range coder (RFC 6716 §4.1 / §5.1): RangeDecoder — the shared
entropy primitive consumed by both layers, including the §4.1.2
two-step ec_decode / ec_dec_update path and the Laplace / iCDF
helpers — and RangeEncoder, its bit-exact §5.1 write-side mirror
(validated by per-primitive roundtrips, tell/tell_frac lockstep,
a 5000-seed mixed-symbol fuzz roundtrip, and a coverage-guided
libfuzzer differential target).
SILK encode side (RFC 6716 §5.2 bitstream back end): write-side
mirrors of every §4.2.7 stage sharing the decode tables
(SilkFrameHeader::encode_pre_gains / encode_lsf_stage1,
SubframeGains::encode/quantize, LsfStage2::encode,
LsfInterpolated::encode_index, encode_lcg_seed,
LtpParameters::encode, Excitation::encode), the Table-5
whole-frame composition encode_silk_frame, the §4.2.3/§4.2.4
header-bit writer SilkHeaderBits::encode (mono + two-channel), the
§3.1 TOC composer OpusTocByte::compose_byte, and the packet-level
encode_silk_only_packet_mono / encode_silk_only_packet_stereo
(each with a _with_lbrr variant for §4.2.5 in-band-FEC emission;
the stereo entry writes the §4.2.2 mid/side interleave with the
§4.2.7.1 weights and gated §4.2.7.2 mid-only flag per interval and
threads two independent per-channel carried states, exactly
mirroring the decoder's stereo walk) — every layer
roundtrip-verified against the decoder, up to whole packets decoding
end-to-end through OpusDecoder::decode_packet (mono and stereo)
and FEC recovery through decode_packet_fec.
Stereo encode analysis (§4.2.7.1 / §4.2.8 write half):
stereo_lr_to_ms — the exact algebraic inverse of the §4.2.8
unmixer (frame-aligned L/R → mid/side with the decoder's
weight-interpolation ramp, one-sample lookahead for the final p0,
StereoDownmixState history; a multi-frame roundtrip through
stereo_ms_to_lr reproduces the input at the §4.2.8 one-sample
delay) — estimate_stereo_weights (least-squares fit of the raw
side onto the p0 / mid predictor pair, f64 normal equations) and
StereoWeightSymbols::quantize (exhaustive deterministic argmin
over the 5625-quintuple §4.2.7.1 codebook; representable targets
roundtrip value-exactly).
Packet-framing / RFC 7845 write side: compose_packet /
compose_packet_code3 / compose_self_delimited / encode_length —
the §3.2 + Appendix-B framing writers (all four codes, CBR/VBR,
§3.2.5 padding chains, every parser-enforced requirement validated
before writing; roundtripped against OpusPacket::parse /
parse_self_delimited, including chained self-delimited buffers and
multi-frame SILK packets decoding end-to-end) — plus
OpusHead::compose (byte-identical reparse, full §5.1/§5.1.1 MUST
validation) and assemble_multistream_packet (§3 stream packing via
the Appendix-B writer, equal-duration constraint enforced,
sample-identical decode through MultistreamDecoder).
SILK (RFC 6716 §4.2): frame-header decode (§4.2.7.1–§4.2.7.5.1),
subframe gains (§4.2.7.4), the full LSF chain (stage-2 residual → NLSF
reconstruction → stabilization → interpolation → NLSF→LPC →
bandwidth-expansion → prediction-gain limiting, §4.2.7.5.2–§4.2.7.5.8),
LTP parameters (§4.2.7.6), LCG seed (§4.2.7.7), excitation
(§4.2.7.8), LTP + LPC synthesis filters (§4.2.7.9), stereo unmixing
(§4.2.8), the §4.2.9 resampler delay budget, and in-band FEC
recovery (§2.1.7 / §4.2.5): OpusDecoder::decode_packet_fec
reconstructs a lost frame's audio from the Low Bit-Rate Redundancy
(LBRR) frames carried in the next received packet — decoding the §4.2.5
LBRR frame(s) (mono, or interleaved mid/side for stereo), running the
full §4.2.7.9 synthesis from a fresh state, unmixing a stereo recovery
via §4.2.8, and resampling to 48 kHz, reported through FecDecodeStatus.
CELT (RFC 6716 §4.3 / §4.5): the §4.3 band layout (Table 55), the
pre-band header symbols (silence / post-filter / transient / intra),
the §4.3.4.5 time-frequency change decode (celt_tf_decode — the
per-band tf_change flag loop, first band absolute and subsequent
bands difference-coded relative to the previous band's choice, plus the
§4.3.1-gated tf_select flag and the resulting per-band TF adjustment
vector) layered on the §4.3.4.5 TF-resolution adjustment tables, the
coarse-energy Laplace
parameter tables (§4.3.2.1), the allocation parameter surfaces
(log2-frac / alloc-trim / cache-caps / static-allocation), the
§4.3.4.1 Bits-to-Pulses pulse-cost cache (the run-packed
cache_bits50 / cache_index50 lookup plus the budget-to-pulse-count
inversion), the §4.3.6 band denormalisation (unit-norm PVQ shape ×
sqrt(2**log2_energy), laid out across the coded bands into the
inverse-MDCT input buffer), the §4.3.7 inverse MDCT transform core (the
N frequency-domain bins → 2N time-domain samples mapping, scaled by
1/2, with the §4.3.7 overlap-add window already landed at
celt_mdct_window), the §4.3.7 weighted overlap-add (celt_overlap_add
— the stateful per-channel adder that windows each 2N inverse-MDCT
block with the low-overlap synthesis window and overlap-adds the leading
half with the previous block's windowed trailing half at hop N,
carrying the overlap history across frames and reconstructing the
aliasing-free time-domain signal), the §4.3.4.5 time-frequency Hadamard
transform (celt_tf_hadamard — the across-block / sequency-order
orthonormal Walsh–Hadamard reshaping that consumes the per-band
TfDirection, preserving the unit-norm shape energy), the §4.3.4
per-band shape decode orchestrator (celt_band_shape — composing
§4.3.4.2 PVQ decode → §4.3.4.3 spreading → §4.3.4.5 TF transform into
one decode_band_shape call given a band's (N, K, spread, tf_adjust, nb_blocks)), and the §4.5 redundancy / mode-transition state-reset
machinery.
The one structural blocker on the CELT-only real-PCM path is the §4.3.3
allocation orchestration (the reference interp_bits2pulses:
reallocation of unused bits with concurrent skip decoding, the
fine-energy-vs-shape split, and the final reallocation) together with
the §4.3.4.4 split-decoding gain precision ("derived from the
current allocation"). RFC 6716 §4.3.3 names these steps (p. 111) but
provides no algorithm for them — they live only in the reference
rate.c / bands.c, which the clean-room wall bars. Without the
per-band pulse count K they produce, the §4.3.4 shape decode (now
fully composed) cannot be driven against a real bitstream, so non-silent
CELT-only frames still emit correct-length silence after consuming their
prefix + coarse energy. This is a precise docs gap: a clean-room trace
of interp_bits2pulses + the split-gain qb derivation would unblock
the end-to-end CELT path. The §4.3.5 anti-collapse remains separately
gapped (no PRNG / energy-injection algorithm in the RFC narrative).
Clean-room sources
The rebuild consults only:
- RFC 6716 — Definition of the Opus Audio Codec.
- RFC 8251 — Updates to the Opus Audio Codec.
- RFC 7587 — RTP Payload Format for Opus.
- RFC 7845 — Ogg Encapsulation for Opus.
- Black-box invocations of the
opusdec/opusencbinaries (not their source) as opaque validators.
No external library source is permitted as a reference under the workspace clean-room policy.
License
MIT. See LICENSE.