Skip to content

File Utils.h

File List > Amplitude > Math > Utils.h

Go to the documentation of this file

// Copyright (c) 2021-present Sparky Studios. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#ifndef _AM_MATH_UTILS_H
#define _AM_MATH_UTILS_H

#include <SparkyStudios/Audio/Amplitude/Core/AudioBuffer.h>
#include <SparkyStudios/Audio/Amplitude/Math/LinearAlgebra.h>

#define AM_LCG_M 2147483647
#define AM_LCG_A 48271
#define AM_LCG_C 0
#define AM_PI 3.14159265358979323846
#define AM_PI32 3.14159265359f
#define AM_DEG180 180.0
#define AM_DEG18032 180.0f
#define AM_TURNHALF 0.5
#define AM_TURNHALF32 0.5f
#define AM_RadToDeg ((float)(AM_DEG180 / AM_PI))
#define AM_RadToTurn ((float)(AM_TURNHALF / AM_PI))
#define AM_DegToRad ((float)(AM_PI / AM_DEG180))
#define AM_DegToTurn ((float)(AM_TURNHALF / AM_DEG180))
#define AM_TurnToRad ((float)(AM_PI / AM_TURNHALF))
#define AM_TurnToDeg ((float)(AM_DEG180 / AM_TURNHALF))

namespace SparkyStudios::Audio::Amplitude
{
    struct BezierCurveControlPoints
    {
        AmReal32 x1;

        AmReal32 y1;

        AmReal32 x2;

        AmReal32 y2;
    };

    AM_API_PRIVATE struct
    {
        AmInt32 state;
    } gLCG = { 4321 };

    AM_API_PRIVATE AM_INLINE AmReal32 AmDitherReal32(const AmReal32 ditherMin, const AmReal32 ditherMax)
    {
        gLCG.state = (AM_LCG_A * gLCG.state + AM_LCG_C) % AM_LCG_M;
        const AmReal32 x = gLCG.state / static_cast<double>(0x7FFFFFFF);
        return ditherMin + x * (ditherMax - ditherMin);
    }

    AM_API_PRIVATE AM_INLINE AmInt32 AmFloatToFixedPoint(const AmReal32 x)
    {
        return static_cast<AmInt32>(x * kAmFixedPointUnit);
    }

    AM_API_PRIVATE AM_INLINE AmReal32 AmInt16ToReal32(const AmInt16 x)
    {
        auto y = static_cast<AmReal32>(x);

#if defined(AM_ACCURATE_CONVERSION)
        // The accurate way.
        y = y + 32768.0f; // -32768..32767 to 0..65535
        y = y * 0.00003051804379339284f; // 0..65535 to 0..2
        y = y - 1; // 0..2 to -1..1
#else
        // The fast way.
        y = y * 0.000030517578125f; // -32768..32767 to -1..0.999969482421875
#endif

        return y;
    }

    AM_API_PRIVATE AM_INLINE AmReal32 AmInt32ToReal32(const AmInt32 x)
    {
        auto y = static_cast<AmReal32>(x);

#if defined(AM_ACCURATE_CONVERSION)
        // The accurate way.
        y = y + 32768.0f; // -32768..32767 to 0..65535
        y = y * 0.00003051804379339284f; // 0..65535 to 0..2
        y = y - 1; // 0..2 to -1..1
#else
        // The fast way.
        y = y * 0.000030517578125f; // -32768..32767 to -1..0.999969482421875
#endif

        return y;
    }

    AM_API_PRIVATE AM_INLINE AmInt16 AmReal32ToInt16(const AmReal32 x, bool dithering = false)
    {
        AmReal32 y = x;

        if (dithering)
        {
            // Performs a rectangular dithering
            y += AmDitherReal32(1.0f / INT16_MIN, 1.0f / INT16_MAX);
        }

        y = AM_CLAMP(y, -1.0f, 1.0f);

#if defined(AM_ACCURATE_CONVERSION)
        // The accurate way.
        y = y + 1; // -1..1 to 0..2
        y = y * 32767.5f; // 0..2 to 0..65535
        y = y - 32768.0f; // 0...65535 to -32768..32767
#else
        // The fast way.
        y = y * 32767.0f; // -1..1 to -32767..32767
#endif

        return static_cast<AmInt16>(y);
    }

    AM_API_PRIVATE AM_INLINE AmReal32
    CatmullRom(const AmReal32 t, const AmReal32 p0, const AmReal32 p1, const AmReal32 p2, const AmReal32 p3)
    {
        // clang-format off
        return 0.5f * (
            (2 * p1) +
            (-p0 + p2) * t +
            (2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t +
            (-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t
        );
        // clang-format on
    }

    template<typename T>
    AM_API_PRIVATE AM_INLINE T NextPowerOf2(const T& val)
    {
        T nextPowerOf2 = 1;
        while (nextPowerOf2 < val)
            nextPowerOf2 *= 2;

        return nextPowerOf2;
    }

    template<typename T>
    AM_API_PRIVATE AM_INLINE T IntegerPow(T base, AmInt32 exp)
    {
        AMPLITUDE_ASSERT(exp >= 0);
        T result = static_cast<T>(1);

        while (true)
        {
            if (exp & 1)
                result *= base;

            exp >>= 1;

            if (!exp)
                break;

            base *= base;
        }

        return result;
    }

    AM_API_PRIVATE AM_INLINE AmInt64 FindGCD(AmInt64 a, AmInt64 b)
    {
        a = std::abs(a);
        b = std::abs(b);

        AmInt64 c = 0;

        while (b != 0)
        {
            c = b;
            b = a % b;
            a = c;
        }

        return a;
    }

    AM_API_PRIVATE AM_INLINE AmReal32 InverseSquareRoot(AmReal32 x)
    {
        // Use the fast inverse square root method (Quake III)
        AmReal32 h = 0.5f * x;
        AmInt32 i = *(AmInt32*)&x;
        i = 0x5f3759df - (i >> 1);
        x = *(AmReal32*)&i;
        x = x * (1.5f - h * x * x); // 1st iteration
#if defined(AM_ACCURATE_CONVERSION)
        x = x * (1.5f - h * x * x); // 2nd iteration for more accuracy
#endif
        return x;
    }

    AM_API_PRIVATE AM_INLINE AmReal32 Lerp(const AmReal32 t, const AmReal32 p0, const AmReal32 p1)
    {
        return (1.0f - t) * p0 + t * p1;
    }
} // namespace SparkyStudios::Audio::Amplitude

#endif // _AM_MATH_UTILS_H