/** Audio callback
 * consumes 44100 samples per second, each sample is 8 bit uint
 * Callback is called every 1024 samples, or 43 times per second.
 * Userdata is null, don't use.
 *
 *
 */

long long int totalSamples = 0;
const double pi = 3.1415926536;

double fract(double a)
{
	return a - floor(a);
}

// t - time in seconds
// start - time in seconds to start
// rise - infade in seconds
// fade - out fade in seconds
double sawFade(double start, double rise, double fade, double t)
{
	double r = (t - start) / rise;
	double f = (t - start) / fade;
	if (t < start)
		return 0.0f;
	if (t < start + rise)
		return fract(r);
	if (t < start + fade)
		return 1.0f - fract(f);
	return 0.0f;
}

double saw(double s)
{
	double a = 0.0f;
	for (int k=1; k < 41; k++)
		a += (sin(2.0 * pi * ((double) k) * s) / ((double)k));
	return (1.0/2.0) - (1.0 / pi) * a - 0.5;
}

double clamp(double val)
{
	if (val > 1.0f)
		val = 1.0f;
	else if (val < 0.0f)
		val = 0.0f;
	return val;
}

double pitch(double p)
{
	return 440.0 * pow(1.059460646483, p);
}

double note(double t, double f)
{
	return sin(2.0 * pi * f * t);
}

double aMinor(double t)
{
	return 0.33 * (note(t, pitch(0.0)) + note(t, pitch(3.0)) + note(t, pitch(7.0)));
}

double bMinor(double t)
{
	return 0.33 * (note(t, pitch(-1.0)) + note(t, pitch(2.0)) + note(t, pitch(6.0)));
}

double cMinor(double t)
{
	return 0.5 * (note(t, pitch(-8.0)) + note(t, pitch(-6.0)));
}

double part1(double s)
{
	double a = aMinor(s) * sawFade(0.5, 0.02, 3.0, fmod(s, 4.0));
	double b = bMinor(s) * sawFade(0.0, 0.02, 1.0, fmod(s, 1.0));
	return 0.5 * (a + b);
}

double part2(double t)
{
	double base = -24.0;
	double s = 0.0;
	double semitones[] = {0.0, 4.0, 7.0, 9.0};

	// Loop through our four voices.
	for (int i=0;i<4;i++)
	{
		double f = pitch(base + semitones[i]);
		// Add four slightly detuned sawtooth waves together.
		for (int u=0;u<4;u++)
		{
			double fu = (double)u;
			double new_f = f + fu * sin(fu);
			s += saw(t * new_f) * 0.25 * 0.25;
		}
	}
	return s;
}

double part3(double s)
{
	double c = cMinor(s) * sawFade(0.5, 0.05, 0.3, 4.0 * fmod(s, 0.2));
	return c;
}

double part4(double s)
{
	double a =  0.0;
	for(int i = 1; i < 9; ++i)
		a += note(s * (double) i, pitch(-16.0)) / (double)i;
	return a * sawFade(0.0, 0.1, 0.3, 4.0 * fmod(s, 0.2));
}

double part(double s)
{
	double sample = 0.0f;
	sample += part1(s) * sawFade(0.0, 0.5, 1000.0, s) * 0.3;
	//sample += part2(s) * 0.2; // * sawFade(5.0f, 5.0f, 55.0f, s);
	sample += part3(s) * sawFade(20.5, 5.0, 10.0, s) * 0.3;
	sample += part4(s) * sawFade(0.0f, 5.0f, 10.0f, fmod(s, 15.0f)) * 0.3f;
	return 0.5 + sample;
}

void syna(void* userdata, uint8_t* stream, int len)
{
	for(int i = 0; i < len; i+=2)
	{
		double t = (double)(totalSamples);
		double s = t / 44100.0;
		double sample = part(s);
		stream[i] = 250 * sample;
		stream[i+1] = 250 * sample;
		totalSamples++;
	}
}

