64 lines
2.3 KiB
JavaScript
64 lines
2.3 KiB
JavaScript
function assertPowerOfTwo(value) {
|
|
if (!Number.isInteger(value) || value < 2 || (value & (value - 1)) !== 0) {
|
|
throw new Error(`FFT 크기는 2 이상의 2의 거듭제곱이어야 합니다: ${value}`);
|
|
}
|
|
}
|
|
|
|
export function fftInPlace(real, imaginary, inverse = false) {
|
|
const size = real.length;
|
|
if (imaginary.length !== size) throw new Error("FFT real/imaginary 길이가 다릅니다.");
|
|
assertPowerOfTwo(size);
|
|
|
|
for (let index = 1, reversed = 0; index < size; index += 1) {
|
|
let bit = size >> 1;
|
|
while (reversed & bit) {
|
|
reversed ^= bit;
|
|
bit >>= 1;
|
|
}
|
|
reversed ^= bit;
|
|
if (index < reversed) {
|
|
[real[index], real[reversed]] = [real[reversed], real[index]];
|
|
[imaginary[index], imaginary[reversed]] = [imaginary[reversed], imaginary[index]];
|
|
}
|
|
}
|
|
|
|
const direction = inverse ? 1 : -1;
|
|
for (let length = 2; length <= size; length <<= 1) {
|
|
const angle = (direction * 2 * Math.PI) / length;
|
|
const stepReal = Math.cos(angle);
|
|
const stepImaginary = Math.sin(angle);
|
|
for (let start = 0; start < size; start += length) {
|
|
let twiddleReal = 1;
|
|
let twiddleImaginary = 0;
|
|
const halfLength = length >> 1;
|
|
for (let offset = 0; offset < halfLength; offset += 1) {
|
|
const evenIndex = start + offset;
|
|
const oddIndex = evenIndex + halfLength;
|
|
const oddReal = real[oddIndex] * twiddleReal - imaginary[oddIndex] * twiddleImaginary;
|
|
const oddImaginary = real[oddIndex] * twiddleImaginary + imaginary[oddIndex] * twiddleReal;
|
|
real[oddIndex] = real[evenIndex] - oddReal;
|
|
imaginary[oddIndex] = imaginary[evenIndex] - oddImaginary;
|
|
real[evenIndex] += oddReal;
|
|
imaginary[evenIndex] += oddImaginary;
|
|
const nextTwiddleReal = twiddleReal * stepReal - twiddleImaginary * stepImaginary;
|
|
twiddleImaginary = twiddleReal * stepImaginary + twiddleImaginary * stepReal;
|
|
twiddleReal = nextTwiddleReal;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inverse) {
|
|
for (let index = 0; index < size; index += 1) {
|
|
real[index] /= size;
|
|
imaginary[index] /= size;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function fftReal(samples) {
|
|
const real = Float64Array.from(samples);
|
|
const imaginary = new Float64Array(real.length);
|
|
fftInPlace(real, imaginary);
|
|
return { real, imaginary };
|
|
}
|