Program Listing for File BackendLess.hpp

Return to documentation for file (PDJE-Godot-Plugin/Project-DJ-Engine/include/util/function/stft/BackendLess.hpp)

#pragma once

#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <vector>

namespace PDJE_PARALLEL
{

namespace detail {

static inline float
ClampUnitFloat(const float value)
{
    if (!std::isfinite(value)) {
        return 0.0f;
    }

    return std::clamp(value, 0.0f, 1.0f);
}

struct RgbBandBoundaries {
    std::size_t low_begin  = 0;
    std::size_t low_end    = 0;
    std::size_t mid_begin  = 0;
    std::size_t mid_end    = 0;
    std::size_t high_begin = 0;
    std::size_t high_end   = 0;
};

static inline float
SanitizeShiftedValue(const float value, const float shift) noexcept
{
    if (!std::isfinite(value)) {
        return 0.0f;
    }

    return std::max(value + shift, 0.0f);
}

static inline RgbBandBoundaries
ComputeRgbBandBoundaries(const std::size_t melSize) noexcept
{
    if (melSize < 3u) {
        return {};
    }

    std::array<std::size_t, 3> counts{
        (melSize * 180u) / 1000u,
        (melSize * 350u) / 1000u,
        0u,
    };
    counts[2] = melSize - counts[0] - counts[1];

    for (std::size_t idx = 0; idx < counts.size(); ++idx) {
        if (counts[idx] != 0u) {
            continue;
        }

        std::size_t donor = idx;
        for (std::size_t candidate = 0; candidate < counts.size(); ++candidate) {
            if (counts[candidate] > counts[donor]) {
                donor = candidate;
            }
        }

        if (counts[donor] > 1u) {
            --counts[donor];
            counts[idx] = 1u;
        }
    }

    return {
        .low_begin = 0u,
        .low_end = counts[0],
        .mid_begin = counts[0],
        .mid_end = counts[0] + counts[1],
        .high_begin = counts[0] + counts[1],
        .high_end = melSize,
    };
}

static inline float
SanitizeFrameRange(const std::vector<float> &vec,
                   const std::size_t         begin,
                   const std::size_t         end) noexcept
{
    float minValue = std::numeric_limits<float>::infinity();
    bool  hasFiniteValue = false;

    const std::size_t clampedEnd = std::min(end, vec.size());
    for (std::size_t idx = begin; idx < clampedEnd; ++idx) {
        const float value = vec[idx];
        if (!std::isfinite(value)) {
            continue;
        }

        minValue       = std::min(minValue, value);
        hasFiniteValue = true;
    }

    if (!hasFiniteValue || minValue >= 0.0f) {
        return 0.0f;
    }

    return -minValue;
}

static inline float
BandRms(const std::vector<float> &vec,
        const std::size_t         begin,
        const std::size_t         end,
        const float               shift) noexcept
{
    if (begin >= end || begin >= vec.size()) {
        return 0.0f;
    }

    const std::size_t clampedEnd = std::min(end, vec.size());
    const std::size_t count      = clampedEnd - begin;
    if (count == 0u) {
        return 0.0f;
    }

    double sumSquares = 0.0;
    for (std::size_t idx = begin; idx < clampedEnd; ++idx) {
        const double value = static_cast<double>(
            SanitizeShiftedValue(vec[idx], shift));
        sumSquares += value * value;
    }

    return static_cast<float>(
        std::sqrt(sumSquares / static_cast<double>(count)));
}

static inline float
FrameMean(const std::vector<float> &vec,
          const std::size_t         begin,
          const std::size_t         end,
          const float               shift) noexcept
{
    if (begin >= end || begin >= vec.size()) {
        return 0.0f;
    }

    const std::size_t clampedEnd = std::min(end, vec.size());
    const std::size_t count      = clampedEnd - begin;
    if (count == 0u) {
        return 0.0f;
    }

    double sum = 0.0;
    for (std::size_t idx = begin; idx < clampedEnd; ++idx) {
        sum += static_cast<double>(SanitizeShiftedValue(vec[idx], shift));
    }

    return static_cast<float>(sum / static_cast<double>(count));
}

static inline float
Percentile(std::vector<float> values, const float fraction)
{
    if (values.empty()) {
        return 0.0f;
    }

    const float clampedFraction = std::clamp(fraction, 0.0f, 1.0f);
    const auto percentileIndex = static_cast<std::size_t>(
        std::ceil(static_cast<double>(values.size()) *
                  static_cast<double>(clampedFraction))) -
                                 1u;
    std::nth_element(values.begin(),
                     values.begin() +
                         static_cast<std::ptrdiff_t>(percentileIndex),
                     values.end());
    return values[percentileIndex];
}

} // namespace detail

static inline void
Normalize_minmax(std::vector<float> &vec, const uint32_t chunkSZ)
{
    if (vec.empty() || chunkSZ == 0) {
        return;
    }

    const std::size_t chunkSize = static_cast<std::size_t>(chunkSZ);

    for (std::size_t chunkBegin = 0; chunkBegin < vec.size();
         chunkBegin += chunkSize) {
        const std::size_t chunkEnd = std::min(vec.size(), chunkBegin + chunkSize);

        float minValue = std::numeric_limits<float>::infinity();
        float maxValue = -std::numeric_limits<float>::infinity();
        bool  hasFiniteValue = false;

        for (std::size_t idx = chunkBegin; idx < chunkEnd; ++idx) {
            const float value = vec[idx];
            if (!std::isfinite(value)) {
                continue;
            }

            minValue = std::min(minValue, value);
            maxValue = std::max(maxValue, value);
            hasFiniteValue = true;
        }

        if (!hasFiniteValue || maxValue <= minValue) {
            std::fill(vec.begin() + static_cast<std::ptrdiff_t>(chunkBegin),
                      vec.begin() + static_cast<std::ptrdiff_t>(chunkEnd),
                      0.0f);
            continue;
        }

        const float invRange = 1.0f / (maxValue - minValue);

        for (std::size_t idx = chunkBegin; idx < chunkEnd; ++idx) {
            const float value = vec[idx];
            if (!std::isfinite(value)) {
                vec[idx] = 0.0f;
                continue;
            }

            vec[idx] =
                detail::ClampUnitFloat((value - minValue) * invRange);
        }
    }
}

static inline std::vector<float>
TO_RGB(const std::vector<float> &vec, const uint32_t melSZ)
{
    if (vec.empty() || melSZ < 3) {
        return {};
    }

    const std::size_t melSize = static_cast<std::size_t>(melSZ);
    if ((vec.size() % melSize) != 0) {
        return {};
    }

    const std::size_t frameCount = vec.size() / melSize;
    if (frameCount > (std::numeric_limits<std::size_t>::max() / 3u)) {
        return {};
    }

    std::vector<float> rgb(frameCount * 3u, 0.0f);
    const auto         boundaries = detail::ComputeRgbBandBoundaries(melSize);
    constexpr float    kEpsilon   = 1.0e-6f;
    constexpr float    kRedGain   = 1.00f;
    constexpr float    kGreenGain = 1.18f;
    constexpr float    kBlueGain  = 1.55f;
    constexpr float    kPastelMin = 0.18f;
    constexpr float    kRedChromaGamma   = 1.08f;
    constexpr float    kGreenChromaGamma = 0.92f;
    constexpr float    kBlueChromaGamma  = 0.84f;

    std::vector<float> rawBrightness(frameCount, 0.0f);
    std::vector<float> positiveBrightness;
    positiveBrightness.reserve(frameCount);

    for (std::size_t frameIdx = 0; frameIdx < frameCount; ++frameIdx) {
        const std::size_t frameBase = frameIdx * melSize;
        const float shift =
            detail::SanitizeFrameRange(vec, frameBase, frameBase + melSize);
        const float brightness =
            detail::FrameMean(vec, frameBase, frameBase + melSize, shift);
        rawBrightness[frameIdx] = brightness;

        if (brightness > kEpsilon) {
            positiveBrightness.push_back(brightness);
        }
    }

    if (positiveBrightness.empty()) {
        return rgb;
    }

    const float brightnessP15 =
        detail::Percentile(positiveBrightness, 0.15f);
    const float brightnessP85 =
        detail::Percentile(std::move(positiveBrightness), 0.85f);
    const bool  hasBrightnessRange =
        brightnessP85 > (brightnessP15 + kEpsilon);
    const float brightnessRange =
        std::max(brightnessP85 - brightnessP15, kEpsilon);
    const float silenceThreshold =
        std::max(kEpsilon, brightnessP15 * 0.25f);

    for (std::size_t frameIdx = 0; frameIdx < frameCount; ++frameIdx) {
        const std::size_t frameBase = frameIdx * melSize;
        const std::size_t rgbBase   = frameIdx * 3u;
        const float shift =
            detail::SanitizeFrameRange(vec, frameBase, frameBase + melSize);

        const float lowRaw = detail::BandRms(
            vec,
            frameBase + boundaries.low_begin,
            frameBase + boundaries.low_end,
            shift);
        const float midRaw = detail::BandRms(
            vec,
            frameBase + boundaries.mid_begin,
            frameBase + boundaries.mid_end,
            shift);
        const float highRaw = detail::BandRms(
            vec,
            frameBase + boundaries.high_begin,
            frameBase + boundaries.high_end,
            shift);

        const float red   = std::log1p(std::max(lowRaw, 0.0f)) * kRedGain;
        const float green = std::log1p(std::max(midRaw, 0.0f)) * kGreenGain;
        const float blue  = std::log1p(std::max(highRaw, 0.0f)) * kBlueGain;
        const float sum   = red + green + blue;
        if (sum <= kEpsilon) {
            continue;
        }

        std::array<float, 3> chroma{
            red / sum,
            green / sum,
            blue / sum,
        };
        chroma[0] =
            kPastelMin +
            ((1.0f - kPastelMin) *
             std::pow(detail::ClampUnitFloat(chroma[0]), kRedChromaGamma));
        chroma[1] =
            kPastelMin +
            ((1.0f - kPastelMin) *
             std::pow(detail::ClampUnitFloat(chroma[1]), kGreenChromaGamma));
        chroma[2] =
            kPastelMin +
            ((1.0f - kPastelMin) *
             std::pow(detail::ClampUnitFloat(chroma[2]), kBlueChromaGamma));

        if (rawBrightness[frameIdx] <= silenceThreshold) {
            continue;
        }

        const float brightnessNorm =
            hasBrightnessRange
                ? detail::ClampUnitFloat(
                      (rawBrightness[frameIdx] - brightnessP15) / brightnessRange)
                : 1.0f;
        const float brightness =
            0.58f + (0.42f * std::pow(brightnessNorm, 0.80f));

        rgb[rgbBase + 0u] =
            detail::ClampUnitFloat(chroma[0] * brightness);
        rgb[rgbBase + 1u] =
            detail::ClampUnitFloat(chroma[1] * brightness);
        rgb[rgbBase + 2u] =
            detail::ClampUnitFloat(chroma[2] * brightness);
    }

    return rgb;
}

} // namespace PDJE_PARALLEL