#include "noise.h"

static unsigned lcg_seed;
static float phase = 0.f;

/* Augments a given signal by adding some random noise and a sinusoidal waveform */
float AddNoise(float x, float amplitude, float ratio /* 0.f-1.f */, unsigned nsamples)
{
    float noise = .5f * (sflcg() + sflcg());
    float sine = fsindeg(phase);

    x += amplitude * (ratio * noise + (1.f - ratio) * sine);

    phase += 360.f / nsamples;
    phase -= 360.f * ((int)phase / 360);

    return x;
}

/* Resets the sinusoidal phase and the LCG */
void ResetNoise()
{
    slcg(0xBAADF00D);
    phase = 0.f;
}

/* Initializes the LCG */
void slcg(unsigned seed)
{
    lcg_seed = seed;
}

/* [0,32767] Linear congruential generator (PRNG) */
unsigned lcg()
{
    lcg_seed = lcg_seed * 0x343FD + 0x269EC3;
    return (lcg_seed >> 16) & 0x7FFF;
}

/* [0,1] LCG */
float flcg()
{
    return (float)lcg() / 0x7FFF;
}

/* [-1,1] LCG */
float sflcg()
{
    return 2.f * flcg() - 1.f;
}

/* Bhaskara sine approximation (in deg) */
float fsindeg(float x)
{
    float t, s = 1.f;

    if (x < 0.f) { s = -s; x = -x; }
    x -= 360.f * ((int)x / 360);
    if (x > 180.f) { s = -s; x -= 180.f; }
    t = x * (180.f - x);
    return (4.f * s * t) / (40500.f - t);
}

/* Bhaskara cosine approximation (in deg) */
float fcosdeg(float x)
{
    return fsindeg(x + 90.f);
}