@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.
Files changed (3) hide show
  1. package/README.md +5 -0
  2. package/package.json +29 -0
  3. package/sbr.js +52 -0
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @audio/effect-sbr
2
+
3
+ > Spectral band replication — extend HF from midband harmonics (aural exciter family; De-Slop bandwidth recovery)
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-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
+ }