@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.
Files changed (2) hide show
  1. package/fdn.js +60 -0
  2. 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
+ }