178 const auto& bins = broadFft.
raw();
179 const int n =
static_cast<int>(bins.size());
188 float magnitudeSum = 0.0f;
191 int flatnessCount = 0;
194 for (
int i = 0; i < n; ++i) {
195 const float mag = bins[i];
202 if (mag > peak) peak = mag;
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;
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;
226 for (
int i = 0; i < n; ++i) {
227 const float mag = bins[i];
228 if (mag >= densityThreshold) ++densityCount;
229 float norm = mag * invSum;
254 auto linearBins =
fft.linear();
255 if (linearBins.size() < 4)
return 0.0f;
257 const int numBins =
static_cast<int>(linearBins.size());
258 const float fmin =
fft.linearFmin();
259 const float fmax =
fft.linearFmax();
260 const float binWidth = (fmax - fmin) /
static_cast<float>(numBins);
263 auto freqToLinBin = [&](
float freq) ->
int {
264 if (freq <= fmin)
return 0;
265 if (freq >= fmax)
return numBins - 1;
266 return fl::min(numBins - 1,
static_cast<int>((freq - fmin) / binWidth));
269 const int presMinBin = freqToLinBin(2000.0f);
270 const int presMaxBin = freqToLinBin(4000.0f);
271 const int bassMinBin = freqToLinBin(200.0f);
272 const int bassMaxBin = freqToLinBin(500.0f);
274 float presEnergy = 0.0f;
276 for (
int i = presMinBin; i <= presMaxBin && i < numBins; ++i) {
277 presEnergy += linearBins[i] * linearBins[i];
280 if (presCount > 0) presEnergy /=
static_cast<float>(presCount);
282 float bassEnergy = 0.0f;
284 for (
int i = bassMinBin; i <= bassMaxBin && i < numBins; ++i) {
285 bassEnergy += linearBins[i] * linearBins[i];
288 if (bassCount > 0) bassEnergy /=
static_cast<float>(bassCount);
290 if (bassEnergy < 1e-12f)
return 0.0f;
291 return presEnergy / bassEnergy;
299 const int n =
static_cast<int>(pcm.
size());
306 const float normFactor = 1.0f / 32768.0f;
308 const int winSize = 2 * halfWin + 1;
309 const float invWinSize = 1.0f /
static_cast<float>(winSize);
312 float windowSum = 0.0f;
313 for (
int j = 0; j < winSize && j < n; ++j) {
314 windowSum +=
fl::abs(
static_cast<float>(pcm[j])) * normFactor;
321 float sumPeaks = 0.0f;
322 float sumSqPeaks = 0.0f;
324 float currentPeak = 0.0f;
325 bool wasPositive = pcm[halfWin] >= 0;
328 int prevCrossing = -1;
329 int numIntervals = 0;
330 float sumIntervals = 0.0f;
331 float sumSqIntervals = 0.0f;
333 for (
int i = halfWin; i < n - halfWin; ++i) {
334 float absVal =
fl::abs(
static_cast<float>(pcm[i])) * normFactor;
335 float smoothed = windowSum * invWinSize;
338 sumDev +=
fl::abs(absVal - smoothed);
342 currentPeak =
fl::max(currentPeak, absVal);
343 bool isPositive = pcm[i] >= 0;
344 if (isPositive != wasPositive) {
346 if (currentPeak > 0.01f) {
347 sumPeaks += currentPeak;
348 sumSqPeaks += currentPeak * currentPeak;
354 if (prevCrossing >= 0) {
355 float interval =
static_cast<float>(i - prevCrossing);
356 sumIntervals += interval;
357 sumSqIntervals += interval * interval;
362 wasPositive = isPositive;
365 if (i + 1 < n - halfWin) {
366 windowSum -=
fl::abs(
static_cast<float>(pcm[i - halfWin])) * normFactor;
367 windowSum +=
fl::abs(
static_cast<float>(pcm[i + halfWin + 1])) * normFactor;
372 if (sumEnv < 1e-6f || count == 0) {
375 float envelopeJitter = (sumDev /
static_cast<float>(count))
376 / (sumEnv /
static_cast<float>(count));
378 float shimmer = 0.0f;
379 if (numCycles >= 3) {
380 float meanPeak = sumPeaks /
static_cast<float>(numCycles);
381 if (meanPeak > 0.01f) {
382 float variance = sumSqPeaks /
static_cast<float>(numCycles)
383 - meanPeak * meanPeak;
384 if (variance < 0.0f) variance = 0.0f;
385 shimmer =
fl::sqrtf(variance) / meanPeak;
392 if (numIntervals < 2) {
395 float mean = sumIntervals /
static_cast<float>(numIntervals);
399 float variance = sumSqIntervals /
static_cast<float>(numIntervals)
401 if (variance < 0.0f) variance = 0.0f;
448 float spectralFlatness,
float harmonicDensity,
449 float vocalPresenceRatio,
float spectralFlux,
450 float spectralVariance) {
461 if (formantRatio < 0.12f) {
462 formantScore = formantRatio / 0.12f * 0.30f;
465 float dist =
fl::abs(formantRatio - 0.5f);
466 formantScore =
fl::max(0.30f, 1.0f - dist / 0.70f);
473 if (spectralFlatness < 0.20f) {
475 flatnessScore = spectralFlatness / 0.20f * 0.3f;
476 }
else if (spectralFlatness <= 0.65f) {
478 float dist =
fl::abs(spectralFlatness - 0.43f);
479 flatnessScore =
fl::max(0.0f, 1.0f - dist / 0.22f);
482 flatnessScore =
fl::max(0.0f, 1.0f - (spectralFlatness - 0.65f) / 0.10f);
488 if (harmonicDensity < 2.5f) {
490 densityScore = harmonicDensity / 2.5f * 0.3f;
491 }
else if (harmonicDensity <= 12.5f) {
493 densityScore = 0.3f + (harmonicDensity - 2.5f) / 10.0f * 0.7f;
496 densityScore =
fl::max(0.0f, 1.0f - (harmonicDensity - 12.5f) / 3.5f);
502 float spectralFluxScore;
503 if (spectralFlux < 0.01f) {
504 spectralFluxScore = 0.0f;
505 }
else if (spectralFlux < 0.15f) {
506 spectralFluxScore = (spectralFlux - 0.01f) / 0.14f;
508 spectralFluxScore = 1.0f;
514 float weightedAvg = 0.33f * formantScore
515 + 0.35f * flatnessScore
516 + 0.12f * densityScore
517 + 0.10f * spectralFluxScore;
524 float totalBoost = 1.0f;
529 if (spectralVariance > 0.01f) {
530 float varianceBoost = 1.0f + 0.40f * (spectralVariance - 0.74f) / 0.76f;
531 varianceBoost =
fl::clamp(varianceBoost, 0.90f, 1.25f);
532 totalBoost *= varianceBoost;
538 float periodicPenalty = 1.0f - 0.25f * (0.15f -
mEnvelopeJitter) / 0.15f;
539 totalBoost *= periodicPenalty;
546 jitterBoost =
fl::clamp(jitterBoost, 1.0f, 1.15f);
547 totalBoost *= jitterBoost;
553 acfBoost =
fl::clamp(acfBoost, 1.0f, 1.25f);
554 totalBoost *= acfBoost;
561 totalBoost *= zcPenalty;
574 zcBoost =
fl::clamp(zcBoost, 0.80f, 1.20f);
575 totalBoost *= zcBoost;
582 float combinedBoost = 1.0f + 1.5f * minExcess;
583 combinedBoost =
fl::clamp(combinedBoost, 1.0f, 1.20f);
584 totalBoost *= combinedBoost;
588 totalBoost =
fl::clamp(totalBoost, 0.50f, 1.60f);
589 weightedAvg *= totalBoost;
592 float formantMultiplier;
593 if (formantScore < 0.40f) {
594 formantMultiplier = formantScore * 2.5f;
596 formantMultiplier = 1.0f + 0.40f * (formantScore - 0.40f);