@audio/speech-lpc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/lpc.d.ts +26 -0
  2. package/lpc.js +88 -0
  3. package/package.json +40 -0
package/lpc.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ type Buf = Float32Array | Float64Array | number[]
2
+ interface BiquadCoef { b0: number; b1: number; b2: number; a1: number; a2: number }
3
+ type SOS = BiquadCoef[]
4
+
5
+ export interface LpcParams {
6
+ order?: number // LPC order (default 12)
7
+ [key: string]: unknown
8
+ }
9
+
10
+ export interface LpcResult {
11
+ coefs: Float64Array // LPC coefficients a[1..order] of A(z) = 1 + Σ a_k·z⁻ᵏ
12
+ gain: number // prediction-error gain (per-sample std of the whitening filter output, pre-normalization)
13
+ residual: Float64Array // whitened residual e[n] = A(z)x[n], normalized to unit power (divided by gain) — feed to lpcSynthesize as-is, or scale by a new gain to resynthesize
14
+ }
15
+
16
+ /** LPC analysis — autocorrelation + Levinson-Durbin. Throws if params.order is not set on a fresh params object (no default without one). */
17
+ export function lpcAnalysis(data: Buf, params: LpcParams): LpcResult
18
+
19
+ export interface LpcSynthParams {
20
+ coefs: Float64Array | number[]
21
+ gain?: number // default 1
22
+ [key: string]: unknown
23
+ }
24
+
25
+ /** LPC synthesis — all-pole filter reconstruction. y[n] = gain·excitation[n] − Σ coefs[k]·y[n−1−k], the algebraic inverse of lpcAnalysis's residual. */
26
+ export function lpcSynthesize(residual: Buf, params: LpcSynthParams): Buf
package/lpc.js ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Linear Predictive Coding analysis/synthesis.
3
+ * Autocorrelation method with Levinson-Durbin recursion.
4
+ *
5
+ * @module audio-filter/speech/lpc
6
+ */
7
+
8
+ /**
9
+ * LPC analysis via autocorrelation + Levinson-Durbin.
10
+ * @param {Float64Array} data - Input signal
11
+ * @param {object} params - { order (12) }
12
+ * @returns {{ coefs: Float64Array, gain: number, residual: Float64Array }}
13
+ */
14
+ export function lpcAnalysis(data, params) {
15
+ let order = params.order || 12
16
+ let N = data.length
17
+
18
+ // Autocorrelation r[0..order], normalized per-sample (Rabiner & Schafer biased
19
+ // estimator) so E/gain are frame-length invariant, not sum-length dependent
20
+ let r = new Float64Array(order + 1)
21
+ for (let i = 0; i <= order; i++) {
22
+ for (let n = i; n < N; n++) r[i] += data[n] * data[n - i]
23
+ r[i] /= N
24
+ }
25
+
26
+ // Levinson-Durbin
27
+ let a = new Float64Array(order + 1)
28
+ let prev = new Float64Array(order + 1)
29
+ a[0] = 1
30
+ let E = r[0]
31
+
32
+ for (let i = 1; i <= order; i++) {
33
+ let sum = 0
34
+ for (let j = 1; j < i; j++) sum += a[j] * r[i - j]
35
+ let k = -(r[i] + sum) / E
36
+
37
+ // Copy current coefficients
38
+ prev.set(a)
39
+
40
+ for (let j = 1; j < i; j++) a[j] = prev[j] + k * prev[i - j]
41
+ a[i] = k
42
+
43
+ E *= (1 - k * k)
44
+ }
45
+
46
+ let gain = Math.sqrt(E) // per-sample prediction-error std (E is already per-sample)
47
+ let coefs = a.subarray(1) // a[1..order] of A(z) = 1 + Σ a_k z⁻ᵏ
48
+
49
+ // Whitening filter e[n] = A(z)x[n] = x[n] + Σ coefs[k]·x[n−1−k], normalized to unit power
50
+ let residual = new Float64Array(N)
51
+ for (let n = 0; n < N; n++) {
52
+ let sum = 0
53
+ for (let k = 1; k <= order; k++) if (n - k >= 0) sum += coefs[k - 1] * data[n - k]
54
+ residual[n] = (data[n] + sum) / gain
55
+ }
56
+
57
+ return { coefs: new Float64Array(coefs), gain, residual }
58
+ }
59
+
60
+ /**
61
+ * All-pole synthesis filter.
62
+ * @param {Float64Array} excitation - Excitation/residual signal, unit power (modified in-place)
63
+ * @param {object} params - { coefs, gain (1), _s (internal state) }
64
+ * @returns {Float64Array} Synthesized signal
65
+ */
66
+ export function lpcSynthesize(excitation, params) {
67
+ let coefs = params.coefs
68
+ let gain = params.gain != null ? params.gain : 1
69
+ let order = coefs.length
70
+ let N = excitation.length
71
+
72
+ if (!params._s || params._s.length !== order) params._s = new Float64Array(order)
73
+ let s = params._s
74
+
75
+ // y[n] = gain·u[n] - Σ coefs[k]·y[n-1-k] — algebraic inverse of lpcAnalysis's
76
+ // e[n] = x[n] + Σ coefs[k]·x[n-1-k], u = e/gain
77
+ for (let n = 0; n < N; n++) {
78
+ let y = gain * excitation[n]
79
+ for (let k = 0; k < order; k++) y -= coefs[k] * s[k]
80
+ excitation[n] = y
81
+
82
+ // Shift state
83
+ for (let k = order - 1; k > 0; k--) s[k] = s[k - 1]
84
+ s[0] = y
85
+ }
86
+
87
+ return excitation
88
+ }
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@audio/speech-lpc",
3
+ "version": "1.0.0",
4
+ "description": "Linear Predictive Coding analysis/synthesis",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "lpc.js",
8
+ "exports": {
9
+ ".": "./lpc.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "lpc.js",
14
+ "lpc.d.ts"
15
+ ],
16
+ "keywords": [
17
+ "audio",
18
+ "dsp",
19
+ "speech",
20
+ "lpc"
21
+ ],
22
+ "license": "MIT",
23
+ "author": "Dmitry Iv. <dfcreative@gmail.com>",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/audiojs/filter.git",
27
+ "directory": "packages/speech-lpc"
28
+ },
29
+ "homepage": "https://github.com/audiojs/filter/tree/main/packages/speech-lpc",
30
+ "bugs": {
31
+ "url": "https://github.com/audiojs/filter/issues"
32
+ },
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "types": "lpc.d.ts"
40
+ }