@audio/filter-moog-ladder 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/moog-ladder.js +61 -0
- package/package.json +38 -0
package/moog-ladder.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moog 4-pole transistor ladder filter (-24 dB/oct lowpass with resonance).
|
|
3
|
+
* Zero-delay feedback (ZDF) via trapezoidal integration.
|
|
4
|
+
*
|
|
5
|
+
* Ref: Zavalishin, "The Art of VA Filter Design" (2012), Ch. 6.
|
|
6
|
+
* Välimäki & Smith, "Discrete-Time Synthesis of the Moog VCF" (2006).
|
|
7
|
+
*
|
|
8
|
+
* 4 cascaded one-pole trapezoidal integrators with implicit feedback solving.
|
|
9
|
+
* No unit delay in feedback path — resonance tracks cutoff accurately to Nyquist.
|
|
10
|
+
*
|
|
11
|
+
* @module audio-filter/analog/moog-ladder
|
|
12
|
+
* @param {Float32Array|Float64Array} data - audio buffer (modified in place)
|
|
13
|
+
* @param {Object} params
|
|
14
|
+
* @param {number} [params.fc=1000] - cutoff frequency Hz
|
|
15
|
+
* @param {number} [params.resonance=0] - resonance 0–1 (self-oscillation at 1)
|
|
16
|
+
* @param {number} [params.fs=44100] - sample rate
|
|
17
|
+
* @param {number} [params.drive=1] - input drive (saturation amount)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
let {tan, tanh, PI, min} = Math
|
|
21
|
+
|
|
22
|
+
export default function moogLadder (data, params) {
|
|
23
|
+
let fc = params.fc || 1000
|
|
24
|
+
let res = params.resonance != null ? params.resonance : 0
|
|
25
|
+
let fs = params.fs || 44100
|
|
26
|
+
let drive = params.drive ?? 1
|
|
27
|
+
|
|
28
|
+
// Trapezoidal integrator coefficient
|
|
29
|
+
let g = tan(PI * min(fc, fs * 0.49) / fs)
|
|
30
|
+
let G = g / (1 + g) // one-pole gain factor
|
|
31
|
+
let G2 = G * G, G3 = G2 * G, G4 = G3 * G
|
|
32
|
+
let k = res * 4 // feedback coefficient: 0–4, self-oscillation at 4
|
|
33
|
+
|
|
34
|
+
// State: 4 one-pole integrator states
|
|
35
|
+
if (!params._s) params._s = new Float64Array(4)
|
|
36
|
+
let s = params._s
|
|
37
|
+
|
|
38
|
+
for (let i = 0, n = data.length; i < n; i++) {
|
|
39
|
+
// Estimate output from current state (zero-input response of cascade)
|
|
40
|
+
// S = weighted sum of integrator states propagated through the cascade
|
|
41
|
+
let S = G3 * s[0] + G2 * s[1] + G * s[2] + s[3]
|
|
42
|
+
|
|
43
|
+
// Implicit feedback solve: u*(1 + k*G^4) = input - k*S
|
|
44
|
+
let u = (data[i] - k * S) / (1 + k * G4)
|
|
45
|
+
|
|
46
|
+
// Nonlinear saturation at input (transistor ladder characteristic)
|
|
47
|
+
u = tanh(u * drive)
|
|
48
|
+
|
|
49
|
+
// 4 cascaded trapezoidal one-pole lowpass stages
|
|
50
|
+
let v = u
|
|
51
|
+
for (let j = 0; j < 4; j++) {
|
|
52
|
+
let y = G * (v - s[j]) + s[j] // trapezoidal integrator output
|
|
53
|
+
s[j] = 2 * y - s[j] // state update (2y - s for trapezoidal rule)
|
|
54
|
+
v = y
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
data[i] = v
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return data
|
|
61
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@audio/filter-moog-ladder",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Moog 4-pole transistor ladder filter (-24 dB/oct lowpass with resonance)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "moog-ladder.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./moog-ladder.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"moog-ladder.js"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"audio",
|
|
17
|
+
"dsp",
|
|
18
|
+
"filter",
|
|
19
|
+
"moog-ladder"
|
|
20
|
+
],
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "Dmitry Iv. <dfcreative@gmail.com>",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/audiojs/filter.git",
|
|
26
|
+
"directory": "packages/filter-moog-ladder"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/audiojs/filter/tree/main/packages/filter-moog-ladder",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/audiojs/filter/issues"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
}
|
|
38
|
+
}
|