| 130 | } |
| 131 | |
| 132 | void Context::setSample(const Sample& sample) { |
| 133 | // Save current fft::FFT to history (use first cached entry if available) |
| 134 | if (!mFFTCache.empty() && mFFTHistoryDepth > 0) { |
| 135 | const shared_ptr<fft::Bins>& first = mFFTCache[0].bins; |
| 136 | if (first) { |
| 137 | if (static_cast<int>(mFFTHistory.size()) < mFFTHistoryDepth) { |
| 138 | mFFTHistory.push_back(*first); |
| 139 | // When the history fills up, wrap index to 0 for ring buffer mode |
| 140 | mFFTHistoryIndex = static_cast<int>(mFFTHistory.size()) % mFFTHistoryDepth; |
| 141 | } else { |
| 142 | mFFTHistory[mFFTHistoryIndex] = *first; |
| 143 | mFFTHistoryIndex = (mFFTHistoryIndex + 1) % mFFTHistoryDepth; |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | // Recycle bins that only the cache holds (use_count == 1). |
| 149 | // These can be reused next frame without allocation. |
| 150 | mRecyclePool.clear(); |
| 151 | for (size i = 0; i < mFFTCache.size(); i++) { |
| 152 | if (mFFTCache[i].bins.use_count() == 1) { |
| 153 | mRecyclePool.push_back(fl::move(mFFTCache[i].bins)); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | mSample = sample; |
| 158 | // Clear per-frame fft::FFT cache (new sample = new data) |
| 159 | mFFTCache.clear(); |
| 160 | // Reset silence flag — pipeline must re-populate after NFT update this frame. |
| 161 | mIsSilent = false; |
| 162 | } |
| 163 | |
| 164 | void Context::clearCache() { |
| 165 | mFFTCache.clear(); |
no test coverage detected