v0.0.10
Other
- §4.3 Table 55 CELT MDCT-band layout (round 24)
- §4.2.7.4 SILK gain dequant tail (silk_log2lin) — round 23
- §3.4 R1..R7 malformed-input rejection audit (round 22)
- §3.1 / §4.2 framing dispatch (round 21)
- land §4.3 Table 56 CELT pre-band header symbols
- Round 19: §4.2.9 SILK resampler delay budget + sample-rate accounting
- round 18: §4.2.3 SILK header bits + §4.2.4 per-frame LBRR flags
- §4.2.8 stereo unmixing — silk_stereo_MS_to_LR (round 17)
- fix the round-16 test count phrasing
- §4.2.7.9.1 LTP synthesis filter (round 16)
- round 15 — RFC 6716 §4.2.7.9.2 LPC synthesis filter
- §4.2.7.7 LCG seed + §4.2.7.8 excitation reconstruction
- §4.2.7.6 LTP parameters (pitch lags + LTP filter + LTP scaling)
- §4.2.7.5.8 LPC prediction-gain stability limiting
- §4.2.7.5.7 LPC range-limiting bandwidth expansion
- round 10: RFC 6716 §4.2.7.5.6 SILK NLSF→LPC core conversion
- clean-room round 9 — SILK §4.2.7.5.5 NLSF interpolation
- round 8: RFC 6716 §4.2.7.5.4 SILK NLSF stabilization
- round 7: RFC 6716 §4.2.7.5.3 SILK NLSF reconstruction
- round 6: RFC 6716 §4.2.7.5.2 SILK LSF Stage-2
- round 5 fix: compare iCDF slices by value, not pointer identity
- round 5: RFC 6716 §4.2.7.4 SILK subframe gains
- round 4: RFC 6716 §4.2.7.1–§4.2.7.5.1 SILK frame header
- round 3: RFC 6716 §4.1 range decoder
- round 2: RFC 6716 §3.2 frame-packing parser
- round 1: RFC 6716 §3.1 packet TOC byte parser
- orphan rebuild: clean-room scaffold post 2026-05-20 audit
Added
-
Clean-room round 24 (2026-05-29): §4.3 CELT MDCT-band layout —
a newcelt_band_layoutmodule owning RFC 6716 Table 55 (the
21-band partition with per-band MDCT bin counts at 2.5 / 5 / 10 /
20 ms and the0..=20000 Hzband-edge frequencies) and the §4.3
"first 17 bands (up to 8 kHz) are not coded in Hybrid mode" rule.
ExposesCeltFrameSize(the four CELT frame-size variants whose
repr(u8)discriminants double as Table 55 column index0..=3,
plusfrom_frame_tenths_ms/to_frame_tenths_ms),
celt_band_bins_per_channel(band, fs) -> Option<u16>,
celt_band_start_hz(b)/celt_band_stop_hz(b)band edges,
celt_band_at_hz(hz) -> Option<usize>reverse lookup,
celt_first_coded_band(is_hybrid)/celt_end_coded_band()
iterator bounds,celt_total_bins_per_channel(fs, is_hybrid)
column-sum helper, and named constantsCELT_NUM_BANDS = 21,
HYBRID_FIRST_CODED_BAND = 17,CELT_MAX_BINS_PER_BAND = 176.
The "Custom" mode of §6.2 is explicitly out of scope.This is the layout the deferred §4.3.2 coarse-energy decoder,
§4.3.3 bit allocator, §4.3.4 PVQ shape decoder, §4.3.6
denormalisation, and §4.3.7 inverse MDCT all need before any band
loop can iterate.Twenty new module tests (401 lib tests total, up from 381 at
round-23 close; 20 integration tests unchanged) cover: full-band
start / stop pin (band 0 starts at 0 Hz, band 20 stops at 20 kHz);
adjacent bands tile without gaps (stop(b) == start(b + 1)for
everyb ∈ 0..=19); positive band widths everywhere; the
power-of-two column-scaling invariant (column(c) == 1 << c * column(0)per band); every cell∈ [1, 176]per the §4.3 prose;
hand-pinned Table 55 cells at bands 0 / 8 / 12 / 15 / 17 / 20;
band-edge spot pins at start, the §4.3 Hybrid boundary
(stop(16) = 8000=start(17)), and tail (stop(20) = 20000);
out-of-range band index returnsNone;
CeltFrameSize::from_frame_tenths_msround-trip with explicit
SILK-only rejection (400 / 600 ms); discriminant-vs-column-index
agreement; the Hybrid-vs-CELT-only first-coded-band split with the
8 kHz boundary pin;celt_total_bins_per_channelcolumn-sum
agreement against an independent iterator sum for each mode; strict
hybrid_total < celt_only_totalinvariant; pinned CELT-only column
sums (100 / 200 / 400 / 800) and Hybrid column sums (60 / 120 /
240 / 480);celt_band_at_hzround-trip against the band-edge
triple (start, midpoint,stop - 1all land on the same band);
>= 20 kHzrejection ofcelt_band_at_hz;
celt_band_at_hz(8000) == 17lining up with
HYBRID_FIRST_CODED_BAND; multiple-of-200-Hz band-width
invariant with three pinned widths (200 Hz for band 0, 400 Hz for
band 8, 4400 Hz for band 20);CELT_MAX_BINS_PER_BAND == max(every cell). -
Clean-room round 23 (2026-05-29): §4.2.7.4 SILK gain
dequantization tail — a newsilk_log2linmodule exposing
silk_log2lin(the spec's piecewise-linear approximation of
2^(inLog_Q7/128)) andsilk_gains_dequant(the composed
log_gain ∈ 0..=63 → gain_Q16pipeline
silk_log2lin((0x1D1C71*log_gain >> 16) + 2090)), plus the named
constantsSILK_LOG_GAIN_MULTIPLIER,SILK_LOG_GAIN_BIAS,
SILK_GAIN_Q16_MIN = 81_920,SILK_GAIN_Q16_MAX = 1_686_110_208.
Also adds aSubframeGains::dequant_q16convenience that maps an
entire decoded frame'slog_gain[]into the fixed-size[u32; SILK_MAX_SUBFRAMES]array consumed by the §4.2.7.9.1 LTP and
§4.2.7.9.2 LPC synthesis filters (with trailing unused slots left
at zero for two-subframe frames).This closes the §4.2.7.4 tail-end conversion that was previously
noted as deferred since round 5; the §4.2.7.9 synthesis filters
already accept again_q16input but were missing the official
RFC-spec mapping from the decodedlog_gaininteger to the linear
Q16 gain.Nineteen new module tests (381 lib tests total, up from 362 at
round-22 close, plus the 20 integration tests unchanged) cover:- The §4.2.7.4 documented endpoints —
log_gain = 0returns
81920(= 1.25 in linear scale) andlog_gain = 63returns
1_686_110_208(≈ 25 728 in linear). - Strict monotonicity:
silk_gains_dequant(g+1) > silk_gains_dequant(g)
for everyg ∈ 0..=62. - Spec-range invariant across the full
0..=63sweep. - Pure-power-of-two collapse:
silk_log2lin(128*i) == 1<<ifor
i ∈ 0..=30. silk_log2lin(0) == 1andsilk_log2lin(1) == 1(the
approximation can't resolve sub-128 Q7 belowi = 7).- Pinned
silk_log2lin(7*128 | 64) = 181— exact match of the
§4.2.7.4 approximation against the true2^7.5 ≈ 181.019…
halfway between2^7and2^8, exercising both thebowed
correction and the linear term. - Independent i64 oracle of the §4.2.7.4 formula matched bit-for-bit
by the production i32 implementation across (a) every reachable
inLog_Q7from thelog_gaindequant pipeline and (b) the full
i ∈ 0..=30 × f ∈ 0..=127Q7 domain. - Endpoint algebra pinned independently of
silk_gains_dequant:
log_gain = 0 → in_log_q7 = 2090;log_gain = 63 → in_log_q7 = 3923(=30*128 + 83),silk_log2lin(2090) = 81_920,
silk_log2lin(3923) = 1_686_110_208. SubframeGains::dequant_q16leaves trailing slots at zero for a
two-subframe frame and matches per-subframesilk_gains_dequant
calls across the four-subframe frame.
- The §4.2.7.4 documented endpoints —
-
Clean-room round 22 (2026-05-27): §3.4 R1..R7 malformed-input
rejection audit — a dedicated integration-level test file
(tests/malformed_input.rs, 20 tests) that pins every concrete
failure mode RFC 6716 §3.4 enumerates for a malformed packet, plus
property-style sweeps proving the §4.2.3 / §4.2.4 SILK header
decoder is panic-free on any truncation of a previously-valid
bitstream (§4.1.4 zero-extension contract). Covers R1 (empty
packet), R2 (frame > 1275 B for codes 0 and 1, plus the §3.2.1
boundary at 1275 B), R3 (code-1 odd body length), R4 (code-2
length-byte truncations + length > remaining + DTX boundary), R5
(M = 0+M > 48rejection), R6 (CBRR % M != 0), R7 (VBR
declared length overrun), §3.2.5 padding-chain pathologies, TOC
total-function self-consistency, §4.2.3 / §4.2.4 truncation
panic-freeness across(num_silk_frames, stereo)× prefix-length
1..=32, §4.2.4 LBRR-bitmap-never-zero invariant for 40 / 60 ms,
mono channel never emits side state, parsed-frame slice
lifetimes, and a code × body-len short-packet panic sweep. Test
totals: 362 lib + 20 integration = 382 (was 362 lib + 0
integration after round 21). -
Clean-room round 21 (2026-05-27): §3.1 / §4.2 framing dispatch —
a newframingmodule exposingOpusFrameRouting,OperatingMode,
andSilkBandwidth.OpusFrameRouting::from_tocturns the parsed
OpusTocByteinto the per-Opus-frame dispatch decision a §4 decoder
needs before it touches the range coder:operating_mode— SilkOnly / Hybrid / CeltOnly from §3.1 Table 2.silk_layer/celt_layer— which layers are present.silk_bandwidth— internal SILK bandwidth (NB / MB / WB), pinned
to WB for every Hybrid frame regardless of the TOC's SWB / FB per
RFC 6716 §4.2 first paragraph ("the LP layer itself still only runs
in WB").silk_frames_per_channel— §4.2.2 LP-layer organisation (1 for
10 / 20 ms Opus frames, 2 for 40 ms, 3 for 60 ms;Nonefor
CELT-only).total_silk_frames— channel-count × per-channel SILK frames.has_per_frame_lbrr_bits— §4.2.4 duration gate (true for SILK-
bearing frames longer than 20 ms).
Thirteen new unit tests cover the SILK-only Table 2 row-by-row
expectations (12 cells), the Hybrid WB-pin, the CELT-only frames
sweep across mono/stereo, the §4.2.4 per-frame LBRR gate against
every Table 2 cell, thetotal_silk_framesformula across all 32
configs × {mono, stereo}, a 60 ms stereo SILK-only worked example,
thec-bit independence of the routing, the channel-mapping
pass-through, and thesilk_layer ⇔ silk_bandwidth.is_some() ⇔ silk_frames_per_channel.is_some()invariants across the entire
Table 2 grid. -
Clean-room round 20 (2026-05-26): first CELT-layer fragment —
the RFC 6716 §4.3 / Table 56 pre-band header symbols behind a new
celt_headermodule (CeltHeaderPrefix/CeltPostFilter).silence—dec_icdfagainst the 2-entry{32767, 1}/32768
iCDF[1, 0]at ftb=15. When the flag fires the rest of the
CELT prefix is force-defaulted per the §4.3 shortcut (no
post-filter, no transient, no intra).- §4.3.7.1 pitch post-filter parameter group: one
dec_bit_logp(1)
enable bit, then on the enabled branchoctavevia
dec_uint(6)(uniform on0..=5), the4 + octaveraw-bit
fine_pitchfield throughdec_bits, the §4.3.7.1 pitch-period
reconstructionT = (16 << octave) + fine_pitch - 1(bounded
15..=1022), the 3-bitgain_indexraw field whose downstream
gain isG = 3 * (gain_index + 1) / 32, and the §4.3.7.1
tapset{2, 1, 1}/4iCDF[2, 1, 0]at ftb=2. - §4.3.1
transientand §4.3.2.1intraflags via
dec_bit_logp(3)(PDF{7, 1}/8). - This is the only Table-56 segment that fits between the SILK
pipeline already wired up and the §4.3.2.1 coarse-energy
(Laplace decoder +e_prob_modeltable, gated on a docs gap)
/ §4.3.3 bit allocation (cache_caps50+LOG2_FRAC_TABLE,
also gated on a docs gap) sub-pieces; the per-bandtf_change
symbols (§4.3.1) live in the band loop and are decoded after
coarse energyper Table 56, so they're deferred as well. - Ten new unit tests cover the iCDF transcription self-checks
(silence PDF sums to 32768, tapset PDF sums to 4), the pitch
period formula at the global minimum (15), maximum (1022), and
per-octave boundaries, an all-zero buffer (most-likely symbol
on every branch ⇒ no silence / no post-filter / no transient /
no intra), an all-ones buffer (every produced field stays in
its declared range), atell()-advance proof, a 256-buffer
fuzz-style range sweep over the post-filter fields, and the
silence-shortcut post-condition.
-
Clean-room round 19 (2026-05-26): RFC 6716 §4.2.9 SILK resampler
delay budget and the internal-vs-output sample-rate accounting
behind a newsilk_resamplermodule
(silk_resampler_delay_ms/silk_resampler_delay_samples_at/
silk_internal_rate_hz/silk_frame_samples_internal/
silk_frame_samples_at_output/is_supported_output_rate/
SUPPORTED_OUTPUT_RATES_HZ/REFERENCE_RATE_HZplus the
SILK_RESAMPLER_DELAY_MS_{NB,MB,WB}constants).- Table 54 normative delay allocation: NB = 0.538 ms, MB = 0.692 ms,
WB = 0.706 ms. The §4.2.9 resampler itself is non-normative ("a
decoder can use any method it wants to perform the resampling"),
but the delay budget is normative so the encoder can apply a
matching pre-delay and keep SILK/CELT aligned across a §4.5 mode
switch. SWB and FB never reach the §4.2.9 SILK stage and return
None. - Internal SILK sample rates per bandwidth (NB = 8 kHz, MB = 12 kHz,
WB = 16 kHz) and per-frame sample-count accounting at both the
internal rate and any output rate. NB 20 ms = 160 internal samples
or 960 output samples at 48 kHz; MB 20 ms = 240 / 960; WB 20 ms =
320 / 960. - The five §4.2.9 supported output rates (8 / 12 / 16 / 24 / 48 kHz),
the rates "the reference implementation is able to resample to …
within or near this delay constraint". - Delay-samples helper rounds half away from zero per the §4.2.9
caveat that exact whole-sample delays may be unachievable at
arbitrary output rates.
18 new module tests (339 lib tests total, up from 321): Table 54
transcription + SWB/FB exclusion + strict NB < MB < WB monotonicity;
Table 54 expansion to 48 kHz samples (26 / 33 / 34) and the
internal-rate / 24 kHz intermediate-rate expansions; SWB / FB /
zero-rate rejections; the §4.2.9 supported-output-rate set plus a
sweep of unsupported rates; the SILK internal rate per bandwidth
and its membership in the supported-output set; canonical per-frame
sample counts at internal + output rates plus rejection of
non-SILK durations; and a cross-check that the Table 54 delay is
strictly less than one 10 ms SILK frame at every supported output
rate × every SILK bandwidth. - Table 54 normative delay allocation: NB = 0.538 ms, MB = 0.692 ms,
-
Clean-room round 18 (2026-05-26): RFC 6716 §4.2.3 SILK
packet-level header bits and §4.2.4 per-frame LBRR flags behind a
newSilkHeaderBits/SilkChannelHeader/PerFrameLbrr/
silk_frame_countAPI (silk_headermodule). The decoder reads
the §4.2.2 Figures 15/16 prefix:- Per channel (mono: 1; stereo: 2):
Nuniform-binary VAD bits
plus one global LBRR flag viaRangeDecoder::dec_bit_logp(1),
whereNis the SILK-frame count from §4.2.2 (1 for 10/20 ms
Opus frames, 2 for 40 ms, 3 for 60 ms). - For Opus frames > 20 ms with the channel's global LBRR flag set,
one §4.2.4 per-frame LBRR symbol from Table 4
({0, 53, 53, 150}/256for 40 ms or
{0, 41, 20, 29, 41, 15, 28, 82}/256for 60 ms). Both PDFs have
a leading zero entry per §4.1.3.3, so the iCDF tables
(PER_FRAME_LBRR_{40MS,60MS}_ICDF) drop the leading zero and the
helper adds offset 1, producing a 2- or 3-bit LBRR bitmap packed
LSB-to-MSB (biti↔ SILK framei). - For Opus frames ≤ 20 ms the per-frame LBRR bitmap mirrors the
global LBRR flag without consuming any extra bits, per §4.2.4.
Output is a
SilkHeaderBitswith per-channel VAD bitmaps, global
LBRR flags, and a fully-expandedPerFrameLbrrbitmap for the
downstream §4.2.5 / §4.2.6 LBRR + regular SILK loop.14 new module tests (321 lib tests total, up from 307): Table 4
PDF/iCDF transcription self-checks (40 ms + 60 ms, with
strictly-decreasing + terminator-zero invariants);per_frame_lbrr_pdf
dispatch fallback;silk_frame_count§4.2.2 dispatch including the
2.5/5 ms CELT-onlyNonearm; mono 10 ms decode consumes exactly
2 bits; stereo 60 ms decode populates 3-bit bitmaps within range;
rejection ofnum_silk_frames ∉ {1, 2, 3}; the §4.2.3-implied
per-frame mirror on 10 ms with the global flag set (verifying no
extra symbol consumed); the §4.2.4 skip path on 60 ms with both
global flags unset (verifying exactly 8 bits consumed); VAD / LBRR
accessors for present-side and missing-side cases; exhaustive 40 ms
and 60 msdecode_per_frame_lbrrsymbol-range sweeps plus a 60 ms
full-coverage sweep over{1..=7}. - Per channel (mono: 1; stereo: 2):
-
Clean-room round 17 (2026-05-25): RFC 6716 §4.2.8 SILK stereo
unmixing (silk_stereo_MS_to_LR) behind a newstereo_ms_to_lr/
StereoUnmixState/StereoWeightsQ13/StereoFrameAPI
(silk_stereomodule). Converts the decoded mid/sideout[]signals
into left/right:p0 = (mid[i-2] + 2*mid[i-1] + mid[i]) / 4.0is the low-passed,
one-sample-delayed mid term; the unfiltered mid is also delayed one
sample (mid[i-1]).left[i] = clamp(-1.0, (1+w1)*mid[i-1] + side[i-1] + w0*p0, 1.0),
right[i] = clamp(-1.0, (1-w1)*mid[i-1] - side[i-1] - w0*p0, 1.0).- Phase 1 (first
n1= 64 NB / 96 MB / 128 WB samples) interpolates
the §4.2.7.1 Q13 weights from the previous frame's
(prev_w0_Q13, prev_w1_Q13)to the current frame's; phase 2 uses
the current weights. - An uncoded side channel (§4.2.7.2 mid-only flag) is treated as
all-zero. StereoUnmixStatecarries the two trailing mid samples, one
trailing side sample, and the previous-frame weights across the
frame boundary, cleared to zero on a decoder reset per §4.2.8.
9 module tests: the
interp_phase_samplestable, fresh/reset state,
empty/length validation, zero-weight delayed-mono collapse, a
hand-computed constant-weight mid/side reconstruction, phase-1 ramp
endpoints, mid- and side-history carry across frame boundaries, and
output clamping. -
Clean-room round 16 (2026-05-25): RFC 6716 §4.2.7.9.1 SILK LTP
synthesis filter behind a newltp_synthesis_subframe/
ltp_synth_commit_subframe/LtpSynthState/LtpSynthSubframe
API (silk_ltp_synthmodule). Two regimes:- Unvoiced (
signal_type != Voiced):res[i] = e_Q23[i] / 2^23
(a normalised copy of the §4.2.7.8 excitation). - Voiced: 5-tap Q7 LTP convolution
res[i] = e_Q23[i]/2^23 + Σ_{k=0..4} res[i - pitch_lag + 2 - k] * b_Q7[k]/128, where the "prior res[]" values come from rewhitening
the previous subframes' outputs through the current subframe's LPC
coefficients. Two rewhitening regions:- Region A (out[] rewhiten,
(j - pitch_lag - 2) <= i < out_end):
res[i] = 4*LTP_scale_Q14/gain_Q16 * clamp(out[i] - Σ out[i-k-1] * a_Q12[k]/4096, -1, 1). - Region B (lpc[] rewhiten,
out_end <= i < j):
res[i] = 65536/gain_Q16 * (lpc[i] - Σ lpc[i-k-1] * a_Q12[k]/4096).
- Region A (out[] rewhiten,
out_endand the effectiveLTP_scale_Q14follow the §4.2.7.9.1
LSF-interpolation-split branch: third/fourth subframe of a 20 ms
SILK frame withw_Q2 < 4⇒out_end = j - (s-2)*nand
LTP_scale_Q14 = 16384; otherwiseout_end = j - s*nand the
§4.2.7.6.3 decoded scaling factor is used.LtpSynthStatecarries 306 samples ofout[]history (lag_max 288 + d_LPC 16 + 2) and 256 samples oflpc[]history (3 prior WB subframes 240 + d_LPC 16) — the buffer sizes called out in the
§4.2.7.9.1 paragraphs.reset()clears both for the §4.5.2
decoder-reset / uncoded-side-channel-frame paths;
ltp_synth_commit_subframepushes the §4.2.7.9.2 outputs back into
the state for the next subframe's rewhitening.Twenty-one new unit tests (298 lib tests total) cover the
spec-stated buffer-size constants,LtpSynthStated_LPC routing +
zero-init + reset + start_frame + push_subframe ordering, the
unvoiced normalised-excitation identity (NB / Wb sweeps with
Inactive and Unvoiced both routed to the unvoiced path), four
input-validation rejections (mismatched lengths, bandwidth, subframe
index, non-positive pitch lag), the voiced zero-history /
zero-excitation / zero-b identity, the voicedb == 0pass-through
identity, the voicedb_Q7[0]region-A pitch-lookback algebra
(0.5 * 4*LTP_scale_Q14/gain_Q16 * out[j-14]), the voicedb_Q7[2]
region-B (lpc[]) rewhiten algebra, the LSF-interpolation-split
override (effective scale becomes4*16384/65536 = 1.0exactly),
voiced-decode determinism, and a no-panic finite-output sweep across
3 buffers × {NB, MB, WB} × {10 ms, 20 ms} × 4 subframes with state
carried vialtp_synth_commit_subframe. - Unvoiced (
-
Clean-room round 15 (2026-05-25): RFC 6716 §4.2.7.9.2 SILK LPC
synthesis filter behind a newlpc_synthesis_subframe/
lpc_synthesis_frame/LpcSynthStateAPI (silk_lpc_synthmodule).
Given the §4.2.7.9.1 LPC residualres[]for the current subframe, the
§4.2.7.4 Q16 quantization gaingain_Q16[s], and the §4.2.7.5.8
stabilised Q12 short-term predictora_Q12[k], the filter runs:d_LPC-1 gain_Q16[s] __ a_Q12[k] lpc[i] = ----------- * res[i] + \ lpc[i-k-1] * -------- 65536.0 /_ 4096.0 k=0 out[i] = clamp(-1.0, lpc[i], 1.0)Each subframe carries d_LPC unclamped
lpc[i]history samples forward
into the next subframe throughLpcSynthState, which is cleared to
zero on a decoder reset (RFC 6716 §4.5.2) or after an uncoded regular
SILK frame for the channel. The §4.2.7.9 preamble explicitly licenses
a floating-point implementation here ("the remainder of the
reconstruction process for the frame does not need to be bit-exact"),
so the filter accumulates inf32with the spec's left-to-right
formula. The §4.2.7.9.2 wording that "the decoder saves the unclamped
values lpc[i] to feed into the LPC filter for the next subframe, but
saves the clamped values out[i] for rewhitening in voiced frames" is
implemented exactly: state holds unclamped values; the rendered output
is the clamped vector. d_LPC routing follows §4.2.7.5: 10 for NB / MB
and 16 for WB; SWB / FB rejected at the SILK layer.Eighteen new unit tests (277 lib tests total, up from 259 at round-14
close) coversubframe_samples(40 / 60 / 80 for NB / MB / WB + SWB /
FB rejection);LpcSynthStated_LPC routing + zero initialisation +
reset; the three input-validation rejections (mismatchedres/
out_clamped/a_q12lengths); the algebraic identities (a_Q12 = 0
giveslpc = gain_Q16/65536 * res; res = 0 with zero history gives
identically zero output regardless of a_Q12 / gain); a hand-pinned
single-tap unity-gain NB filter (impulse response is the constant
1.0); a hand-pinned single-tap half-gain WB filter (impulse response
is the geometric series0.5^(i+1)and the history holds the final 16
unclamped samples to 1e-9 precision); a hand-traced two-tap NB filter
with non-trivialres[][1, 2, 3, 0, ...]yielding the exact
sequence[1.0, 2.5, 4.5, 2.875, 2.5625, ...]plus the per-sample
clamp; the cross-subframe history carry-over (an impulse decays into a
unit-feedback subframe and the next subframe of zero residual still
emits1.0everywhere); the decoder-reset path zeroes history; the
out[]∈[-1.0, 1.0]clamp post-condition under deliberately
over-driven input; the spec wording thatstate.historystores the
unclampedlpc[i]and not the saturatedout[i]; the
lpc_synthesis_framewrapper matches an explicit per-subframe loop
bit-for-bit (state included) and rejects bad input lengths; and a
sweep over {NB, MB, WB} × {10, 20 ms} that asserts no panics, the
correct output length, the clamp post-condition, and the correct
history length. The §4.2.7.9.1 LTP synthesis filter that produces the
voiced-frameres[]is deferred to a later round; this stage can
already be driven directly offe_Q23[i] / 2^23for unvoiced
subframes per the §4.2.7.9.1 wording. -
Clean-room round 14 (2026-05-25): RFC 6716 §4.2.7.7 SILK LCG seed
(silk_lcg_seedmodule) and §4.2.7.8 SILK excitation decoder behind a
newExcitation/ExcitationConfigAPI (silk_excitationmodule).The §4.2.7.7 LCG seed is a single uniform 4-entry symbol from Table
43 ({64, 64, 64, 64}/256) producing a value in0..=3that
initialises the LCG used by §4.2.7.8.6 reconstruction.The §4.2.7.8 excitation runs in six substeps: §4.2.7.8.1 rate level
(one symbol per SILK frame from one of two Table 45 PDFs chosen by
signal type, producing0..=8); §4.2.7.8.2 per-shell-block pulse
count (Table 46 PDFs at the chosen rate level, with the special
value 17 chaining into rate level 9, then to rate level 10 on the
10th occurrence — capping extra LSBs at 10 per block per the
§4.2.7.8.2 spec note); §4.2.7.8.3 recursive pulse-location decoding
(partition halves 16 → 8 → 4 → 2 → 1 with Tables 47/48/49/50 split
PDFs selected by partition size + remaining pulse count); §4.2.7.8.4
per-coefficient LSB decoding (Table 51{136, 120}/256, doubling
the magnitude and adding each bit MSB-first); §4.2.7.8.5 sign
decoding (Table 52, picked by(signal_type, qoff_type, min(pulses_in_block, 6))— 42 PDFs in all); and §4.2.7.8.6
reconstruction withe_Q23[i] = (e_raw << 8) - sign(e_raw)*20 + offset_Q23(Table 53 offsets{25, 60, 25, 60, 8, 25}) plus the
32-bit LCG stepseed = (196314165*seed + 907633515) & 0xFFFFFFFF
whose MSB drives a per-sample sign flip, followed by
seed = (seed + e_raw[i]) & 0xFFFFFFFFfor the next iteration.Table 44 routes
(bandwidth, frame_size)to the shell-block count
(5/8/10/10/15/20 for the six NB/MB/WB × 10ms/20ms cells; SWB/FB
rejected as not paired with the SILK layer). The 10 ms MB special
case decodes 8 shell blocks (128 samples) of which the trailing 8
are discarded by the caller per the §4.2.7.8 preamble.Thirty new unit tests (259 lib tests total, up from 229 at round-13
close) cover the Table 43 transcription + the 0..=3 + 2-bits-per-
symbol invariants; Table 44 (all six cells + SWB/FB rejection); both
Table 45 PDFs; all eleven Table 46 PDFs including the L10 cell-17 =
0 boundary; spot-checks on Tables 47/48/49/50 (1- and ≥7-pulse
cells); Table 51; six Table 52 spot-checks across each
(signal_type, qoff_type)quadrant + the "6 or more" saturation;
all six Table 53 offsets; the LCG recurrence pinned algebraically
for the first two steps from seed=0;Excitation::decoderejections
(invalid LCG seed, SWB/FB bandwidth); per-cell sample count; the
§4.2.7.8 "fits in 24 bits including sign" invariant across three
buffers × all (NB/MB/WB × 10/20 ms) cells; per-block pulse-count ≤
16 and LSB-count ≤ 10 invariants; a hand-pinned reconstruction of
an isolated mag=5 sign=-1 sample producing ±1235; the
zero-magnitude|e_Q23[i]| == offset_Q23identity; cross-pass
reproducibility; LCG-seed divergence; and a no-panic sweep over
three buffers × {NB, MB, WB} × {10, 20 ms} × 3 signal types × 2
qoff types × 4 seeds. The §4.2.7.9 LTP / LPC synthesis filters that
consumee_Q23[]are deferred to a later round. -
Clean-room round 13 (2026-05-24): RFC 6716 §4.2.7.6 SILK Long-Term
Prediction parameters behind a newLtpParameters/LtpConfigAPI
(silk_ltpmodule). Decodes the §4.2.7.6.1 primary pitch lag (either
absolute, via Table 29 high-part + Table 30 bandwidth-conditioned
low-part / scale / lag-range, or relative, via the Table 31 21-entry
delta PDF with a zero-delta fallback to absolute coding), the
pitch-contour VQ index (Table 32 PDF; Tables 33–36 codebooks) that
refines the primary lag into per-subframe pitch lags clamped to the
bandwidth's[lag_min, lag_max], the §4.2.7.6.2 periodicity index
(Table 37) and per-subframe 5-tap Q7 LTP filter taps (Table 38 PDFs;
Tables 39–41 codebooks of sizes 8 / 16 / 32), and the optional
§4.2.7.6.3 Q14 LTP scaling factor (Table 42 →{15565, 12288, 8192};
default15565≈ 0.95 when not coded or for non-voiced frames).
Non-voiced frames consume no LTP bits. The §4.2.7.9 LTP synthesis
filter that consumes these parameters is deferred to a later round.Nineteen new unit tests (229 lib tests total in the crate, up from
210 at round-12 close) cover the eleven PDF → iCDF transcriptions
(Tables 29 / 30 NB-MB-WB / 31 / 32 four PDFs / 37 / 38 three PDFs /
42), Table 30 scale + lag-range values, contour codebook
size-matches-PDF self-checks + index-0 all-zero rows + four
interior-row spot-checks against the spec, LTP filter codebook sizes
(8 / 16 / 32) + four boundary-row spot-checks against Tables 39–41,
the non-voiced no-bits-consumed property (both Inactive and Unvoiced),
rejection of non-2-non-4num_subframesand SWB / FB bandwidths,
in-range absolute-coding lags + production / independent formula
agreement across {NB, MB, WB} × {2, 4} subframes, relative-coding
non-zero-delta + zero-delta-fallback paths, LTP-scaling-present output
∈{15565, 12288, 8192}and absent-uses-default-without-reading bit
positioning, and a sweep over {NB, MB, WB} × {2, 4} × {absent,
present} × {Absolute, Relative} × three buffers asserting no panics,
the[lag_min, lag_max]post-condition, and periodicity ≤ 2. -
Clean-room round 12 (2026-05-24): RFC 6716 §4.2.7.5.8 SILK LPC
prediction-gain limiting behind a newLpcQ17::prediction_gain_limited
method returning a newLpcQ12type (silk_lsf_to_lpcmodule).
Consumes the (range-limited) §4.2.7.5.7a32_Q17[]and produces the
final stable Q12 filtera_Q12[k]for the §4.2.7.9.2 LPC synthesis.- Up to 16 rounds of stability-driven bandwidth expansion. Each
round converts to the real Q12 coefficients
a32_Q12[n] = (a32_Q17[n] + 16) >> 5and runs the
silk_LPC_inverse_pred_gain_QA()stability test. If the filter is
stable the Q12 coefficients are returned; otherwise a chirp round with
sc_Q16[0] = 65536 - (2<<i)is applied via the same
silk_bwexpander_32as §4.2.7.5.7. On round 15sc_Q16[0] = 0,
zeroing every coefficient so an all-zero (trivially stable) filter is
the worst-case outcome. silk_LPC_inverse_pred_gain_QA()stability test (is_lpc_stable).
The DC-response check (DC_resp = Σ a32_Q12[n] > 4096⇒ unstable)
followed by the fixed-point Levinson recurrence on the Q24-widened
coefficients: initializeinv_gain_Q30[d_LPC] = 1<<30and
a32_Q24[d_LPC-1][n] = a32_Q12[n] << 12, then for eachkfrom
d_LPC-1down to0reject onabs(a32_Q24[k][k]) > 16773022
(≈ 0.99975 in Q24) orinv_gain_Q30[k] < 107374(≈ 1/10000 in Q30)
viarc_Q31 = -a32_Q24[k][k] << 7,
div_Q30 = (1<<30) - (rc_Q31*rc_Q31 >> 32),
inv_gain_Q30[k] = (inv_gain_Q30[k+1]*div_Q30 >> 32) << 2. Each
surviving step (fork > 0) computes rowk-1via the spec's
b1 = ilog(div_Q30),inv_Qb2,err_Q29,gain_Qb1,num_Q24[n],
a32_Q24[k-1][n]formulas. Every multiply the spec marks as needing
more than 32 bits is performed ini64.
LpcQ12exposesa_q12(),len(),is_empty(), androunds()(the
number of chirp rounds that ran before the filter was deemed stable).9 new unit tests (210 lib tests total in the crate; up from 201 in the
round-11 close) covering:is_lpc_stableagrees with an independent 2D-matrix spec
transcription oracle on hand-built filters (all-zero, gentle decay,
near-unit single tap at the DC=4096 boundary, DC over the ceiling,
mixed-sign moderate).- The all-zero filter is stable for both NB/MB and WB widths.
- DC response
> 4096is rejected before the Levinson recurrence; the
DC=4096 boundary is not rejected by the DC check alone. - A real §4.2.7.5.7 → §4.2.7.5.8 conversion of a typical decoded NLSF
vector returns on round 0 witha_Q12 == (a32_Q17 + 16) >> 5. - Deliberately unstable Q17 inputs (near-unit tap, high-gain resonant
alternating taps, DC over the ceiling) always converge to a stable
Q12 filter within ≤ 16 rounds. - A persistently-unstable input zeroes every coefficient if it reaches
the forced round-15 (sc_Q16[0] = 0) step. - The emitted Q12 filter fits a signed 16-bit value.
- A real §4.2.7.5.2 → … → §4.2.7.5.7 → §4.2.7.5.8 pipeline sweep across
all 32I1values × {NB, MB, WB} on three buffers: the emitted Q12
filter is always stable (cross-checked vs the oracle) and the round
count is ≤ 16. ilog64(the i64 variant used by §4.2.7.5.8) matches the §1.1.10
definition for the spec examples plus the2^30/2^30 - 1
div_Q30-domain boundaries.
- Up to 16 rounds of stability-driven bandwidth expansion. Each
-
Clean-room round 11 (2026-05-24): RFC 6716 §4.2.7.5.7 SILK LPC
range-limiting bandwidth expansion behind a new
LpcQ17::range_limitedmethod (silk_lsf_to_lpcmodule). Consumes the
raw §4.2.7.5.6a32_Q17[]and reduces it so it fits a signed 16-bit
Q12 value:- Up to 10 rounds of
silk_bwexpander_32chirping. Each round
finds the indexkof the largestabs(a32_Q17[k])(ties to the
lowestk), computesmaxabs_Q12 = min((maxabs_Q17 + 16) >> 5, 163838), and stops oncemaxabs_Q12 <= 32767. Otherwise it derives
the chirp factorsc_Q16[0] = 65470 - ((maxabs_Q12 - 32767) << 14) / ((maxabs_Q12 * (k+1)) >> 2)(integer division) and runs the
silk_bwexpander_32recurrence `a32_Q17[k] = (a32_Q17[k]*sc_Q16[k])16
,sc_Q16[k+1] = (sc_Q16[0]*sc_Q16[k] + 32768) >> 16`. The
first multiply runs in i64 ("up to 48 bits of precision"); the second
is performed unsigned per the spec to avoid 32-bit overflow. - Post-loop Q12 saturation. If
maxabs_Q12is still greater than
32767 after the 10th round, each coefficient is saturated in the Q12
domain and converted back to Q17:
a32_Q17[k] = clamp(-32768, (a32_Q17[k] + 16) >> 5, 32767) << 5.
The result is returned in the Q17 domain (the §4.2.7.5.8
prediction-gain limiting that follows consumes Q17 coefficients), so
it shares theLpcQ17representation. The §4.2.7.5.8 stability check
is deferred to a subsequent round.
maxabs_Q17is taken viai32::unsigned_abs()so ani32::MIN
coefficient from an adversarial §4.2.7.5.6 output does not panic.6 new unit tests (201 lib tests total in the crate; up from 195 in the
round-10 close) covering:- Small coefficients already fitting Q12 pass through unchanged.
- Production agrees bit-for-bit with an independent i128 transcription
of the §4.2.7.5.7 loop on synthetic overflow vectors (single peak,
peak at a non-zero index, mixed-sign large coefficients, a moderate
overshoot) and on an extreme input pinned to the 163838 cap. - Every range-limited output fits a signed 16-bit Q12 value.
- The
i32::MINcoefficient no-panic edge. - The post-loop Q12 saturation formula pinned in isolation (the
adaptive chirp converges every realistic input within 10 rounds, so
the engaged branch is effectively unreachable; the formula is pinned
directly to catch a transcription typo). - A real §4.2.7.5.2 → §4.2.7.5.3 → §4.2.7.5.4 → §4.2.7.5.6 →
§4.2.7.5.7 pipeline sweep across all 32I1values × {NB, MB, WB}
asserting the Q12 fit and production/oracle agreement.
- Up to 10 rounds of
-
Clean-room round 10 (2026-05-24): RFC 6716 §4.2.7.5.6 SILK
Normalized LSF → LPC core conversion behind a newLpcQ17API
(silk_lsf_to_lpcmodule). Consumes a stabilized / interpolated
nlsf_q15[](the §4.2.7.5.4 / §4.2.7.5.5 output) and runs the
silk_NLSF2Aprocedure in three steps:- Table 27 ordering + Table 28 cosine table (
silk_NLSF2A_cos).
The 129-entry Q12 cosine table (cos_Q12[0]=4096,cos_Q12[64]=0,
cos_Q12[128]=-4096, anti-symmetric about i=64) is transcribed
verbatim. For each coefficienti = nlsf >> 8,f = nlsf & 255
and the §4.2.7.5.6 piecewise-linear interpolation
`c_Q17[ordering[k]] = (cos_Q12[i]*256 + (cos_Q12[i+1]-cos_Q12[i])*f-
-
3
lands the re-ordered Q17 cosine vector. The Table 27ordering[]vectors are NB/MB[0,9,6,3,4,5,8,1,2,7]and WB[0,15,8,7,4,11,12,3,2,13,10,5,6,9,14,1]`.
-
-
silk_NLSF2A_find_polyP/Q recurrence. Two rolling-row passes
on the even-indexed (P) and odd-indexed (Q)c_Q17[]cells run
p[k][j] = p[k-1][j] + p[k-1][j-2] - ((c*p[k-1][j-1] + 32768)>>16)
in i64 to absorb the spec's noted "up to 48 bits of intermediate
precision" requirement, with the §4.2.7.5.6 boundary conditions
p[k][j<0] = 0andp[k][k+2] = p[k][k].silk_NLSF2Alast-row assembly. The final i64 rows are folded
into the 32-bit Q17 LPC coefficients via the §4.2.7.5.6 sum/diff
pair:a32_Q17[k] = -((q_diff) + (p_sum))and
a32_Q17[d_LPC-k-1] = (q_diff) - (p_sum), where
q_diff = q[d2-1][k+1] - q[d2-1][k]and
p_sum = p[d2-1][k+1] + p[d2-1][k].
The §4.2.7.5.7 range-limiting bandwidth-expansion loop (up to 10
rounds shrinkinga32_Q17[]so it fits Q12) and the §4.2.7.5.8
prediction-gain stability check (up to 16 chirp rounds + the
silk_LPC_inverse_pred_gain_QAtest) are deferred to subsequent
rounds.22 new unit tests (195 lib tests total in the crate; up from 173 in
the round-9 close) covering:- Table 27 row-widths, permutation-of-0..d_LPC self-checks, and
bandwidth routing (ordering()rejects SWB / FB). - Table 28 length (129), the three anchors (0 → 4096; 64 → 0;
128 → -4096), the strict-monotone-decreasing pairwise check, the
anti-symmetric-about-64 invariant, the Q12-range bound, and four
row spot-checks (rows 0, 16, 60, 64, 124). nlsf_to_c_q17at the table anchor points (f == 0round-trip
againstcos_Q12[8*k]) and at the linear-interpolation midpoint
(f == 128matching the16*(a+b)algebraic identity).nlsf_to_c_q17rejects SWB / FB andnlsf_q15.len() != d_LPC.LpcQ17length, SWB / FB and length-mismatch rejection.- Production
LpcQ17::from_nlsfagrees bit-for-bit with an
independent 2D-matrix spec-transcription oracle of the §4.2.7.5.6
recurrence on synthetic ascending NLSF vectors for both NB and WB. - Production
LpcQ17::from_nlsfagrees with the same oracle when
driven by the full §4.2.7.5.2 → §4.2.7.5.3 → §4.2.7.5.4 decoder
pipeline across all 32I1values × {NB, MB, WB}. - A no-panic sweep over three buffers × all 32
I1× {NB, MB, WB}
confirming the full §4.2.7.5.2..§4.2.7.5.6 path is panic-free.
- Table 27 ordering + Table 28 cosine table (
-
Clean-room round 9 (2026-05-24): RFC 6716 §4.2.7.5.5 SILK
Normalized LSF interpolation behind a newLsfInterpolated/
LsfInterpContextAPI (silk_lsf_interpmodule). For a 20 ms SILK
frame the first half (the first two subframes) may use NLSF
coefficients interpolated between the most recent coded frame's
vectorn0_Q15[]and the current §4.2.7.5.4-stabilized vector
n2_Q15[]:TwentyMsdecodes the Q2 factorw_Q2 ∈ 0..=4from the
Table 26 PDF ({13, 22, 29, 11, 181}/256; iCDF
[243, 221, 192, 181, 0]) and computes
n1_Q15[k] = n0_Q15[k] + (w_Q2*(n2_Q15[k] - n0_Q15[k]) >> 2).TwentyMsAfterResetOrUncodedstill decodes the factor (to keep
the range coder in sync) but discards it and uses4instead, so
n1_Q15[] == n2_Q15[]. The same forced-4 behaviour applies when
n0_Q15[]isNone(no prior-frame history).TenMsreads no factor (it is not present in the bitstream)
and produces no first-half vector.
The result exposes the decoded
w_q2()(Nonefor 10 ms) and the
first-halfn1_q15()(Nonefor 10 ms). The second half of a 20 ms
frame and the whole of a 10 ms frame always usen2_Q15[]directly.10 new unit tests (173 lib tests total in the crate; up from 163 in
the round-8 close) covering:- Table 26 PDF→iCDF transcription (sum-to-256 and strict
monotone-decreasing iCDF self-checks; exactly five factors). - 10 ms path reads nothing (
tell()unchanged) and has no
first-half vector. - End-to-end 20 ms interpolation matching an independent
re-derivation of the §4.2.7.5.5 formula. - The
w_Q2 == 0 → n1 == n0andw_Q2 == 4 → n1 == n2algebraic
identities. - The reset/uncoded context decodes the factor then forces
4
(assertingtell()parity with the normal context). - The no-history
n0 = Nonepath forcesn1 == n2even in the
normal context while still decoding the factor. n0-length-mismatch rejection (Error::MalformedPacket).- A sweep asserting every interpolated value stays in
[0, 32767]
across {NB, MB, WB} × all 32I1×w_Q2 ∈ 0..=4.
-
Clean-room round 8 (2026-05-23): RFC 6716 §4.2.7.5.4 SILK
Normalized LSF stabilization behind a newNlsfStabilizedAPI
(silk_lsf_stabilizemodule). Consumes the §4.2.7.5.3
NlsfReconstructedoutput and enforces the Table 25 minimum spacing
between consecutiveNLSF_Q15[]coefficients, with the boundary
conventionsNLSF_Q15[-1] = 0/NLSF_Q15[d_LPC] = 32768and a
Table 25 column ofd_LPC + 1entries.- Up to 20 distortion-minimizing passes. Each pass finds the
smallestNLSF_Q15[i] - NLSF_Q15[i-1] - NDeltaMin_Q15[i]over
i ∈ 0..=d_LPC(ties to loweri); stops if non-negative.
Otherwisei == 0→NLSF_Q15[0] = NDeltaMin_Q15[0],
i == d_LPC→NLSF_Q15[d_LPC-1] = 32768 - NDeltaMin_Q15[d_LPC],
and any interiorire-centres the pair via the
min_center/max_centerrunning sums and
center_freq = clamp(min_center, (NLSF[i-1]+NLSF[i]+1)>>1, max_center), thenNLSF_Q15[i-1] = center_freq - (NDeltaMin_Q15[i]>>1)andNLSF_Q15[i] = NLSF_Q15[i-1] + NDeltaMin_Q15[i]. - Fallback (once after the 20th pass). Sort ascending, then a
forwardmax(NLSF[k], NLSF[k-1] + NDeltaMin[k])sweep and a
backwardmin(NLSF[k], NLSF[k+1] - NDeltaMin[k+1])sweep. - RFC 8251 §7 erratum. The fallback forward sweep's addition
uses 16-bit saturating addition (silk_ADD_SAT16) to avoid the
integer wrap-around the erratum documents on adversarial inputs
with extremely large high-LSF parameters.
Table 25 is transcribed verbatim: NB/MB column
{250, 3, 6, 3, 3, 3, 4, 3, 3, 3, 461}, WB column
{100, 3, 40, 3, 3, 3, 5, 14, 14, 10, 11, 3, 8, 9, 7, 3, 347}.19 new unit tests (163 lib tests total in the crate; up from 144 in
the round-7 close) covering:- Table 25 lengths (
d_LPC + 1for NB/MB and WB) and spot-checks. ndelta_min_q15rejects SWB / FB.add_sat16saturates at bothi16bounds.- An already-stable comfortably-spaced input is left bit-identical
(NB and WB). - First-coefficient-too-low pushed up to
NDeltaMin[0];
last-coefficient-too-high pulled down to32768 - NDeltaMin[d_LPC]. - Interior re-centring with hand-computed exact
NLSF_Q15[i-1]/
NLSF_Q15[i]values for an isolated tight pair. - The fallback sort + sweeps fix a fully-reversed input; all-zero
and all-32767 inputs are spread to valid spacing. - The RFC 8251 §7 no-wrap guard: an all-
i16::MAXinput stays in
[0, 32767](a wrap-around would produce a negative value). - End-to-end sweep across all 32
I1values × {NB, MB, WB} wired
through the §4.2.7.5.2 / §4.2.7.5.3 decoders, asserting the
spacing post-condition, the[0, 32767]bound, and strict
monotonicity of every stabilized vector. from_reconstructedrejects SWB / FB and a bandwidth ↔ recon
length mismatch.
- Up to 20 distortion-minimizing passes. Each pass finds the
-
Clean-room round 7 (2026-05-22): RFC 6716 §4.2.7.5.3 SILK
Normalized LSF reconstruction behind a newNlsfReconstructedAPI
(silk_lsf_reconmodule). Lifts the stage-2 residualres_Q10[]
(returned by round 6'sLsfStage2) to the finalNLSF_Q15[]
coefficient vector in three steps:- Tables 23 / 24 lookup. The 32 × 10 NB/MB and 32 × 16 WB
stage-1 codebook vectorscb1_Q8[]are transcribed verbatim from
the RFC text. The(bandwidth, I1)lookup yields a slice of
d_LPCQ8 cells. - IHMW weights
w_Q9[k]. The low-complexity Inverse Harmonic
Mean Weighting derivation
w2_Q18[k] = (1024/(cb1_Q8[k]-cb1_Q8[k-1]) + 1024/(cb1_Q8[k+1]-cb1_Q8[k])) << 16
(with boundarycb1_Q8[-1] = 0andcb1_Q8[d_LPC] = 256and
integer division) is reduced through the spec's square-root
approximation:i = ilog(w2_Q18[k]),
f = (w2_Q18[k] >> (i-8)) & 127,
y = ((i & 1) ? 32768 : 46214) >> ((32-i) >> 1),
w_Q9[k] = y + ((213 * f * y) >> 16). Every weight across the
full 32 × {NB/MB d_LPC=10, WB d_LPC=16} sweep falls inside the
spec's documented 13-bit[1819, 5227]range. - Final NLSF.
NLSF_Q15[k] = clamp(0, (cb1_Q8[k]<<7) + (res_Q10[k]<<14)/w_Q9[k], 32767),
integer division throughout. The §4.2.7.5.4 stabilization and
§4.2.7.5.5 interpolation passes that consumeNLSF_Q15[]are
deferred to a later round.
26 new unit tests (144 lib tests total in the crate; up from 118 in
the round-6 close) covering:ilog(n)matches the RFC §1.1.10 examples forn ∈ {-1, 0, 1, 2, 3, 4, 7}, plus 8 / 255 / 256 / 2^24.- Tables 23 and 24 rows are strictly monotone increasing (a
pre-condition of the IHMW divisor being positive). - Tables 23 / 24 row widths equal
D_LPC_NB_MB(10) and
D_LPC_WB(16). - Table 23 row 0 (
12 35 60 83 108 132 157 180 206 228),
Table 23 row 31, Table 24 row 0
(7 23 38 54 69 85 100 116 131 147 162 178 193 208 223 239),
Table 24 row 31 spot-checks. cb1_q8()routes Nb / Mb to Table 23 and Wb to Table 24, and
rejectsI1 >= 32and Swb / Fb (SILK never sees the latter
after the §4.2.2 hybrid split).- All 32 × NB IHMW weights and all 32 × WB IHMW weights are in
[1819, 5227](the spec's own documented range for the 13-bit
tabulated form). - Concrete hand-computed IHMW match: NB I1=0 k=0 → 2897; WB I1=0
k=0 → 3657 — both derived from1024/diffinteger arithmetic
against the transcribedcb1_Q8cells. - With
res_Q10[k] == 0, every reconstructedNLSF_Q15[k]equals
cb1_Q8[k] << 7(bounded by242 << 7 = 30976, within the
32767clamp). - Sweep across all 32
I1values × {NB, MB, WB} via a synthetic
range-decoder buffer: every reconstructedNLSF_Q15[k]is in
[0, 32767]and exactly reproduces the §4.2.7.5.3 formula
re-applied to the decodedres_Q10[k]andw_Q9[k]. from_stage1_and_stage2rejects mismatched bandwidth ↔ stage-2
length (e.g. WB-decoded stage-2 with NB reconstruction),
out-of-rangeI1, and Swb / Fb bandwidths.
- Tables 23 / 24 lookup. The 32 × 10 NB/MB and 32 × 16 WB
-
Clean-room round 6 (2026-05-22): RFC 6716 §4.2.7.5.2 Normalized
LSF Stage-2 decoder behind a newLsfStage2API. The caller passes
the SILK-layer bandwidth (Nb/Mb/Wb) and the stage-1 codebook
indexI1 ∈ 0..32(returned by the §4.2.7.5.1 decoder). The decoder:- Reads
d_LPCstage-2 residual indicesI2[k](10 cells for
NB / MB, 16 for WB) using one of 16 signal-shape codebook PDFs
(Tables 15 a..h for NB/MB, Table 16 i..p for WB). The
(bandwidth, I1) → codebookmapping comes from Table 17 (NB/MB)
or Table 18 (WB). Each raw symbol is0..=8; after the-4
subtraction the index is[-4, 4]. If|idx| == 4, the Table 19
extension PDF ({156, 60, 24, 9, 4, 2, 1}/256) supplies an
additional0..=6magnitude with the same sign, giving
I2[k] ∈ [-10, 10]. - Undoes the backwards-prediction step with the §4.2.7.5.2 formula
`res_Q10[k] = (k+1 < d_LPC ? (res_Q10[k+1]*pred_Q8[k])>>8 : 0)- ((((I2[k]<<10) - sign(I2[k])*102) * qstep) >> 16)
runningk = d_LPC-1down to0.qstep = 11796 (Q16)for NB / MB and9830` for WB. The Q8 prediction weight is chosen per-coefficient
from Table 20 lists A / B (NB/MB) or C / D (WB) via Table 21
(NB/MB) or Table 22 (WB).
- ((((I2[k]<<10) - sign(I2[k])*102) * qstep) >> 16)
- Returns
i2[]andres_Q10[]; the §4.2.7.5.3 reconstruction
(stage-1 codebook lookup + IHMW weights + finalNLSF_Q15[k]),
§4.2.7.5.4 stabilization, and §4.2.7.5.5 interpolation are
deferred to round 7+.
The RFC 6716 Table 17 row at
I1 = 6is mislabelledgin the
source PDF; the row's cells (a c c c c c c c c b) are valid
codebook letters and the table is transcribed with the I1 row-label
restored, matching the table's documented intent.30 new unit tests (148 total in the crate) covering:
- All 16 stage-2 PDFs sum to 256 and their transcribed iCDFs are
monotone non-increasing with a trailing zero (Tables 15, 16). - The Table 19 extension PDF sums to 256 and the iCDF cells match
256 - fh[k]. - Tables 17, 18, 21, 22 row widths match
d_LPC(NB/MB) and
d_LPC/d_LPC - 1(WB pred-weight); all entries fall in
0..=7(codebook selection) or0..=1(prediction-weight
selection). - Table 17
I1 = 0(all-a),I1 = 2, and theI1 = 6typo-row
spot-checks; Table 18I1 = 0(all-i),I1 = 6(all-i),
I1 = 9(k j i ...) spot-checks. - Table 20 A[0] = 179, B[0] = 116, A[8] = 163, B[8] = 92,
C[0] = 175, D[0] = 68, C[14] = 182, D[14] = 155 spot-checks;
Table 21 / 22I1 = 0rows. pred_weightresolves the right A/B and C/D list cells per
coefficient against the Table 21 / 22 selection rows.- End-to-end decode for
(Nb, I1=0),(Mb, I1=5),(Wb, I1=0),
(Wb, I1=9)withi2[k] ∈ [-10, 10]for every populated
coefficient. - Independent rejection of
I1 = 32(out of range),Swb, and
Fb(SILK never sees SWB / FB after the §4.2.2 hybrid split). res_Q10[]fromLsfStage2::decodeexactly reproduces the
§4.2.7.5.2 formula re-applied to the decodedi2[]for both
NB/MB and WB.- Sweep across all 32 I1 values × {NB, MB, WB} confirming every
decode succeeds andi2[k] ∈ [-10, 10]for every coefficient. RangeDecoder::tell()is monotone non-decreasing across a
stage-2 decode (the decoder consumes ≥ 1 bit).
- Reads
-
Clean-room round 5 (2026-05-22): RFC 6716 §4.2.7.4 SILK
subframe quantization-gain decoder behind a newSubframeGains/
SubframeGainsConfigAPI. Two coding paths:- Independent (Table 11 signal-type-conditioned 3-bit MSB +
Table 12 uniform 3-bit LSB) joined intogain_index ∈ 0..=63
and clamped with `log_gain = max(gain_index, previous_log_gain- 16)` per §4.2.7.4. The clamp is skipped when the caller
passes no previous gain (decoder reset / side-channel
previously uncoded / packet loss).
- 16)` per §4.2.7.4. The clamp is skipped when the caller
- Delta (Table 13 41-symbol iCDF) folded into the previous gain
vialog_gain = clamp(0, max(2*delta - 16, prev + delta - 4), 63).
The first subframe of a SILK frame uses the independent path
iff the §4.2.7.4 enumeration triggers ("first SILK frame of its
type for this channel in the current Opus frame, OR previous
SILK frame of the same type was not coded"); every other
subframe uses the delta path. Output is the integerlog_gain ∈ 0..=63per subframe; the §4.2.7.4 tail-end conversion to
gain_Q16viasilk_log2linis part of the excitation stage
and not wired up here.
20 new unit tests (88 total in the crate) covering PDF→iCDF
transcription self-checks (Tables 11 / 12 / 13 each sum to
256), all fourSignalType→ iCDF routings, the §4.2.7.4
clamp behaviour across the four prev-value regimes (None, low,
high, sub-16-saturate-to-zero), the delta path's dual-max +
clamp formula reproduced against an independent range-decoder
pass, end-to-end decode for mono-inactive 4-subframe,
mono-unvoiced 2-subframe-with-prev, mono-voiced 4-subframe with
high prev (asserting the floor clamp), the rejection of a
pathological "first-subframe-delta without prev" config and
num_subframes ∉ {2, 4}, and a four-subframe chain-consistency
check that re-derives the gain chain from the raw PDF reads.
- Independent (Table 11 signal-type-conditioned 3-bit MSB +
-
Clean-room round 4 (2026-05-21): RFC 6716 §4.2.7.1 through
§4.2.7.5.1 SILK frame-header decoder behind a newSilkFrameHeader
type. The caller passes aSilkFrameHeaderConfigdescribing whether
the current SILK frame is mid- or side-channel of a stereo Opus
frame, whether the side channel is otherwise required (driving the
presence of the mid-only flag), the frame kind (regular-inactive /
regular-active / LBRR), and the SILK-layer audio bandwidth (NB / MB
/ WB).SilkFrameHeader::decodereturns:stereo_pred: Option<StereoPredictionWeights>per §4.2.7.1 with
the three sub-symbols (Table 6 stage-1 25-cell, two stage-2
3-cell, two stage-3 5-cell) composed into(w0_Q13, w1_Q13)per
the spec formula and Table 7 weight table.mid_only_flag: Option<bool>per §4.2.7.2 (Table 8 PDF
{192, 64}/256).frame_type: u8in0..=5per §4.2.7.3 (Table 9 inactive /
active PDFs; active rows use a 4-entry tail-truncated iCDF with
a +2 caller offset, since the §4.1.3.3 primitive cannot model
leading-zero-mass cells).signal_type: SignalType,qoff_type: QuantizationOffsetType
decoded fromframe_typevia Table 10.lsf_stage1: u8in0..32per §4.2.7.5.1 with the PDF chosen
from Table 14 by(bandwidth, signal_type).
17 new unit tests (68 total in the crate) covering PDF→iCDF
transcription self-checks (Tables 6 / 8 / 9 / 14 each sum to 256
with monotone-decreasing iCDFs), the Table 7 weight-table symmetry
(w[15-k] == -w[k]), the Table 10 frame-type →(signal, qoff)
mapping, end-to-end decode against the range coder for mono
inactive / mono active / stereo mid-channel / stereo side-channel
/ LBRR configurations, and a random-buffer sweep of
decode_stereo_predto confirmwi0/wi1 ≤ 14clamping keeps the
Table 7 lookup in-bounds.
-
Clean-room round 3 (2026-05-21): RFC 6716 §4.1 range decoder
behind a newRangeDecoderAPI — the shared entropy primitive
consumed by every SILK and CELT symbol. Implements §4.1.1
initialization, §4.1.2 generic symbol decode, §4.1.2.1
renormalization (with §4.1.4 zero-extension past EOF), §4.1.3.1
decode_binfor power-of-twoft, §4.1.3.2dec_bit_logpfor
2^-logpbinaries, §4.1.3.3dec_icdffor inverse-CDF tables,
§4.1.4dec_bitsLSB-first raw bits from the END of the frame,
§4.1.5dec_uint(both small-ftb range-only and large-ftb
range-plus-raw branches, with the corrupt-frame sticky error
latch), §4.1.6.1tell(), and §4.1.6.2tell_frac()(with the
tell() == ceil(tell_frac() / 8.0)identity holding across mixed
operations). The siblingoxideav-celtcrate carries an
independent clean-room copy of the same primitive — both own
their own copy until a shared low-level primitives crate exists.
19 new unit tests (51 total in the crate). -
Clean-room round 2 (2026-05-21): RFC 6716 §3.2 frame-packing
parser behind a newOpusPacket::parseentry point covering all
fourccodes:- Code 0 (§3.2.2) — one frame, remaining
N - 1bytes. - Code 1 (§3.2.3) — two equal-size frames; R3 odd-payload rejection.
- Code 2 (§3.2.4) — one- or two-byte §3.2.1 length sequence then
N1+ remainder; R4 length-exceeds-remaining rejection. - Code 3 (§3.2.5) —
M ∈ 1..=48(R5) frame-count byte with the
v|p|Mbit layout; optional Opus padding with the §3.2.5
255-byte-extension chain; CBR with R6R % M == 0check; VBR
withM - 1declared lengths and implicit final-frame size,
R7 length-overrun rejection. - §3.2.1 length helper:
0(DTX),1..=251single-byte,
252..=255two-byte(second * 4 + first)up to 1275 (R2).
Frame slices borrow from the input buffer viaOpusPacket::frames() -> &[&[u8]]; padding length is exposed separately. Adds
Error::MalformedPacketfor §3.2 requirement violations. 27 new
unit tests (32 total in the crate).
- Code 0 (§3.2.2) — one frame, remaining
-
Clean-room round 1 (2026-05-20): RFC 6716 §3.1 packet TOC byte
parser.OpusTocByte::parse/OpusTocByte::from_bytedecode the
five-bitconfigagainst Table 2 (32 mode × bandwidth × frame-size
tuples), thesstereo bit against the Table 3 prose, and thec
frame-count code against the Table 4 prose (one frame /
two-equal / two-unequal / arbitrary).frame_count_range()gives
the implied(min, max)frame count without consulting further
bytes (code 3 reports the legal(1, 48)range derived from
§3.2.5's "no more than 120 ms total"). Five unit tests sweep the
full enumeration plus the R1 empty-packet rejection.
Changed
-
Orphan rebuild (2026-05-20). The crate was reset to a clean-room
scaffold. The prior implementation contained module-level docstrings
and inline comments whose provenance could not be defended against
the workspace clean-room rule. Orphan-master rebuild per workspace
policy; nooldbranch retained. License also reset to clean MIT.Higher-level decode / encode paths still return
Error::NotImplemented. A clean-room re-implementation of the
SILK / CELT inner decoders, the §3.2 frame-packing layer, the §4
range coder, and the §5 encoder pipeline against RFC 6716 +
RFC 8251 + RFC 7587 + RFC 7845 is planned for subsequent rounds.