@audio/denoise-detect 0.1.2 → 0.1.3
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/denoise.js +32 -24
- package/package.json +1 -1
package/denoise.js
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
// in `data` and dispatches to the most suitable single-pass method.
|
|
3
3
|
//
|
|
4
4
|
// Classification (single STFT sweep over the input):
|
|
5
|
-
// -
|
|
6
|
-
// -
|
|
7
|
-
// -
|
|
8
|
-
// -
|
|
9
|
-
// -
|
|
10
|
-
// -
|
|
11
|
-
// - otherwise → wiener (transparent broadband)
|
|
5
|
+
// - hum — narrow peaks at mains harmonics (Goertzel) → dehum
|
|
6
|
+
// - click — high kurtosis of AR residual → declick
|
|
7
|
+
// - hi — 5–9 kHz / mid energy ratio → deesser
|
|
8
|
+
// - lf — LF/mid energy ratio → dewind
|
|
9
|
+
// - nonstationarity — frame-energy CV → omlsa
|
|
10
|
+
// - otherwise → wiener (transparent broadband)
|
|
12
11
|
//
|
|
12
|
+
// dereverb has no reliable single-pass signature, so auto-mode never selects it —
|
|
13
|
+
// reach it explicitly via `denoise(data, { force: 'dereverb' })` or `dereverb()`.
|
|
13
14
|
// Returns { out, plan } so callers can inspect which method ran.
|
|
14
15
|
|
|
15
16
|
import { stftAnalyse } from '@audio/stft'
|
|
@@ -56,7 +57,7 @@ export function classify(data, fs = 44100) {
|
|
|
56
57
|
let f = k * fs / N
|
|
57
58
|
if (f < 200) lf += p
|
|
58
59
|
else if (f < 2000) mf += p
|
|
59
|
-
else if (f < 9000) hi += p
|
|
60
|
+
else if (f >= 5000 && f < 9000) hi += p // sibilance band, kept specific (not 2–9 kHz)
|
|
60
61
|
}
|
|
61
62
|
lfSum += lf; mfSum += mf; hiSum += hi
|
|
62
63
|
frameVar.push(lf + mf + hi)
|
|
@@ -91,23 +92,30 @@ export function classify(data, fs = 44100) {
|
|
|
91
92
|
// HI/MF ratio
|
|
92
93
|
let hiRatio = hiSum / Math.max(mfSum, 1e-30)
|
|
93
94
|
|
|
94
|
-
// Click score: AR
|
|
95
|
+
// Click score: max AR-residual excess kurtosis over windows spanning the whole
|
|
96
|
+
// signal. Clicks are sparse and impulsive — a fixed prefix window can miss them
|
|
97
|
+
// entirely, so scan across and take the peak (the window holding a click spikes).
|
|
95
98
|
let clickScore = 0
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
let
|
|
101
|
-
|
|
102
|
-
let
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
99
|
+
let cw = 4096
|
|
100
|
+
if (data.length >= cw) {
|
|
101
|
+
let stride = Math.max(cw, Math.floor((data.length - cw) / 8) || cw)
|
|
102
|
+
for (let start = 0; start + cw <= data.length; start += stride) {
|
|
103
|
+
let seg = data.subarray(start, start + cw)
|
|
104
|
+
try {
|
|
105
|
+
let { a } = arFit(seg, 30)
|
|
106
|
+
let resid = new Float64Array(cw), mean = 0, m2 = 0, m4 = 0
|
|
107
|
+
for (let i = 30; i < cw; i++) {
|
|
108
|
+
let s = seg[i]
|
|
109
|
+
for (let k = 1; k <= 30; k++) s += a[k] * seg[i - k]
|
|
110
|
+
resid[i] = s; mean += s
|
|
111
|
+
}
|
|
112
|
+
mean /= (cw - 30)
|
|
113
|
+
for (let i = 30; i < cw; i++) { let d = resid[i] - mean; m2 += d * d; m4 += d * d * d * d }
|
|
114
|
+
m2 /= (cw - 30); m4 /= (cw - 30)
|
|
115
|
+
let ex = m2 > 0 ? m4 / (m2 * m2) - 3 : 0 // excess kurtosis
|
|
116
|
+
if (ex > clickScore) clickScore = ex
|
|
117
|
+
} catch {}
|
|
118
|
+
}
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
// Stationarity: variance-of-variances of frame energy
|