src/FFT.js
/**
* Class containing methods for Fast Fourier Transform
* @class
*/
export default class FFT {
/**
* Get Hamming window
* @param {Number} bufferSize - windows size
* @return {Array} wnd - Hamming window
*/
static getHammingWindow(bufferSize) {
const a = 25 / 46;
const b = 21 / 46;
const scale = 1 / bufferSize / 0.54;
const sqrtBufferSize = Math.sqrt(bufferSize);
const factor = (Math.PI * 2) / bufferSize;
let wnd = [];
for (let i = 0; i < bufferSize; i++) {
wnd[i] = sqrtBufferSize * (scale * (a - b * Math.cos(factor * i))) ;
}
return wnd;
}
/**
* Computes FFT and converts the results to magnitude representation
* @param {Array} re - the real part of the input data and the magnitude of the output data
* @param {Array} im - the imaginary part of the input data
*/
static getSpectrum(re, im) {
const direction = -1;
const n = re.length;
const bits = Math.round(Math.log(n) / Math.log(2));
const twoPI = Math.PI * 2;
if (n != (1 << bits))
throw new Error("FFT data must be power of 2");
let localN;
let j = 0;
for (let i = 0; i < n-1; i++) {
if (i < j) {
let temp = re[j];
re[j] = re[i];
re[i] = temp;
temp = im[j];
im[j] = im[i];
im[i] = temp;
}
let k = n / 2;
while ((k >= 1) && (k - 1 < j)) {
j = j - k;
k = k / 2;
}
j = j + k;
}
for(let m = 1; m <= bits; m++) {
localN = 1 << m;
let Wjk_r = 1;
let Wjk_i = 0;
let theta = twoPI / localN;
let Wj_r = Math.cos(theta);
let Wj_i = direction * Math.sin(theta);
let nby2 = localN / 2;
for (j = 0; j < nby2; j++) {
for (let k = j; k < n; k += localN) {
let id = k + nby2;
let tempr = Wjk_r * re[id] - Wjk_i * im[id];
let tempi = Wjk_r * im[id] + Wjk_i * re[id];
re[id] = re[k] - tempr;
im[id] = im[k] - tempi;
re[k] += tempr;
im[k] += tempi;
}
let wtemp = Wjk_r;
Wjk_r = Wj_r * Wjk_r - Wj_i * Wjk_i;
Wjk_i = Wj_r * Wjk_i + Wj_i * wtemp;
}
}
for (let i = 0; i < re.length; i++) {
let pow = re[i] * re[i] + im[i] * im[i];
//im[i] = Math.atan2(im[i], re[i]);
re[i] = pow;
}
for (let i = 0; i < re.length; i++)
re[i] = Math.sqrt(re[i]);
}
}