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