# WAI Extension: Opus Deep Redundancy, Decoded Exactly > Status: Draft. Registers two companion-only capabilities for the deep redundancy (DRED) of > draft-ietf-mlcodec-opus-dred-07: a compact neural latent of the recent speech, carried in an > Opus packet's padding, from which a receiver rebuilds the acoustic features of packets that > were lost. `wai.audio.opus.dred` is that decoding as the draft defines it, held to the draft's > own conformance test (its §4.1), which admits any decoder whose features lie within its > thresholds of the reference. `wai.audio.opus.dred.int` is the same decoding through an > **integer profile**, `dred-int/1`: the draft's own decoder network and its own weights file, > pinned by digest, lowered to `wai-ops/1` graphs ([integer-operators](integer-operators.md)) > whose output is the same integers on every machine and on every fabric whose operator > implementation conforms. With it, a stream under loss has one reconstruction, named by a > digest that a verifier recomputes from the packets, the loss and the weights, and a player can > sign that digest in the log of [synthesis](synthesis.md) §10.3. This extension also says where > the working group's other documents stand in WAI's registry and in synthesis's lattice (§2.3, > §7.1), and measures the profile against the floating-point network (§6). No new transport. > The reference implementation is the `dred_exact` module of `wai-rs` (feature `dred_exact`); > the corpus is [`live-conformance/dred/`](../live-conformance/dred/), and its second > implementation is `live-conformance/dred_verify.py`. The integer profile as a proposal to the > working group is drafted in [`proposals/dred-integer-profile.md`](../proposals/dred-integer-profile.md), > which is not filed. > Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174. > > Texts read for this revision, each pinned to the version named: draft-ietf-mlcodec-opus-dred-07 > (4 August 2026), with the files its §7 references by SHA-256 ([DRED-Weights], > [DRED-Test-Vectors], [DRED-Compare], [DRED-Ref-Impl]); draft-ietf-mlcodec-opus-extension-06; > draft-ietf-mlcodec-opus-speech-coding-enhancement-04; draft-ietf-mlcodec-opus-scalable-quality-extension-02; > draft-ietf-mlcodec-test-battery-00; RFC 6716 §3, §4.1 and §5.1; RFC 7587. ## 1. Scope The draft's decoder has two parts (its §4): a **feature decoder**, a recurrent network that turns the latents into 20 acoustic features per 10 ms, which the draft makes normative and tests within tolerances; and a **vocoder** that turns features into samples, which the draft leaves to the implementation (its §2.2.4, §4.2). This extension's profile is of the first part. **Conformance targets.** - A **sink** that advertises `wai.audio.opus.dred.int` finds DRED in a packet (§3), decodes it through `dred-int/1` (§4), and gives exactly the features §4 gives. - A **verifier** of a reconstruction recomputes a stream's reconstruction from its packets, its loss pattern and the weights, and checks a record of it (§5) and a player's statement of it (§7.2). **Out of scope.** - The vocoder. A sink renders features to samples with any vocoder the draft admits; those samples are its own, and §7.2 says what a player states of them. - The decoding of an Opus packet's own frames (RFC 6716 §4), and concealment of a frame no DRED rebuilds. §7.1 classes both. - Encoding. The draft's encoder is not normative (its §2.2.1); the corpus's packets are written by a generator that quantizes as §9 describes. ## 2. Registration ### 2.1 Two strings, one payload | string | kind | role | replicate class | parameter set | |---|---|---|---|---| | `wai.audio.opus.dred` | companion | enhancement layer over `wai.audio.opus.packet` | `StandardTolerance`: features within the draft's §4.1 thresholds of the reference | the draft's, fixed by the draft | | `wai.audio.opus.dred.int` | companion | enhancement layer over `wai.audio.opus.packet` | `NeuralIntegerExact`: the features of §4, exactly | one file, the draft's weights file (§4.1) | Both carry the same payload. They differ in what a sink that advertises them promises, which is what a sender chooses between: a sender that needs any rebuilt speech names the first; a sender whose reconstruction is to be recomputed, signed or presented at `decode-equivalence` names the second, with a pin on the weights file (SPEC §3.1) that declares `tier` `decode-equivalence`. Two strings are needed because a capability's class bounds the tier a pin may claim (SPEC §3.1, *Tier*): a decode admitted within tolerances cannot back `replicate`, and the draft's decoding is admitted within tolerances. ### 2.2 Why a companion, not a refinement A refinement-only capability is applied to the output of the stage below it, bound to that output's producer by content hash ([staged-delivery](staged-delivery.md)): its payload corrects what the base decoded. DRED corrects nothing the base decoded: it is a second, coarser encoding of the same speech, sent ahead so that a receiver can rebuild what a loss took, and it is read only where the base's own frames are missing. Its payload has no meaning without the encode it was made with: its offsets index that encode's frames (draft §3), and its first rebuilt samples continue that encode's last decoded ones (draft §2.2.4). That is the model of an **enhancement layer** (SPEC §5): a companion-only capability, decoded by its own standard over one base encode, bound to exactly one selectable rendition of a base it lists, and the base of no second layer. So both strings are registered as enhancement layers. Their one base is `wai.audio.opus.packet`, one Opus packet per object, because there the envelope itself aligns the DRED payload with the packet it belongs to; this revision pins no alignment of a separate DRED stream with an Ogg Opus stream (`wai.audio.opus`), as SPEC §5 pins none for a separately carried LCEVC stream. DRED is carried one of two ways, as an enhancement layer is (SPEC §5, *Enhancement layers*): - **Embedded.** The DRED extension rides in the base packet's padding (draft §3), under extension ID 32, or, until the IANA action the draft asks for, under the experimental ID 126 with the two-byte prefix `'D'`, 12 (draft §3, §5). The payload is the base's bytes, under the base's capability; a sink without DRED discards the padding (draft-ietf-mlcodec-opus-extension-06 §2). No DRED component exists. - **Separate.** A `WAI2` companion of the base rendition (SPEC §7.1), `role` `"companion"`, `companion_of` `wai.audio.opus.packet`. Its payload is one DRED extension payload as draft §3 defines it (the bytes after the extension's ID byte and any length, with no experimental prefix), for frame 0 of the base packet in the same envelope (a frame offset of 0, §3.3). ### 2.3 The working group's other documents - **draft-ietf-mlcodec-opus-extension-06** defines the padding's extension format that §3.2 reads. It carries no payload of its own and registers no capability here. - **draft-ietf-mlcodec-opus-speech-coding-enhancement-04** sets requirements for a decoder-side enhancement of the Silk layer's output, and for a blind extension of its bandwidth above 8 kHz. It changes no byte of a packet, so it is no WAI capability: it is a property of the sink's Opus decoder, and a stream decoded with it is still `wai.audio.opus` or `wai.audio.opus.packet`, decoded within RFC 6716 §6's tolerances by a decoder that also passes that draft's objective tests. §7.1 classes what it does to an object. - **draft-ietf-mlcodec-opus-scalable-quality-extension-02** adds refinement bits for the CELT layer, and bands above 20 kHz, in the same padding. It is not a neural decoder, and the refinement rides embedded in the base packets, decoded by the base's decoder; this revision registers no string for it. - **draft-ietf-mlcodec-test-battery-00** describes test material for these extensions. WAI reads it as informative. ### 2.4 Licence class Both strings are classed `patent-pool`, the conservative class REGISTRY.md gives a capability whose royalty status is not established. One IPR disclosure on the individual draft that became draft-ietf-mlcodec-opus-dred, the IETF's disclosure 6063, states an intent to license royalty-free if the technology is included in a standard the IETF adopts; until that happens the status is not established. A later revision MAY move both strings to `royalty-free` once it is. ## 3. Finding DRED and its integers ### 3.1 The packet A sink reads an Opus packet's framing as RFC 6716 §3.2 defines it, and holds it to §3.4's rules R1–R7. A packet that breaks one is not a valid Opus packet: nothing in it is read, and its DRED is refused (`packet_malformed`). From a valid packet it takes the number of frames, each frame's duration in units of 2.5 ms (RFC 6716 §3.1: 4, 8, 16 or 24 for configurations 0–11 by configuration modulo 4; 4 or 8 for 12–15 by configuration modulo 2; 1, 2, 4 or 8 for 16–31 by configuration modulo 4), and the padding of a code-3 packet. ### 3.2 The extensions The padding's extensions are read as draft-ietf-mlcodec-opus-extension-06 §2 defines them, including the frame separator (ID 1) and the repeat mechanism (ID 2, §2.3), in bitstream order, each instance with its frame index. Iteration stops at the first extension whose length, or whose frame index, would read past the padding or the packet's last frame (§2.7); the instances before it stand. The packet's **DRED payload** is the first instance, in that order, that is either: - ID 32 with at least one byte of data: the data; or - ID 126 whose data is longer than two bytes and starts with `'D'` (0x44), 12: the data after those two bytes. Its **frame offset** is its instance's frame index times the frame duration, in units of 2.5 ms. A packet with no such instance carries no DRED. (The reference implementation the draft names in its Appendix B finds DRED this way, under ID 126.) ### 3.3 The payload The payload is decoded with the range decoder of RFC 6716 §4.1, as draft §3 and §3.1 define it. This section fixes the details the draft's prose leaves open, as the draft's reference decoder does; where the two could be read apart, the reference decoder's reading is this profile's. 1. The header: `q0 = uint(16)`, `dQ = uint(8)`, `X = uint(2)`; if `X = 1`, an extended offset `E = uint(256)`, else `E = 0`; an indicator `I = uint(32)`. `uint(n)` is `ec_dec_uint` of RFC 6716 §4.1.5 for `n ≤ 256`, one symbol of `n` equally likely values. `encoded_offset = 32·E + I`. 2. `qmax`: 15, unless `q0 < 14` and `dQ ≠ 0`. Then, with `n = 14 − q0`, a symbol is decoded over a total of `2n` (`ec_decode`): a value `s ≥ n` gives `qmax = q0 + (s − n) + 1` and is consumed as `[s, s + 1)`; a value `s < n` gives 15 and is consumed as `[0, n)`. 3. The **offset**, `dred_offset = 16 − encoded_offset + frame_offset`, in units of 2.5 ms: the newest feature vector the payload rebuilds ends this long after the start of the frame that carries the extension. 4. The **initial state**: 50 coefficients `k = 0 … 49`, at quantizer `q0`, with the state tables (draft Tables 6, 8 and 9). Then **latent vectors** `l = 0, 1, …`, newest first, each of 25 coefficients at quantizer `q_l = min(qmax, q0 + ⌊(D[dQ]·l + 8) / 16⌋)` with `D = [0, 2, 3, 4, 6, 8, 12, 16]`, the slope table of draft §3 in sixteenths, with the latent tables (Tables 2, 4 and 5). Before each latent vector, if `8·len − tell ≤ 7`, where `len` is the payload's length in bytes and `tell` is `ec_tell` (RFC 6716 §4.1.6.1), no more are decoded. 5. **A coefficient** with decay `r` and zero probability `p0` (the tables' entries for its coefficient and quantizer): if `r = 0` or `p0 = 255`, it is 0 and nothing is decoded. Otherwise, with `P = 128·p0` and `S = 32768 − P`, a symbol is decoded by `ec_dec_icdf16` (with `ftb = 15`) against the table `[S, ⌊S/2⌋, 0]`: 0 is a zero coefficient, 1 positive, 2 negative. A non-zero coefficient's magnitude is then `1 + v_1 + v_2 + …`, each `v_j` decoded by `ec_dec_icdf16` against `icdf[0] = max(7, 128·r)`, `icdf[i] = max(7 − i, ⌊icdf[i − 1]·128·r / 2^15⌋)` for `i = 1 … 6`, `icdf[7] = 0`, while the symbol just decoded is 7. When the magnitude being summed passes `2^16`, the payload is refused (`dred_latent_range`): the draft's escape rule bounds nothing, and past a payload's last byte the decoder reads zero bytes (RFC 6716 §4.1.2), so a crafted payload could otherwise keep a decoder reading. The draft's test bit files carry payloads with no header (each record gives `q0`, with no slope, and a count of latents); §6 reads them so. ## 4. The integer profile `dred-int/1` ### 4.1 The parameter set The parameter set of `wai.audio.opus.dred.int` is **one file**: the decoder weights file draft §2.2.2.1 and §7.1 name ([DRED-Weights]), whose SHA-256 is `394f0ecf19926cebd678019dc94a54c88ae078340c16325069707f286cdf28a6`. A sink MUST check the file against that digest before it reads it, and MUST refuse any other file (`weights_digest_mismatch`): the draft makes another set of weights non-conforming (its §4.1, "an alternate DNN model is not allowed"), and §4.4 makes these weights' integers a function of this file's bytes. A pin over the set names that digest. The file is read as the draft's Appendix A defines it. The profile reads, for each layer of the table below, the arrays `dec__weights_float` and `dec__bias`, and for a block-sparse layer `dec__weights_idx`; it reads no other array. A dense matrix is stored input-major (`weights[in·outputs + out]`, A.3.1). A block-sparse one (A.3.2) is a run of groups of 8 outputs, each a count of blocks and then each block's first input (a multiple of 4), and its weights are blocks of 8 outputs by 4 inputs stored input-major inside the block (`w[in·8 + out]`); every position no block covers is 0. | layer | inputs | outputs | sparse | |---|---|---|---| | `dense1` | 26 | 96 | no | | `hidden_init` | 50 | 128 | no | | `gru_init` | 128 | 320 | yes | | `gru1_input` … `gru5_input` | 96, 192, 288, 384, 480 | 192 | yes | | `gru1_recurrent` … `gru5_recurrent` | 64 | 192 | no | | `glu1` … `glu5` | 64 | 64 | no | | `conv_dense1` … `conv_dense5` | 160, 256, 352, 448, 544 | 32 | yes | | `conv1` … `conv5` | 64 | 32 | no | | `output` | 576 | 80 | yes | The weights are not in this repository. The corpus names the file by digest, and its fetch script (`live-conformance/dred/fetch.sh`) fetches it from the location the draft gives and checks the digest. ### 4.2 The tables The decoder reads draft Tables 2, 4, 5, 6, 8 and 9 (scale, decay and P(0), for the latents and for the state). Serialized row by row (coefficient `k` from 0, the sixteen quantizers in order, one byte each), in the order latent scale, latent decay, latent P(0), state scale, state decay, state P(0), their SHA-256 is `03f7d6b22e2aca5f5a28525a1311e1113d94b917fbc74a5493b04bbed5c97ded`. An implementation that transcribes them checks its transcription against this digest. ### 4.3 The inputs Every graph input is an integer at `2^16` (Q16). A coefficient `x` of the initial state, with state scale `s` at `q0`, and a coefficient of latent `l`, with latent scale `s` at `q_l`, stand for `x·256 / max(1, s)` (draft §3.1), which the input holds as `⌊(2·x·2^24 + s′) / (2·s′)⌋` with `s′ = max(1, s)`: the quotient rounded to the nearest integer, a tie upward. A latent's 26th input is `q_l/8 − 1` (draft §2.2.2), held as `(q_l − 8)·2^13`. ### 4.4 Weights to integers Every weight becomes an integer at `2^24`, and every bias an integer at the scale of the accumulator it joins: `2^40` for `dense1` and `hidden_init`, whose inputs are at `2^16`, and `2^39` for every other layer, whose inputs are activations at `2^15`. Each is computed from the float32 value's bits alone, with no floating-point arithmetic: - a float32 with sign `σ`, exponent field `e` and fraction field `m` is `(−1)^σ·M·2^E`, with `M = m`, `E = −149` when `e = 0`, and `M = m + 2^23`, `E = e − 150` otherwise; - at scale `2^f`, its integer is `(−1)^σ·R_half_even(M, −(E + f))` when `E + f < 0`, and `(−1)^σ·M·2^(E + f)` otherwise, with `R_half_even` of [integer-operators](integer-operators.md) §2 (0 for a shift of 63 or more). A value that is not finite (`e = 255`), a weight whose integer does not fit i32, a missing array, an array of the wrong length, a block that starts at an input that is not a multiple of 4 or past the layer's inputs, a group that names one block twice, or a record the file format refuses, makes the set fail to lower (`model_mismatch`, integer-operators §10.5). The pinned file does none of these. ### 4.5 Activations Activations are integers at `2^15` (Q15), in `[−32767, 32767]`. `tanh` and the sigmoid are `lut16` tables (integer-operators §5.6), read at `2^12` and `2^11`. Their entries are this integer computation, not the functions' exact values: - `C_0 = 4609434768280048980` (`⌊e^(−1/2048)·2^62⌋`), and `C_(k+1) = ⌊C_k² / 2^62⌋`; - for `0 ≤ u < 2^15`, `X(u)` starts at `2^62`, and for each bit `k` of `u` from bit 0 to bit 14 that is set, in that order, `X ← ⌊X·C_k / 2^62⌋`; - `tanh`: entry `v + 32768`, for `v` in `−32767 … 32767`, is `sgn(v)·min(32767, ⌊(2a + b) / (2b)⌋)` with `a = 2^15·(2^62 − X(|v|))` and `b = 2^62 + X(|v|)`; - sigmoid: entry `v + 32768` is `min(32767, ⌊(2a + b) / (2b)⌋)` with `b = 2^62 + X(|v|)`, and `a = 2^77` for `v ≥ 0`, `a = 2^15·X(|v|)` for `v < 0`; - entry 0 (`v = −32768`) of each is 0, as `lut16` requires. No certified input reaches it. Every intermediate fits 128 bits. Serialized as their 65,536 entries in order, each a 16-bit two's-complement little-endian integer, the tables' SHA-256 are `aea887ef206a10ea37bd31ef310a75c2ff05ca17df7edbbc15916969bccb4c22` (`tanh`) and `1fe2d7c6b3255127f612576093f2f50e9e13d88da546ad4c04de264e2bbc8c36` (sigmoid). ### 4.6 The graphs `dred-int/1` lowers the set to two `WHG1` graphs (integer-operators §10), `init` and `step`, in which every tensor is `[C, 1, 1]`. Below, `dense(x; L, s)` is a `conv` with `kh = kw = 1` and `groups = 1` over `x`, with layer `L`'s integer weights (`w_bytes` 4), and an `RQ` with no multiplier, one shift `s`, `L`'s integer bias placed `pre` (`b_bytes` 8) and `round` `half_up`; `T_tanh` and `T_sig` are `lut16` with the tables of §4.5; `C(·)` marks an output tensor whose policy is `clamp [−32767, 32767]` (every table input, and every GRU state); `mul(a, b)` is `mul` with an `RQ` of shift 15, no bias, `half_up`; `[a, b]` is `concat`; every other tensor's policy is `derived`. **`init`.** Input 0 is the initial state `[50]`, policy `clamp [−(2^31 − 1), 2^31 − 1]`. 1. `a = T_tanh(C(dense(IS; hidden_init, 28)))` `[128]`; 2. `s = T_tanh(C(dense(a; gru_init, 27)))` `[320]`; 3. outputs: the five GRU states `h_k = split(s, 64·(k − 1), 64)`, `k = 1 … 5`. **`step`.** Inputs: the latent `Z` `[26]`, policy `clamp [−(2^31 − 1), 2^31 − 1]`; the GRU states `h_1 … h_5` `[64]`; the convolution memories `c_1 … c_5` `[32]`; each of the last ten with policy `clamp [−32767, 32767]`. Then: 1. `t = T_tanh(C(dense(Z; dense1, 28)))` `[96]`. 2. For `k = 1 … 5`, with `x = t`, `h = h_k`, `I = gru_input` and `U = gru_recurrent`: - the **gates**: `g = T_sig(C(conv([x, h])))`, `[128]`, where the `conv` is `dense` with shift 28 whose output `o < 128` has the weights of row `o` of `I` followed by those of row `o` of `U`, and the bias `b_I[o] + b_U[o]` (each converted by §4.4 on its own); `z = split(g, 0, 64)`, `r = split(g, 64, 64)`; - the **candidate**: `p = dense(x; rows 128–191 of I, 19) + mul(r, dense(h; rows 128–191 of U, 19))` (`add`), at `2^20`; then `hc = T_tanh(C(rescale(p)))`, the `rescale` with one shift 8, no bias, `half_up`; - the **state**: `h′ = C(hc + mul(z, h + rescale⁻(hc)))`, where `rescale⁻` is `rescale` with one multiplier −1 (`m_bytes` 4), shift 0, no bias, `floor`, and each `+` is `add`. This is draft §2.2.2's `z·h + (1 − z)·hc`, written so that each product has one rounding; with `0 ≤ z < 2^15` its value lies between `hc` and `h`, so the clamp changes nothing; - the **GLU**: `u = mul(h′, T_sig(C(dense(h′; glu, 28))))`; - `t ← [t, u]`; - the **causal convolution**: `c = T_tanh(C(dense(t; conv_dense, 27)))` `[32]` and `v = T_tanh(C(dense([c_k, c]; conv, 27)))` `[32]`; - `t ← [t, v]`; the new `h_k` is `h′` and the new `c_k` is `c`. 3. `x = dense(t; output, 23)` `[80]`, at `2^16`, policy `clamp [−2^31, 2^31 − 1]`. Outputs, in order: `x`, the five new GRU states, the five new memories. Tensors are numbered as integer-operators §13.0 numbers a lowered graph (inputs first, then each operator's output in operator order), every input declares `max_h = max_w = 1`, every exponent is none, and the operators are written in the order above (for the GRU: `concat`, the gates' `conv`, `lut16`, the two `split`s, the candidate's two `conv`s, `mul`, `add`, `rescale`, `lut16`, then `rescale⁻`, `add`, `mul`, `add`). For the pinned set, the graphs' identities (the SHA-256 of their `WHG1` bytes) are `6527304e48bbd6c972fb41cb24ab5f387a3adee6f88347dfc8f10f47060e5c9d` (`init`) and `4c76f2e5f6b2657b985aa171f7872fc18ae48b9419447e69cb3e52a3225b34d2` (`step`). ### 4.7 The driver A payload is decoded as a **sequence** of graph runs: 1. `init` on the state input (§4.3); its outputs are the GRU states. The memories are 0. 2. For latent `l = 0, 1, …`, newest first: `step` on the latent's input (§4.3), the GRU states and the memories; its first output `x` gives four feature vectors, and the others are the states and memories of the next latent. The **rows** of the payload are those vectors, newest first: row `4l + j` is `x[20j], …, x[20j + 19]` of latent `l` (draft §2.2.2: the four vectors of a latent are in reverse chronological order), and each row is 10 ms older than the row before it. Row 0 ends at the payload's offset (§3.3 step 3). Each value is the feature at `2^16`, an i32. Decoding the first `n` latents gives the first `4n` rows, the same integers whatever follows: the network runs newest first, so a row never depends on an older latent. A sink MAY stop after the rows it needs. ### 4.8 Profile and fabrics Every operator of both graphs is in `wai-ops/1-i32` (integer-operators §9.1), and both graphs' certificates meet `acc64` and not `acc32`: a 16-bit activation times a 32-bit weight, summed over up to 576 inputs, passes 32 bits. The registry's `ops` member of `wai.audio.opus.dred.int` is `{"set": "wai-ops/1", "profile": "acc64", "subset": "full", "lowering": "dred-int/1"}`; the payload's range decoder is RFC 6716's and not a stage operator of integer-operators §5E, so the member has no `stage`. On a GPU, the reference implementation's WGSL kernels (`wai-ops-gpu`) run both graphs at `acc64`, and `wai-rs/tests/dred_exact_gpu.rs` holds their outputs to the CPU's graph by graph. ## 5. A stream under loss ### 5.1 The loss pattern A loss pattern is a seed of 32 bytes and two thresholds, `enter` and `stay`, each an integer in `0 … 2^16`. Packet `n` (from 0) draws `u_n`, the first two bytes, big-endian, of SHA-256(`"wai:dred-loss\x01"` ‖ seed ‖ `n` as a big-endian u32). It is lost when `u_n < enter` and the packet before it was received (or `n = 0`), and when `u_n < stay` and the packet before it was lost. ### 5.2 Slots A stream is a run of packets of one frame each, all of duration `d` (in units of 2.5 ms), so packet `n` starts at `n·d`. Each lost packet's frame is two 10 ms **slots**, half 0 (the earlier) and half 1, and half `h` of frame `m` ends at `E = m·d + 4·(h + 1)`. A maximal run of lost packets is **recovered** by the packet after it, when that packet was received and carries DRED whose payload is not refused (§3); then, with `A = n·d + dred_offset` the end of its row 0 (`n` the recovering packet), half `h` of frame `m` takes row `r = ⌊(A − E) / 4⌋` of it when `A − E ≥ 0` and `r` is less than four times the payload's latent count. Otherwise the slot is **not covered**: no DRED rebuilds it. ### 5.3 The reconstruction buffer The reconstruction of a stream is its slots in order (frame ascending, then half), as bytes: for each, the frame (u32), the half (u8) and 1 if it is covered or 0 if not (u8); for a covered one then the recovering packet (u32), the row (u32) and the row's 20 features (i32 each); every integer little-endian. Its digest is SHA-256 over those bytes. The buffer of one run is the same over that run's slots alone. ### 5.4 The record A record of a reconstruction states: - `weights_sha256`: the weights file the decode used; - `lost`: the indices of the packets the stream lost, ascending; - `slots`: each slot as `[frame, half, packet, row]`, or `[frame, half, null, null]` for one not covered; - `output`: the reconstruction's digest, hexadecimal. ### 5.5 Verification A verifier is given the stream (each received packet as the object a live track carries it in: a `WAI1` envelope of `wai.audio.opus.packet`, with nothing where a packet was lost), its loss pattern, the record, and the weights file. It checks, in order, and refuses at the first check that fails: 1. the record's `weights_sha256` is the digest of §4.1 (`weights_digest_mismatch`); 2. the packets the loss pattern loses are the record's `lost`, and are exactly the packets the stream lacks (`loss_pattern_mismatch`); 3. the slots §5.2 gives are the record's `slots` (`reconstruction_mismatch`); 4. the digest of the reconstruction §4 gives is the record's `output` (`reconstruction_mismatch`). A reconstruction that passes is the one every conforming sink gives: the profile admits no other. ## 6. Measurement What the profile changes is the arithmetic: the same network, the same weights, at fixed point. `wai-rs/tests/dred_exact_measure.rs` measures how far that moves the features, on the draft's own test vectors ([DRED-Test-Vectors]): all eight DRED bit files, 20 records each, every record's latents decoded through the profile, and through the network in floating point (`f64`, from the same float32 weights, with `tanh` and the sigmoid computed exactly to `f64` precision); and every packet of the eight packet files that carries DRED, its DRED decoded both ways. Each pair is compared by the draft's feature comparison (its §4.1 and its tool [DRED-Compare]): per feature, the mean squared difference over the frames; `total` the square root of their sum, `max` of their largest, the pitch feature left out of both; `pitch` the mean absolute pitch difference over the frames the reference voices. The draft's DRED decoding test passes at `total ≤ 0.5`, `max ≤ 0.15` and `pitch ≤ 0.02`. Two figures are added: the largest absolute difference of any feature but pitch, and a band log-spectral distance. The 18 cepstral features are the orthonormal DCT-II of the log10 band energies (draft §2.1.3), so the inverse DCT of their difference is the difference of the log band energies, and ten times its root mean square over the bands is a distance in decibels for each frame; its mean over the frames and its largest are given. The reference features are the test vectors' own. "8-bit emulation" is the floating-point network computing each layer the weights file also holds 8-bit weights for as the reference implementation does when it reads them: each input rounded to `⌊0.5 + 127·x⌋`, the 8-bit weights, each output scaled by its scale. | compared | frames | total | max | pitch | largest difference | band LSD mean (dB) | band LSD max (dB) | |---|---|---|---|---|---|---|---| | integer, reference | 16000 | 0.1314 | 0.0767 | 0.00492 | 0.4139 | 0.2945 | 1.0034 | | float, reference | 16000 | 0.1314 | 0.0767 | 0.00492 | 0.4151 | 0.2944 | 1.0060 | | 8-bit emulation, reference | 16000 | 0.0831 | 0.0458 | 0.00255 | 0.2542 | 0.1844 | 0.6929 | | integer, float | 16000 | 0.0021 | 0.0013 | 0.00011 | 0.0084 | 0.0048 | 0.0210 | | integer, float (packets) | 80452 | 0.0022 | 0.0013 | 0.00004 | 0.0085 | 0.0049 | 0.0208 | What the table shows: - **The profile passes the draft's test** against the draft's reference features, on each of the eight vectors and over all of them, with `total`, `max` and `pitch` well inside the thresholds. - **The profile is close to the network.** Against the floating-point network on the same latents, its features differ by `total` 0.0021, at most 0.0084 in any feature, 0.005 dB of band log-spectral distance on average: about one sixtieth of the distance between the network and the draft's reference features. - **The reference features are not the network's.** The floating-point network and the profile lie the same distance from them, and the 8-bit emulation lies closer, but not at them. So the draft's test vectors carry the arithmetic of the build that wrote them. The last point was also checked once, informatively, on one arm64 host, by building the draft's reference implementation ([DRED-Ref-Impl]) twice: in its default configuration, which reads the 8-bit weights, and with its float weights, which it computes in float32 with a rational approximation of `tanh`. Over the eight vectors, the default build's features lie at `total` 0.090–0.096 from the test vectors and the float build's at 0.131–0.136; the profile and the floating-point network each lie at 0.015–0.018 from the float build, and at 0.129–0.135 from the default build. The two builds differ from each other by about 0.13 (on the three vectors compared directly), and neither gives the test vectors' features. Two builds of the draft's own decoder on one machine, and the build that wrote the test vectors, give three different sets of features from the same latents; under the integer profile there is one. ## 7. Receipts and the synthesis lattice ### 7.1 Classes In [synthesis](synthesis.md)'s lattice (§3.2): - **A reconstruction from DRED is a `convert`.** The features are what the packets carried: a second encoding of the same speech, which a decoder with a pinned parameter set rebuilds (synthesis §3.2: "a decoder's reconstruction (neural or not)"). The vocoder that renders them continues that reconstruction; it generates no content the packets did not describe. So an object rebuilt from DRED keeps its input's class, `reconstructed` from a capture. - **Concealment beyond DRED is a `generate`.** Where no DRED covers a slot (§5.2), a sink that fills it with speech predicted by a model (packet loss concealment, which draft §2 names as what DRED's synthesis may continue from) produces content the packets never carried, by a trained model: that is `generate`, class `synthesized`, with the model in `models`. It is not `assistive`: synthesis's `assist` edits an input without substantially altering it, and a concealed slot has no input to edit. - **Speech coding enhancement is an `assist`.** A decoder-side enhancement of decoded speech (draft-ietf-mlcodec-opus-speech-coding-enhancement-04) edits one input without substantially altering it, and that draft's tests bound how far it may move the decoder's own output (its §3 and §4); a blind bandwidth extension adds content above 8 kHz the packets did not carry, as an upscale adds detail, which synthesis also classes `assist` (§3.4). Neither is a pinned derivation of synthesis §4, so their evidence states `tier` `attest`. ### 7.2 The presentation statement A player that presents a reconstruction under the profile signs it in its log (synthesis §10.3): for each run of lost packets it presents, a `present` entry whose - `subject` is the content hash (jwp-receipts §2.1) of the recovering packet's object; - `tier` is `decode-equivalence`, `decoder` is `wai.audio.opus.dred.int` and `variant` is `dred-int/1`; - `output` is the digest of that run's reconstruction buffer (§5.3), `origin` and `class` as synthesis §10.3 has them (`null` where the player applies no claim). A verifier checks the log as synthesis §10.3 does (`record_malformed`, `bad_key`, `bad_signature`, `player_chain_broken`), then each `present` entry: its decoder, variant and tier are those above, its subject is the content hash of the object that recovers a run of the stream, and its `output` is the digest of that run's buffer that §4 gives. Otherwise the entry states an output the object does not decode to (`present_output_irreproducible`, synthesis §9.2). The features are the statement's subject because they are what the profile fixes; a player that also states the digest of its vocoder's samples states its own output, which no other sink reproduces unless it runs the same vocoder, exactly. ## 8. Refusals The codes this extension adds, each with where it is raised: | reason | raised at | |---|---| | `packet_malformed` | §3.1 | | `dred_latent_range` | §3.3 step 5 | | `weights_digest_mismatch` | §4.1, §5.5 step 1 | | `loss_pattern_mismatch` | §5.5 step 2 | | `reconstruction_mismatch` | §5.5 steps 3 and 4 | A set that fails to lower or load is refused `model_mismatch` (integer-operators §10.5). A verifier of a presentation statement also raises synthesis's codes for a log (§7.2). ## 9. Conformance [`live-conformance/dred/`](../live-conformance/dred/) holds the corpus, written from fixed seeds by `wai_dred_vectors gen` with the weights file of §4.1, which it names and does not hold: `vectors.json`, its README, and `fetch.sh`. It has: - **The profile's identities**: the tables' digest (§4.2), the constant and the two tables' digests (§4.5), and the two graphs' identities (§4.6). - **Payload vectors**, each a packet and what a decoder states of it: for a packet with DRED, its header, frame offset, offset, latent count, the digest of its integers, the digest of every row and row 0 itself; for one without, that it has none; for one refused, the code. The digest of a payload's integers is SHA-256 over `q0`, `dQ`, `X`, `E`, `I` and `qmax`, one byte each; the latent count (u32); the state's 50 indices (i32 each); then, for each latent, its quantizer (one byte) and its 25 indices (i32 each); little-endian. The digest of the rows is SHA-256 over every row in order, each feature an i32, little-endian. They cover the experimental ID with its prefix and ID 32; a slope with a `qmax` below 15 and an extended offset; a DRED extension on a packet's second frame after a frame separator; an experimental extension of another version, a truncated extension and plain padding, which carry none; two malformed packets; and a payload whose magnitude passes the cap. - **Streams**, each a run of packets as `WAI1` objects with the lost ones absent, and a loss pattern, and **cases** over them, each a record, a player's log, and the outcome: the reconstruction as the profile gives it, with a log of its recoveries, for two streams (one with a slope and an offset that leaves the newest frames of every run uncovered); and one refused case for each check of §5.5 and §7.2: a record naming another weights file; a record listing a received packet as lost; a record naming a row one older; a digest with one bit changed; the digest of a floating-point decode of the same latents, rounded to `2^16`; a log presenting that decode at `decode-equivalence`; and a log entry whose signature is broken. Every packet of the corpus is written by its generator: its DRED indices are drawn from the draft's own distributions, by decoding a seeded byte stream with §3.3's coefficient rule at `q0`, and requantized to each chunk's quantizer (`x·s_q / s_q0`, rounded half up, where `q` codes the coefficient). The primary frames are empty (length 0, RFC 6716 §3.2.1). No recorded speech is in the corpus. **Feature equivalence.** For each payload vector and each case, a conforming implementation reaches the outcome the vector states, and for an accepted one, the same digests, rows and statements, byte for byte. **On every fabric.** `wai-rs/tests/dred_exact_gpu.rs` decodes the corpus's payloads with every graph on each GPU adapter found and holds the output to the CPU's. ## 10. Security and privacy considerations - **Bounded work.** A payload's work is bounded by its length and by the magnitude cap (§3.3): without the cap, the escape rule could read without end past the payload's last byte. A sink SHOULD also bound the latents it decodes to the span it needs (§4.7), as the draft lets it (its §3: "decoders MAY ignore any redundancy data beyond a certain amount"). - **No floating point in the decode.** The profile's decode forms no float, so the denormal slow-downs draft §6 warns of do not arise in it. - **Speech before joining.** DRED can carry speech from before a receiver joined (draft §6). A reconstruction record names the packets and slots it rebuilt; a verifier given a room's history learns nothing from a record that its packets did not already carry, but a relay that forwards DRED forwards that speech, and the draft's `ext32-dred-duration` limits apply. - **What a statement proves.** A presentation statement over a reconstruction's digest proves which features the player's decode fixed, from packets the verifier holds. It does not prove that the packets are authentic or that the samples a listener heard are those features rendered; the object's own receipts (jwp-receipts) and the vocoder's exactness are separate matters.