
// ****************************************************************************
//
//                         Page NA - noise analog
//
// ****************************************************************************

#include "../include.h"

#define NA_STEPBITS	10	// number of bits per X integer step
#define NA_PHASEMASK	((1<<NA_STEPBITS)-1)	// phase fraction mask

#define NA_LOOP_NUM	(NA_STEPBITS+1)	// number of loop setup

#define NA_OCTNUM	11	// number of octaves

#define NA_BUFBITS	12	// buffer size in bits
#define NA_BUFNUM	(1<<NA_BUFBITS) // buffer number of entries (= 4096)
#define NA_BUFMASK	(NA_BUFNUM-1)	// buffer index mask

u32 NAPhaseInc;		// NA phase increment
u32 NAPhaseAcc;		// NA phase accumulator
int NANext = 0;		// NA next sample
int NALoop = 7;		// current selected loop

const char* const NA_LoopTxt[NA_LOOP_NUM] = {
	"17s", "9s", "4s", "2s", "1s", "546s", "273ms", "137ms", "68ms", "34ms", "17ms",
};

// Perlin noise - hash and gradient
//   xi ... signed integer part -32767..+32767
//   xf ... fractional part 0..65535 (= 0..1) or -65535..0 (= -1..0)
// result: -xf or +xf (= -1 .. +1)
s32 NA_GradHash(s32 xi, s32 xf)
{
	xi ^= xi >> 7;
	xi ^= xi << 9;
	xi ^= xi >> 13;
	if ((xi & 1) == 0) xf = -xf;
	xf >>= 1;
	return xf;
}

// Perlin noise 1D
//  x ... X coordinate in Q16 16.16 signed format
//  mask ... mask of integer part
// result is -32767..+32767
s32 NA_Perlin1D(s32 x, u32 mask)
{
	// split X from 16.16 format to integer and fractional part
	s32 xi = x >> 16;	// integer part -32767..+32767
	s32 xf = x & 0xffff;	// fractional part 0..65535

	// hash and gradient of 2 points ... result is -xf/2 or +xf/2
	s32 g0 = NA_GradHash(xi, xf);
	s32 g1 = NA_GradHash((xi+1) & mask, xf-0x10000);

	// quadratic fade ... result is 0..65535
	u32 t2 = ((u32)xf*(u32)xf) >> 16;
	xf = (s32)(u32)((t2 * (3*65535 - 2*(u32)xf)) >> 16);

	// linear interpolation
	s32 res = g0 + ((xf*(g1 - g0)) >> 16);
	if (res < -32767) res = -32767;
	if (res > 32767) res = 32767;
	return res;
}

// generate noise to buffer
void NA_Gener()
{
	int inx, oct;
	u8* b = (u8*)Buf;
	for (inx = 0; inx < NA_BUFNUM; inx++)
	{
		int s = 0;
		int shift = 5;
		// 1st octave shift index 0..0x0fff 5 bits left to 0..0x1ffe0
		// 11th octave shift index 0..0x0fff 15 bits left to 0..0x07ff8000
		int m = 1;
		// 1st octave mask 0x0001, 2 pulses per buffer
		// 11th octave mask 0x7ff, 2048 pulses per buffer
		for (oct = NA_OCTNUM-1; oct >= 0; oct--)
		{
			s += NA_Perlin1D(inx << shift, m);
			m = (m << 1) | 1;
			shift++;
		}
		s += 65535;		// range is now 0..131070
		if (s < 0) s = 0;
		if (s > 131070) s = 131070;
		s = (s * NA_LOOP) >> 17;  // convert to range 0..LOOP-1
		*b++ = (u8)s;
	}
}

/*
// NA Timer 2 interrupt handler ... handler located at page_asm.S
//HANDLER void TIM2_IRQHandler()
HANDLER void NA_Handler()
{
	// set next sample
	TIM2_Comp1(NANext);
	cb();

	// clear interrupt flag
	TIM2_UpIntClr();

	// shift phase accumulator
	u32 acc = NAPhaseAcc + NAPhaseInc;
	NAPhaseAcc = acc;

	u8* b = (u8*)Buf;
	int off = (acc >> NA_STEPBITS) & NA_BUFMASK;
	u8 s1 = b[off];
	u8 s2 = b[(off+1) & NA_BUFMASK];
	int s = ((((s32)s2 - (s32)s1)*(acc & NA_PHASEMASK)) >> NA_STEPBITS) + s1;

	NANext = s;
}
*/

// NA setup
void NA_Setup()
{
	di();
	NAPhaseInc = 1 << NALoop;
	NAPhaseAcc = 0;
	ei();
}

// NA output initialize
void NA_Init()
{
	// generate noise to buffer
	NA_Gener();

	// setup
	NA_Setup();

	// Enable timer clock source
	TIM2_ClkEnable();

	// Reset timer to default setup
	TIM2_Reset();

	// select input from internal clock CK_INT
	TIM2_InMode(TIM_INMODE_INT);

	// enable compare output
	TIM2_CC1Enable();

	// setup prescaler
	TIM2_Presc(0);
	TIM2_Load(NA_LOOP-1);
	TIM2_Comp1(NA_LOOP >> 1);

	// direction up
	TIM2_DirUp();

	// enable auto-reload of preload compare register
	TIM2_AutoReloadEnable();

	// setup compare
	TIM2_OC1Mode(TIM_COMP_PWM1);	// set compare mode
	TIM2_OC1PreEnable();		// enable preload compare register

	// enable main output
	TIM2_OCEnable();

	// reload immediately
	TIM2_Update();

	// remap Timer 2
	// Output: PC5, Timer 2 channel 1 mapping 2
	//	2 ... >>> PC5:CH1/ETR <<<, PC2:CH2, PD2:CH3/CH1N, PC1:CH4/CH2N
	GPIO_Remap_TIM2(2);

	// setup GPIO pin
	GPIO_Mode(NA_GPIO, GPIO_MODE_AF);

	// set PWM Timer 2 interrupt handler
	SetHandler(IRQ_TIM2, NA_Handler);

	// enable update interrupt
	TIM2_UpIntEnable();

	// interrupt enable, handler TIM2_IRQHandler()
	NVIC_IRQEnable(IRQ_TIM2);
	TIM2_UpIntClr();

	// enable timer
	TIM2_Enable();
}

// NA output terminate
void NA_Term()
{
	// interrupt disable
	NVIC_IRQDisable(IRQ_TIM2);
	TIM2_UpIntDisable();
	TIM2_UpIntClr();

	// reset GPIO pin
	GPIO_PinReset(NA_GPIO);

	// reset Timer 2
	TIM2_Reset();

	// Timer 2 clock disable
	RCC_TIM2ClkDisable();
}

// NA display
void NA_Disp()
{
	// clear area
	DrawRectClrFast(0, TITLE_H, WIDTH, HEIGHT-TITLE_H);

	// select font 8x12
	SelFont12();

	// display current loop
	DrawText("loop", (WIDTH-4*8)/2, ROW1_Y);
	const char* t = NA_LoopTxt[NALoop];
	int len = StrLen(t);
	DrawText2(t, (WIDTH-len*16)/2, ROW2_Y);

	// display update
	DispUpdate();
}

// Page NA (returns key PREV/NEXT)
u8 PageNA()
{
	u8 key;
	int i;

	// NA initialize
	NA_Init();

	// display NA
	NA_Disp();

	while (True)
	{
		// reload watchdog counter
		IWDG_Reload();

		// update buffer
		// get keyboard
		key = KeyGet();
		switch (key)
		{
		// Prev
		case KEY_PREV:
		// Next
		case KEY_NEXT:
			// NA terminate
			NA_Term();
			return key;

		// Slow
		case KEY_SLOW:
			i = NALoop;
			if (i > 0)
			{
				NALoop = i-1;
				NA_Setup();
				NA_Disp();
			}
			break;

		// Fast
		case KEY_FAST:
			i = NALoop;
			if (i < NA_LOOP_NUM-1)
			{
				NALoop = i+1;
				NA_Setup();
				NA_Disp();
			}
			break;
		}
	}
}
