@audio/reverb-spring 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/package.json +29 -0
- package/spring.js +42 -0
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@audio/reverb-spring",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Spring reverb — dispersive allpass-chain loop (Parker-Välimäki class simplified model)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "spring.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./spring.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"spring.js"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"audio",
|
|
17
|
+
"dsp",
|
|
18
|
+
"reverb",
|
|
19
|
+
"spring"
|
|
20
|
+
],
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "Dmitry Iv. <dfcreative@gmail.com>",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/spring.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Spring reverb model — feedback loop of stretched (dispersive) allpass chains plus a
|
|
2
|
+
// short delay: the cascaded allpasses make group delay frequency-dependent, producing
|
|
3
|
+
// the characteristic spring "boing" chirps. Simplified digital spring after
|
|
4
|
+
// Parker & Välimäki, "Spring reverberation: a physical perspective" (DAFx-10) family.
|
|
5
|
+
|
|
6
|
+
const AP_COUNT = 24
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {Float32Array} data — mono PCM, processed in place
|
|
10
|
+
* @param {object} opts — { decay=0.6 (0..1), tension=0.5 (0..1 → allpass coefficient),
|
|
11
|
+
* damping=0.4, mix=0.35, fs=44100 }
|
|
12
|
+
*/
|
|
13
|
+
export default function spring (data, { decay = 0.6, tension = 0.5, damping = 0.4, mix = 0.35, fs = 44100 } = {}) {
|
|
14
|
+
let sc = fs / 44100
|
|
15
|
+
let a = 0.3 + 0.45 * tension // dispersion allpass coefficient
|
|
16
|
+
let aps = Array.from({ length: AP_COUNT }, () => ({ x1: 0, y1: 0 }))
|
|
17
|
+
let loop = { buf: new Float64Array(Math.max(1, Math.round(1801 * sc))), i: 0 }
|
|
18
|
+
let pre = { buf: new Float64Array(Math.max(1, Math.round(541 * sc))), i: 0 }
|
|
19
|
+
let lp = 0, fb = 0.55 + 0.42 * decay
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < data.length; i++) {
|
|
22
|
+
// early dispersion into the loop
|
|
23
|
+
let x = data[i] * 0.6 + loop.buf[loop.i] * fb
|
|
24
|
+
|
|
25
|
+
// first-order allpass chain: y = a·x + x1 − a·y1
|
|
26
|
+
for (let ap of aps) {
|
|
27
|
+
let y = a * x + ap.x1 - a * ap.y1
|
|
28
|
+
ap.x1 = x; ap.y1 = y
|
|
29
|
+
x = y
|
|
30
|
+
}
|
|
31
|
+
lp = x * (1 - damping) + lp * damping
|
|
32
|
+
loop.buf[loop.i] = lp
|
|
33
|
+
if (++loop.i >= loop.buf.length) loop.i = 0
|
|
34
|
+
|
|
35
|
+
let wetPre = pre.buf[pre.i]
|
|
36
|
+
pre.buf[pre.i] = lp
|
|
37
|
+
if (++pre.i >= pre.buf.length) pre.i = 0
|
|
38
|
+
|
|
39
|
+
data[i] = data[i] * (1 - mix) + (lp * 0.7 + wetPre * 0.3) * mix
|
|
40
|
+
}
|
|
41
|
+
return data
|
|
42
|
+
}
|