@audio/spectral-freeze 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/freeze.js +47 -0
  2. package/package.json +32 -0
package/freeze.js ADDED
@@ -0,0 +1,47 @@
1
+ // Spectral freeze — sustain one spectral frame: keep its magnitudes, advance phase by
2
+ // the bin frequency each hop with per-bin random phase dispersion against buzz
3
+ // (Ableton Spectral Time / Paulstretch-freeze class).
4
+
5
+ import { fft, ifft } from 'fourier-transform'
6
+
7
+ /**
8
+ * @param {Float32Array} data — mono PCM
9
+ * @param {object} opts — { fs=44100, at=0 (s, frame to freeze), duration=2 (s of output),
10
+ * frameSize=4096, seed=1 }
11
+ * @returns {Float32Array}
12
+ */
13
+ export default function freeze (data, { fs = 44100, at = 0, duration = 2, frameSize = 4096, seed = 1 } = {}) {
14
+ let half = frameSize / 2, hop = frameSize / 4
15
+ let win = new Float64Array(frameSize)
16
+ for (let i = 0; i < frameSize; i++) win[i] = 0.5 - 0.5 * Math.cos(2 * Math.PI * i / frameSize)
17
+ let start = Math.min(Math.max(0, Math.round(at * fs)), Math.max(0, data.length - frameSize))
18
+ let buf = new Float64Array(frameSize)
19
+ for (let i = 0; i < frameSize; i++) buf[i] = (data[start + i] || 0) * win[i]
20
+ let fr = fft(buf)
21
+ let mag = new Float64Array(half + 1)
22
+ for (let k = 0; k <= half; k++) mag[k] = Math.hypot(fr[0][k], fr[1][k])
23
+
24
+ let n = Math.round(duration * fs)
25
+ let out = new Float64Array(n + frameSize)
26
+ let norm = new Float64Array(n + frameSize)
27
+ let s = seed >>> 0 || 1
28
+ let rand = () => { s = (s * 1103515245 + 12345) & 0x7fffffff; return s / 0x7fffffff }
29
+ let phase = new Float64Array(half + 1)
30
+ for (let k = 0; k <= half; k++) phase[k] = rand() * 2 * Math.PI
31
+ let re = new Float64Array(half + 1), im = new Float64Array(half + 1)
32
+ for (let pos = 0; pos < n; pos += hop) {
33
+ for (let k = 0; k <= half; k++) {
34
+ phase[k] += 2 * Math.PI * k * hop / frameSize + (rand() - 0.5) * 0.3
35
+ re[k] = mag[k] * Math.cos(phase[k])
36
+ im[k] = mag[k] * Math.sin(phase[k])
37
+ }
38
+ let y = ifft(re, im)
39
+ for (let i = 0; i < frameSize; i++) {
40
+ out[pos + i] += y[i] * win[i]
41
+ norm[pos + i] += win[i] * win[i]
42
+ }
43
+ }
44
+ let res = new Float32Array(n)
45
+ for (let i = 0; i < n; i++) res[i] = norm[i] > 1e-9 ? out[i] / norm[i] : 0
46
+ return res
47
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@audio/spectral-freeze",
3
+ "version": "1.0.0",
4
+ "description": "Spectral freeze — sustain a spectral frame with phase dispersion (Spectral Time class)",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "freeze.js",
8
+ "exports": {
9
+ ".": "./freeze.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "freeze.js"
14
+ ],
15
+ "dependencies": {
16
+ "fourier-transform": "^2.2.0"
17
+ },
18
+ "keywords": [
19
+ "audio",
20
+ "dsp",
21
+ "spectral",
22
+ "freeze"
23
+ ],
24
+ "license": "MIT",
25
+ "author": "Dmitry Iv. <dfcreative@gmail.com>",
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ }
32
+ }