| 73 | } |
| 74 | |
| 75 | void FrequencyBinMapper::calculateBinMappings() { |
| 76 | const size numBins = static_cast<size>(mConfig.mode); |
| 77 | |
| 78 | mBinMappings.clear(); |
| 79 | mBinMappings.reserve(numBins); |
| 80 | |
| 81 | // For each output frequency bin, determine which fft::FFT bins contribute |
| 82 | for (size i = 0; i < numBins; ++i) { |
| 83 | float minFreq = mBinFrequencies[i]; |
| 84 | float maxFreq = mBinFrequencies[i + 1]; |
| 85 | |
| 86 | // Convert frequencies to fft::FFT bin indices |
| 87 | float startBinFloat = frequencyToFFTBin(minFreq); |
| 88 | float endBinFloat = frequencyToFFTBin(maxFreq); |
| 89 | |
| 90 | // Round to integer fft::FFT bin indices |
| 91 | u32 startBin = static_cast<u32>(startBinFloat); |
| 92 | u32 endBin = static_cast<u32>(fl::ceilf(endBinFloat)); |
| 93 | |
| 94 | // Clamp to valid fft::FFT bin range |
| 95 | if (startBin >= mConfig.fftBinCount) { |
| 96 | startBin = mConfig.fftBinCount - 1; |
| 97 | } |
| 98 | if (endBin > mConfig.fftBinCount) { |
| 99 | endBin = mConfig.fftBinCount; |
| 100 | } |
| 101 | |
| 102 | // Ensure at least one fft::FFT bin per output bin |
| 103 | if (endBin <= startBin) { |
| 104 | endBin = startBin + 1; |
| 105 | } |
| 106 | |
| 107 | BinMapping mapping; |
| 108 | mapping.startBin = startBin; |
| 109 | mapping.endBin = endBin; |
| 110 | mBinMappings.push_back(mapping); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | float FrequencyBinMapper::frequencyToFFTBin(float frequency) const { |
| 115 | // fft::FFT bin index = (frequency / sampleRate) * fftSize |