| 33 | Context::~Context() FL_NOEXCEPT = default; |
| 34 | |
| 35 | shared_ptr<const fft::Bins> Context::getFFT(int bands, float fmin, float fmax, fft::Mode mode, fft::Window window) { |
| 36 | fft::Args args(mSample.size(), bands, fmin, fmax, mSampleRate, mode, window); |
| 37 | |
| 38 | // O(1) cache lookup using hash map |
| 39 | fl::size argsHash = hashFFTArgs(args); |
| 40 | auto it = mFFTCacheMap.find(argsHash); |
| 41 | if (it != mFFTCacheMap.end()) { |
| 42 | int idx = it->second; |
| 43 | if (idx >= 0 && idx < static_cast<int>(mFFTCache.size())) { |
| 44 | // Double-check args match in case of hash collision |
| 45 | if (mFFTCache[idx].args == args) { |
| 46 | return mFFTCache[idx].bins; |
| 47 | } |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | // Not cached — try to recycle a previously-used fft::Bins with matching band count |
| 52 | shared_ptr<fft::Bins> bins; |
| 53 | for (size i = 0; i < mRecyclePool.size(); i++) { |
| 54 | if (static_cast<int>(mRecyclePool[i]->bands()) == bands) { |
| 55 | bins = fl::move(mRecyclePool[i]); |
| 56 | mRecyclePool.erase(mRecyclePool.begin() + i); |
| 57 | bins->clear(); // Vectors keep capacity — zero allocs |
| 58 | break; |
| 59 | } |
| 60 | } |
| 61 | if (!bins) { |
| 62 | bins = fl::make_shared<fft::Bins>(bands); |
| 63 | } |
| 64 | |
| 65 | fl::span<const fl::i16> sample = mSample.pcm(); |
| 66 | mFFT.run(sample, bins.get(), args); |
| 67 | |
| 68 | // Evict oldest if at capacity |
| 69 | if (static_cast<int>(mFFTCache.size()) >= MAX_FFT_CACHE_ENTRIES) { |
| 70 | // Remove the oldest entry's hash from hash map |
| 71 | fl::size oldHash = hashFFTArgs(mFFTCache[0].args); |
| 72 | mFFTCacheMap.erase(oldHash); |
| 73 | |
| 74 | // Shift all remaining entries and update map indices |
| 75 | for (size i = 1; i < mFFTCache.size(); i++) { |
| 76 | fl::size key = hashFFTArgs(mFFTCache[i].args); |
| 77 | mFFTCacheMap[key] = static_cast<int>(i - 1); |
| 78 | } |
| 79 | mFFTCache.erase(mFFTCache.begin()); |
| 80 | } |
| 81 | |
| 82 | FFTCacheEntry entry; |
| 83 | entry.args = args; |
| 84 | entry.bins = bins; |
| 85 | int newIndex = static_cast<int>(mFFTCache.size()); |
| 86 | mFFTCache.push_back(fl::move(entry)); |
| 87 | |
| 88 | // Add to hash map for O(1) future lookups |
| 89 | mFFTCacheMap[argsHash] = newIndex; |
| 90 | |
| 91 | return bins; |
| 92 | } |
no test coverage detected