LC3 General Codec Description

 

LC3 General Codec Description
Chapter 3, Section 3.1 — Introduction, Mathematical Symbols, and Operators
Chapter
3 — Section 3.1
Sections
3.1.1 + 3.1.2 + 3.1.3
Level
Intermediate

This tutorial covers Section 3.1 of the LC3 Technical Specification: the codec’s feature summary, the global mathematical symbols used throughout the entire spec, and the operator notation. You need to understand these before reading any of the encoding or decoding sections.

Keywords

MDCT Frame Duration Algorithmic Delay Band Index Spectral Coefficients argmax / argmin Floor / Ceil

3.1.1 Introduction — Feature Summary

LC3 is described in the specification using both floating-point equations and integer pseudocode. This means implementations can run on anything from a simple 16-bit fixed-point hearing aid MCU to a full floating-point PC. The spec does not provide source code — only the algorithm. The table below is the feature summary for a single audio channel.

Feature Supported Range / Values
Frame duration 10 ms (10.88 ms @ 44.1 kHz) and 7.5 ms (8.163 ms @ 44.1 kHz)
MDCT look-ahead delay 2.5 ms for 10 ms frame; 4 ms for 7.5 ms frame (slightly longer at 44.1 kHz)
Total algorithmic delay 12.5 ms (10 ms frame) / 11.5 ms (7.5 ms frame) — longer at 44.1 kHz
Supported sampling rates 8 kHz, 16 kHz (HA-SQ), 24 kHz (HA-HQ), 32 kHz, 44.1 kHz, 48 kHz
Supported bitrate 20 to 400 bytes per frame per audio channel
Bits per audio sample No algorithm restriction. Optimized for 16-, 24-, and 32-bit. Minimum 16 bits, maximum 32 bits.

HA-SQ = Hearing Aid Speech Quality (16 kHz). HA-HQ = Hearing Aid High Quality (24 kHz). These labels come from hearing aid industry terminology.

3.1.2 Mathematical Symbols

These variables are global — they appear in equations throughout Sections 3.2, 3.3, and 3.4. Memorize these before reading any encoding or decoding section.

Symbol Description Simple Explanation
fs Sampling rate The audio sample rate: 8000, 16000, 24000, 32000, 44100, or 48000 Hz.
fscal Scale factor for 44.1 kHz = 48000/44100 when fs = 44100, else = 1. Used to adjust frame sizes for 44.1 kHz.
NF Frame size in samples How many PCM samples are in one frame. E.g., 480 samples for 48 kHz, 10 ms.
Nms Frame duration (ms) Either 7.5 ms or 10 ms. This is the nominal duration — actual duration at 44.1 kHz is longer.
nbytes Number of bytes per frame The byte_count given to the encoder. Range: 20 to 400.
nbits Number of bits per frame Simply nbytes × 8. Used in bit budget calculations.
Nb Number of frequency bands 64 for most configurations. 60 only for 7.5 ms at 8 kHz. Bands group the MDCT coefficients.
NE Number of encoded spectral lines How many MDCT frequency bins are actually encoded. = 400 for NF=480, = 300 for NF=360, else = NF.
Nbw Number of bandwidth sections Used by the bandwidth detector. Counts the detectable bands (NB/WB/SSWB/SWB/FB).
Ifs(n) Band index table A lookup table mapping band number n to its starting MDCT coefficient index. Depends on fs.
D Algorithmic delay (ms) Total end-to-end codec delay = frame duration + MDCT look-ahead.
DMDCT MDCT look-ahead delay Extra delay because the MDCT window reaches into the next frame. = NF − Z samples, where Z is the number of leading zeros in the MDCT window.
EB(b) Energy per band b The average power of MDCT coefficients in band b. Used by SNS, bandwidth detector, and TNS.
wN LD-MDCT window The asymmetric low-delay window applied to the time buffer before the MDCT transform. Pre-computed coefficients (in Section 3.7.3).
X(k) Frequency coefficients The MDCT output: NF complex spectral coefficients for the current frame.
Z Leading zeros in MDCT window = 7NF/30 for 7.5 ms frames; = 3NF/8 for 10 ms frames. These are zero-padded leading samples that create the look-ahead.
xb(n) Time domain sample Sample n in frame b. Negative indices mean samples from a previous frame.
Xb(k) Frequency coefficient MDCT coefficient k in frame b.

3.1.3 Operators

The spec uses standard mathematical notation throughout. Here is a reference table for every operator used:

Notation Name Meaning / Example
⌊x⌋ Floor Round down to nearest integer. ⌊3.7⌋ = 3, ⌊−3.2⌋ = −4.
⌈x⌉ Ceil Round up to nearest integer. ⌈3.2⌉ = 4, ⌈−3.7⌉ = −3.
⌊x⌉ or nint(x) Round to nearest integer Rounds to nearest. ⌊3.5⌉ = 4, ⌊4.5⌉ = 5. Tie-breaking may be platform-dependent.
argmax X Argument of maximum Returns the index of the first occurrence of the maximum value in array X.
argmin X Argument of minimum Returns the index of the first occurrence of the minimum value in array X.
x ← y In-place assignment Assigns y to x. Used in update equations. E.g., x(n) ← x(n+1) shifts samples left by one.
{a, b, ..} Ordered sequence An array/vector of values. Indexing starts at 0 unless stated otherwise.
a(n..m) Subsequence The slice of array a from index n to index m inclusive: {a(n), a(n+1), …, a(m)}.
xT, XT Transpose Transpose of vector x or matrix X. Used in SNS vector quantization equations.
{x | condition(x)} Set builder The set of values x that satisfy the given condition.
a|b Set construction Elements a such that condition b is satisfied.

One important note: the spec uses negative indexing heavily for time-domain signals. For example, xs(−1) means the last sample from the previous frame. This pattern appears in the MDCT buffer update, LTPF, and attack detector sections.

Operators in C — Quick Reference

The spec’s pseudo-code maps to standard C operations. Here is a quick translation reference:

#include <math.h>
#include <stdlib.h>

/* ⌊x⌋  floor */       int floor_val = (int)floor(x);
/* ⌈x⌉  ceil  */       int ceil_val  = (int)ceil(x);
/* nint(x)     */       int nint_val  = (int)round(x);  /* nearest integer */

/* argmax: find index of maximum in array X[0..N-1] */
int argmax(float *X, int N) {
    int best = 0;
    for (int i = 1; i < N; i++)
        if (X[i] > X[best]) best = i;
    return best;
}

/* Negative indexing: maintain a ring buffer for previous frame data.    */
/* xs[-1] = last sample of previous frame = stored in xs_prev[NF - 1]   */
/* xs[-2] = xs_prev[NF - 2], etc.                                        */
float get_xs(float *xs_curr, float *xs_prev, int n, int NF) {
    if (n >= 0) return xs_curr[n];
    return xs_prev[NF + n];   /* n is negative, so NF + n < NF */
}

/* Band index lookup: Ifs[b] gives the start MDCT coefficient of band b  */
/* Example for 8 kHz, 10 ms (NF = 80, Nb = 64 bands):                   */
static const int I_8000_10ms[65] = {
    0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
    16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
    32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
    48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
    64 /* end sentinel */
};

Next in this Series

Section 3.2 — General Codec Parameters: sampling rates, bits per sample, frame size formula, and bit budget

Next Tutorial → All Tutorials

Leave a Reply

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