89 if (size < 2 || (size & (size - 1)) != 0)
91#if defined(DSPARK_NO_EXCEPTIONS)
92 assert(
false &&
"FFTComplex size must be a power of two >= 2");
95 throw std::invalid_argument(
"FFTComplex size must be a power of two >= 2");
100 computeBitReversalTable();
107 [[nodiscard]]
size_t getSize() const noexcept {
return size_; }
121 butterflyPass(data,
false);
132 butterflyPass(data,
true);
134 const T invN = T(1) /
static_cast<T
>(size_);
135 const size_t total = size_ * 2;
136 for (
size_t i = 0; i < total; ++i)
146 static inline __m128 addsubPs(__m128 a, __m128 b)
noexcept
148#if defined(__SSE3__) || defined(_MSC_VER)
149 return _mm_addsub_ps(a, b);
151 return _mm_add_ps(a, _mm_xor_ps(b, _mm_setr_ps(-0.0f, 0.0f, -0.0f, 0.0f)));
154 static inline __m128d addsubPd(__m128d a, __m128d b)
noexcept
156#if defined(__SSE3__) || defined(_MSC_VER)
157 return _mm_addsub_pd(a, b);
159 return _mm_add_pd(a, _mm_xor_pd(b, _mm_setr_pd(-0.0, 0.0)));
164 void computeTwiddles()
167 twiddles_.reserve((size_ - 1) * 2);
168 size_t numStages = 0;
169 for (
size_t s = size_; s > 1; s >>= 1) ++numStages;
175 for (
size_t stage = 0; stage < numStages; ++stage)
177 size_t halfStride = stride / 2;
178 for (
size_t k = 0; k < halfStride; ++k)
180 double angle = -2.0 * std::numbers::pi_v<double> *
static_cast<double>(k)
181 /
static_cast<double>(stride);
182 twiddles_.push_back(
static_cast<T
>(std::cos(angle)));
183 twiddles_.push_back(
static_cast<T
>(std::sin(angle)));
189 void computeBitReversalTable()
191 bitrev_.resize(size_);
193 for (
size_t s = size_; s > 1; s >>= 1) ++bits;
195 for (
size_t i = 0; i < size_; ++i)
199 for (
size_t b = 0; b < bits; ++b)
201 rev = (rev << 1) | (val & 1);
208 void bitReverse(T* data)
const noexcept
210 for (
size_t i = 0; i < size_; ++i)
212 size_t j = bitrev_[i];
215 std::swap(data[2 * i], data[2 * j]);
216 std::swap(data[2 * i + 1], data[2 * j + 1]);
221 void butterflyPass(T* data,
bool isInverse)
const noexcept
228 for (
size_t group = 0; group < size_; group += 2)
230 const size_t eIdx = 2 * group;
231 const size_t oIdx = eIdx + 2;
233 const T tr = data[oIdx];
234 const T ti = data[oIdx + 1];
235 data[oIdx] = data[eIdx] - tr;
236 data[oIdx + 1] = data[eIdx + 1] - ti;
238 data[eIdx + 1] += ti;
242 size_t twiddleOffset = 2;
245 while (stride <= size_)
247 size_t halfStride = stride / 2;
249 for (
size_t group = 0; group < size_; group += stride)
255 if constexpr (std::is_same_v<T, float>)
257 __m128 invTwMask = _mm_setzero_ps();
260 alignas(16)
static constexpr float kInvTw[4] = { 0.0f, -0.0f, 0.0f, -0.0f };
261 invTwMask = _mm_load_ps(kInvTw);
264 for (; k + 1 < halfStride; k += 2)
266 const size_t twIdx = twiddleOffset + k * 2;
267 const size_t eIdx = 2 * (group + k);
268 const size_t oIdx = 2 * (group + k + halfStride);
275 __m128 e = _mm_loadu_ps(&data[eIdx]);
276 __m128 o = _mm_loadu_ps(&data[oIdx]);
277 __m128 w = _mm_xor_ps(_mm_loadu_ps(&twiddles_[twIdx]), invTwMask);
279 __m128 o_re = _mm_shuffle_ps(o, o, _MM_SHUFFLE(2,2,0,0));
280 __m128 o_im = _mm_shuffle_ps(o, o, _MM_SHUFFLE(3,3,1,1));
281 __m128 w_sw = _mm_shuffle_ps(w, w, _MM_SHUFFLE(2,3,0,1));
283 __m128 p1 = _mm_mul_ps(w, o_re);
284 __m128 p2 = _mm_mul_ps(w_sw, o_im);
288 __m128 t = addsubPs(p1, p2);
290 _mm_storeu_ps(&data[eIdx], _mm_add_ps(e, t));
291 _mm_storeu_ps(&data[oIdx], _mm_sub_ps(e, t));
297 if constexpr (std::is_same_v<T, double>)
299 __m128d invTwMaskD = _mm_setzero_pd();
302 alignas(16)
static constexpr double kInvTwD[2] = { 0.0, -0.0 };
303 invTwMaskD = _mm_load_pd(kInvTwD);
306 for (; k < halfStride; ++k)
308 const size_t twIdx = twiddleOffset + k * 2;
309 const size_t eIdx = 2 * (group + k);
310 const size_t oIdx = 2 * (group + k + halfStride);
312 __m128d e = _mm_loadu_pd(&data[eIdx]);
313 __m128d o = _mm_loadu_pd(&data[oIdx]);
314 __m128d w = _mm_xor_pd(_mm_loadu_pd(&twiddles_[twIdx]), invTwMaskD);
316 __m128d w_re = _mm_unpacklo_pd(w, w);
317 __m128d w_im = _mm_unpackhi_pd(w, w);
318 __m128d o_sw = _mm_shuffle_pd(o, o, 1);
320 __m128d p1 = _mm_mul_pd(w_re, o);
321 __m128d p2 = _mm_mul_pd(w_im, o_sw);
322 __m128d t = addsubPd(p1, p2);
324 _mm_storeu_pd(&data[eIdx], _mm_add_pd(e, t));
325 _mm_storeu_pd(&data[oIdx], _mm_sub_pd(e, t));
330#ifdef DSPARK_FFT_NEON
331 if constexpr (std::is_same_v<T, float>)
333 static constexpr uint32_t kAddSub[4] = { 0x80000000u, 0, 0x80000000u, 0 };
334 const uint32x4_t addsubMask = vld1q_u32(kAddSub);
336 static constexpr uint32_t kInvTw[4] = { 0, 0x80000000u, 0, 0x80000000u };
337 const uint32x4_t invTwMask = isInverse ? vld1q_u32(kInvTw) : vdupq_n_u32(0);
339 for (; k + 1 < halfStride; k += 2)
341 const size_t twIdx = twiddleOffset + k * 2;
342 const size_t eIdx = 2 * (group + k);
343 const size_t oIdx = 2 * (group + k + halfStride);
345 float32x4_t e = vld1q_f32(&data[eIdx]);
346 float32x4_t o = vld1q_f32(&data[oIdx]);
347 float32x4_t w = vreinterpretq_f32_u32(veorq_u32(
348 vreinterpretq_u32_f32(vld1q_f32(&twiddles_[twIdx])), invTwMask));
350 float32x4_t o_re = vtrn1q_f32(o, o);
351 float32x4_t o_im = vtrn2q_f32(o, o);
352 float32x4_t w_sw = vrev64q_f32(w);
353 float32x4_t p1 = vmulq_f32(w, o_re);
354 float32x4_t p2 = vmulq_f32(w_sw, o_im);
355 float32x4_t t = vaddq_f32(p1,
356 vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(p2), addsubMask)));
358 vst1q_f32(&data[eIdx], vaddq_f32(e, t));
359 vst1q_f32(&data[oIdx], vsubq_f32(e, t));
365 for (; k < halfStride; ++k)
367 size_t idx = twiddleOffset + k * 2;
368 T wr = twiddles_[idx];
369 T wi = twiddles_[idx + 1];
371 if (isInverse) wi = -wi;
373 size_t evenIdx = 2 * (group + k);
374 size_t oddIdx = 2 * (group + k + halfStride);
376 T tr = wr * data[oddIdx] - wi * data[oddIdx + 1];
377 T ti = wr * data[oddIdx + 1] + wi * data[oddIdx];
379 data[oddIdx] = data[evenIdx] - tr;
380 data[oddIdx + 1] = data[evenIdx + 1] - ti;
382 data[evenIdx + 1] += ti;
386 twiddleOffset += halfStride * 2;
392 std::vector<T> twiddles_;
393 std::vector<size_t> bitrev_;
428 : realSize_(validateSize(size))
429 , halfSize_(realSize_ / 2)
430 , complexFFT_(realSize_ / 2)
432 computePostTwiddles();
433 workBuffer_.resize(realSize_);
437 [[nodiscard]]
size_t getSize() const noexcept {
return realSize_; }
443 [[nodiscard]]
size_t getNumBins() const noexcept {
return halfSize_ + 1; }
450 void forward(
const T* timeData, T* freqData)
noexcept
452 T* work = workBuffer_.data();
455 std::memcpy(work, timeData, realSize_ *
sizeof(T));
457 complexFFT_.forward(work);
458 unpackForward(work, freqData);
466 void inverse(
const T* freqData, T* timeData)
noexcept
468 T* work = workBuffer_.data();
469 packInverse(freqData, work);
470 complexFFT_.inverse(work);
472 std::memcpy(timeData, work, realSize_ *
sizeof(T));
482 for (
size_t k = 0; k <= halfSize_; ++k)
484 T re = freqData[2 * k];
485 T im = freqData[2 * k + 1];
486 magnitudes[k] = std::sqrt(re * re + im * im);
497 for (
size_t k = 0; k <= halfSize_; ++k)
499 T re = freqData[2 * k];
500 T im = freqData[2 * k + 1];
501 phases[k] = std::atan2(im, re);
512 for (
size_t k = 0; k <= halfSize_; ++k)
514 T re = freqData[2 * k];
515 T im = freqData[2 * k + 1];
516 power[k] = re * re + im * im;
527 [[nodiscard]]
static T
binToFrequency(
size_t binIndex,
double sampleRate,
size_t fftSize)
noexcept
529 return static_cast<T
>(
static_cast<double>(binIndex) * sampleRate /
static_cast<double>(fftSize));
539 [[nodiscard]]
static size_t frequencyToBin(
double frequency,
double sampleRate,
size_t fftSize)
noexcept
541 return static_cast<size_t>(std::round(frequency *
static_cast<double>(fftSize) / sampleRate));
549 static size_t validateSize(
size_t size)
551 if (size < 4 || (size & (size - 1)) != 0)
553#if defined(DSPARK_NO_EXCEPTIONS)
554 assert(
false &&
"FFTReal size must be a power of two >= 4");
557 throw std::invalid_argument(
"FFTReal size must be a power of two >= 4");
563 void computePostTwiddles()
565 postTwiddles_.resize(halfSize_ * 2);
566 for (
size_t k = 0; k < halfSize_; ++k)
568 double angle = -2.0 * std::numbers::pi_v<double> *
static_cast<double>(k)
569 /
static_cast<double>(realSize_);
570 postTwiddles_[2 * k] =
static_cast<T
>(std::cos(angle));
571 postTwiddles_[2 * k + 1] =
static_cast<T
>(std::sin(angle));
575 void unpackForward(
const T* halfFFT, T* fullSpectrum)
const noexcept
577 const size_t N2 = halfSize_;
579 T dcRe = halfFFT[0] + halfFFT[1];
580 T nyRe = halfFFT[0] - halfFFT[1];
582 fullSpectrum[0] = dcRe;
583 fullSpectrum[1] = T(0);
584 fullSpectrum[2 * N2] = nyRe;
585 fullSpectrum[2 * N2 + 1] = T(0);
587 for (
size_t k = 1; k < N2; ++k)
589 size_t kConj = N2 - k;
591 T hkRe = halfFFT[2 * k];
592 T hkIm = halfFFT[2 * k + 1];
593 T hcRe = halfFFT[2 * kConj];
594 T hcIm = halfFFT[2 * kConj + 1];
596 T xeRe = T(0.5) * (hkRe + hcRe);
597 T xeIm = T(0.5) * (hkIm - hcIm);
599 T xoRe = T(0.5) * (hkRe - hcRe);
600 T xoIm = T(0.5) * (hkIm + hcIm);
602 T wr = postTwiddles_[2 * k];
603 T wi = postTwiddles_[2 * k + 1];
608 T twRe = wr * joRe - wi * joIm;
609 T twIm = wr * joIm + wi * joRe;
611 fullSpectrum[2 * k] = xeRe + twRe;
612 fullSpectrum[2 * k + 1] = xeIm + twIm;
616 void packInverse(
const T* fullSpectrum, T* halfFFT)
const noexcept
618 const size_t N2 = halfSize_;
620 T dcRe = fullSpectrum[0];
621 T nyRe = fullSpectrum[2 * N2];
623 halfFFT[0] = T(0.5) * (dcRe + nyRe);
624 halfFFT[1] = T(0.5) * (dcRe - nyRe);
626 for (
size_t k = 1; k < N2; ++k)
628 size_t kConj = N2 - k;
630 T xkRe = fullSpectrum[2 * k];
631 T xkIm = fullSpectrum[2 * k + 1];
632 T xcRe = fullSpectrum[2 * kConj];
633 T xcIm = fullSpectrum[2 * kConj + 1];
635 T xeRe = T(0.5) * (xkRe + xcRe);
636 T xeIm = T(0.5) * (xkIm - xcIm);
638 T diffRe = T(0.5) * (xkRe - xcRe);
639 T diffIm = T(0.5) * (xkIm + xcIm);
641 T wr = postTwiddles_[2 * k];
642 T wi = -postTwiddles_[2 * k + 1];
644 T twRe = wr * diffRe - wi * diffIm;
645 T twIm = wr * diffIm + wi * diffRe;
650 halfFFT[2 * k] = xeRe + xoRe;
651 halfFFT[2 * k + 1] = xeIm + xoIm;
657 FFTComplex<T> complexFFT_;
658 std::vector<T> postTwiddles_;
659 std::vector<T> workBuffer_;