@audio/lpc
v1.2.0
Published
Linear predictive coding — Levinson-Durbin AR analysis, prediction, forward extrapolation, and least-squares gap interpolation
Readme
@audio/lpc
Linear predictive coding — model a signal as an autoregressive process, then predict, extrapolate, or fill gaps from it.
The Levinson-Durbin solution to the Yule-Walker equations, plus the AR-prediction tools that classical audio restoration is built on. Pure math, no dependencies.
import { lpc, arExtrapolate, arInterpolate } from '@audio/lpc'
let { a, e } = lpc(window, 16) // AR(16) coefficients a[], residual variance e
let filled = arExtrapolate(context, a, 32) // project 32 samples forward (de-clip)lpc(x, p) / arFit(x, p)
LPC analysis of order p over window x (lpc is the conventional alias). Returns { a: Float64Array(p+1), e } — a[0] === 1, e is the residual variance. Internally levinson(autocorr(x, p), p).
autocorr(x, p) · levinson(R, p)
The two stages, exposed separately: biased autocorrelation R[0..p], and the Levinson-Durbin Toeplitz solve. Call directly when you already have an autocorrelation, or want the reflection-coefficient path.
arPredict(a, hist)
One-step prediction x̂[n] = -∑ a[k]·x[n−k] from a history buffer.
arExtrapolate(context, a, m)
Project m samples forward from context under model a. Used to reconstruct clipped regions from their un-clipped neighbourhood.
arInterpolate(x, gap, a)
Least-squares fill of missing indices gap (sorted) inside x, in place, under model a — the interpolator behind de-crackle. Gauss-Seidel, 30 sweeps: close for a few scattered samples; arFill solves the same exactly.
arFill(x, gap, a)
arInterpolate's problem solved exactly, for unknowns scattered anywhere in x: the normal equations couple two unknowns only when they lie within p samples of each other, so they are banded in the gap's order, and Cholesky within that band solves them in O(m·b²) for m unknowns with at most b of them within p of one another. Returns false (and leaves x as it was) when the model makes the system singular. The interpolator behind denoise-declip, where every clipped sample of a window is unknown at once (Janssen, Veldhuis & Vries 1986).
let gap = [] // every clipped sample of the window
for (let i = 0; i < w.length; i++) if (Math.abs(w[i]) >= rail) gap.push(i)
let { a } = lpc(w, 64)
arFill(w, gap, a) // the least-squares fill of all of them at once, in placearBridge(x, from, to, a)
The same least-squares fill for one contiguous gap x[from..to), in place, solved exactly: the normal equations are Toeplitz in the autocorrelation of a, so Levinson recursion takes O(m²) for m missing samples at any order (Janssen, Veldhuis & Vries 1986; the solve of the Audio Inpainting Toolbox's Janssen step, Adler et al. 2012). Where arInterpolate suits scattered clicks at order ~60, this suits a dropout of hundreds or thousands of samples at order ~1000: the interpolator behind denoise-repair's AR tier.
segment.fill(0, 4096, 4096 + 882) // a 20 ms dropout at 44.1 kHz, zeroed
let { a } = lpc(segment, 1024) // AR fit on its surroundings
arBridge(segment, 4096, 4096 + 882, a) // least-squares fill, in place; refit and refill once more for Janssen's methodNotes
Foundation for LPC vocoders and formant tracking (Markel & Gray 1976); restoration methods follow Godsill & Rayner (1998). Used by @audio/denoise's declick/decrackle/declip/repair. MIT.
