#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "kernel.h"
#include "swis.h"
#include "time.h"

#include "Mem.h"
#include "FTables.h"
#include "FSong.h"
#include "Instrument.h"
#include "Note.h"
#include "NoteHandler.h"
#include "FNotes.h"

#define M_PI 3.14159265358979323846

extern void CITFilter_Params(Note*, int);

static void test_RelTone(void)
{
	int max = 0;
	int maxc = 0;
	int sum = 0;
	int maxix = 0;

	for (int c = 1; c <= 192000; c++)
	{
		double x = 12*256*log(c/8363.)/log(2);
		int ix = (int) (x + .5);
		int cx = Convert_RelTone(8363, c);
		int abx = abs(ix - cx);
		if (max < abx)
		{
			max = abx;
			maxc = c;
			maxix = ix;
		}
		else if ((max == abx) && (maxix > ix))
		{
			maxc = c;
			maxix = ix;
		}
		sum += abx;
	}

//	for (int c = 0; c <= 4096; c++)
//	{
//		double x = 12*256*(log(4096+c)-log(4096))/log(2);
//		printf(", %03x", (int) (x + .5));
//		if ((c & 7) == 7) printf("\n");
//	}
	printf("Convert_RelTone(8363, x) deviates by %d at freq %d, total dev. is %d / %d\n"
		, max, maxc, sum, 192000);
}

static void test_ToneToPeriod(void)
{
	int max = 0;
	int maxc = 0;
	int sum = 0;
	int maxix = 0;

	for (int c = -8*256; c <= 248*256; c++)
	{
		double x = 428*256*pow(2,((double)(int)(64*256-c))/(12*256.));
		int ix = (int) (x + .5);
		int cx = Convert_ToneToPeriod(c);
		int abx = abs(ix - cx);
		if (max < abx)
		{
			max = abx;
			maxc = c;
			maxix = ix;
		}
		else if ((max == abx) && (maxix > ix))
		{
			maxc = c;
			maxix = ix;
		}
		sum += abx;
	}

	printf("Convert_ToneToPeriod() deviates by %d (period = %d) at tone %04x, total dev. is %d / %d\n"
		, max, maxix, maxc, sum, 256*256);
}

static void test_PeriodToTone(void)
{
	int max = 0;
	int maxc = 0;
	int sum = 0;
	int maxix = 0;

	for (int c = 256; c <= 0x6b0000; c += 16)
	{
		double x = 12*256*log(428*256/(double)c)/log(2);
		int ix = 64*256 + (int) (x + .5);
		int cx = Convert_PeriodToTone(c);
		int abx = abs(ix - cx);
		if (max < abx)
		{
			max = abx;
			maxc = c;
			maxix = ix;
		}
		else if ((max == abx) && (maxix > ix))
		{
			maxc = c;
			maxix = ix;
		}
		sum += abx;
	}

	printf("Convert_PeriodToTone() deviates by %d (tone = %04x) at period %d, total dev. is %d / %d\n"
		, max, maxix, maxc, sum, (0x6b0000 - 256) / 16);
}

static void test_ToneToFrac(void)
{
	int max = 0;
	int maxc = 0;
	int sum = 0;
	int maxix = 0;

	for (int c = -24*256; c <= 232*256; c++)
	{
		double x = 65536*pow(2,((double)(int)(c-64*256))/(12*256.));
		int ix = (int) (x + .5);
		int cx = Convert_ToneToFrac(c);
		int abx = abs(ix - cx);
		if (max < abx)
		{
			max = abx;
			maxc = c;
			maxix = ix;
		}
		else if ((max == abx) && (maxix > ix))
		{
			maxc = c;
			maxix = ix;
		}
		sum += abx;
	}

	printf("Convert_ToneToFrac() deviates by %d (frac = %04x) at tone %04x, total dev. is %d / %d\n"
		, max, maxix, maxc, sum, 256*256);
}

static void test_FracToTone(void)
{
	int max = 0;
	int maxc = 0;
	int sum = 0;
	int maxix = 0;

	for (int c = 1 << (16-7); c <= 0x400000; c += 16)
	{
		double x = 12*256*log((double)c/65536.)/log(2);
		int ix = 64*256 + (int) (x + .5);
		int cx = Convert_FracToTone(c);
		int abx = abs(ix - cx);
		if (max < abx)
		{
			max = abx;
			maxc = c;
			maxix = ix;
		}
		else if ((max == abx) && (maxix > ix))
		{
			maxc = c;
			maxix = ix;
		}
		sum += abx;
	}

	printf("Convert_TracToTone() deviates by %d (tone = %04x) at frac %04x, total dev. is %d / %d\n"
		, max, maxix, maxc, sum, (0x400000 - 1 << (16-7)) / 16);
}

static void test_CutOff(void)
{
	int max = 0;
	int maxc = 0;
	int sum = 0;
	int maxix = 0;

	for (int c = 0; c <= 127*256; c+=2)
	{
		double x = pow(2., -((double)c)/(24*256.));
		int ix = (int)(x*(1<<30));
		int cx = Convert_Cutoff(c);
		int abx = abs(ix - cx);
		if (max < abx)
		{
			max = abx;
			maxc = c;
			maxix = ix;
		}
		else if ((max == abx) && (maxix > ix))
		{
			maxc = c;
			maxix = ix;
		}
		sum += abx;
	}

	printf("Convert_CutOff() deviates by %d (val = %d) at C %04x, total dev. is %d / %d\n"
		, max, maxix, maxc, sum, 127*256 / 2);
}

static void test_ITFilters(int samplerate)
{
	Note note;
	double f = (double) samplerate;
	double nat = 1/ (2.*M_PI*110.*pow(2., .25));
	int maxx = 0;
	int maxbx = 0;
	int sumx = 0;

	printf("\nTesting IT filter params at sampling rate: %d\n\n", samplerate);

	for(int s = 0; s <= 127; s++)
	{
		int max = 0;
		int maxb = 0;
		int maxc = 0;
		int maxs = 0;
		int sum = 0;
		double l = pow(10., -24.*s/(128*20));

		for (int c = 0; c <= 127*256; c++)
		{
			if ((s == 0) && ((c >> 8) == 127)) continue;
			double x = pow(2., -((double)c)/(24*256.));
			double r = f * nat * x;
			double d;
			if ((1. - l) > (2. *r))
				d = l*r - 2.*r;
			else
				d = l*r + l - 1.;
			double e = r * r;
			double a0 = 1./(1.+d+e);
			double b2 = -e * a0;
			double b1 = 1. - b2 - a0;
			int i0 = (int)(a0*(1<<28));
			int i1 = (int)(b1*(1<<28));
			int i2 = (int)(b2*(1<<28));

			note.cutoff = c >> 8;
			note.resonance = s;
			note.realcutoff = c;
			CITFilter_Params(&note, (int) f);
			if (max < abs(note.stream.filter.a0 - i0))
			{
				max = abs(note.stream.filter.a0 - i0);
			}
			sum += abs(note.stream.filter.a0 - i0);
			if (maxb < abs(note.stream.filter.b1 - i1))
			{
				maxb = abs(note.stream.filter.b1 - i1);
				maxc = c;
				maxs = s;
			}
			sum += abs(note.stream.filter.b1 - i1);
			if (maxb < abs(note.stream.filter.b2 - i2))
			{
				maxb = abs(note.stream.filter.b2 - i2);
				maxc = c;
				maxs = s;
			}
			sum += abs(note.stream.filter.b2 - i2);
		}

		printf("S %02x: MAX %08x, MAXB %08x at C %04x, sum errors is %d / %d\n"
			, maxs, max, maxb, maxc, sum, 127* 256);
		if (maxx < max) maxx = max;
		if (maxbx < maxb) maxbx = maxb;
		sumx += sum;
	}
	printf("Largest MAX %08x, largest MAXB %08x\n", maxx, maxbx);
}

static void test_vibratos(void)
{
	int max = 0;
	int maxc = 0;
	int c;
	int sum = 0;

	// 0 Sin
	for (c = 0; c < 256; c++)
	{
		double x = sin(2*M_PI*c/256)*255;
		if (x < 0) x-=.5;
		if (x > 0) x+=.5;
		int ix = (int) x;
		int cx = Get_VibratoValue(0, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	printf("Get_VibratoValue(0, x) deviates by %d at %d, total dev. is %d\n", max, maxc, sum);

	// 1 square
	max = 0; maxc = 0; sum = 0;

	for (c = 0; c < 128; c++)
	{
		int ix = 255;
		int cx = Get_VibratoValue(1, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	for (c = 128; c < 256; c++)
	{
		int ix = -255;
		int cx = Get_VibratoValue(1, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	printf("Get_VibratoValue(1, x) deviates by %d at %d, total dev. is %d\n", max, maxc, sum);

	// 2 ramp
	max = 0; maxc = 0; sum = 0;

	for (c = 0; c < 128; c++)
	{
		int ix = 1+c*2;
		int cx = Get_VibratoValue(2, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	for (c = 128; c < 256; c++)
	{
		int ix = -255+2*(c-128);
		int cx = Get_VibratoValue(2, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	printf("Get_VibratoValue(2, x) deviates by %d at %d, total dev. is %d\n", max, maxc, sum);

	// 3 ramp down
	max = 0; maxc = 0; sum = 0;

	for (c = 0; c < 256; c++)
	{
		int ix = 255 - c*2;
		int cx = Get_VibratoValue(3, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	printf("Get_VibratoValue(3, x) deviates by %d at %d, total dev. is %d\n", max, maxc, sum);

	// 4 random

	// 5 saw
	max = 0; maxc = 0; sum = 0;

	for (c = 0; c < 64; c++)
	{
		int ix = 1+c*4;
		int cx = Get_VibratoValue(5, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	for (c = 64; c < 192; c++)
	{
		int ix = 255-4*(c-64);
		int cx = Get_VibratoValue(5, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	for (c = 192; c < 256; c++)
	{
		int ix = 4*(c-192)-255;
		int cx = Get_VibratoValue(5, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	printf("Get_VibratoValue(5, x) deviates by %d at %d, total dev. is %d\n", max, maxc, sum);

	// 6 half square
	max = 0; maxc = 0; sum = 0;

	for (c = 0; c < 128; c++)
	{
		int ix = 255;
		int cx = Get_VibratoValue(6, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	for (c = 128; c < 256; c++)
	{
		int ix = 0;
		int cx = Get_VibratoValue(6, c);
		if (max < abs(ix - cx))
		{
			max = abs(ix - cx);
			maxc = c;
		}
		sum += abs(ix - cx);
	}

	printf("Get_VibratoValue(6, x) deviates by %d at %d, total dev. is %d\n", max, maxc, sum);
}

typedef struct
{
 	uint32_t ticks;
	uint32_t pos;
	int val;
	uint32_t done;
} eres;

// Notes in this table ticks & pos are on exit of Process_Envelopes, i.e. ready for next tick.
// End point is part of loop in IT but not in XM. In effect, IT increases pos only after ticks count
// exceeds ticks[pos+1]. Both starts fading when no loop is active as soon as pos = maxpos.
// For XM this means: loop never reach the final value and fading thr tick before ticks[maxpos].

// IT, no loop
static const eres e0[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 1}
, {  8, 2, 190, 1}
, {  8, 2, 190, 1}
, {  8, 2, 190, 1}
};

// IT, sustain [0,0], no sustain at 3
static const eres es00_3[] =
{ {  0, 0,  10, 0}
, {  0, 0,  10, 0}
, {  0, 0,  10, 0}
, {  1, 0,  10, 0} //
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 1}
};

// IT, sustain [0,1], no sustain at 3
static const eres es01_3[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0} //
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 1}
};

// IT, sustain [0,1], no sustain at 4
static const eres es01_4[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0} //
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 1}
};

// IT, sustain [0,1], no sustain at 7
static const eres es01_7[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  0, 0, 110, 0}
, {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0} //
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 1}
};

// IT, sustain [1,1], no sustain at 7
static const eres es11_7[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  4, 1, 110, 0}
, {  4, 1, 110, 0}
, {  4, 1, 110, 0}
, {  5, 1, 110, 0} //
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 1}
};

// IT, sustain [1,2], no sustain at 7
static const eres es12_7[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0} //
, {  8, 2, 190, 1}
};

// IT, sustain [1,2], no sustain at 8
static const eres es12_8[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 1} //
};

// IT, sustain [1,2], no sustain at 9
static const eres es12_9[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  4, 1, 190, 0}
, {  5, 1, 110, 0} //
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 1}
};

// IT, sustain [2,2], no sustain at 7
static const eres es22_7[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0} //
, {  8, 2, 190, 1}
};

// IT, sustain [2,2], no sustain at 8
static const eres es22_8[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 1} //
};

// IT, sustain [2,2], no sustain at 9
static const eres es22_9[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 0}
, {  8, 2, 190, 1} //
};

// IT, sustain [0,0], loop [1,1] no sustain at 2
static const eres es00l11_2[] =
{ {  0, 0,  10, 0}
, {  0, 0,  10, 0}
, {  1, 0,  10, 0} //
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  4, 1, 110, 0}
, {  4, 1, 110, 0}
};

// IT, sustain [0,0], loop [1,2] no sustain at 2
static const eres es00l12_2[] =
{ {  0, 0,  10, 0}
, {  0, 0,  10, 0}
, {  1, 0,  10, 0} //
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  4, 1, 190, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  4, 1, 190, 0}
};

// IT, sustain [1,1], loop [0,2] no sustain at 6
static const eres es11l02_6[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  4, 1, 110, 0}
, {  4, 1, 110, 0}
, {  5, 1, 110, 0} //
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  0, 0, 190, 0}
, {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  0, 0, 190, 0}
};

// IT, sustain [2,2], loop [1,2] no sustain at 10
static const eres es22l12_10[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 0}
, {  8, 2, 190, 0}
, {  4, 1, 190, 0} //
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  4, 1, 190, 0}
, {  5, 1, 110, 0}
};

// IT, sustain [2,2], loop [0,1] no sustain at 10
static const eres es22l01_10[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  8, 2, 190, 0}
, {  8, 2, 190, 0}
, {  0, 0, 190, 0} //
, {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  0, 0, 110, 0}
, {  1, 0,  10, 0}
};

// IT, loop [0,0] no sustain at 2
static const eres el00_2[] =
{ {  0, 0,  10, 0}
, {  0, 0,  10, 0}
, {  0, 0,  10, 0} //
, {  0, 0,  10, 0}
, {  0, 0,  10, 0}
};

// IT, loop [0,1] no sustain at 6
static const eres el01_6[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  0, 0, 110, 0}
, {  1, 0,  10, 0}
, {  2, 0,  35, 0} //
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  0, 0, 110, 0}
};

// IT, loop [1,1] no sustain at 6
static const eres el11_6[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  4, 1, 110, 0}
, {  4, 1, 110, 0}
, {  4, 1, 110, 0} //
, {  4, 1, 110, 0}
, {  4, 1, 110, 0}
};

// IT, loop [1,2] no sustain at 10
static const eres el12_10[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 0,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  4, 1, 190, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0} //
, {  7, 1, 150, 0}
, {  8, 1, 170, 0}
, {  4, 1, 190, 0}
};

// XM, sustain [0,1], no sustain at 3
static const eres ex01_3[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 1,  85, 0} //
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 2, 170, 1}
, {  8, 2, 190, 1}
};

// XM, sustain [0,1], no sustain at 4
static const eres ex01_4[] =
{ {  1, 0,  10, 0}
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  0, 0,  85, 0}
, {  1, 0,  10, 0} //
, {  2, 0,  35, 0}
, {  3, 0,  60, 0}
, {  4, 1,  85, 0}
, {  5, 1, 110, 0}
, {  6, 1, 130, 0}
, {  7, 1, 150, 0}
, {  8, 2, 170, 1}
, {  8, 2, 190, 1}
};

// XM, width = 1tick
static const eres ex_1[] =
{ {  1, 1,  10, 1}
, {  1, 1, 110, 1}
, {  1, 1, 110, 1}
, {  1, 1, 110, 1}
};

static int cmpe(const char* title, const eres* pres, int i, uint32_t ticks, uint32_t pos, int  val, uint32_t done)
{
	if ((pres[i].ticks != ticks)
	||  (pres[i].pos != pos)
	||  (pres[i].val != val)
	||  (pres[i].done != done))
	{
		printf("%s incorrect after %d ticks.\n", title, i);
		printf("Expected ticks %d, pos %d, val %d, done %d\n"
			, pres[i].ticks, pres[i].pos, pres[i].val, pres[i].done);
		printf("Was      ticks %d, pos %d, val %d, done %d\n"
			, ticks, pos, val, done);
		return 1;
	}

	return 0;
}

static void test_envelopes(void)
{
	uint32_t ticks;
	uint32_t pos;
	uint32_t flags;
	int val;
	int i;
	uint32_t table[4];
	Envelope e;
	NoteHandler h;

	printf("Testing enevlopes\n");

	table[0] = ( 10 << 16) + 0; // point 0
	table[1] = (110 << 16) + 4; // point 1
	table[2] = (190 << 16) + 8; // point 2
	table[3] = (  0 << 16) + 0; // "gargbage"
	e.Points = 3;
	e.pTable = &table[0];

	// Note, loop & sustain loop are passed as flags
	// with sustain flag forced to 0 once sustain is off
	// Also since on exit of Process_Envelope is replaced by boolean
	// indicating if envelope was finished, "flags" must be filled
	// again for the next call.

	// IT cases
	h.Flags = hnotes_flag_ITenvelopes;

	// IT No loop, no sustain, never set sustain off
	e.SustainStart = 0;
	e.SustainEnd = 0;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(e0)/sizeof(eres); i++)
	{
		flags = 0;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("e0", e0, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[0, 0] off at 3
	e.SustainStart = 0;
	e.SustainEnd = 0;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es00_3)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 3) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es00_3", es00_3, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[0, 1] off at 3
	e.SustainStart = 0;
	e.SustainEnd = 1;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es01_3)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 3) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es01_3", es01_3, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[0, 1] off at 4
	e.SustainStart = 0;
	e.SustainEnd = 1;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es01_4)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 4) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es01_4", es01_4, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[0, 1] off at 7
	e.SustainStart = 0;
	e.SustainEnd = 1;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es01_7)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 7) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es01_7", es01_7, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[1, 1] off at 7
	e.SustainStart = 1;
	e.SustainEnd = 1;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es11_7)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 7) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es11_7", es11_7, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[1, 2] off at 7
	e.SustainStart = 1;
	e.SustainEnd = 2;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es12_7)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 7) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es12_7", es12_7, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[1, 2] off at 8
	e.SustainStart = 1;
	e.SustainEnd = 2;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es12_8)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 8) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es12_8", es12_8, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[1, 2] off at 9
	e.SustainStart = 1;
	e.SustainEnd = 2;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es12_9)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 9) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es12_9", es12_9, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[2, 2] off at 7
	e.SustainStart = 2;
	e.SustainEnd = 2;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es22_7)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 7) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es22_7", es22_7, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[2, 2] off at 8
	e.SustainStart = 2;
	e.SustainEnd = 2;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es22_8)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 8) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es22_8", es22_8, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[2, 2] off at 9
	e.SustainStart = 2;
	e.SustainEnd = 2;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es22_9)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 9) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es22_9", es22_9, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[0, 0], loop [1,1] off at 2
	e.SustainStart = 0;
	e.SustainEnd = 0;
	e.LoopStart = 1;
	e.LoopEnd = 1;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es00l11_2)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Loop | Envelope_Flag_Sustain;
		if (i >= 2) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es00l11_2", es00l11_2, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[0, 0], loop [1,2] off at 2
	e.SustainStart = 0;
	e.SustainEnd = 0;
	e.LoopStart = 1;
	e.LoopEnd = 2;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es00l12_2)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Loop | Envelope_Flag_Sustain;
		if (i >= 2) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es00l12_2", es00l12_2, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[1, 1], loop [0,2] off at 6
	e.SustainStart = 1;
	e.SustainEnd = 1;
	e.LoopStart = 0;
	e.LoopEnd = 2;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es11l02_6)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Loop | Envelope_Flag_Sustain;
		if (i >= 6) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es11l02_6", es11l02_6, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[2, 2], loop [1,2] off at 10
	e.SustainStart = 2;
	e.SustainEnd = 2;
	e.LoopStart = 1;
	e.LoopEnd = 2;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es22l12_10)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Loop | Envelope_Flag_Sustain;
		if (i >= 10) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es22l12_10", es22l12_10, i, ticks, pos, val, flags))
			break;
	}

	// IT sustain[2, 2], loop [0,1] off at 10
	e.SustainStart = 2;
	e.SustainEnd = 2;
	e.LoopStart = 0;
	e.LoopEnd = 1;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(es22l01_10)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Loop | Envelope_Flag_Sustain;
		if (i >= 10) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("es22l01_10", es22l01_10, i, ticks, pos, val, flags))
			break;
	}

	// IT loop [0,0] off at 2
	e.SustainStart = 0;
	e.SustainEnd = 0;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(el00_2)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Loop;
		if (i >= 2) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("el00_2", el00_2, i, ticks, pos, val, flags))
			break;
	}

	// IT loop [0,1] off at 6
	e.SustainStart = 0;
	e.SustainEnd = 0;
	e.LoopStart = 0;
	e.LoopEnd = 1;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(el01_6)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Loop;
		if (i >= 6) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("el01_6", el01_6, i, ticks, pos, val, flags))
			break;
	}

	// IT loop [1,1] off at 6
	e.SustainStart = 0;
	e.SustainEnd = 0;
	e.LoopStart = 1;
	e.LoopEnd = 1;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(el11_6)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Loop;
		if (i >= 6) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("el11_6", el11_6, i, ticks, pos, val, flags))
			break;
	}

	// IT loop [1,2] off at 10
	e.SustainStart = 0;
	e.SustainEnd = 0;
	e.LoopStart = 1;
	e.LoopEnd = 2;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(el12_10)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Loop;
		if (i >= 10) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("el12_10", el12_10, i, ticks, pos, val, flags))
			break;
	}

	// XM cases
	h.Flags = 0;

	// XM sustain[0, 1] off at 3
	e.SustainStart = 0;
	e.SustainEnd = 1;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(ex01_3)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 3) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("ex01_3", ex01_3, i, ticks, pos, val, flags))
			break;
	}

	// XM sustain[0, 1] off at 4
	e.SustainStart = 0;
	e.SustainEnd = 1;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(ex01_4)/sizeof(eres); i++)
	{
		flags = Envelope_Flag_Sustain;
		if (i >= 4) flags &= ~Envelope_Flag_Sustain;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("ex01_4", ex01_4, i, ticks, pos, val, flags))
			break;
	}

	// XM width = 1 tick
	e.Points = 2;
	table[1] = (110 << 16) + 1; // point 1
	e.SustainStart = 0;
	e.SustainEnd = 0;
	e.LoopStart = 0;
	e.LoopEnd = 0;
	for (i = 0, pos = 0, ticks = 0; i < sizeof(ex_1)/sizeof(eres); i++)
	{
		flags = 0;
		val = Process_Envelope(&h, &e, &ticks, &pos, &flags);
		if (cmpe("ex_1", ex_1, i, ticks, pos, val, flags))
			break;
	}
}

int main(int argc, char** argv)
{
	IGNORE(argc);
	IGNORE(argv);

//	test_ITFilters(5000);
//	test_ITFilters(44100);
//	test_ITFilters(192000);
	test_RelTone();
	test_ToneToPeriod();
	test_PeriodToTone();
	test_ToneToFrac();
	test_FracToTone();
	test_CutOff();
	test_vibratos();
	test_envelopes();

	return EXIT_SUCCESS;
}
