| 173 | } |
| 174 | |
| 175 | void Vocal::computeBroadSpectralFeatures(const fft::Bins& broadFft) { |
| 176 | // Broad spectral features from low-res wide FFT (16 bins, 174.6-4698.3 Hz). |
| 177 | // Computes flatness, harmonic density, and spectral flux. |
| 178 | const auto& bins = broadFft.raw(); |
| 179 | const int n = static_cast<int>(bins.size()); |
| 180 | if (n == 0) { |
| 181 | mSpectralFlatness = 0.0f; |
| 182 | mHarmonicDensity = 0.0f; |
| 183 | mSpectralFlux = 0.0f; |
| 184 | return; |
| 185 | } |
| 186 | |
| 187 | // === PASS 1: Flatness + density peak + magnitudeSum === |
| 188 | float magnitudeSum = 0.0f; |
| 189 | float sumLn = 0.0f; |
| 190 | float sumRaw = 0.0f; |
| 191 | int flatnessCount = 0; |
| 192 | float peak = 0.0f; |
| 193 | |
| 194 | for (int i = 0; i < n; ++i) { |
| 195 | const float mag = bins[i]; |
| 196 | magnitudeSum += mag; |
| 197 | if (mag > 1e-6f) { |
| 198 | sumLn += fast_logf_approx(mag); |
| 199 | sumRaw += mag; |
| 200 | ++flatnessCount; |
| 201 | } |
| 202 | if (mag > peak) peak = mag; |
| 203 | } |
| 204 | |
| 205 | // --- Flatness result --- |
| 206 | if (flatnessCount >= 2) { |
| 207 | float geometricMean = fl::expf(sumLn / static_cast<float>(flatnessCount)); |
| 208 | float arithmeticMean = sumRaw / static_cast<float>(flatnessCount); |
| 209 | mSpectralFlatness = (arithmeticMean < 1e-6f) ? 0.0f : geometricMean / arithmeticMean; |
| 210 | } else { |
| 211 | mSpectralFlatness = 0.0f; |
| 212 | } |
| 213 | |
| 214 | // === PASS 2: Density count + spectral flux === |
| 215 | mHarmonicDensity = 0.0f; |
| 216 | mSpectralFlux = 0.0f; |
| 217 | |
| 218 | if (magnitudeSum >= 1e-6f) { |
| 219 | const float densityThreshold = peak * 0.1f; |
| 220 | const float invSum = 1.0f / magnitudeSum; |
| 221 | const bool hasPrev = (mPrevBins.size() == static_cast<fl::size>(n)); |
| 222 | int densityCount = 0; |
| 223 | float flux = 0.0f; |
| 224 | |
| 225 | mFluxNormBins.resize(n); |
| 226 | for (int i = 0; i < n; ++i) { |
| 227 | const float mag = bins[i]; |
| 228 | if (mag >= densityThreshold) ++densityCount; |
| 229 | float norm = mag * invSum; |
| 230 | mFluxNormBins[i] = norm; |
| 231 | if (hasPrev) { |
| 232 | float diff = norm - mPrevBins[i]; |