fef783aVoice-driven rhythm capture: voices trained by example, takes classified onto them, synth playbackJeremy Magland 1/** In-place radix-2 complex FFT; lengths must be powers of two. Small and
2 * sufficient for the analysis windows used here (1024 and 4096). */
3export function fft(re: Float32Array, im: Float32Array): void {
4 const n = re.length;
5 if ((n & (n - 1)) !== 0) throw new Error(`fft wants a power of two, got ${n}`);
6 for (let i = 1, j = 0; i < n; i++) {
7 let bit = n >> 1;
8 for (; j & bit; bit >>= 1) j ^= bit;
9 j ^= bit;
10 if (i < j) {
11 let t = re[i]; re[i] = re[j]; re[j] = t;
12 t = im[i]; im[i] = im[j]; im[j] = t;
13 }
14 }
15 for (let len = 2; len <= n; len <<= 1) {
16 const half = len >> 1;
17 const ang = (-2 * Math.PI) / len;
18 const wr = Math.cos(ang), wi = Math.sin(ang);
19 for (let i = 0; i < n; i += len) {
20 let cr = 1, ci = 0;
21 for (let k = 0; k < half; k++) {
22 const ar = re[i + k], ai = im[i + k];
23 const br = re[i + k + half] * cr - im[i + k + half] * ci;
24 const bi = re[i + k + half] * ci + im[i + k + half] * cr;
25 re[i + k] = ar + br; im[i + k] = ai + bi;
26 re[i + k + half] = ar - br; im[i + k + half] = ai - bi;
27 const t = cr * wr - ci * wi;
28 ci = cr * wi + ci * wr;
29 cr = t;
30 }
31 }
32 }
33}
35/** Hann-windowed power spectrum of x zero-padded to size n (power of two);
36 * returns n/2 bins. Bin k is frequency k * sr / n. */
37export function powerSpectrum(x: Float32Array, n: number): Float32Array {
38 const re = new Float32Array(n);
39 const im = new Float32Array(n);
40 const m = Math.min(x.length, n);
41 for (let i = 0; i < m; i++) {
42 const w = 0.5 - 0.5 * Math.cos((2 * Math.PI * i) / (m - 1 || 1));
43 re[i] = x[i] * w;
44 }
45 fft(re, im);
46 const p = new Float32Array(n >> 1);
47 for (let k = 0; k < p.length; k++) p[k] = re[k] * re[k] + im[k] * im[k];
48 return p;
49}