3 — Sections 3.4.3–3.4.6
Intermediate
After bitstream decoding gives us the raw quantized spectrum Xq̂, four more processing steps restore the full spectral representation before the inverse MDCT: residual refinement sharpens non-zero values, noise filling replaces zeros with plausible random noise, global gain recovers the original amplitude scale, and the TNS synthesis filter un-does the TNS analysis filtering done at the encoder.
Keywords
3.4.3 Residual Decoding
Residual decoding applies only when lsbMode = 0. The residual bits read in Section 3.4.2.6 are used to fine-tune each non-zero quantized coefficient by ±0.1875 or ±0.3125 of the quantization step. This sub-step-size refinement improves fidelity without changing the arithmetic coder complexity.
For each non-zero Xq̂(k), one residual bit is consumed:
| resBit | Xq̂(k) > 0 | Xq̂(k) < 0 |
|---|---|---|
| 0 | Subtract 0.1875 (move toward zero) | Subtract 0.3125 (move away from zero) |
| 1 | Add 0.3125 (move away from zero) | Add 0.1875 (move toward zero) |
The asymmetric adjustments (0.1875 vs 0.3125) reflect the encoder’s dead-zone quantizer, which uses a threshold of ±0.375 — the residual bit splits this range asymmetrically to indicate whether the original signal was in the upper or lower half of the quantization interval.
/* Residual decoding (Section 3.4.3) */
k = 0; n = 0;
while (k < NE && n < nResBits) {
if (Xqh[k] != 0) {
if (resBits[n++] == 0) {
Xqh[k] += (Xqh[k] > 0) ? -0.1875f : -0.3125f;
} else {
Xqh[k] += (Xqh[k] > 0) ? 0.3125f : 0.1875f;
}
}
k++;
}
3.4.4 Noise Filling
Noise filling replaces zero-valued spectral coefficients with random noise. It is skipped when zeroFrameFlag = 1 (silence frame). The coefficients targeted are those within the active bandwidth, starting from NFstart, and that are surrounded by other zeros within ±NFwidth bins (i.e., isolated zeros — not gaps at spectral edges).
The noise amplitude is derived from the transmitted noise factor FNF:
A linear congruential PRNG provides the noise sign deterministically using nf_seed derived from the decoded spectrum:
if INF(k) == 1:
nf_seed = (13849 + nf_seed × 31821) & 0xFFFF
Xq̂(k) = (nf_seed < 0x8000) ? +L̂NF : −L̂NF
Both encoder and decoder use the same nf_seed (derived from the same decoded Xq̂), so the noise is deterministic — if you re-decode the same frame you get the same noise pattern.
| Parameter | 10 ms | 7.5 ms |
|---|---|---|
| NFstart | 24 | 18 |
| NFwidth | 3 | 2 |
3.4.5 Global Gain Recovery
The global gain is applied to all NE coefficients to restore their original amplitude scale. The global gain is computed from the decoded ggind and the same ggoff formula used by the encoder:
X̂f(k) = Xq̂(k) × 10^((ggind + ggoff) / 28), for k = 0..NE−1
The ggoff formula is computed from nbits (the current frame’s byte count) — this is why the decoder needs byte_count as an input for every frame, not just at session setup.
3.4.6 TNS Decoder — Synthesis Filter
The TNS encoder applied an all-zero (FIR) analysis filter. The decoder applies the inverse: an all-pole (IIR) synthesis filter. This is implemented as a lattice synthesis filter — the exact reverse of the analysis lattice.
The quantized reflection coefficients are first recovered from the indices:
The synthesis filter (Section 3.4.6 pseudocode):
/* TNS Synthesis Lattice Filter (Section 3.4.6) */
float s[8] = {0}; /* lattice states */
for each active filter f:
if rcorder(f) > 0:
for n = start_freq(f) to stop_freq(f)−1:
t = Xfh(n) − rcq(rcorder(f)−1, f) * s[rcorder(f)−1]
for k = rcorder(f)−2 downto 0:
t = t − rcq(k, f) * s[k]
s[k+1] = rcq(k, f) * t + s[k]
Xsh(n) = t
s[0] = t
Notice the key difference from the analysis filter: the order of operations is reversed (from high k downward) and the sign of the rc contribution is subtracted (not added). The output X̂s(k) is the SNS-shaped spectrum ready for the SNS decoder.
Next in this Series
Section 3.4.7 — SNS Decoder: scale factor decoding, MPVQ de-enumeration, interpolation, and spectral shaping
