@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.
- package/README.md +5 -0
- package/package.json +29 -0
- package/subbass.js +48 -0
package/README.md
ADDED
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
|
+
}
|