@audio/effect-subbass 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 (3) hide show
  1. package/README.md +5 -0
  2. package/package.json +29 -0
  3. package/subbass.js +48 -0
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @audio/effect-subbass
2
+
3
+ > Psychoacoustic bass enhancer — harmonic synthesis below cutoff (MaxxBass/RBass class)
4
+
5
+ Planned — not implemented yet. See the umbrella README for status and sources.
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@audio/effect-subbass",
3
+ "version": "1.0.0",
4
+ "description": "Psychoacoustic bass enhancer — harmonic synthesis below cutoff (MaxxBass/RBass class)",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "keywords": [
8
+ "audio",
9
+ "dsp",
10
+ "effect",
11
+ "subbass"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "Dmitry Iv. <dfcreative@gmail.com>",
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "main": "subbass.js",
19
+ "exports": {
20
+ ".": "./subbass.js",
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "subbass.js"
25
+ ],
26
+ "publishConfig": {
27
+ "access": "public"
28
+ }
29
+ }
package/subbass.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Psychoacoustic bass enhancer (MaxxBass/RBass class) — extract the band below cutoff,
3
+ * generate its harmonic series with an even/odd waveshaper, band-limit the harmonics to
4
+ * the audible low-mids, and mix against dry. Small speakers reproduce the harmonics; the
5
+ * ear reconstructs the missing fundamental.
6
+ */
7
+
8
+ let { sin, tanh, PI } = Math
9
+
10
+ // state-variable filter tick: returns [lp, bp, hp], mutates s = {l, b}
11
+ function svf (s, x, f, q) {
12
+ s.l += f * s.b
13
+ let h = x - s.l - q * s.b
14
+ s.b += f * h
15
+ return [s.l, s.b, h]
16
+ }
17
+
18
+ export default function subbass (data, params) {
19
+ let freq = params.freq ?? 80 // sub cutoff, Hz — harmonics built from below here
20
+ let amount = params.amount ?? 0.5 // harmonic level 0..1
21
+ let drive = params.drive ?? 0.5 // waveshaper intensity 0..1
22
+ let keep = params.keep ?? 1 // how much original sub to keep (0 = replace)
23
+ let fs = params.fs || 44100
24
+
25
+ if (!params._sub) {
26
+ params._sub = { l: 0, b: 0 } // sub extraction LP
27
+ params._out = { l: 0, b: 0 } // harmonic band BP
28
+ params._dc = 0
29
+ }
30
+ let f1 = 2 * sin(PI * Math.min(freq, fs / 4) / fs)
31
+ let f2 = 2 * sin(PI * Math.min(freq * 2.5, fs / 4) / fs)
32
+ let g = 1 + drive * 6
33
+ let aDc = 1 - Math.exp(-2 * PI * 10 / fs)
34
+
35
+ for (let i = 0, l = data.length; i < l; i++) {
36
+ let x = data[i]
37
+ let [sub] = svf(params._sub, x, f1, 1.2)
38
+ // even harmonics from |x| rectification + odd from tanh — full series
39
+ let h = tanh(sub * g) * 0.6 + Math.abs(sub) * g * 0.4
40
+ params._dc += aDc * (h - params._dc) // DC-block the rectifier output
41
+ h -= params._dc
42
+ // keep harmonics in the speaker-friendly band around 2–5× freq
43
+ svf(params._out, h, f2, 0.8)
44
+ let harm = params._out.b // bandpass output
45
+ data[i] = x - (1 - keep) * sub + amount * 2 * harm
46
+ }
47
+ return data
48
+ }