@audio/reverb-fdn 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/fdn.js +60 -0
- package/package.json +29 -0
package/fdn.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Feedback delay network reverb — N mutually-prime delays, Householder feedback matrix
|
|
2
|
+
// (I − 2/N·11ᵀ, applied in O(N)), per-line lowpass damping, and Jot's per-line decay
|
|
3
|
+
// gains g_k = 10^(−3·len_k / (T60·fs)) so every line decays at the same rate → a uniform,
|
|
4
|
+
// colorless T60 (Jot & Chaigne 1991). Output taps use orthogonal ± patterns for stereo.
|
|
5
|
+
|
|
6
|
+
const DELAYS = [1129, 1291, 1471, 1663, 1811, 1979, 2143, 2311] // mutually prime, @44.1k
|
|
7
|
+
const SIGN_L = [1, -1, 1, -1, 1, -1, 1, -1]
|
|
8
|
+
const SIGN_R = [1, 1, -1, -1, 1, 1, -1, -1]
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {Float32Array|Float32Array[]} data — mono buffer or [L, R], processed in place
|
|
12
|
+
* @param {object} opts — { t60 (seconds; overrides decay), decay=0.6 (0..1 convenience →
|
|
13
|
+
* t60 = 0.3 + 5·decay²), damping=0.3 (0..1 high-frequency loss), mix=0.3, fs=44100 }
|
|
14
|
+
*/
|
|
15
|
+
export default function fdn (data, { t60, decay = 0.6, damping = 0.3, mix = 0.3, fs = 44100 } = {}) {
|
|
16
|
+
let N = DELAYS.length
|
|
17
|
+
let sc = fs / 44100
|
|
18
|
+
let T60 = t60 ?? (0.3 + 5 * decay * decay)
|
|
19
|
+
let lines = DELAYS.map(d => {
|
|
20
|
+
let len = Math.max(1, Math.round(d * sc))
|
|
21
|
+
return { buf: new Float64Array(len), i: 0, lp: 0, g: 10 ** (-3 * len / (T60 * fs)) }
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
let stereo = data[0]?.length !== undefined
|
|
25
|
+
let L = stereo ? data[0] : data
|
|
26
|
+
let R = stereo ? data[1] : null
|
|
27
|
+
let n = L.length
|
|
28
|
+
let outs = new Float64Array(N)
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < n; i++) {
|
|
31
|
+
let x = stereo ? (L[i] + R[i]) * 0.5 : L[i]
|
|
32
|
+
|
|
33
|
+
let sum = 0
|
|
34
|
+
for (let k = 0; k < N; k++) {
|
|
35
|
+
outs[k] = lines[k].buf[lines[k].i]
|
|
36
|
+
sum += outs[k]
|
|
37
|
+
}
|
|
38
|
+
let h = 2 / N * sum // Householder reflection: y_k = x_k − (2/N)·Σx
|
|
39
|
+
|
|
40
|
+
let yL = 0, yR = 0
|
|
41
|
+
for (let k = 0; k < N; k++) {
|
|
42
|
+
let line = lines[k]
|
|
43
|
+
let fb = (outs[k] - h) * line.g
|
|
44
|
+
line.lp = fb * (1 - damping) + line.lp * damping
|
|
45
|
+
line.buf[line.i] = x + line.lp
|
|
46
|
+
if (++line.i >= line.buf.length) line.i = 0
|
|
47
|
+
yL += SIGN_L[k] * outs[k]
|
|
48
|
+
yR += SIGN_R[k] * outs[k]
|
|
49
|
+
}
|
|
50
|
+
yL /= Math.sqrt(N); yR /= Math.sqrt(N)
|
|
51
|
+
|
|
52
|
+
if (stereo) {
|
|
53
|
+
L[i] = L[i] * (1 - mix) + yL * mix
|
|
54
|
+
R[i] = R[i] * (1 - mix) + yR * mix
|
|
55
|
+
} else {
|
|
56
|
+
L[i] = L[i] * (1 - mix) + (yL + yR) * 0.5 * mix
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return data
|
|
60
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@audio/reverb-fdn",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "FDN reverb — 8 mutually-prime delays, O(N) Householder feedback, per-line damping (Jot topology)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "fdn.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./fdn.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"fdn.js"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"audio",
|
|
17
|
+
"dsp",
|
|
18
|
+
"reverb",
|
|
19
|
+
"fdn"
|
|
20
|
+
],
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "Dmitry Iv. <dfcreative@gmail.com>",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
}
|
|
29
|
+
}
|