@audio/auditory-gammatone 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 (2) hide show
  1. package/gammatone.js +76 -0
  2. package/package.json +30 -0
package/gammatone.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Gammatone auditory filter (cochlear model).
3
+ * Cascade of complex one-pole filters (4th-order by default).
4
+ */
5
+
6
+ let { cos, sin, exp, PI } = Math
7
+
8
+ /**
9
+ * Apply gammatone filter in-place.
10
+ * @param {Float32Array|Float64Array|Array<number>} data - Input/output buffer, filtered in-place
11
+ * @param {object} params - { fc: 1000, fs: 44100, order: 4 }, plus persisted `_`-prefixed cache/state
12
+ * @param {number} params.fc - Center frequency in Hz (default 1000)
13
+ * @param {number} params.fs - Sample rate in Hz (default 44100)
14
+ * @param {number} params.order - Cascade order, i.e. number of one-pole stages (default 4)
15
+ * @returns {Float32Array|Float64Array|Array<number>} data, filtered in-place
16
+ */
17
+ export default function gammatone (data, params) {
18
+ let fc = params.fc || 1000
19
+ let fs = params.fs || 44100
20
+ let order = params.order || 4
21
+
22
+ // ERB bandwidth
23
+ let erb = 24.7 * (4.37 * fc / 1000 + 1)
24
+ let b = 2 * PI * 1.019 * erb
25
+
26
+ let T = 1 / fs
27
+ let w = 2 * PI * fc * T
28
+ let a = exp(-b * T)
29
+ let cosW = a * cos(w), sinW = a * sin(w)
30
+
31
+ // Gain normalization: compute frequency-domain peak (at fc) from a test impulse,
32
+ // then scale so the filter has 0 dB at its center frequency.
33
+ let gain = 1
34
+ if (params._gain == null || params._fc !== fc || params._order !== order || params._fs !== fs) {
35
+ let ts = new Float64Array(order * 2)
36
+ let irLen = Math.max(4096, Math.round(fs / erb * 10)) // enough cycles to capture the ringing
37
+ // Sum impulse response at center frequency (= DFT at fc = sum of h[n]*e^(-j*w*n))
38
+ let re = 0, im = 0
39
+ for (let i = 0; i < irLen; i++) {
40
+ let v = i === 0 ? 1 : 0
41
+ for (let j = 0; j < order; j++) {
42
+ let sre = ts[j*2], sim = ts[j*2+1]
43
+ ts[j*2] = cosW * sre - sinW * sim + v
44
+ ts[j*2+1] = sinW * sre + cosW * sim
45
+ v = ts[j*2]
46
+ }
47
+ let phase = w * i
48
+ re += v * cos(phase)
49
+ im -= v * sin(phase)
50
+ }
51
+ let mag = Math.sqrt(re * re + im * im)
52
+ params._gain = mag > 0 ? 1 / mag : 1
53
+ params._fc = fc; params._order = order; params._fs = fs
54
+ }
55
+ gain = params._gain
56
+
57
+ if (!params._s || params._s.length !== order * 2) params._s = new Float64Array(order * 2)
58
+ let s = params._s
59
+
60
+ for (let i = 0, n = data.length; i < n; i++) {
61
+ let x = data[i] * gain
62
+
63
+ for (let j = 0; j < order; j++) {
64
+ let re = s[j * 2], im = s[j * 2 + 1]
65
+ let newRe = cosW * re - sinW * im + x
66
+ let newIm = sinW * re + cosW * im
67
+ s[j * 2] = newRe
68
+ s[j * 2 + 1] = newIm
69
+ x = newRe
70
+ }
71
+
72
+ data[i] = x
73
+ }
74
+
75
+ return data
76
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@audio/auditory-gammatone",
3
+ "version": "1.0.0",
4
+ "description": "Gammatone auditory filter (cochlear model)",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "gammatone.js",
8
+ "exports": {
9
+ ".": "./gammatone.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "gammatone.js"
14
+ ],
15
+ "keywords": [
16
+ "audio",
17
+ "dsp",
18
+ "auditory",
19
+ "filterbank",
20
+ "gammatone"
21
+ ],
22
+ "license": "MIT",
23
+ "author": "Dmitry Iv. <dfcreative@gmail.com>",
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }