@audio/eq-fir 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.
- package/fir.js +66 -0
- package/package.json +31 -0
package/fir.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// FIR convolution EQ — linear-phase filter from an arbitrary magnitude response.
|
|
2
|
+
// Frequency-sampling design (odd-length type-I symmetric FIR, Hann-windowed against Gibbs),
|
|
3
|
+
// zero-latency apply (group delay compensated).
|
|
4
|
+
// FFmpeg firequalizer class; the Matchering match-EQ substrate.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Design linear-phase FIR coefficients from a magnitude response.
|
|
8
|
+
* @param {Array<{f:number,gain:number}>|function} response — dB gain points (linearly
|
|
9
|
+
* interpolated over frequency, clamped outside) or `f => dB` function
|
|
10
|
+
* @param {object} opts — { taps=511 (made odd), fs=44100 }
|
|
11
|
+
* @returns {Float64Array} symmetric coefficients
|
|
12
|
+
*/
|
|
13
|
+
export function design (response, { taps = 511, fs = 44100 } = {}) {
|
|
14
|
+
let N = taps | 1 // force odd (type-I)
|
|
15
|
+
let dbAt
|
|
16
|
+
if (typeof response === 'function') dbAt = response
|
|
17
|
+
else {
|
|
18
|
+
let pts = [...response].sort((a, b) => a.f - b.f)
|
|
19
|
+
dbAt = f => {
|
|
20
|
+
if (f <= pts[0].f) return pts[0].gain
|
|
21
|
+
if (f >= pts[pts.length - 1].f) return pts[pts.length - 1].gain
|
|
22
|
+
for (let i = 1; i < pts.length; i++) {
|
|
23
|
+
if (f <= pts[i].f) {
|
|
24
|
+
let t = (f - pts[i - 1].f) / (pts[i].f - pts[i - 1].f)
|
|
25
|
+
return pts[i - 1].gain + t * (pts[i].gain - pts[i - 1].gain)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return pts[pts.length - 1].gain
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let half = (N - 1) / 2
|
|
33
|
+
let M = new Float64Array(half + 1)
|
|
34
|
+
for (let k = 0; k <= half; k++) M[k] = 10 ** (dbAt(k * fs / N) / 20)
|
|
35
|
+
|
|
36
|
+
// h[n] = (1/N)·[M0 + 2·Σ M_k·cos(2πk(n−α)/N)], α = (N−1)/2 → symmetric, linear phase
|
|
37
|
+
let h = new Float64Array(N)
|
|
38
|
+
for (let n = 0; n < N; n++) {
|
|
39
|
+
let sum = M[0]
|
|
40
|
+
for (let k = 1; k <= half; k++) sum += 2 * M[k] * Math.cos(2 * Math.PI * k * (n - half) / N)
|
|
41
|
+
let win = 0.5 - 0.5 * Math.cos(2 * Math.PI * n / (N - 1)) // Hann against Gibbs ripple
|
|
42
|
+
h[n] = (sum / N) * win
|
|
43
|
+
}
|
|
44
|
+
return h
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Apply the EQ in place, delay-compensated (output time-aligned with input).
|
|
49
|
+
* @param {Float32Array} data — mono PCM
|
|
50
|
+
* @param {object} opts — { response, taps, fs } or precomputed { coefs }
|
|
51
|
+
*/
|
|
52
|
+
export default function firEq (data, opts = {}) {
|
|
53
|
+
let h = opts.coefs || design(opts.response, opts)
|
|
54
|
+
let N = h.length, half = (N - 1) / 2
|
|
55
|
+
let out = new Float32Array(data.length)
|
|
56
|
+
for (let i = 0; i < data.length; i++) {
|
|
57
|
+
let sum = 0
|
|
58
|
+
for (let j = 0; j < N; j++) {
|
|
59
|
+
let idx = i - j + half
|
|
60
|
+
if (idx >= 0 && idx < data.length) sum += h[j] * data[idx]
|
|
61
|
+
}
|
|
62
|
+
out[i] = sum
|
|
63
|
+
}
|
|
64
|
+
data.set(out)
|
|
65
|
+
return data
|
|
66
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@audio/eq-fir",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "FIR convolution EQ — linear-phase, arbitrary magnitude response (frequency-sampling design; firequalizer / match-EQ class)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "fir.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./fir.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"fir.js"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"audio",
|
|
17
|
+
"dsp",
|
|
18
|
+
"eq",
|
|
19
|
+
"fir",
|
|
20
|
+
"linear-phase",
|
|
21
|
+
"firequalizer"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "Dmitry Iv. <dfcreative@gmail.com>",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
}
|
|
31
|
+
}
|