LC3 Encoding: Temporal Noise Shaping (TNS)

 

LC3 Encoding: Temporal Noise Shaping (TNS)
Chapter 3, Section 3.3.8 — TNS Overview, LPC Analysis, Quantization, and Lattice Filtering
Chapter
3 — Section 3.3.8
Subsections
3.3.8.1 to 3.3.8.4
Level
Advanced

TNS — Temporal Noise Shaping — is the second noise-shaping stage after SNS. Where SNS shapes noise in the frequency domain, TNS shapes noise in the time domain within each MDCT frame. It applies a linear predictor (LPC filter) to the spectral coefficients, which concentrates quantization noise in time-domain regions where the signal is already loud, making it less audible. This tutorial covers the full TNS encoder: LPC analysis, coefficient quantization, bit consumption, and the analysis filter.

Keywords

Temporal Noise Shaping LPC / Linear Prediction Levinson-Durbin Reflection Coefficients Lattice Filter predGain Arcsine Quantization

3.3.8.1 TNS Overview

After SNS shapes the spectrum so that quantization noise follows the signal energy across frequencies, there is still a second problem: the MDCT frame may contain a strong temporal event (like a vowel or a burst) concentrated in part of the frame’s time span. The quantization noise for this frame is spread across the whole time duration — you may hear a “pre-echo” before the burst.

TNS solves this by applying a linear prediction (LPC) filter in the frequency domain. This is the dual operation of time-domain LPC: instead of predicting time samples from past time samples, TNS predicts spectral coefficients from neighboring spectral coefficients. The effect is to concentrate the quantization noise temporally, reducing pre-echo.

Key facts about TNS in LC3:

  • Up to 2 TNS filters per frame (1 for NB/WB/SSWB, 2 for SWB/FB)
  • Each filter covers a specific frequency sub-range determined by the detected bandwidth
  • Order: up to 8 reflection coefficients per filter
  • TNS is disabled when near_nyquist_flag = 1 (to avoid aliasing artifacts)
Nms Bandwidth num_tns_filters start_freq(f) stop_freq(f)
10ms NB 1 {12} {80}
10ms WB 1 {12} {160}
10ms SSWB 1 {12} {240}
10ms SWB 2 {12, 160} {160, 320}
10ms FB 2 {12, 200} {200, 400}
7.5ms NB 1 {9} {60}
7.5ms WB 1 {9} {120}
7.5ms SWB 2 {9, 120} {120, 240}
7.5ms FB 2 {9, 150} {150, 300}

3.3.8.2 TNS Analysis — LPC from Spectral Coefficients

TNS analysis is done independently for each filter f. The SNS-shaped spectrum Xs(k) (output of Section 3.3.7.5) is used as input. The analysis computes LPC coefficients and then converts them to reflection coefficients.

Step 1: Autocorrelation of the spectral sub-band

Each filter covers 3 sub-bands (sub_start, sub_stop from Table 3.15). The normalized autocorrelation r(k) for lags k = 0..8 is computed. If all three sub-band energies e(s) are zero, r(k) defaults to {3, 0, 0, …}.

Step 2: Lag windowing

rw(k) = r(k) × exp(−0.5 × (0.02π × k)²), k = 0..8

This Gaussian window reduces the autocorrelation at higher lags, which stabilizes the Levinson-Durbin recursion by broadening spectral peaks.

Step 3: Levinson-Durbin recursion

The Levinson-Durbin algorithm converts the autocorrelation sequence rw into LPC coefficients a(k) (order 8) and a prediction error err:

err = rw[0];  a[0] = 1;
for k = 1 to 8:
    rc = − SUM(a[n] × rw[k−n], n=0..k−1) / err
    tmp[0..k] = update using a and rc
    a[0..k] = tmp[0..k]
    err = (1 − rc²) × err

Step 4: Prediction gain check

The TNS filter f is activated only if the prediction gain exceeds 1.5:

predGain = rw(0) / err
If predGain > 1.5 AND near_nyquist_flag = 0: activate TNS filter f

predGain = 1.5 means the LPC model explains only a modest amount of the spectral variance. Below this threshold, TNS would add complexity without meaningful benefit.

Step 5: LPC weighting (low bitrate only)

At low bitrates (nbits < 48 × Nms), LPC coefficients are bandwidth-expanded with a weighting factor γ to reduce filter sharpness:

If tns_lpc_weighting = 1 AND predGain < 2:
γ = 1 − (1 − 0.85) × (2 − predGain) / (2 − 1.5)
else: γ = 1aw(k) = γ^k × a(k), k = 0..8

Step 6: Convert LPC to reflection coefficients

The weighted LPC coefficients aw are converted back to 8 reflection coefficients rc(k) using the step-down procedure (inverse Levinson). The final rc(k, f) values are what get quantized and transmitted.

If TNS filter f is off: all rc(k, f) = 0.

3.3.8.3 Quantization of Reflection Coefficients

Each reflection coefficient rc(k, f) is in the range (−1, 1). They are quantized using a uniform scalar quantizer in the arcsine domain — this spreads the quantization levels more evenly across the distribution of reflection coefficient values:

rci(k, f) = nint[ arcsin(rc(k, f)) / Δ ] + 8
rcq(k, f) = sin( Δ × (rci(k, f) − 8) )
where Δ = π/17

The index rci ranges from 0 to 16 (17 levels, centered at 8 which corresponds to rc = 0). The quantized value rcq is the sine of the dequantized arcsine index.

Filter order determination: The effective filter order rcorder(f) is the largest k+1 for which rcq(k, f) ≠ 0. Trailing zero coefficients are not transmitted.

Bit count for TNS:

nbits_TNS_order(f) = ac_tns_order_bits[tns_lpc_weighting][rcorder(f)−1]
nbits_TNS_coef(f) = SUM[k=0..rcorder(f)−1] ac_tns_coef_bits[k][rci(k,f)]
nbits_TNS = SUM[f] ceil( (2048 + nbits_TNS_order(f) + nbits_TNS_coef(f)) / 2048 )

The tables ac_tns_order_bits and ac_tns_coef_bits are in Section 3.7.5. They give the fractional bit costs (scaled by 2048) for arithmetic coding.

3.3.8.4 TNS Analysis Filter (Lattice Filter)

The actual filtering of the spectrum is done using a lattice structure, which implements the all-zero (FIR analysis) filter for each active TNS filter. The lattice form is used because it is numerically stable and directly uses the reflection coefficients without converting them to direct-form FIR coefficients.

The filter processes the spectral range [start_freq(f), stop_freq(f)) of Xs(k):

/* TNS Analysis Lattice Filter — Section 3.3.8.4 */
/* Initialize lattice states */
float st[8] = {0};

for each active filter f (f = 0 to num_tns_filters−1):
    if rcorder(f) > 0:
        for n = start_freq(f) to stop_freq(f)−1:
            t = Xs(n)
            st_save = t
            for k = 0 to rcorder(f)−2:
                st_tmp = rcq(k,f) * t + st[k]
                t      = t + rcq(k,f) * st[k]
                st[k]  = st_save
                st_save = st_tmp
            t        = t + rcq(rcorder(f)−1, f) * st[rcorder(f)−1]
            st[rcorder(f)−1] = st_save
            Xf(n) = t   /* filtered output */

The output Xf(k) is the TNS-filtered spectrum. The lattice states carry over from filter 0 to filter 1 if there are two filters. The initial state is all zeros at the start of each frame.

At the decoder, the inverse operation (synthesis filter — all-pole IIR) is applied to reconstruct Xs from Xf. The synthesis filter uses the same reflection coefficients but in reverse order and with opposite sign operations.

Next in this Series

Section 3.3.9 — Long Term Postfilter (LTPF): pitch detection, resampling, and LTPF bitstream encoding

Next Tutorial → All Tutorials

Leave a Reply

Your email address will not be published. Required fields are marked *