@audio/reverb-dattorro 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/dattorro.js +92 -0
  2. package/package.json +29 -0
package/dattorro.js ADDED
@@ -0,0 +1,92 @@
1
+ // Dattorro plate reverb — input diffusion (4 allpasses) into a figure-eight tank of two
2
+ // cross-coupled branches (allpass → delay → damping → decay → allpass → delay).
3
+ // Modulation omitted (static tank). Structure, delay lengths and output taps from Dattorro, "Effect Design Part 1:
4
+ // Reverberator and Other Filters", JAES 45(9), 1997 (reference rate 29761 Hz, scaled to fs).
5
+
6
+ const REF = 29761
7
+
8
+ class Delay {
9
+ constructor (n) { this.buf = new Float64Array(Math.max(1, n)); this.i = 0 }
10
+ read (tap) { let j = this.i - tap; j %= this.buf.length; if (j < 0) j += this.buf.length; return this.buf[j] }
11
+ step (x) { this.buf[this.i] = x; if (++this.i >= this.buf.length) this.i = 0 }
12
+ tail () { return this.read(this.buf.length - 1) }
13
+ }
14
+ class Allpass {
15
+ constructor (n, g) { this.d = new Delay(n); this.g = g }
16
+ step (x) {
17
+ let z = this.d.read(this.d.buf.length - 1)
18
+ let y = x + z * -this.g
19
+ this.d.step(y)
20
+ return z + y * this.g
21
+ }
22
+ read (tap) { return this.d.read(tap) }
23
+ }
24
+
25
+ /**
26
+ * @param {Float32Array|Float32Array[]} data — mono buffer or [L, R], processed in place
27
+ * @param {object} opts — { decay=0.5 (0..1), damping=0.3 (0..1), bandwidth=0.9995,
28
+ * mix=0.3, fs=44100 }
29
+ */
30
+ export default function dattorro (data, { decay = 0.5, damping = 0.3, bandwidth = 0.9995, mix = 0.3, fs = 44100 } = {}) {
31
+ let sc = fs / REF
32
+ let S = n => Math.max(1, Math.round(n * sc))
33
+
34
+ // input chain
35
+ let lpIn = 0
36
+ let inAp = [new Allpass(S(142), 0.75), new Allpass(S(107), 0.75), new Allpass(S(379), 0.625), new Allpass(S(277), 0.625)]
37
+ // tank branch 1
38
+ let ap1 = new Allpass(S(672), 0.7)
39
+ let del1a = new Delay(S(4453))
40
+ let lp1 = 0
41
+ let ap1b = new Allpass(S(1800), 0.5)
42
+ let del1b = new Delay(S(3720))
43
+ // tank branch 2
44
+ let ap2 = new Allpass(S(908), 0.7)
45
+ let del2a = new Delay(S(4217))
46
+ let lp2 = 0
47
+ let ap2b = new Allpass(S(2656), 0.5)
48
+ let del2b = new Delay(S(3163))
49
+
50
+ let stereo = data[0]?.length !== undefined
51
+ let L = stereo ? data[0] : data
52
+ let R = stereo ? data[1] : null
53
+ let n = L.length
54
+
55
+ for (let i = 0; i < n; i++) {
56
+ let dry = stereo ? (L[i] + R[i]) * 0.5 : L[i]
57
+
58
+ lpIn = dry * bandwidth + lpIn * (1 - bandwidth)
59
+ let x = lpIn
60
+ for (let ap of inAp) x = ap.step(x)
61
+
62
+ // figure-eight: each branch is fed by the other branch's end
63
+ let in1 = x + del2b.tail() * decay
64
+ let in2 = x + del1b.tail() * decay
65
+
66
+ let a = ap1.step(in1)
67
+ del1a.step(a)
68
+ lp1 = del1a.tail() * (1 - damping) + lp1 * damping
69
+ let b = ap1b.step(lp1 * decay)
70
+ del1b.step(b)
71
+
72
+ let c = ap2.step(in2)
73
+ del2a.step(c)
74
+ lp2 = del2a.tail() * (1 - damping) + lp2 * damping
75
+ let d = ap2b.step(lp2 * decay)
76
+ del2b.step(d)
77
+
78
+ // output taps (Dattorro Table 2), scaled
79
+ let yL = 0.6 * (del2a.read(S(266)) + del2a.read(S(2974)) - ap2b.read(S(1913)) + del2b.read(S(1996))
80
+ - del1a.read(S(1990)) - ap1b.read(S(187)) - del1b.read(S(1066)))
81
+ let yR = 0.6 * (del1a.read(S(353)) + del1a.read(S(3627)) - ap1b.read(S(1228)) + del1b.read(S(2673))
82
+ - del2a.read(S(2111)) - ap2b.read(S(335)) - del2b.read(S(121)))
83
+
84
+ if (stereo) {
85
+ L[i] = L[i] * (1 - mix) + yL * mix
86
+ R[i] = R[i] * (1 - mix) + yR * mix
87
+ } else {
88
+ L[i] = L[i] * (1 - mix) + (yL + yR) * 0.5 * mix
89
+ }
90
+ }
91
+ return data
92
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@audio/reverb-dattorro",
3
+ "version": "1.0.0",
4
+ "description": "Dattorro plate reverb — input diffusers + figure-eight tank (JAES 1997 topology and taps)",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "dattorro.js",
8
+ "exports": {
9
+ ".": "./dattorro.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "dattorro.js"
14
+ ],
15
+ "keywords": [
16
+ "audio",
17
+ "dsp",
18
+ "reverb",
19
+ "dattorro"
20
+ ],
21
+ "license": "MIT",
22
+ "author": "Dmitry Iv. <dfcreative@gmail.com>",
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ }
29
+ }