Files
2026-08-28 09:48:34 +09:00

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 };
}