//
// This file is part of the GNU ARM Eclipse distribution.
// Copyright (c) 2014 Liviu Ionescu.
//

// ----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <stm32f4_discovery_audio.h>
#include "diag/Trace.h"

#include "Timer.h"
#include "sinetable.h"
#include "freqtable.h"

// ----------------------------------------------------------------------------
//
// Standalone STM32F4 led blink sample (trace via ITM).
//
// In debug configurations, demonstrate how to print a greeting message
// on the trace device. In release configurations the message is
// simply discarded.
//
// Then demonstrates how to blink a led with 1 Hz, using a
// continuous loop and SysTick delays.
//
// Trace support is enabled by adding the TRACE macro definition.
// By default the trace messages are forwarded to the ITM output,
// but can be rerouted to any device or completely suppressed, by
// changing the definitions required in system/src/diag/trace_impl.c
// (currently OS_USE_TRACE_ITM, OS_USE_TRACE_SEMIHOSTING_DEBUG/_STDOUT).
//


// ----- main() ---------------------------------------------------------------

// Sample pragmas to cope with warnings. Please note the related line at
// the end of this function, used to pop the compiler diagnostics status.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#pragma GCC diagnostic ignored "-Wreturn-type"


// pins connected on discovery board
// A0 4 5 6 7 9 10 11 12
// B6 9 10
// C0 3 4 7 10 12 14 15
// D4 5 12 13 14 15
// E0 1 3
// DAC B0, B1,
// SWITCH D0 1 2 3
// Timer, LED and Switch globals
TIM_HandleTypeDef TimHandle;
float millisecs;
int32_t ledselected;
int32_t time, button_time[16];

// ADC globals
ADC_HandleTypeDef AdcHandle;
__IO uint16_t adcBuffer[8];
uint32_t smoothadc[8], avgtotal[8];
uint32_t adc0average[64];
uint32_t adc1average[64];
int16_t avgindex;
float filtknob;
float filtgain;

// DAC globals
DAC_HandleTypeDef    DacHandle;
uint16_t dacBuffer[32]; // 32 samples X 1 channels

// I2S globals
extern I2S_HandleTypeDef       hAudioOutI2s;
extern I2S_HandleTypeDef       hAudioInI2s;
int16_t codecBuffer[64];	// 32 samples X 2 channels
int bspAudioState;

// Sound globals
void audioBlock(float *input, float *output, int32_t samples);
float inBuffer[1024], outBuffer[1024]; // interleaved - LRLRLRLRLRLRLRLRLRLRLR - inBuffer[frame << 1] inBuffer[(frame << 1) + 1]

// sample library - acetone
// clave - 5568
// cl hat - 8125
// cowbell - 19895
// cymball - 33214
// guiro - 49167
// hi bongo - 53827
// kick - 66557
// lo bongo - 72310
// lo conga - 80442
// maracas - 82480
// rim - 82912
// snare - 88550

// Synthesis types
float oneOver2tt31;
struct rando
{
	uint32_t seed;
};
struct osco
{
	float pi;	// phase increment
	float phs;	// phase
	float *tab;	// table address
	uint32_t ts;	// table size
	uint32_t tm;	// table mask aka table size minus one
};

struct lino
{
	float val;		// current value
	float dst;		// value to move to
	float inc;		// increment per sample to get from val to dst
};

typedef struct filter0
{
	float stt;
} 	filter0;

typedef struct filter1
{
	float in1;
	float out1;
}	filter1;

typedef struct filter2
{
	float c;
	float r;
	float a1;
	float a2;
	float a3;
	float b1;
	float b2;
	float x1;
	float x2;
	float y1;
	float y2;
}	filter2;

// Synthesis globals
struct rando r;
struct osco o[6];
filter0 f0;
filter1 f1;
filter2 f2;
struct lino amp[3];
struct lino sampno[4];

int16_t *acetone;
int32_t note[3];
float echoBuf[16384];
int32_t echoPos;
float agcState;


void
led_init()
{
  // Enable GPIO Peripheral clock
  __HAL_RCC_GPIOD_CLK_ENABLE();

  GPIO_InitTypeDef GPIO_InitStructure;

  // Configure pin in output push/pull mode
  GPIO_InitStructure.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
  GPIO_InitStructure.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);

}

void
switch_init()
{
  // Enable GPIO Peripheral clock
	  __HAL_RCC_GPIOA_CLK_ENABLE();
	  __HAL_RCC_GPIOD_CLK_ENABLE();

  GPIO_InitTypeDef GPIO_InitStructure;

  // Configure pin for polling
/*  GPIO_InitStructure.Pin = GPIO_PIN_0;
  GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
  GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
  GPIO_InitStructure.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);*/


  // Configure for interrupt
  /* Configure PA0 pin as input floating */
  GPIO_InitStructure.Pin = GPIO_PIN_0;
  GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStructure.Pull = GPIO_NOPULL;
  GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
//  HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);

  GPIO_InitStructure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
  GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStructure.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);

  /* Enable and set EXTI Line0 Interrupt to the lowest priority */
   HAL_NVIC_SetPriority(EXTI0_IRQn, 8, 3);
   HAL_NVIC_EnableIRQ(EXTI0_IRQn);

   HAL_NVIC_SetPriority(EXTI1_IRQn, 8, 2);
   HAL_NVIC_EnableIRQ(EXTI1_IRQn);

   HAL_NVIC_SetPriority(EXTI2_IRQn, 8, 1);
   HAL_NVIC_EnableIRQ(EXTI2_IRQn);

   HAL_NVIC_SetPriority(EXTI3_IRQn, 8, 0);
   HAL_NVIC_EnableIRQ(EXTI3_IRQn);
}

void
timer3_init()
{
	  __HAL_RCC_TIM3_CLK_ENABLE();

	  TimHandle.Instance = TIM3;
	  TimHandle.Init.Period = 8399; // Clock = 168000000/2 = 84000000/(8399+1) = 10000
	  TimHandle.Init.Prescaler = 0;
	  TimHandle.Init.ClockDivision = 0;
	  TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
	  HAL_TIM_Base_Init(&TimHandle) ;
	  HAL_TIM_Base_Start_IT(&TimHandle);

	  /*##-2- Configure the NVIC for TIMx ########################################*/
	  /* Set Interrupt Group Priority */
	  HAL_NVIC_SetPriority(TIM3_IRQn, 4, 0);
	  HAL_NVIC_EnableIRQ(TIM3_IRQn);
}

void timer6_init(void)
{
  static TIM_HandleTypeDef  htim;
  TIM_MasterConfigTypeDef sMasterConfig;

  /*##-1- Configure the TIM peripheral #######################################*/
  /* Time base configuration */
  __HAL_RCC_TIM6_CLK_ENABLE();
  htim.Instance = TIM6;

//  htim.Init.Period = 874;	// 96000 clock - suitable for 2 channel 48000
  htim.Init.Period = 1749;	// 48000 clock - suitable for 1 channel 48000
  htim.Init.Prescaler = 0;
  htim.Init.ClockDivision = 0;
  htim.Init.CounterMode = TIM_COUNTERMODE_UP;
  HAL_TIM_Base_Init(&htim);

  /* TIM6 TRGO selection */
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

  HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);

  /*##-2- Enable TIM peripheral counter ######################################*/
  HAL_TIM_Base_Start(&htim);
}

void adc1_init(void)
{
	int i;

	filtknob = 0.0;
	for(i = 0; i < 64; i++)
		adc0average[i] = adc1average[i] = 0.0f;
    avgindex = 0;
    avgtotal[0] = avgtotal[1] = 0;

    ADC_ChannelConfTypeDef sConfig;
	  AdcHandle.Instance = ADC1;

	  AdcHandle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
	  AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
	  AdcHandle.Init.ScanConvMode = ENABLE;	// to scan multiple channels
	  AdcHandle.Init.ContinuousConvMode = ENABLE;
	  AdcHandle.Init.DiscontinuousConvMode = DISABLE;
	  AdcHandle.Init.NbrOfDiscConversion = 0;
	  AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
	  AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
	  AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
	  AdcHandle.Init.NbrOfConversion = 2;
	  AdcHandle.Init.DMAContinuousRequests = ENABLE;
	  AdcHandle.Init.EOCSelection = DISABLE;

	  if(HAL_ADC_Init(&AdcHandle) != HAL_OK)
	  {
	    /* Initialization Error */
	    ;
	  }

	  /*##-2- Configure ADC regular channel ######################################*/
	  /* Note: Considering IT occurring after each number of size of              */
	  /*       "uhADCxConvertedValue"  ADC conversions (IT by DMA end             */
	  /*       of transfer), select sampling time and ADC clock with sufficient   */
	  /*       duration to not create an overhead situation in IRQHandler.        */
	  sConfig.Channel = ADC_CHANNEL_9;	// Port B, Pin 1
	  sConfig.Rank = 1;
	  sConfig.SamplingTime = ADC_SAMPLETIME_84CYCLES;
	  sConfig.Offset = 0;

	  HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);


	  /*##-3- Start the conversion process and enable interrupt ##################*/
	  /* Note: Considering IT occurring after each number of ADC conversions      */
	  /*       (IT by DMA end of transfer), select sampling time and ADC clock    */
	  /*       with sufficient duration to not create an overhead situation in    */
	  /*        IRQHandler. */
	  HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)&adcBuffer[0], 1);
}

void dac1_init(void)
{
	static DAC_ChannelConfTypeDef sConfig;
	DacHandle.Instance = DAC;

    HAL_DAC_Init(&DacHandle);
    sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;

    HAL_DAC_ConfigChannel(&DacHandle, &sConfig, DAC_CHANNEL_1);

    /*##-2- Enable DAC Channel1 and associated DMA #############################*/
    HAL_DAC_Start_DMA(&DacHandle, DAC_CHANNEL_1, (uint32_t*)dacBuffer, 32, DAC_ALIGN_12B_R);

}
int
main(int argc, char* argv[])
{
	int i;
	oneOver2tt31 = 1.0f/2147483647.0f;
	timer_start();

	// synth init
	for (i = 0; i<6; i++)
	{
		o[i].phs = 0.0f;
		o[i].pi = 0.01f;
		o[i].tab = sinetable;
		o[i].ts = 8192;
		o[i].tm = 8191;
	}
	for (i = 0; i<3; i++)
	{
		amp[i].val = 0.0f;
		note[i] = (8191 * 24)/72;
	}
	for (i = 0; i<4; i++)
	{
		sampno[i].val = 0.0f;
	}
	f0.stt = 0.0f;
	echoPos = 0;
	filtgain = 0.0f;
	float oneOver2tt31 = 1.0f/(float)(0x10 << 31);
	led_init();
	switch_init();
	timer3_init();
//	timer6_init();
	adc1_init();
//	dac1_init();
	// output device, volume, sample rate
	BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_HEADPHONE, 90, 48000);

	// initialize globals
	time = 240;
	button_time[0] = button_time[1] = button_time[2] = button_time[3] = button_time[4] = 0;
	millisecs = 0.0f;
	ledselected = 0;
	bspAudioState = 0;
	acetone = (int16_t *)(0x08080000);

	// set up first buffer (in bytes?!?!)
	BSP_AUDIO_OUT_Play((uint16_t *)codecBuffer, 128);

	// Infinite loop
	while (1)
	{
		if(bspAudioState == 1)
		{
			bspAudioState = 0;
			audioBlock(inBuffer, outBuffer, 16);
			for(i = 0; i < 32; i+=2)
			{
				codecBuffer[i+0] = (int16_t)((outBuffer[i]) * 32767.0f);
				codecBuffer[i+1] = (int16_t)((outBuffer[i+1]) * 32767.0f);
			}

		}
		else if(bspAudioState == 2)
		{
			bspAudioState = 0;

			audioBlock(inBuffer, outBuffer, 16);
			for(i = 0; i < 32; i+=2)
			{
				codecBuffer[i+32] = (int16_t)((outBuffer[i]) * 32767.0f);
				codecBuffer[i+33] = (int16_t)((outBuffer[i+1]) * 32767.0f);
			}
			// set up next buffer (in samples)
		}
	}
  // Infinite loop, never return.
}
float sat3(float sample)
{
	if(sample > 1.0f)
		sample = 1.0f;
	if(sample < -1.0f)
		sample = -1.0f;

	sample = sample * 1.5f - sample * sample * sample * 0.5f;
	return(sample);
}

float sat2(float sample)
{
	if(sample > 1.0f)
		sample = 1.0f;
	if(sample < -1.0f)
		sample = -1.0f;
	if(sample > 0.0f)
		sample = sample * 2.0 - sample * sample;
	else
		sample = sample * 2.0f + sample * sample;
	return(sample);
}

float lp0(float sample, float c, float *state)
{
	*state = sample + (*state - sample)  * c;
	return(*state);
}

// lowpass based on a first order allpass
float lp1(float sample, float c, filter1 *f)
{
	float out;

    out = c * sample + f->in1 - c * f->out1; // out is allpass
    f->out1 = out;
    f->in1 = sample;
    return((sample + out) * 0.5f);

}

float hp1(float sample, float c, filter1 *f)
{
	float out;

    out = c * sample + f->in1 - c * f->out1; // out is allpass
    f->out1 = out;
    f->in1 = sample;
    return((sample - out) * 0.5f);

}

float lp2(float sample, float c, float r, filter2 *f)
{
	float out;
	if(c != f->c || r != f->r)
	{
		f->a1 = 1.0f/(1.0f + r * c + c * c);
		f->a2 = 2.0f * f->a1;
		f->a3 = f->a1;
		f->b1 = 2.0f * (1.0f - c * c) * f->a1;
		f->b2 = (1.0f - r * c + c * c) * f->a1;
		f->c = c;
		f->r = r;
	}

	out = f->a1 * sample + f->a2 * f->x1 + f->a3 * f->x2 - f->b1 * f->y1 - f->b2 * f->y2;
	f->x2 = f->x1;
	f->x1 = sample;
	f->y2 = f->y1;
	f->y1 = out;

	return(out);
}

float agc(float samp)
{
	float rectSamp;
	int32_t agcIndex;

	agcState *= 0.999f;
	rectSamp = samp;
	if(rectSamp < 0.0f)
		rectSamp *= -1.0f;
	if(rectSamp > agcState)
		agcState = rectSamp;
	if(agcState > 0.25f)
	{
		agcIndex = (agcState - 0.25f) * 14;
		if(agcIndex > 24) agcIndex = 24;
		filtgain = agcgain[agcIndex] * 0.01f + filtgain * 0.99f;
		return(samp * filtgain);
//		return(samp * agcgain[agcIndex]);
	}
	else
	{
		filtgain = 1.0f;
		return(samp);
	}
}
// allpass filter, non-interpolating
float allpass(float input, int32_t delaySamps, float gain, float *apBuffer, int32_t *writePos)
{
	float readOut, writeIn;
	int32_t i, readPos;

	readPos = *(writePos);
	/* read from delay line */
	readOut =  *(apBuffer+readPos);

	/* mix input and delayed with saturation */
	/* put data in */
	writeIn = input + (readOut * gain);
	*(apBuffer+readPos) = writeIn;

	readPos++;
	readPos = (readPos == delaySamps) ? 0 : readPos;

	*writePos = readPos;

	readOut -= (writeIn * gain);

	return(readOut);
}

float echo(float sample, float time, float feedback, int32_t *wPos, int32_t size)
{
	float rPos, rPosMix, samp0, samp1;
	int32_t rPos0, rPos1, mask;

	mask = size - 1;

	// read first, then write - so you can add feedback
	rPos = *wPos + time;
	rPos0 = (int32_t)rPos;
	rPos1 = rPos0 + 1;
	rPosMix = rPos - (float)rPos0;
	rPos0 &= mask;
	rPos1 &= mask;

    samp0 = *(echoBuf+rPos0);
    samp1 = *(echoBuf+rPos1);

    samp0 = samp0 + (rPosMix * (samp1 - samp0));

    (*wPos)--;
    *wPos &= mask;

    // take output of delay buffer > filter it, mix with input, apply AGC, saturate
    *(echoBuf+(*wPos)) = sat3(agc(lp2((samp0 * feedback), p2cf2[105], 1.1f, &f2) + sample));
    return(*(echoBuf+(*wPos)));
}

float decimate(float sample, float bits)
{
	int32_t intsamp, intbits, mask0, mask1;
	float bitmix;

	intbits = (int32_t)bits;
	bitmix = bits - (float)intbits;

	intsamp = (int32_t)(sample * 2147483647.0f);

	mask0 = 0xffffffff << (32-intbits);
	mask1 = 0xffffffff << (31-intbits);

	intsamp = (mask0 & intsamp) + (int32_t)(bitmix * ((mask1 & intsamp) - (mask0 & intsamp)));
	return(oneOver2tt31 * (float)intsamp);
}

void audioBlock(float *input, float *output, int32_t samples)
{
	int32_t iphase, i, v, val0, val1;
	uint8_t Volume;
	float phasemod, samp, valfrac;
	float knob0, c;
	float fmOut[3];

	knob0 = (float)(smoothadc[0]) * 0.00028571428571f;

	filtknob = knob0 * 0.01f + filtknob * 0.99f;
	//c = (knob0 * 2.0f) - 1.0f;
	i = knob0 * 128.0f;
	if(i > 128)
		i = 127;
	if(i < 0)
		i = 0;
	c = p2cf2[i];

	o[0].pi = sineinc[note[0]];
	o[1].pi = o[0].pi * 1.999f;

	o[2].pi = sineinc[note[1]];
	o[3].pi = o[2].pi * 2.01f;

	o[4].pi = sineinc[note[2]];
	o[5].pi = o[4].pi * 2.02f;

/*	fm note oscillator
 * for(i = 0; i < samples; i++)
	{
		output[i<<1] = output[(i<<1) + 1] = 0.0f;
		for(v = 0; v < 6; v+=2)
		{
			o[v].phs += o[v].pi;
			iphase = (int32_t)(o[v].phs * 8192.0f);	// might overflow if samples is too large.

			o[v+1].phs += o[v+1].pi;
			phasemod = (o[v+1].phs) + (o[v].tab[iphase & o[v].tm] * amp[v>>1].val * amp[v>>1].val * amp[v>>1].val * 3.0f);	// might overflow if samples is too large.
			iphase = (int32_t)(phasemod * 8192.0f);	// might overflow if samples is too large.
			fmOut[v>>1]= o[v+1].tab[iphase & o[v+1].tm] * amp[v>>1].val;

			amp[v>>1].val += amp[v>>1].inc;
			if(amp[v>>1].inc < 0.0f && amp[v>>1].val < amp[v>>1].dst)
				amp[v>>1].val = amp[v>>1].dst;
			if(amp[v>>1].inc > 0.0f && amp[v>>1].val > amp[v>>1].dst)
				amp[v>>1].val = amp[v>>1].dst;
		}
		output[i<<1] = sat3((fmOut[0] * .4f + fmOut[1] * .2f) * knob0 * 10.0f;
		if(output[i<<1] > 1.0f) output[i<<1] = 1.0f;
		if(output[i<<1] < -1.0f) output[i<<1] = -1.0f;
		output[(i<<1) + 1] = sat3((fmOut[2] * .4f + fmOut[1] * .2f) * knob0 * 10.0f);
	}

	for(v = 0; v < 6; v++)
	{
		while(o[v].phs >= 1.0f) o[v].phs -= 1.0f;
		while(o[v].phs < 0.0f) o[v].phs += 1.0f;
	}*/
/*	noise + simple one pole filter
 * for(i = 0; i < samples; i++)
	{
	    r.seed = (r.seed * 196314165) + 907633515;
	    samp = (float)r.seed * 0.000000000465661f + -1.0f;
	    output[i<<1] = output[(i<<1) + 1] = samp;
	}*/
// too simple sample playback
		for(i = 0; i < samples; i++)
		{
			samp = 0.0f;
			for(v = 0; v < 4; v++)
			{
				sampno[v].val += sampno[v].inc * 0.75f;

				if(sampno[v].inc < 0.0f && sampno[v].val < sampno[v].dst)
					sampno[v].val = sampno[v].dst;
				if(sampno[v].inc > 0.0f && sampno[v].val > sampno[v].dst)
					sampno[v].val = sampno[v].dst;
				val0 = (int32_t)sampno[v].val;
				valfrac = sampno[v].val - (float)val0;
				val1 = val0 + 1;
				samp +=  ((float)acetone[val0] - (valfrac * (float)(acetone[val1] - acetone[val0]))) * 0.00003051757813f;
			}
//			samp *= 0.25f;
//			samp = sat2(samp * 6.0f);
		    samp = decimate(samp, knob0 * 16.0f);
		    samp = echo(samp, 13000.0f, 0.8f, &echoPos, 16384);
			output[i<<1] = output[(i<<1) + 1] = samp;
		}
}

// interrupt from switch - pin A0, D0
void EXTI0_IRQHandler(void)
{
	 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
// interrupt from switch - pin D1
void EXTI1_IRQHandler(void)
{
	 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}
// interrupt from switch - pin D2
void EXTI2_IRQHandler(void)
{
	 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
}
// interrupt from switch - pin D3
void EXTI3_IRQHandler(void)
{
	 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
}

// callback from switch interrupt function HAL_GPIO_EXTI_IRQHandler()
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
// debouncing
	int32_t time_elapsed;
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0))
	{
		time_elapsed = HAL_GetTick() - button_time[0];
		if(time_elapsed > 1)
		{
			// register new button down
			button_time[0] = HAL_GetTick();
		}
	}
	if(HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_0))
	{
		time_elapsed = HAL_GetTick() - button_time[1];
		if(time_elapsed > 100)
		{
			// register new button down
			button_time[1] = HAL_GetTick();
			sampno[0].val = 53827.0f;
			sampno[0].dst = 66557.0f;
			sampno[0].inc = 1.0f;
		}
/*		amp[0].val = amp[1].val = amp[2].val = 1.0f;
		amp[0].inc = amp[1].inc = amp[2].inc = (amp[1].dst - amp[1].val)/(5.0f * 48000.0f);*/
	}
	if(HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_1))
	{
		time_elapsed = HAL_GetTick() - button_time[2];
		if(time_elapsed > 100)
		{
			// register new button down
			button_time[2] = HAL_GetTick();
/*		    r.seed = (r.seed * 196314165) + 907633515;
			note[1] = (((r.seed >> 28) + 12) * 8191)/72;
			amp[1].val = 1.0f;
			amp[1].dst = 0.0f;
			amp[1].inc = (amp[1].dst - amp[1].val)/(2.0f * 48000.0f);*/
			sampno[1].val = 82912.0f;
			sampno[1].dst = 88550.0f;
			sampno[1].inc = 1.0f;
		}
	}
	if(HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_2))
	{
		time_elapsed = HAL_GetTick() - button_time[3];
		if(time_elapsed > 100)
		{
			// register new button down
			button_time[3] = HAL_GetTick();
/*		    r.seed = (r.seed * 196314165) + 907633515;
			note[2] = (((r.seed >> 28) + 12) * 8191)/72;
			amp[2].val = 1.0f;
			amp[2].dst = 0.0f;
			amp[2].inc = (amp[2].dst - amp[2].val)/(2.0f * 48000.0f);*/
			sampno[2].val = 5568.0f;
			sampno[2].dst = 8125.0f;
			sampno[2].inc = 1.0f;
		}
	}
	if(HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_3))
	{
		time_elapsed = HAL_GetTick() - button_time[4];
		if(time_elapsed > 100)
		{
			// register new button down
			button_time[4] = HAL_GetTick();
/*		    r.seed = (r.seed * 196314165) + 907633515;
			note[0] = (((r.seed >> 27) + 12) * 8191)/72;
			amp[0].val = 1.0f;
			amp[0].dst = 0.0f;
			amp[0].inc = (amp[0].dst - amp[0].val)/(2.0f * 48000.0f);*/
			sampno[3].val = 8125.0f;
			sampno[3].dst = 19895.0f;
			sampno[3].inc = 1.0f;
		}
	}
}

// interrupt from timer 3
void TIM3_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&TimHandle);
}

// callback from timer interrupt function HAL_TIM_IRQHandler()
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//	HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14);
	millisecs += 0.1f;
	if(millisecs > 218)
	{
		millisecs = 0.0f;
		switch(ledselected)
		{
		case 0:
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
			break;
		case 1:
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
			break;
		case 2:
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_SET);
			break;
		case 3:
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
			break;
		}
		ledselected += 1;
		ledselected &= 3;
	}
}

// interrupt from DMA 2 - Stream 0 (ADC1)
void DMA2_Stream0_IRQHandler(void)
{
  HAL_DMA_IRQHandler(AdcHandle.DMA_Handle);
}

// callback from DMA interrupt function HAL_DMA_IRQHandler()
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
  time = (adcBuffer[0] * adcBuffer[0]) >> 12;

  avgtotal[0] -= adc0average[avgindex];
  adc0average[avgindex] = adcBuffer[0];
  avgtotal[0] += adc0average[avgindex];
  smoothadc[0] = avgtotal[0] >> 6;

  avgtotal[1] -= adc1average[avgindex];
  adc1average[avgindex] = adcBuffer[1];
  avgtotal[1] += adc1average[avgindex];
  smoothadc[1] = avgtotal[1] >> 6;
  avgindex++;
  if(avgindex > 63) avgindex = 0;
}

// interrupt from DMA 1 - Stream 5 (DAC Ch 1)
void DMA1_Stream5_IRQHandler(void)
{
  HAL_DMA_IRQHandler(DacHandle.DMA_Handle1);
}

// callback from DMA interrupt function HAL_DMA_IRQHandler()

void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef* DacHandle)
{
	int i,j;


	audioBlock(inBuffer, outBuffer, 16);
	for(i = 0, j = 0; i < 16; i++, j+=2)
	{
		dacBuffer[i] = (int16_t)((outBuffer[j] + outBuffer[j+1] + 2.0f) * 1023.0f);
	}
}

void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef* DacHandle)
{
	int i;

	audioBlock(inBuffer, outBuffer, 16);
	for(i = 0; i < 16; i++)
	{
		dacBuffer[i+16] = (int16_t)((outBuffer[(i * 2)] + outBuffer[(i * 2)+1] + 2.0f) * 1023.0f);
	}
}
/**
  * @brief  This function handles main I2S interrupt.
  * @param  None
  * @retval 0 if correct communication, else wrong communication
  */
void I2S3_IRQHandler(void)
{
  HAL_DMA_IRQHandler(hAudioOutI2s.hdmatx);
}

/**
  * @brief  This function handles DMA Stream interrupt request.
  * @param  None
  * @retval None
  */
void I2S2_IRQHandler(void)
{
  HAL_DMA_IRQHandler(hAudioInI2s.hdmarx);
}

void BSP_AUDIO_OUT_HalfTransfer_CallBack(void)
{
	bspAudioState = 1;

}

/**
* @brief  Calculates the remaining file size and new position of the pointer.
* @param  None
* @retval None
*/
void BSP_AUDIO_OUT_TransferComplete_CallBack(void)
{

	int i;
	bspAudioState = 2;
	BSP_AUDIO_OUT_ChangeBuffer((uint16_t *)codecBuffer, 64);
}

void BSP_AUDIO_OUT_Error_CallBack(void)
{
  /* Stop the program with an infinite loop */
  while (1)
  {}

  /* Could also generate a system reset to recover from the error */
  /* .... */
}

/**
* @brief  This function handles DMA interrupt request.
* @param  None
* @retval None
*/
void DACx_DMA_IRQHandler2(void)
{
  HAL_DMA_IRQHandler(DacHandle.DMA_Handle2);
}

#pragma GCC diagnostic pop

// ----------------------------------------------------------------------------
