Program Listing for File BackendLess.hpp

Return to documentation for file (include\util\function\stft\BackendLess.hpp)

#pragma once

#include <algorithm>
#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);
}

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

    float       sum   = 0.0f;
    std::size_t count = 0;

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

        sum += ClampUnitFloat(value);
        ++count;
    }

    if (count == 0) {
        return 0.0f;
    }

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

} // 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 {};
    }

    const std::size_t lowEnd = melSize / 3u;
    const std::size_t midEnd = (melSize * 2u) / 3u;

    std::vector<float> rgb(frameCount * 3u, 0.0f);

    for (std::size_t frameIdx = 0; frameIdx < frameCount; ++frameIdx) {
        const std::size_t frameBase = frameIdx * melSize;
        const std::size_t rgbBase   = frameIdx * 3u;

        rgb[rgbBase + 0u] = detail::MeanBandValue(
            vec, frameBase, frameBase + lowEnd);
        rgb[rgbBase + 1u] = detail::MeanBandValue(
            vec, frameBase + lowEnd, frameBase + midEnd);
        rgb[rgbBase + 2u] = detail::MeanBandValue(
            vec, frameBase + midEnd, frameBase + melSize);
    }

    return rgb;
}

} // namespace PDJE_PARALLEL