3 — Section 3.1
3.1.1 + 3.1.2 + 3.1.3
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.
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
