346 : engine_(validateSize(size))
354 [[nodiscard]]
size_t getSize() const noexcept {
return engine_.size(); }
361 void forward(T* data)
noexcept { transform(data,
false); }
368 void inverse(T* data)
noexcept { transform(data,
true); }
371 static size_t validateSize(
size_t size)
373 if (size < 2 || (size & (size - 1)) != 0)
375#if defined(DSPARK_NO_EXCEPTIONS)
376 assert(
false &&
"FFTComplex size must be a power of two >= 2");
379 throw std::invalid_argument(
"FFTComplex size must be a power of two >= 2");
385 void transform(T* data,
bool inverse)
noexcept
387 constexpr int W = detail::fft::kWide<T>;
388 using O = detail::fft::Vec<T, W>;
389 using O1 = detail::fft::Vec<T, 1>;
390 const size_t n = engine_.size();
391 T* re = engine_.inRe();
392 T* im = engine_.inIm();
396 for (; i + W <= n; i += W)
399 O::loadDeinterleave(data + 2 * i, r, m);
401 O::store(im + i,
inverse ? O::sub(O::set1(T(0)), m) : m);
406 im[i] =
inverse ? -data[2 * i + 1] : data[2 * i + 1];
409 const T* outRe =
nullptr;
410 const T* outIm =
nullptr;
411 engine_.run(outRe, outIm);
414 const T scale =
inverse ? T(1) /
static_cast<T
>(n) : T(1);
415 const T imScale =
inverse ? -scale : scale;
416 const auto vs = O::set1(scale), vis = O::set1(imScale);
418 for (; i + W <= n; i += W)
419 O::storeInterleave2(data + 2 * i, O::mul(O::load(outRe + i), vs),
420 O::mul(O::load(outIm + i), vis));
422 O1::storeInterleave2(data + 2 * i, outRe[i] * scale, outIm[i] * imScale);
425 detail::fft::SplitFFT<T> engine_;
461 : realSize_(validateSize(size))
462 , halfSize_(realSize_ / 2)
463 , engine_(realSize_ / 2)
465 computePostTwiddles();
469 [[nodiscard]]
size_t getSize() const noexcept {
return realSize_; }
475 [[nodiscard]]
size_t getNumBins() const noexcept {
return halfSize_ + 1; }
482 void forward(
const T* timeData, T* freqData)
noexcept
484 constexpr int W = detail::fft::kWide<T>;
486 const size_t m = halfSize_;
487 T* re = engine_.inRe();
488 T* im = engine_.inIm();
492 for (; i + W <= m; i += W)
495 O::loadDeinterleave(timeData + 2 * i, r, q);
501 re[i] = timeData[2 * i];
502 im[i] = timeData[2 * i + 1];
505 const T* zr =
nullptr;
506 const T* zi =
nullptr;
508 unpackForward(zr, zi, freqData);
516 void inverse(
const T* freqData, T* timeData)
noexcept
518 constexpr int W = detail::fft::kWide<T>;
520 const size_t m = halfSize_;
524 packInverseConj(freqData, engine_.inRe(), engine_.inIm());
525 const T* zr =
nullptr;
526 const T* zi =
nullptr;
529 const T scale = T(1) /
static_cast<T
>(m);
530 const auto vs = O::set1(scale), vis = O::set1(-scale);
532 for (; i + W <= m; i += W)
533 O::storeInterleave2(timeData + 2 * i, O::mul(O::load(zr + i), vs),
534 O::mul(O::load(zi + i), vis));
537 timeData[2 * i] = zr[i] * scale;
538 timeData[2 * i + 1] = -zi[i] * scale;
549 for (
size_t k = 0; k <= halfSize_; ++k)
551 T re = freqData[2 * k];
552 T im = freqData[2 * k + 1];
553 magnitudes[k] = std::sqrt(re * re + im * im);
564 for (
size_t k = 0; k <= halfSize_; ++k)
566 T re = freqData[2 * k];
567 T im = freqData[2 * k + 1];
568 phases[k] = std::atan2(im, re);
579 for (
size_t k = 0; k <= halfSize_; ++k)
581 T re = freqData[2 * k];
582 T im = freqData[2 * k + 1];
583 power[k] = re * re + im * im;
594 [[nodiscard]]
static T
binToFrequency(
size_t binIndex,
double sampleRate,
size_t fftSize)
noexcept
596 return static_cast<T
>(
static_cast<double>(binIndex) * sampleRate /
static_cast<double>(fftSize));
606 [[nodiscard]]
static size_t frequencyToBin(
double frequency,
double sampleRate,
size_t fftSize)
noexcept
608 return static_cast<size_t>(std::round(frequency *
static_cast<double>(fftSize) / sampleRate));
616 static size_t validateSize(
size_t size)
618 if (size < 4 || (size & (size - 1)) != 0)
620#if defined(DSPARK_NO_EXCEPTIONS)
621 assert(
false &&
"FFTReal size must be a power of two >= 4");
624 throw std::invalid_argument(
"FFTReal size must be a power of two >= 4");
630 void computePostTwiddles()
632 twRe_.resize(halfSize_);
633 twIm_.resize(halfSize_);
634 for (
size_t k = 0; k < halfSize_; ++k)
636 const double angle = -2.0 * std::numbers::pi_v<double> *
static_cast<double>(k)
637 /
static_cast<double>(realSize_);
638 twRe_[k] =
static_cast<T
>(std::cos(angle));
639 twIm_[k] =
static_cast<T
>(std::sin(angle));
647 void unpackForward(
const T* zr,
const T* zi, T* out)
const noexcept
649 constexpr int W = detail::fft::kWide<T>;
650 using O = detail::fft::Vec<T, W>;
651 const size_t m = halfSize_;
653 const T dc = zr[0] + zi[0];
654 const T ny = zr[0] - zi[0];
656 const auto half = O::set1(T(0.5));
658 for (; k + W <= m; k += W)
660 const auto hkR = O::load(zr + k), hkI = O::load(zi + k);
661 const auto hcR = O::reverse(O::load(zr + (m - k - (W - 1))));
662 const auto hcI = O::reverse(O::load(zi + (m - k - (W - 1))));
663 const auto xeR = O::mul(half, O::add(hkR, hcR));
664 const auto xeI = O::mul(half, O::sub(hkI, hcI));
665 const auto xoR = O::mul(half, O::sub(hkR, hcR));
666 const auto xoI = O::mul(half, O::add(hkI, hcI));
668 const auto wr = O::load(twRe_.data() + k), wi = O::load(twIm_.data() + k);
669 const auto tR = O::mulAdd(wr, xoI, wi, xoR);
670 const auto tI = O::mulSub(wi, xoI, wr, xoR);
671 O::storeInterleave2(out + 2 * k, O::add(xeR, tR), O::add(xeI, tI));
675 const size_t c = m - k;
676 const T xeR = T(0.5) * (zr[k] + zr[c]);
677 const T xeI = T(0.5) * (zi[k] - zi[c]);
678 const T xoR = T(0.5) * (zr[k] - zr[c]);
679 const T xoI = T(0.5) * (zi[k] + zi[c]);
680 const T wr = twRe_[k], wi = twIm_[k];
681 out[2 * k] = xeR + (wr * xoI + wi * xoR);
682 out[2 * k + 1] = xeI + (wi * xoI - wr * xoR);
689 out[2 * m + 1] = T(0);
696 void packInverseConj(
const T* in, T* zr, T* zi)
const noexcept
698 constexpr int W = detail::fft::kWide<T>;
699 using O = detail::fft::Vec<T, W>;
700 const size_t m = halfSize_;
703 const T ny = in[2 * m];
704 zr[0] = T(0.5) * (dc + ny);
705 zi[0] = -T(0.5) * (dc - ny);
707 const auto half = O::set1(T(0.5));
708 const auto zero = O::set1(T(0));
710 for (; k + W <= m; k += W)
712 typename O::V xkR, xkI, xcR, xcI;
713 O::loadDeinterleave(in + 2 * k, xkR, xkI);
714 O::loadDeinterleave(in + 2 * (m - k - (W - 1)), xcR, xcI);
715 xcR = O::reverse(xcR);
716 xcI = O::reverse(xcI);
717 const auto xeR = O::mul(half, O::add(xkR, xcR));
718 const auto xeI = O::mul(half, O::sub(xkI, xcI));
719 const auto dR = O::mul(half, O::sub(xkR, xcR));
720 const auto dI = O::mul(half, O::add(xkI, xcI));
722 const auto wr = O::load(twRe_.data() + k), wi = O::load(twIm_.data() + k);
723 const auto tR = O::mulAdd(wr, dR, wi, dI);
724 const auto tI = O::mulSub(wr, dI, wi, dR);
725 O::store(zr + k, O::sub(xeR, tI));
726 O::store(zi + k, O::sub(zero, O::add(xeI, tR)));
730 const size_t c = m - k;
731 const T xeR = T(0.5) * (in[2 * k] + in[2 * c]);
732 const T xeI = T(0.5) * (in[2 * k + 1] - in[2 * c + 1]);
733 const T dR = T(0.5) * (in[2 * k] - in[2 * c]);
734 const T dI = T(0.5) * (in[2 * k + 1] + in[2 * c + 1]);
735 const T wr = twRe_[k], wi = twIm_[k];
736 const T tR = wr * dR + wi * dI;
737 const T tI = wr * dI - wi * dR;
745 detail::fft::SplitFFT<T> engine_;
746 std::vector<T> twRe_, twIm_;