| 120 | } |
| 121 | |
| 122 | void SpectralEqualizer::apply(span<const float> inputBins, span<float> outputBins) const { |
| 123 | if (inputBins.size() != mConfig.numBands) { |
| 124 | FL_WARN("SpectralEqualizer: input size mismatch (" |
| 125 | << inputBins.size() << " != " << mConfig.numBands << ")"); |
| 126 | return; |
| 127 | } |
| 128 | |
| 129 | if (outputBins.size() < mConfig.numBands) { |
| 130 | FL_WARN("SpectralEqualizer: output buffer too small (" |
| 131 | << outputBins.size() << " < " << mConfig.numBands << ")"); |
| 132 | return; |
| 133 | } |
| 134 | |
| 135 | // Track input/output levels for stats |
| 136 | float inputPeak = 0.0f; |
| 137 | float outputPeak = 0.0f; |
| 138 | float inputSum = 0.0f; |
| 139 | float outputSum = 0.0f; |
| 140 | |
| 141 | // Apply per-band gains |
| 142 | for (size i = 0; i < mConfig.numBands; ++i) { |
| 143 | float inputValue = inputBins[i]; |
| 144 | float gain = mGains[i]; |
| 145 | float outputValue = inputValue * gain; |
| 146 | |
| 147 | // Apply compression if enabled |
| 148 | if (mConfig.enableCompression) { |
| 149 | outputValue = applyCompression(outputValue); |
| 150 | } |
| 151 | |
| 152 | outputBins[i] = outputValue; |
| 153 | |
| 154 | // Track levels |
| 155 | if (inputValue > inputPeak) { |
| 156 | inputPeak = inputValue; |
| 157 | } |
| 158 | if (outputValue > outputPeak) { |
| 159 | outputPeak = outputValue; |
| 160 | } |
| 161 | inputSum += inputValue; |
| 162 | outputSum += outputValue; |
| 163 | } |
| 164 | |
| 165 | // Calculate makeup gain if enabled |
| 166 | float makeupGain = 1.0f; |
| 167 | if (mConfig.applyMakeupGain) { |
| 168 | makeupGain = calculateMakeupGain(inputBins, outputBins); |
| 169 | |
| 170 | // Apply makeup gain to all output bins |
| 171 | for (size i = 0; i < mConfig.numBands; ++i) { |
| 172 | outputBins[i] *= makeupGain; |
| 173 | } |
| 174 | |
| 175 | // Adjust output stats |
| 176 | outputPeak *= makeupGain; |
| 177 | outputSum *= makeupGain; |
| 178 | } |
| 179 |
no test coverage detected