@audio/effect-sbr 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/sbr.js +52 -0
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@audio/effect-sbr",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Spectral band replication — extend HF from midband harmonics (aural exciter family; De-Slop bandwidth recovery)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"keywords": [
|
|
8
|
+
"audio",
|
|
9
|
+
"dsp",
|
|
10
|
+
"sbr",
|
|
11
|
+
"bandwidth-extension"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Dmitry Iv. <dfcreative@gmail.com>",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"main": "sbr.js",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./sbr.js",
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"sbr.js"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/sbr.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spectral band replication (aural-exciter family) — recover bandwidth lost to lossy
|
|
3
|
+
* encoding: take the top octave still present below `cutoff`, regenerate its harmonic
|
|
4
|
+
* series with a waveshaper (harmonics land above cutoff), highpass at cutoff, and mix
|
|
5
|
+
* at a level tracking the source band's envelope. De-Slop-style HF reconstruction.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
let { sin, tanh, exp, PI } = Math
|
|
9
|
+
|
|
10
|
+
function svf (s, x, f, q) {
|
|
11
|
+
s.l += f * s.b
|
|
12
|
+
let h = x - s.l - q * s.b
|
|
13
|
+
s.b += f * h
|
|
14
|
+
return h // highpass leg
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function sbr (data, params) {
|
|
18
|
+
let cutoff = params.cutoff ?? 8000 // where the source content dies, Hz
|
|
19
|
+
let amount = params.amount ?? 0.5 // replication level 0..1
|
|
20
|
+
let drive = params.drive ?? 0.5
|
|
21
|
+
let fs = params.fs || 44100
|
|
22
|
+
|
|
23
|
+
if (!params._src) {
|
|
24
|
+
params._src = { l: 0, b: 0 } // source band: bandpass cutoff/2..cutoff
|
|
25
|
+
params._hp1 = { l: 0, b: 0 } // output highpass at cutoff (2 cascaded)
|
|
26
|
+
params._hp2 = { l: 0, b: 0 }
|
|
27
|
+
params._env = 0; params._henv = 0; params._dc = 0
|
|
28
|
+
}
|
|
29
|
+
let fSrc = 2 * sin(PI * Math.min(cutoff * 0.7, fs / 4) / fs)
|
|
30
|
+
let fCut = 2 * sin(PI * Math.min(cutoff, fs / 4) / fs)
|
|
31
|
+
let g = 2 + drive * 8
|
|
32
|
+
let aEnv = 1 - exp(-2 * PI * 20 / fs)
|
|
33
|
+
|
|
34
|
+
for (let i = 0, l = data.length; i < l; i++) {
|
|
35
|
+
let x = data[i]
|
|
36
|
+
// source band = bandpass leg around the top of the surviving spectrum
|
|
37
|
+
svf(params._src, x, fSrc, 0.7)
|
|
38
|
+
let band = params._src.b
|
|
39
|
+
// harmonic series extends past cutoff; even (|x|) + odd (tanh), DC-blocked
|
|
40
|
+
let h = tanh(band * g) * 0.6 + Math.abs(band) * g * 0.55
|
|
41
|
+
params._dc += 0.002 * (h - params._dc)
|
|
42
|
+
h -= params._dc
|
|
43
|
+
h = svf(params._hp1, h, fCut, 0.7)
|
|
44
|
+
h = svf(params._hp2, h, fCut, 0.7)
|
|
45
|
+
// envelope-match: replicated HF follows the source band's energy
|
|
46
|
+
params._env += aEnv * (band * band - params._env)
|
|
47
|
+
params._henv += aEnv * (h * h - params._henv)
|
|
48
|
+
let norm = params._henv > 1e-12 ? Math.sqrt(params._env / params._henv) : 0
|
|
49
|
+
data[i] = x + amount * Math.min(4, norm) * 0.5 * h
|
|
50
|
+
}
|
|
51
|
+
return data
|
|
52
|
+
}
|