| 865 | } |
| 866 | |
| 867 | void runHybrid(span<const i16> buffer, Bins *out) { |
| 868 | const int N = mInputSamples; |
| 869 | const int numRawBins = N / 2 + 1; |
| 870 | |
| 871 | out->setParams(mFmin, mFmax, mSampleRate); |
| 872 | |
| 873 | // Use thread-local scratch buffers |
| 874 | FftScratch &s = scratch(); |
| 875 | s.re.resize(numRawBins); |
| 876 | s.im.resize(numRawBins); |
| 877 | s.mag.resize(numRawBins); |
| 878 | s.windowed.resize(N); |
| 879 | s.rawBinsI.resize(mTotalBands); |
| 880 | |
| 881 | // Phase 1: Windowed 512pt FFT → LOG_REBIN for upper bins |
| 882 | applyWindow(buffer.data(), mWindowBuf.data(), s.windowed.data(), N); |
| 883 | |
| 884 | fl_fft_real_forward(mFftrCfg, mInputSamples, s.windowed.data(), mFftOut.data()); |
| 885 | |
| 886 | deinterleave(mFftOut.data(), s.re.data(), s.im.data(), numRawBins); |
| 887 | batchMag(s.re.data(), s.im.data(), s.mag.data(), numRawBins); |
| 888 | |
| 889 | computeLinearBins(s.mag.data(), N, out); |
| 890 | |
| 891 | // Integer accumulation for log-rebin |
| 892 | fl::memset(s.rawBinsI.data(), 0, sizeof(u32) * mTotalBands); |
| 893 | |
| 894 | // Upper tier: LOG_REBIN for bins [mHybridMidSplitBin, mTotalBands) |
| 895 | logRebinRange(s.mag.data(), N, |
| 896 | static_cast<float>(mSampleRate), |
| 897 | mHybridMidSplitBin, mTotalBands, s.rawBinsI.data(), |
| 898 | mLogBinLut); |
| 899 | |
| 900 | // Decimate signal: 512 → 256 → 128 (2 steps for mid tier) |
| 901 | int workLen = N; |
| 902 | for (int i = 0; i < N; i++) { |
| 903 | mWorkBuf[i] = |
| 904 | (i < static_cast<int>(buffer.size())) ? buffer[i] : 0; |
| 905 | } |
| 906 | decimateBy2(mWorkBuf.data(), workLen); |
| 907 | workLen /= 2; |
| 908 | decimateBy2(mWorkBuf.data(), workLen); |
| 909 | workLen /= 2; |
| 910 | |
| 911 | // Phase 2: Zero-padded 256pt FFT (128 windowed + 128 zeros) → LOG_REBIN for mid bins |
| 912 | if (mHybridMidSplitBin > mHybridSplitBin && mHybridMidFft) { |
| 913 | int midFftN = mHybridMidN * 2; |
| 914 | int midRawBins = midFftN / 2 + 1; |
| 915 | s.windowed.resize(midFftN); |
| 916 | applyWindow(mWorkBuf.data(), mHybridMidWindow.data(), |
| 917 | s.windowed.data(), mHybridMidN); |
| 918 | for (int i = mHybridMidN; i < midFftN; ++i) { |
| 919 | s.windowed[i] = 0; |
| 920 | } |
| 921 | fl_fft_real_forward(mHybridMidFft, mHybridMidN * 2, |
| 922 | s.windowed.data(), |
| 923 | mHybridMidFftOut.data()); |
| 924 | deinterleave(mHybridMidFftOut.data(), s.re.data(), s.im.data(), midRawBins); |
nothing calls this directly
no test coverage detected