@audio/filter-korg35 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/korg35.js +72 -0
  2. package/package.json +38 -0
package/korg35.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Korg MS-20 style filter (Korg35).
3
+ * Zero-delay feedback (ZDF) via trapezoidal integration.
4
+ *
5
+ * Ref: Zavalishin, "The Art of VA Filter Design" (2012), Ch. 5.
6
+ * Stilson & Smith, "Analyzing the Korg MS-20 Filter" (1996).
7
+ *
8
+ * The Korg35 is a 2-pole filter with nonlinear feedback. Unlike SVF, it uses two
9
+ * cascaded one-pole sections with the output fed back to the input. The nonlinear
10
+ * saturation in the feedback path gives the aggressive MS-20 character.
11
+ *
12
+ * Lowpass: output from second stage.
13
+ * Highpass: input minus lowpass output — exactly complementary only at
14
+ * resonance=0; the feedback term in the HP tap makes LP+HP diverge from the
15
+ * input as resonance increases (see readme).
16
+ *
17
+ * Only 2 real poles feed the resonance loop (vs. moog-ladder's 4), so the
18
+ * loop phase never reaches -180° at any audible, non-negligible-gain
19
+ * frequency: resonance adds damping/saturation character but no resonant
20
+ * peak and no sustained self-oscillation at any resonance setting (see readme).
21
+ *
22
+ * @module audio-filter/analog/korg35
23
+ * @param {Float32Array|Float64Array} data - audio buffer (modified in place)
24
+ * @param {Object} params
25
+ * @param {number} [params.fc=1000] - cutoff frequency Hz
26
+ * @param {number} [params.resonance=0] - resonance 0–1 (aggressive damping/saturation; no self-oscillation — see readme)
27
+ * @param {number} [params.fs=44100] - sample rate
28
+ * @param {number} [params.drive=1] - input drive (saturation amount)
29
+ * @param {string} [params.type='lowpass'] - 'lowpass' or 'highpass'
30
+ */
31
+
32
+ let {tan, tanh, PI, min} = Math
33
+
34
+ export default function korg35 (data, params) {
35
+ let fc = params.fc || 1000
36
+ let res = params.resonance != null ? params.resonance : 0
37
+ let fs = params.fs || 44100
38
+ let drive = params.drive ?? 1
39
+ let hp = params.type === 'highpass'
40
+
41
+ // Trapezoidal integrator coefficient
42
+ let g = tan(PI * min(fc, fs * 0.49) / fs)
43
+ let G = g / (1 + g) // one-pole gain
44
+ let G2 = G * G // two-pole gain
45
+ let k = res * 2 // feedback coefficient: 0–2
46
+
47
+ // State: 2 one-pole integrator states
48
+ if (!params._s) params._s = new Float64Array(2)
49
+ let s = params._s
50
+
51
+ for (let i = 0, n = data.length; i < n; i++) {
52
+ // Estimate 2-pole output from state (zero-input cascade response)
53
+ let S = G * s[0] + s[1]
54
+
55
+ // Implicit feedback solve
56
+ let u = (data[i] - k * S) / (1 + k * G2)
57
+
58
+ // Nonlinear saturation in feedback path (MS-20 character)
59
+ u = tanh(u * drive)
60
+
61
+ // Two cascaded trapezoidal one-pole lowpass stages
62
+ let y1 = G * (u - s[0]) + s[0]
63
+ s[0] = 2 * y1 - s[0]
64
+
65
+ let y2 = G * (y1 - s[1]) + s[1]
66
+ s[1] = 2 * y2 - s[1]
67
+
68
+ data[i] = hp ? data[i] - y2 - k * y2 : y2
69
+ }
70
+
71
+ return data
72
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@audio/filter-korg35",
3
+ "version": "1.0.0",
4
+ "description": "Korg MS-20 style filter (Korg35)",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "korg35.js",
8
+ "exports": {
9
+ ".": "./korg35.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "korg35.js"
14
+ ],
15
+ "keywords": [
16
+ "audio",
17
+ "dsp",
18
+ "filter",
19
+ "korg35"
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-korg35"
27
+ },
28
+ "homepage": "https://github.com/audiojs/filter/tree/main/packages/filter-korg35",
29
+ "bugs": {
30
+ "url": "https://github.com/audiojs/filter/issues"
31
+ },
32
+ "engines": {
33
+ "node": ">=18"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ }
38
+ }