//--------------------------------------------------------------
// File     : stm32f7_audio.c
// Datum    : 22.11.2015
// Version  : 1.1
// Autor    : UB
// EMail    : mc-4u(@)t-online.de
// Web      : www.mikrocontroller-4u.de
// CPU      : STM32F746
// IDE      : OpenSTM32
// GCC      : 4.9 2015q2
// Module   : CubeHAL, WM8994
// Funktion : Audio Funktionen (Out/In)
//--------------------------------------------------------------

//--------------------------------------------------------------
// Includes
//--------------------------------------------------------------
#include "stm32f7_audio.h"


//--------------------------------------------------------------
// interne Funktionen
//--------------------------------------------------------------
void  BSP_AUDIO_OUT_ClockConfig(SAI_HandleTypeDef *hsai, uint32_t AudioFreq, void *Params);
void BSP_AUDIO_OUT_MspInit(SAI_HandleTypeDef *hsai, void *Params);
void BSP_AUDIO_IN_MspInit(SAI_HandleTypeDef *hsai, void *Params);
void BSP_AUDIO_OUT_SetAudioFrameSlot(uint32_t AudioFrameSlot);
static void SAIx_Out_DeInit(void);
static void SAIx_Out_Init(uint32_t AudioFreq);
static void SAIx_In_DeInit(void);
static void SAIx_In_Init(uint32_t SaiOutMode, uint32_t SlotActive, uint32_t AudioFreq);
//--------------------------------------------------------------
// WAV-Functions
//--------------------------------------------------------------
void P_initWAV(void);
uint8_t P_CheckWAV(UB_WAV *wav);
uint32_t P_4Bytes(UB_WAV *wav, uint32_t start);
uint16_t P_2Bytes(UB_WAV *wav, uint32_t start);
uint8_t P_FillBufferCompleteWav(void);
uint8_t P_FillBufferFirstWav(void);
uint8_t P_FillBufferSecondWav(void);
//--------------------------------------------------------------
void P_FillBufferFirstSample(void);
void P_FillBufferSecondSample(void);

//--------------------------------------------------------------
// Globale Variabeln
//--------------------------------------------------------------
AUDIO_DrvTypeDef          *audio_drv;
SAI_HandleTypeDef         haudio_out_sai={0};
SAI_HandleTypeDef         haudio_in_sai={0};
WAV_t my_wav;
WAV_Status_t WavAudio;
#define AUDIO_BUFFER_SIZE  1024*4 // 4k
uint16_t audio_buffer[AUDIO_BUFFER_SIZE];
//--------------------------------------------------------------
uint8_t aktive_sample_1=0; // 0=no sample
uint8_t aktive_sample_2=0; // 0=no sample

uint32_t sample_start[5];
uint32_t sample_len[5];
uint32_t sample_aktpos[5];
uint8_t sample_flag[5];
UB_WAV sample_wav[5];


//--------------------------------------------------------------
// return : 0=ok
// device : [OUTPUT_DEVICE_HEADPHONE, OUTPUT_DEVICE_SPEAKER
//           OUTPUT_DEVICE_BOTH, OUTPUT_DEVICE_AUTO]
//--------------------------------------------------------------
uint8_t UB_AUDIO_OUT_Init(uint16_t device, uint8_t volume, uint32_t frq)
{
  uint8_t ret = 1;
  uint32_t deviceid = 0x00;


  /* Disable SAI */
  SAIx_Out_DeInit();

  /* PLL clock is set depending on the AudioFreq (44.1khz vs 48khz groups) */
  BSP_AUDIO_OUT_ClockConfig(&haudio_out_sai, frq, NULL);

  /* SAI data transfer preparation:
  Prepare the Media to be used for the audio transfer from memory to SAI peripheral */
  haudio_out_sai.Instance = AUDIO_OUT_SAIx;
  if(HAL_SAI_GetState(&haudio_out_sai) == HAL_SAI_STATE_RESET)
  {
    /* Init the SAI MSP: this __weak function can be redefined by the application*/
    BSP_AUDIO_OUT_MspInit(&haudio_out_sai, NULL);
  }
  SAIx_Out_Init(frq);


  /* wm8994 codec initialization */
  deviceid = wm8994_drv.ReadID(WM8994_I2C_ADDRESS);

  if((deviceid) == WM8994_ID)
  {
    /* Reset the Codec Registers */
    wm8994_drv.Reset(WM8994_I2C_ADDRESS);
    /* Initialize the audio driver structure */
    audio_drv = &wm8994_drv;
    ret = 0;
  }
  else
  {
    ret = 1;
  }

  if(ret == 0)
  {
    /* Initialize the codec internal registers */
    audio_drv->Init(WM8994_I2C_ADDRESS, device, volume, frq);


    BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02);
    WavAudio.status=1; // init
  }

  return ret;
}


//--------------------------------------------------------------
// return : 0=ok
// size : in Bytes [8bit]
// buffer : word0 = left, word1 = right, word2 = left...
//--------------------------------------------------------------
uint8_t UB_AUDIO_OUT_Play_Array(uint16_t* pBuffer, uint32_t size)
{

  /* Call the audio Codec Play function */
  if(audio_drv->Play(WM8994_I2C_ADDRESS, pBuffer, size) != 0)
  {
    return 1;
  }
  else
  {
    /* Update the Media layer and enable it for play */
    HAL_SAI_Transmit_DMA(&haudio_out_sai, (uint8_t*) pBuffer, DMA_MAX(size / 2));

    return 0;
  }
}

//--------------------------------------------------------------
// return : 0=ok
// device : [OUTPUT_DEVICE_HEADPHONE, OUTPUT_DEVICE_SPEAKER
//           OUTPUT_DEVICE_BOTH, OUTPUT_DEVICE_AUTO]
//--------------------------------------------------------------
uint8_t UB_AUDIO_OUT_Play_Wav(UB_WAV *wav, uint16_t device, uint8_t volume)
{
  uint8_t ret_wert;
  uint32_t frq;

  if(WavAudio.status==2) return 99; // blocked during play

  WavAudio.status=0;

  P_initWAV();

  ret_wert=P_CheckWAV(wav);
  if(ret_wert==0) {
    if(my_wav.channels==1) {
      frq=my_wav.samplerate/2; // mono
    }
    else {
      frq=my_wav.samplerate; // stereo
    }
    ret_wert=UB_AUDIO_OUT_Init(device, volume,frq);
    if(ret_wert!=0) return ret_wert;

    WavAudio.status=2; // play
    WavAudio.akt_adr=my_wav.data_start;
    WavAudio.akt_pos=0;
    my_wav.wav_ptr=wav;
    // fill buffer complete
    P_FillBufferCompleteWav();
    // play audio buffer
    ret_wert=UB_AUDIO_OUT_Play_Array(audio_buffer, AUDIO_BUFFER_SIZE*2);
  }

  return ret_wert;
}

//--------------------------------------------------------------
// play audio buffer forever
//--------------------------------------------------------------
uint8_t AUDIO_Play_Buffer(void)
{
  uint8_t ret_wert,n;

  WavAudio.status=3; // all samples (init)

  for(n=0;n<5;n++) {
	sample_start[n]=0;
	sample_len[n]=0;
	sample_aktpos[n]=0;
	sample_flag[n]=0;
  }

  // play audio buffer
  ret_wert=UB_AUDIO_OUT_Play_Array(audio_buffer, AUDIO_BUFFER_SIZE*2);

  return ret_wert;
}

//--------------------------------------------------------------
uint8_t AUDIO_Init_Samples(uint8_t n, WAV_t *wav_data)
{
	uint8_t ret_wert=0;

	if(n>=5) return 0;

	sample_start[n]=wav_data->data_start;
	sample_len[n]=wav_data->file_len;
	sample_aktpos[n]=sample_start[n];
	sample_wav[n]=*wav_data->wav_ptr;
	sample_flag[n]=0;

	return ret_wert;
}

//--------------------------------------------------------------
uint8_t AUDIO_Set_ActiveSample(uint32_t sample_flags)
{
	uint8_t ret_wert=0,n,cnt=0;

	if(sample_flags==0) {
		aktive_sample_1=0;
		aktive_sample_2=0;
		cnt=0;
	}
	else {
		// sample 1
		aktive_sample_1=0;
		if(((sample_flags&0x01)!=0) && (aktive_sample_1==0)) aktive_sample_1=1;
		if(((sample_flags&0x02)!=0) && (aktive_sample_1==0)) aktive_sample_1=2;
		if(((sample_flags&0x04)!=0) && (aktive_sample_1==0)) aktive_sample_1=3;
		if(((sample_flags&0x08)!=0) && (aktive_sample_1==0)) aktive_sample_1=4;
		if(((sample_flags&0x10)!=0) && (aktive_sample_1==0)) aktive_sample_1=5;
		// sample 2
		aktive_sample_2=0;
		if(((sample_flags&0x10)!=0) && (aktive_sample_2==0)) aktive_sample_2=5;
		if(((sample_flags&0x08)!=0) && (aktive_sample_2==0)) aktive_sample_2=4;
		if(((sample_flags&0x04)!=0) && (aktive_sample_2==0)) aktive_sample_2=3;
		if(((sample_flags&0x02)!=0) && (aktive_sample_2==0)) aktive_sample_2=2;
		if(((sample_flags&0x01)!=0) && (aktive_sample_2==0)) aktive_sample_2=1;

		if(aktive_sample_2==aktive_sample_1) aktive_sample_2=0;

		if(aktive_sample_1>0) cnt=1;
		if(aktive_sample_2>0) cnt=2;
	}

	if(aktive_sample_1>0) sample_flag[aktive_sample_1-1]=1;
	if(aktive_sample_2>0) sample_flag[aktive_sample_2-1]=1;

	if(cnt==0) {
		for(n=0;n<5;n++) {
			if(sample_flag[n]!=0) {
				sample_aktpos[n]=sample_start[n];
				sample_flag[n]=0;
			}
		}
	}
	else if(cnt==1) {
		for(n=0;n<5;n++) {
			if((sample_flag[n]!=0) && (n!=(aktive_sample_1-1))) {
				sample_aktpos[n]=sample_start[n];
				sample_flag[n]=0;
			}
		}
	}
	else {
		for(n=0;n<5;n++) {
			if((sample_flag[n]!=0) && (n!=(aktive_sample_1-1)) && (n!=(aktive_sample_2-1))) {
				sample_aktpos[n]=sample_start[n];
				sample_flag[n]=0;
			}
		}
	}


	WavAudio.status=4; // all samples (play)

	return ret_wert;
}

//--------------------------------------------------------------
// return : 0=ok
//--------------------------------------------------------------
uint8_t UB_AUDIO_OUT_Stop(void)
{
  /* Call the Media layer stop function */
  HAL_SAI_DMAStop(&haudio_out_sai);
  WavAudio.status=0;

  /* Call Audio Codec Stop function */
  if(audio_drv->Stop(WM8994_I2C_ADDRESS, CODEC_PDWN_SW) != 0)
  {
    return 1;
  }
  else
  {
    /* Return AUDIO_OK when all operations are correctly done */
    return 0;
  }
}

//--------------------------------------------------------------
// return : 0=ok
//--------------------------------------------------------------
uint8_t UB_AUDIO_OUT_SetVolume(uint8_t volume)
{

  /* Call the codec volume control function with converted volume value */
  if(audio_drv->SetVolume(WM8994_I2C_ADDRESS, volume) != 0)
  {
    return 1;
  }
  else
  {
    /* Return AUDIO_OK when all operations are correctly done */
    return 0;
  }
}


//--------------------------------------------------------------
// return : 0=ok
// device : [INPUT_DEVICE_INPUT_LINE_1, INPUT_DEVICE_DIGITAL_MICROPHONE_2
//--------------------------------------------------------------
uint8_t UB_AUDIO_IN_Init(uint16_t device, uint8_t volume, uint32_t frq)
{
  uint8_t ret = 1;
  uint32_t deviceid = 0x00;
  uint32_t slot_active;

  if ((device != INPUT_DEVICE_INPUT_LINE_1) &&       /* Only INPUT_LINE_1 and MICROPHONE_2 inputs supported */
      (device != INPUT_DEVICE_DIGITAL_MICROPHONE_2))
  {
    ret = 1;
  }
  else
  {
    /* Disable SAI */
    SAIx_In_DeInit();

    /* PLL clock is set depending on the AudioFreq (44.1khz vs 48khz groups) */
    BSP_AUDIO_OUT_ClockConfig(&haudio_in_sai, frq, NULL); /* Clock config is shared between AUDIO IN and OUT */

    /* SAI data transfer preparation:
    Prepare the Media to be used for the audio transfer from SAI peripheral to memory */
    haudio_in_sai.Instance = AUDIO_IN_SAIx;
    if(HAL_SAI_GetState(&haudio_in_sai) == HAL_SAI_STATE_RESET)
    {
      /* Init the SAI MSP: this __weak function can be redefined by the application*/
      BSP_AUDIO_OUT_MspInit(&haudio_in_sai, NULL);  /* Initialize GPIOs for SAI2 block A Master signals */
      BSP_AUDIO_IN_MspInit(&haudio_in_sai, NULL);
    }

    /* Configure SAI in master RX mode :
     *   - SAI2_block_A in master RX mode
     *   - SAI2_block_B in slave RX mode synchronous from SAI2_block_A
     */
    if (device == INPUT_DEVICE_DIGITAL_MICROPHONE_2)
    {
      slot_active = CODEC_AUDIOFRAME_SLOT_13;
    }
    else
    {
      slot_active = CODEC_AUDIOFRAME_SLOT_02;
    }
    SAIx_In_Init(SAI_MODEMASTER_RX, slot_active, frq);


    /* wm8994 codec initialization */
    deviceid = wm8994_drv.ReadID(WM8994_I2C_ADDRESS);

    if((deviceid) == WM8994_ID)
    {
      /* Reset the Codec Registers */
      wm8994_drv.Reset(WM8994_I2C_ADDRESS);
      /* Initialize the audio driver structure */
      audio_drv = &wm8994_drv;
      ret = 0;
    }
    else
    {
      ret = 1;
    }

    if(ret == 0)
    {
      /* Initialize the codec internal registers */
      audio_drv->Init(WM8994_I2C_ADDRESS, device, volume, frq);
    }
  }
  return ret;
}

//--------------------------------------------------------------
// return : 0=ok
// size : in words [16bit]
//--------------------------------------------------------------
uint8_t UB_AUDIO_IN_Record_Array(uint16_t* pbuf, uint32_t size)
{
  /* Start the process receive DMA */
  HAL_SAI_Receive_DMA(&haudio_in_sai, (uint8_t*)pbuf, size);

  /* Return AUDIO_OK when all operations are correctly done */
  return 0;
}

//--------------------------------------------------------------
// return : 0=ok
//--------------------------------------------------------------
uint8_t UB_AUDIO_IN_Stop(void)
{
  /* Call the Media layer stop function */
  HAL_SAI_DMAStop(&haudio_in_sai);


  /* Call Audio Codec Stop function */
  if(audio_drv->Stop(WM8994_I2C_ADDRESS, CODEC_PDWN_SW) != 0)
  {
    return 1;
  }
  else
  {
    /* Return AUDIO_OK when all operations are correctly done */
    return 0;
  }
}

//--------------------------------------------------------------
// return : 0=ok
//--------------------------------------------------------------
uint8_t BSP_AUDIO_IN_SetVolume(uint8_t volume)
{

  /* Call the codec volume control function with converted volume value */
  if(audio_drv->SetVolume(WM8994_I2C_ADDRESS, volume) != 0)
  {
    return 1;
  }
  else
  {
    /* Return AUDIO_OK when all operations are correctly done */
    return 0;
  }
}


//--------------------------------------------------------------
// route input to output
// return : 0=ok
// device_in : [INPUT_DEVICE_INPUT_LINE_1, INPUT_DEVICE_DIGITAL_MICROPHONE_2
// device_out : [OUTPUT_DEVICE_HEADPHONE, OUTPUT_DEVICE_SPEAKER
//           OUTPUT_DEVICE_BOTH, OUTPUT_DEVICE_AUTO]
//--------------------------------------------------------------
uint8_t UB_AUDIO_IN_OUT_Init(uint16_t device_in, uint16_t device_out, uint8_t volume, uint32_t frq)
{
  uint8_t ret = 1;
  uint32_t deviceid = 0x00;
  uint32_t slot_active;

  if (device_in != INPUT_DEVICE_DIGITAL_MICROPHONE_2)  /* Only MICROPHONE_2 input supported */
  {
    ret = 2;
  }
  else
  {
    /* Disable SAI */
    SAIx_In_DeInit();
    SAIx_Out_DeInit();

    /* PLL clock is set depending on the AudioFreq (44.1khz vs 48khz groups) */
    BSP_AUDIO_OUT_ClockConfig(&haudio_in_sai, frq, NULL); /* Clock config is shared between AUDIO IN and OUT */

    /* SAI data transfer preparation:
    Prepare the Media to be used for the audio transfer from SAI peripheral to memory */
    haudio_in_sai.Instance = AUDIO_IN_SAIx;
    if(HAL_SAI_GetState(&haudio_in_sai) == HAL_SAI_STATE_RESET)
    {
      /* Init the SAI MSP: this __weak function can be redefined by the application*/
      BSP_AUDIO_IN_MspInit(&haudio_in_sai, NULL);
    }

    /* SAI data transfer preparation:
    Prepare the Media to be used for the audio transfer from memory to SAI peripheral */
    haudio_out_sai.Instance = AUDIO_OUT_SAIx;
    if(HAL_SAI_GetState(&haudio_out_sai) == HAL_SAI_STATE_RESET)
    {
      /* Init the SAI MSP: this __weak function can be redefined by the application*/
      BSP_AUDIO_OUT_MspInit(&haudio_out_sai, NULL);
    }

    /* Configure SAI in master mode :
     *   - SAI2_block_A in master TX mode
     *   - SAI2_block_B in slave RX mode synchronous from SAI2_block_A
     */
    if (device_in == INPUT_DEVICE_DIGITAL_MICROPHONE_2)
    {
      slot_active = CODEC_AUDIOFRAME_SLOT_13;
    }
    else
    {
      slot_active = CODEC_AUDIOFRAME_SLOT_02;
    }
    SAIx_In_Init(SAI_MODEMASTER_TX, slot_active, frq);


    /* wm8994 codec initialization */
    deviceid = wm8994_drv.ReadID(WM8994_I2C_ADDRESS);

    if((deviceid) == WM8994_ID)
    {
      /* Reset the Codec Registers */
      wm8994_drv.Reset(WM8994_I2C_ADDRESS);
      /* Initialize the audio driver structure */
      audio_drv = &wm8994_drv;
      ret = 0;
    }
    else
    {
      ret = 3;
    }

    if(ret == 0)
    {
      /* Initialize the codec internal registers */
      audio_drv->Init(WM8994_I2C_ADDRESS, device_in | device_out, volume, frq);

      BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02);
      WavAudio.status=1; // init
    }
  }
  return ret;
}



//--------------------------------------------------------------
__weak void BSP_AUDIO_OUT_ClockConfig(SAI_HandleTypeDef *hsai, uint32_t AudioFreq, void *Params)
{
  RCC_PeriphCLKInitTypeDef RCC_ExCLKInitStruct;

  HAL_RCCEx_GetPeriphCLKConfig(&RCC_ExCLKInitStruct);

  /* Set the PLL configuration according to the audio frequency */
  if((AudioFreq == AUDIO_FREQUENCY_11K) || (AudioFreq == AUDIO_FREQUENCY_22K) || (AudioFreq == AUDIO_FREQUENCY_44K))
  {
    /* Configure PLLSAI prescalers */
    /* PLLI2S_VCO: VCO_429M
    SAI_CLK(first level) = PLLI2S_VCO/PLLSAIQ = 429/2 = 214.5 Mhz
    SAI_CLK_x = SAI_CLK(first level)/PLLI2SDivQ = 214.5/19 = 11.289 Mhz */
    RCC_ExCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI2;
    RCC_ExCLKInitStruct.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLLI2S;
    RCC_ExCLKInitStruct.PLLI2S.PLLI2SP = 8;
    RCC_ExCLKInitStruct.PLLI2S.PLLI2SN = 429;
    RCC_ExCLKInitStruct.PLLI2S.PLLI2SQ = 2;
    RCC_ExCLKInitStruct.PLLI2SDivQ = 19;
    HAL_RCCEx_PeriphCLKConfig(&RCC_ExCLKInitStruct);
  }
  else /* AUDIO_FREQUENCY_8K, AUDIO_FREQUENCY_16K, AUDIO_FREQUENCY_48K), AUDIO_FREQUENCY_96K */
  {
    /* SAI clock config
    PLLI2S_VCO: VCO_344M
    SAI_CLK(first level) = PLLI2S_VCO/PLLSAIQ = 344/7 = 49.142 Mhz
    SAI_CLK_x = SAI_CLK(first level)/PLLI2SDivQ = 49.142/1 = 49.142 Mhz */
    RCC_ExCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI2;
    RCC_ExCLKInitStruct.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLLI2S;
    RCC_ExCLKInitStruct.PLLI2S.PLLI2SP = 8;
    RCC_ExCLKInitStruct.PLLI2S.PLLI2SN = 344;
    RCC_ExCLKInitStruct.PLLI2S.PLLI2SQ = 7;
    RCC_ExCLKInitStruct.PLLI2SDivQ = 1;
    HAL_RCCEx_PeriphCLKConfig(&RCC_ExCLKInitStruct);
  }
}

//--------------------------------------------------------------
__weak void BSP_AUDIO_OUT_MspInit(SAI_HandleTypeDef *hsai, void *Params)
{
  static DMA_HandleTypeDef hdma_sai_tx;
  GPIO_InitTypeDef  gpio_init_structure;

  /* Enable SAI clock */
  AUDIO_OUT_SAIx_CLK_ENABLE();

  /* Enable GPIO clock */
  AUDIO_OUT_SAIx_MCLK_ENABLE();
  AUDIO_OUT_SAIx_SCK_SD_ENABLE();
  AUDIO_OUT_SAIx_FS_ENABLE();


  //I2S out on QSPI chip
  AUDIO_OUT_SAIx_SCK_SD_2_ENABLE();
  AUDIO_OUT_SAIx_FS_2_ENABLE();


  /* CODEC_SAI pins configuration: FS, SCK, MCK and SD pins ------------------*/
  gpio_init_structure.Pin = AUDIO_OUT_SAIx_FS_PIN;
  gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull = GPIO_NOPULL;
  gpio_init_structure.Speed = GPIO_SPEED_HIGH;
  gpio_init_structure.Alternate = AUDIO_OUT_SAIx_FS_SD_MCLK_AF;
  HAL_GPIO_Init(AUDIO_OUT_SAIx_FS_GPIO_PORT, &gpio_init_structure);


  //I2S out on QSPI chip
  gpio_init_structure.Pin = AUDIO_OUT_SAIx_FS_2_PIN;
  gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull = GPIO_NOPULL;
  gpio_init_structure.Speed = GPIO_SPEED_HIGH;
  gpio_init_structure.Alternate = AUDIO_OUT_SAIx_FS_SD_MCLK_AF;
  HAL_GPIO_Init(AUDIO_OUT_SAIx_FS_2_GPIO_PORT, &gpio_init_structure);


  gpio_init_structure.Pin = AUDIO_OUT_SAIx_SCK_PIN;
  gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull = GPIO_NOPULL;
  gpio_init_structure.Speed = GPIO_SPEED_HIGH;
  gpio_init_structure.Alternate = AUDIO_OUT_SAIx_SCK_AF;

  HAL_GPIO_Init(AUDIO_OUT_SAIx_SCK_SD_GPIO_PORT, &gpio_init_structure);



  //I2S out on QSPI chip
  gpio_init_structure.Pin = AUDIO_OUT_SAIx_SCK_2_PIN;
  gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull = GPIO_NOPULL;
  gpio_init_structure.Speed = GPIO_SPEED_HIGH;
  gpio_init_structure.Alternate = AUDIO_OUT_SAIx_SCK_AF;
  HAL_GPIO_Init(AUDIO_OUT_SAIx_SCK_SD_2_GPIO_PORT, &gpio_init_structure);


  gpio_init_structure.Pin =  AUDIO_OUT_SAIx_SD_PIN;
  gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull = GPIO_NOPULL;
  gpio_init_structure.Speed = GPIO_SPEED_HIGH;
  gpio_init_structure.Alternate = AUDIO_OUT_SAIx_FS_SD_MCLK_AF;

  HAL_GPIO_Init(AUDIO_OUT_SAIx_SCK_SD_GPIO_PORT, &gpio_init_structure);



  //I2S out on QSPI chip
  gpio_init_structure.Pin =  AUDIO_OUT_SAIx_SD_2_PIN;
  gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull = GPIO_NOPULL;
  gpio_init_structure.Speed = GPIO_SPEED_HIGH;
  gpio_init_structure.Alternate = AUDIO_OUT_SAIx_FS_SD_MCLK_AF;
  HAL_GPIO_Init(AUDIO_OUT_SAIx_SCK_SD_2_GPIO_PORT, &gpio_init_structure);


  gpio_init_structure.Pin = AUDIO_OUT_SAIx_MCLK_PIN;
  gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull = GPIO_NOPULL;
  gpio_init_structure.Speed = GPIO_SPEED_HIGH;
  gpio_init_structure.Alternate = AUDIO_OUT_SAIx_FS_SD_MCLK_AF;
  HAL_GPIO_Init(AUDIO_OUT_SAIx_MCLK_GPIO_PORT, &gpio_init_structure);

  /* Enable the DMA clock */
  AUDIO_OUT_SAIx_DMAx_CLK_ENABLE();

  if(hsai->Instance == AUDIO_OUT_SAIx)
  {
    /* Configure the hdma_saiTx handle parameters */
    hdma_sai_tx.Init.Channel             = AUDIO_OUT_SAIx_DMAx_CHANNEL;
    hdma_sai_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
    hdma_sai_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
    hdma_sai_tx.Init.MemInc              = DMA_MINC_ENABLE;
    hdma_sai_tx.Init.PeriphDataAlignment = AUDIO_OUT_SAIx_DMAx_PERIPH_DATA_SIZE;
    hdma_sai_tx.Init.MemDataAlignment    = AUDIO_OUT_SAIx_DMAx_MEM_DATA_SIZE;
    hdma_sai_tx.Init.Mode                = DMA_CIRCULAR;
    hdma_sai_tx.Init.Priority            = DMA_PRIORITY_HIGH;
    hdma_sai_tx.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
    hdma_sai_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
    hdma_sai_tx.Init.MemBurst            = DMA_MBURST_SINGLE;
    hdma_sai_tx.Init.PeriphBurst         = DMA_PBURST_SINGLE;

    hdma_sai_tx.Instance = AUDIO_OUT_SAIx_DMAx_STREAM;

    /* Associate the DMA handle */
    __HAL_LINKDMA(hsai, hdmatx, hdma_sai_tx);

    /* Deinitialize the Stream for new transfer */
    HAL_DMA_DeInit(&hdma_sai_tx);

    /* Configure the DMA Stream */
    HAL_DMA_Init(&hdma_sai_tx);
  }

  /* SAI DMA IRQ Channel configuration */
  HAL_NVIC_SetPriority(AUDIO_OUT_SAIx_DMAx_IRQ, AUDIO_OUT_IRQ_PREPRIO, 0);
  HAL_NVIC_EnableIRQ(AUDIO_OUT_SAIx_DMAx_IRQ);
}

//--------------------------------------------------------------
__weak void BSP_AUDIO_IN_MspInit(SAI_HandleTypeDef *hsai, void *Params)
{
  static DMA_HandleTypeDef hdma_sai_rx;
  GPIO_InitTypeDef  gpio_init_structure;

  /* Enable SAI clock */
  AUDIO_IN_SAIx_CLK_ENABLE();

  /* Enable SD GPIO clock */
  AUDIO_IN_SAIx_SD_ENABLE();
  /* CODEC_SAI pin configuration: SD pin */
  gpio_init_structure.Pin = AUDIO_IN_SAIx_SD_PIN;
  gpio_init_structure.Mode = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull = GPIO_NOPULL;
  gpio_init_structure.Speed = GPIO_SPEED_FAST;
  gpio_init_structure.Alternate = AUDIO_IN_SAIx_SD_AF;
  HAL_GPIO_Init(AUDIO_IN_SAIx_SD_GPIO_PORT, &gpio_init_structure);

  /* Enable Audio INT GPIO clock */
  AUDIO_IN_INT_GPIO_ENABLE();
  /* Audio INT pin configuration: input */
  gpio_init_structure.Pin = AUDIO_IN_INT_GPIO_PIN;
  gpio_init_structure.Mode = GPIO_MODE_INPUT;
  gpio_init_structure.Pull = GPIO_NOPULL;
  gpio_init_structure.Speed = GPIO_SPEED_FAST;
  HAL_GPIO_Init(AUDIO_IN_INT_GPIO_PORT, &gpio_init_structure);

  /* Enable the DMA clock */
  AUDIO_IN_SAIx_DMAx_CLK_ENABLE();

  if(hsai->Instance == AUDIO_IN_SAIx)
  {
    /* Configure the hdma_sai_rx handle parameters */
    hdma_sai_rx.Init.Channel             = AUDIO_IN_SAIx_DMAx_CHANNEL;
    hdma_sai_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;
    hdma_sai_rx.Init.PeriphInc           = DMA_PINC_DISABLE;
    hdma_sai_rx.Init.MemInc              = DMA_MINC_ENABLE;
    hdma_sai_rx.Init.PeriphDataAlignment = AUDIO_IN_SAIx_DMAx_PERIPH_DATA_SIZE;
    hdma_sai_rx.Init.MemDataAlignment    = AUDIO_IN_SAIx_DMAx_MEM_DATA_SIZE;
    hdma_sai_rx.Init.Mode                = DMA_CIRCULAR;
    hdma_sai_rx.Init.Priority            = DMA_PRIORITY_HIGH;
    hdma_sai_rx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
    hdma_sai_rx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
    hdma_sai_rx.Init.MemBurst            = DMA_MBURST_SINGLE;
    hdma_sai_rx.Init.PeriphBurst         = DMA_MBURST_SINGLE;

    hdma_sai_rx.Instance = AUDIO_IN_SAIx_DMAx_STREAM;

    /* Associate the DMA handle */
    __HAL_LINKDMA(hsai, hdmarx, hdma_sai_rx);

    /* Deinitialize the Stream for new transfer */
    HAL_DMA_DeInit(&hdma_sai_rx);

    /* Configure the DMA Stream */
    HAL_DMA_Init(&hdma_sai_rx);
  }

  /* SAI DMA IRQ Channel configuration */
  HAL_NVIC_SetPriority(AUDIO_IN_SAIx_DMAx_IRQ, AUDIO_IN_IRQ_PREPRIO, 0);
  HAL_NVIC_EnableIRQ(AUDIO_IN_SAIx_DMAx_IRQ);

  /* Audio INT IRQ Channel configuration */
  HAL_NVIC_SetPriority(AUDIO_IN_INT_IRQ, AUDIO_IN_IRQ_PREPRIO, 0);
  HAL_NVIC_EnableIRQ(AUDIO_IN_INT_IRQ);
}


//--------------------------------------------------------------
void BSP_AUDIO_OUT_SetAudioFrameSlot(uint32_t AudioFrameSlot)
{
  /* Disable SAI peripheral to allow access to SAI internal registers */
  __HAL_SAI_DISABLE(&haudio_out_sai);

  /* Update the SAI audio frame slot configuration */
  haudio_out_sai.SlotInit.SlotActive = AudioFrameSlot;
  HAL_SAI_Init(&haudio_out_sai);

  /* Enable SAI peripheral to generate MCLK */
  __HAL_SAI_ENABLE(&haudio_out_sai);
}


//--------------------------------------------------------------
static void SAIx_Out_DeInit(void)
{
  /* Initialize the haudio_out_sai Instance parameter */
  haudio_out_sai.Instance = AUDIO_OUT_SAIx;

  /* Disable SAI peripheral */
  __HAL_SAI_DISABLE(&haudio_out_sai);

  HAL_SAI_DeInit(&haudio_out_sai);
}

//--------------------------------------------------------------
static void SAIx_Out_Init(uint32_t AudioFreq)
{
  /* Initialize the haudio_out_sai Instance parameter */
  haudio_out_sai.Instance = AUDIO_OUT_SAIx;

  /* Disable SAI peripheral to allow access to SAI internal registers */
  __HAL_SAI_DISABLE(&haudio_out_sai);

  /* Configure SAI_Block_x
  LSBFirst: Disabled
  DataSize: 16 */
  haudio_out_sai.Init.AudioFrequency = AudioFreq;
  haudio_out_sai.Init.AudioMode = SAI_MODEMASTER_TX;
  haudio_out_sai.Init.NoDivider = SAI_MASTERDIVIDER_ENABLED;
  haudio_out_sai.Init.Protocol = SAI_FREE_PROTOCOL;
  haudio_out_sai.Init.DataSize = SAI_DATASIZE_16;
  haudio_out_sai.Init.FirstBit = SAI_FIRSTBIT_MSB;
  haudio_out_sai.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE;
  haudio_out_sai.Init.Synchro = SAI_ASYNCHRONOUS;
  haudio_out_sai.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLED;
  haudio_out_sai.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;

  /* Configure SAI_Block_x Frame
  Frame Length: 64
  Frame active Length: 32
  FS Definition: Start frame + Channel Side identification
  FS Polarity: FS active Low
  FS Offset: FS asserted one bit before the first bit of slot 0 */
  haudio_out_sai.FrameInit.FrameLength = 64;
  haudio_out_sai.FrameInit.ActiveFrameLength = 32;
  haudio_out_sai.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
  haudio_out_sai.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
  haudio_out_sai.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;

  /* Configure SAI Block_x Slot
  Slot First Bit Offset: 0
  Slot Size  : 16
  Slot Number: 4
  Slot Active: All slot active */
  haudio_out_sai.SlotInit.FirstBitOffset = 0;
  haudio_out_sai.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;
  haudio_out_sai.SlotInit.SlotNumber = 4;
  haudio_out_sai.SlotInit.SlotActive = CODEC_AUDIOFRAME_SLOT_0123;

  HAL_SAI_Init(&haudio_out_sai);

  /* Enable SAI peripheral to generate MCLK */
  __HAL_SAI_ENABLE(&haudio_out_sai);
}

//--------------------------------------------------------------
static void SAIx_In_DeInit(void)
{
  /* Initialize the haudio_in_sai Instance parameter */
  haudio_in_sai.Instance = AUDIO_IN_SAIx;

  /* Disable SAI peripheral */
  __HAL_SAI_DISABLE(&haudio_in_sai);

  HAL_SAI_DeInit(&haudio_in_sai);
}

static void SAIx_In_Init(uint32_t SaiOutMode, uint32_t SlotActive, uint32_t AudioFreq)
{
  /* Initialize SAI2 block A in MASTER RX */
  /* Initialize the haudio_out_sai Instance parameter */
  haudio_out_sai.Instance = AUDIO_OUT_SAIx;

  /* Disable SAI peripheral to allow access to SAI internal registers */
  __HAL_SAI_DISABLE(&haudio_out_sai);

  /* Configure SAI_Block_x
  LSBFirst: Disabled
  DataSize: 16 */
  haudio_out_sai.Init.AudioFrequency = AudioFreq;
  haudio_out_sai.Init.AudioMode = SaiOutMode;
  haudio_out_sai.Init.NoDivider = SAI_MASTERDIVIDER_ENABLED;
  haudio_out_sai.Init.Protocol = SAI_FREE_PROTOCOL;
  haudio_out_sai.Init.DataSize = SAI_DATASIZE_16;
  haudio_out_sai.Init.FirstBit = SAI_FIRSTBIT_MSB;
  haudio_out_sai.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE;
  haudio_out_sai.Init.Synchro = SAI_ASYNCHRONOUS;
  haudio_out_sai.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLED;
  haudio_out_sai.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;

  /* Configure SAI_Block_x Frame
  Frame Length: 64
  Frame active Length: 32
  FS Definition: Start frame + Channel Side identification
  FS Polarity: FS active Low
  FS Offset: FS asserted one bit before the first bit of slot 0 */
  haudio_out_sai.FrameInit.FrameLength = 64;
  haudio_out_sai.FrameInit.ActiveFrameLength = 32;
  haudio_out_sai.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
  haudio_out_sai.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
  haudio_out_sai.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;

  /* Configure SAI Block_x Slot
  Slot First Bit Offset: 0
  Slot Size  : 16
  Slot Number: 4
  Slot Active: All slot actives */
  haudio_out_sai.SlotInit.FirstBitOffset = 0;
  haudio_out_sai.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;
  haudio_out_sai.SlotInit.SlotNumber = 4;
  haudio_out_sai.SlotInit.SlotActive = SlotActive;

  HAL_SAI_Init(&haudio_out_sai);

  /* Initialize SAI2 block B in SLAVE RX synchronous from SAI2 block A */
  /* Initialize the haudio_in_sai Instance parameter */
  haudio_in_sai.Instance = AUDIO_IN_SAIx;

  /* Disable SAI peripheral to allow access to SAI internal registers */
  __HAL_SAI_DISABLE(&haudio_in_sai);

  /* Configure SAI_Block_x
  LSBFirst: Disabled
  DataSize: 16 */
  haudio_in_sai.Init.AudioFrequency = AudioFreq;
  haudio_in_sai.Init.AudioMode = SAI_MODESLAVE_RX;
  haudio_in_sai.Init.NoDivider = SAI_MASTERDIVIDER_ENABLED;
  haudio_in_sai.Init.Protocol = SAI_FREE_PROTOCOL;
  haudio_in_sai.Init.DataSize = SAI_DATASIZE_16;
  haudio_in_sai.Init.FirstBit = SAI_FIRSTBIT_MSB;
  haudio_in_sai.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE;
  haudio_in_sai.Init.Synchro = SAI_SYNCHRONOUS;
  haudio_in_sai.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLED;
  haudio_in_sai.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;

  /* Configure SAI_Block_x Frame
  Frame Length: 64
  Frame active Length: 32
  FS Definition: Start frame + Channel Side identification
  FS Polarity: FS active Low
  FS Offset: FS asserted one bit before the first bit of slot 0 */
  haudio_in_sai.FrameInit.FrameLength = 64;
  haudio_in_sai.FrameInit.ActiveFrameLength = 32;
  haudio_in_sai.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
  haudio_in_sai.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
  haudio_in_sai.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;

  /* Configure SAI Block_x Slot
  Slot First Bit Offset: 0
  Slot Size  : 16
  Slot Number: 4
  Slot Active: All slot active */
  haudio_in_sai.SlotInit.FirstBitOffset = 0;
  haudio_in_sai.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;
  haudio_in_sai.SlotInit.SlotNumber = 4;
  haudio_in_sai.SlotInit.SlotActive = SlotActive;

  HAL_SAI_Init(&haudio_in_sai);

  /* Enable SAI peripheral to generate MCLK */
  __HAL_SAI_ENABLE(&haudio_out_sai);

  /* Enable SAI peripheral */
  __HAL_SAI_ENABLE(&haudio_in_sai);
}


//--------------------------------------------------------------
// ISR called at Half Transfer complete (Audio OUT)
//--------------------------------------------------------------
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
  uint8_t check;

  // check if wav playing
  if(WavAudio.status==2) {
    // refill first half of the buffer
    check=P_FillBufferFirstWav();
    if(check!=0) {
      WavAudio.status=1;
      UB_AUDIO_OUT_Stop();
    }
  }
  else if(WavAudio.status==4) {
	// refill first half of the buffer
	P_FillBufferFirstSample();
  }
}


//--------------------------------------------------------------
// ISR called at Transfer complete (Audio OUT)
//--------------------------------------------------------------
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
  uint8_t check;

  // check if wav playing
  if(WavAudio.status==2) {
    // refill second half of the buffer
    check=P_FillBufferSecondWav();
    if(check!=0) {
      WavAudio.status=1;
      UB_AUDIO_OUT_Stop();
    }
  }
  else if(WavAudio.status==4) {
	// refill second half of the buffer
    P_FillBufferSecondSample();
  }
}

//--------------------------------------------------------------
// ISR called at Half Transfer complete (Audio IN)
//--------------------------------------------------------------
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
  // nothing to do
}


//--------------------------------------------------------------
// ISR called at Transfer complete (Audio IN)
//--------------------------------------------------------------
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
  // nothing to do
}


//--------------------------------------------------------------
// ISR called at Error
//--------------------------------------------------------------
void HAL_SAI_ErrorCallback(SAI_HandleTypeDef *hsai)
{
  HAL_SAI_StateTypeDef audio_out_state;
  HAL_SAI_StateTypeDef audio_in_state;


  audio_out_state = HAL_SAI_GetState(&haudio_out_sai);
  audio_in_state = HAL_SAI_GetState(&haudio_in_sai);


  if ((audio_out_state == HAL_SAI_STATE_BUSY) || (audio_out_state == HAL_SAI_STATE_BUSY_TX)
   || (audio_out_state == HAL_SAI_STATE_TIMEOUT) || (audio_out_state == HAL_SAI_STATE_ERROR))
  {
    // error (Audio OUT)
  }

  if ((audio_in_state == HAL_SAI_STATE_BUSY) || (audio_in_state == HAL_SAI_STATE_BUSY_RX)
   || (audio_in_state == HAL_SAI_STATE_TIMEOUT) || (audio_in_state == HAL_SAI_STATE_ERROR))
  {
    // error (Audio IN)
  }

}


//--------------------------------------------------------------
// DMA Handler (Audio OUT)
//--------------------------------------------------------------
void AUDIO_OUT_SAIx_DMAx_IRQHandler(void)
{
  HAL_DMA_IRQHandler(haudio_out_sai.hdmatx);
}

//--------------------------------------------------------------
// DMA Handler (Audio IN)
//--------------------------------------------------------------
void AUDIO_IN_SAIx_DMAx_IRQHandler(void)
{
  HAL_DMA_IRQHandler(haudio_in_sai.hdmarx);
}




//--------------------------------------------------------------
// interne Funktion
// init der WAV-Parameter
//--------------------------------------------------------------
void P_initWAV(void)
{
  my_wav.wav_ptr=NULL;
  my_wav.file_len=0;
  my_wav.fmt_len=0;
  my_wav.typ=0;
  my_wav.channels=0;
  my_wav.samplerate=0;
  my_wav.byterate=0;
  my_wav.framesize=0;
  my_wav.bitpersample=0;
  my_wav.data_len=0;
  my_wav.data_start=0;
}


//--------------------------------------------------------------
// interne Funktion
// prüft Header von WAV auf Plausibilität
// erlaubt sind nur 16bit, PCM-Files (Mono oder Stereo)
// mit 8kHz bis 48kHz
//
// Return_wert :
//  ->    0 , wenn Header ok war
//  -> != 0 , wenn Fehler im Header
//--------------------------------------------------------------
uint8_t P_CheckWAV(UB_WAV *wav)
{
  const uint8_t *wert;
  uint32_t offset=0;


  wert=&wav->table[0];

  // Filesize bei WAV muss > 50 Bytes sein
  if(wav->size<50) return(1);

  // check auf "RIFF-ID"
  if((wert[0]!='R') || (wert[1]!='I') || (wert[2]!='F') || (wert[3]!='F')) return(2);

  // file länge auslesen (egal)
  my_wav.file_len=P_4Bytes(wav,4);

  // check auf "WAV-ID"
  if((wert[8]!='W') || (wert[9]!='A') || (wert[10]!='V') || (wert[11]!='E')) return(3);

  // check auf "fmt-ID"
  if((wert[12]!='f') || (wert[13]!='m') || (wert[14]!='t') || (wert[15]!=' ')) return(4);

  // fmt laenge auslesen (16d oder 18d)
  my_wav.fmt_len=P_4Bytes(wav,16);
  if((my_wav.fmt_len!=16) && (my_wav.fmt_len!=18)) return(5);
  if(my_wav.fmt_len==16) offset=0;
  if(my_wav.fmt_len==18) offset=14;

  // File-Typ auslesen (0x01=PCM)
  my_wav.typ=P_2Bytes(wav,20);
  if(my_wav.typ!=0x01) return(6);

  // Anzahl Kanäle auslesen (0x01=Mono, 0x02=Stereo)
  my_wav.channels=P_2Bytes(wav,22);
  if((my_wav.channels<1) || (my_wav.channels>2)) return(7);

  // Sample Rate auslesen (8kHz bis 48kHz)
  my_wav.samplerate=P_4Bytes(wav,24);
  if((my_wav.samplerate<8000) || (my_wav.samplerate>48000))  return(8);

  // Byterate auslesen (in Bytes/sec)
  my_wav.byterate=P_4Bytes(wav,28);

  // Framesize auslesen (1,2,4)
  my_wav.framesize=P_2Bytes(wav,32);
  if((my_wav.framesize!=1) && (my_wav.framesize!=2) && (my_wav.framesize!=4)) return(9);

  // Bits pro Sample auslesen (16d)
  my_wav.bitpersample=P_2Bytes(wav,34);
  if(my_wav.bitpersample!=16) return(10);

  // check auf "DATA-ID"
  if((wert[36+offset]!='d') || (wert[37+offset]!='a') || (wert[38+offset]!='t') || (wert[39+offset]!='a')) return(11);

  // Data Länge auslesen
  my_wav.data_len=P_4Bytes(wav,40+offset);

  // Data-Start Adresse setzen
  my_wav.data_start=44+offset;

  return 0;
}

//--------------------------------------------------------------
uint8_t AUDIO_Fill_WAV_Data(UB_WAV *wav, WAV_t *wav_data)
{
	  const uint8_t *wert;
	  uint32_t offset=0;


	  wert=&wav->table[0];

	  // Filesize bei WAV muss > 50 Bytes sein
	  if(wav->size<50) return(1);

	  // check auf "RIFF-ID"
	  if((wert[0]!='R') || (wert[1]!='I') || (wert[2]!='F') || (wert[3]!='F')) return(2);

	  // file länge auslesen (egal)
	  wav_data->file_len=P_4Bytes(wav,4);

	  // check auf "WAV-ID"
	  if((wert[8]!='W') || (wert[9]!='A') || (wert[10]!='V') || (wert[11]!='E')) return(3);

	  // check auf "fmt-ID"
	  if((wert[12]!='f') || (wert[13]!='m') || (wert[14]!='t') || (wert[15]!=' ')) return(4);

	  // fmt laenge auslesen (16d oder 18d)
	  wav_data->fmt_len=P_4Bytes(wav,16);
	  if(( wav_data->fmt_len!=16) && ( wav_data->fmt_len!=18)) return(5);
	  if( wav_data->fmt_len==16) offset=0;
	  if( wav_data->fmt_len==18) offset=14;

	  // File-Typ auslesen (0x01=PCM)
	  wav_data->typ=P_2Bytes(wav,20);
	  if( wav_data->typ!=0x01) return(6);

	  // Anzahl Kanäle auslesen (0x01=Mono, 0x02=Stereo)
	  wav_data->channels=P_2Bytes(wav,22);
	  if((wav_data->channels<1) || (wav_data->channels>2)) return(7);

	  // Sample Rate auslesen (8kHz bis 48kHz)
	  wav_data->samplerate=P_4Bytes(wav,24);
	  if((wav_data->samplerate<8000) || (wav_data->samplerate>48000))  return(8);

	  // Byterate auslesen (in Bytes/sec)
	  wav_data->byterate=P_4Bytes(wav,28);

	  // Framesize auslesen (1,2,4)
	  wav_data->framesize=P_2Bytes(wav,32);
	  if((wav_data->framesize!=1) && (wav_data->framesize!=2) && (wav_data->framesize!=4)) return(9);

	  // Bits pro Sample auslesen (16d)
	  wav_data->bitpersample=P_2Bytes(wav,34);
	  if(wav_data->bitpersample!=16) return(10);

	  // check auf "DATA-ID"
	  if((wert[36+offset]!='d') || (wert[37+offset]!='a') || (wert[38+offset]!='t') || (wert[39+offset]!='a')) return(11);

	  // Data Länge auslesen
	  wav_data->data_len=P_4Bytes(wav,40+offset);

	  // Data-Start Adresse setzen
	  wav_data->data_start=44+offset;

	  return 0;
}

//--------------------------------------------------------------
// interne Funktion
// DWORD (4Bytes) vom Puffer ab start auslesen
//--------------------------------------------------------------
uint32_t P_4Bytes(UB_WAV *wav, uint32_t start)
{
  uint32_t ret_wert=0;
  const uint8_t *wert;

  wert=&wav->table[0];

  ret_wert|=(wert[start]);
  ret_wert|=(wert[start+1]<<8);
  ret_wert|=(wert[start+2]<<16);
  ret_wert|=(wert[start+3]<<24);

  return(ret_wert);
}


//--------------------------------------------------------------
// interne Funktion
// WORD (2Bytes) vom Puffer ab start auslesen
//--------------------------------------------------------------
uint16_t P_2Bytes(UB_WAV *wav, uint32_t start)
{
  uint16_t ret_wert=0;
  const uint8_t *wert;

  wert=&wav->table[0];

  ret_wert|=(wert[start]);
  ret_wert|=(wert[start+1]<<8);

  return(ret_wert);
}

//--------------------------------------------------------------
// füllt completten audio buffer mit WAV daten
// return : 1=wav ende
//--------------------------------------------------------------
uint8_t P_FillBufferCompleteWav(void)
{
  uint8_t ret_wert=0,lo,hi;
  uint32_t n;
  uint16_t wav_data;

  if(my_wav.channels==1) {
    for(n=0;n<AUDIO_BUFFER_SIZE;n++) {
      if(WavAudio.akt_pos<my_wav.data_len) {
        // copy two bytes in audio buffer
        lo=my_wav.wav_ptr->table[WavAudio.akt_adr];
        hi=my_wav.wav_ptr->table[WavAudio.akt_adr+1];
        wav_data=(hi<<8)|lo;
        audio_buffer[n]=wav_data; // mono
        WavAudio.akt_adr+=2;
        WavAudio.akt_pos+=2;
      }
      else {
        // end of wav file
        audio_buffer[n]=0;
        ret_wert=1;
      }
    }
  }
  else {
    for(n=0;n<AUDIO_BUFFER_SIZE;n+=2) {
      if(WavAudio.akt_pos<my_wav.data_len) {
        // copy four bytes in audio buffer
        lo=my_wav.wav_ptr->table[WavAudio.akt_adr];
        hi=my_wav.wav_ptr->table[WavAudio.akt_adr+1];
        wav_data=(hi<<8)|lo;
        audio_buffer[n]=wav_data; // links
        lo=my_wav.wav_ptr->table[WavAudio.akt_adr+2];
        hi=my_wav.wav_ptr->table[WavAudio.akt_adr+3];
        wav_data=(hi<<8)|lo;
        audio_buffer[n+1]=wav_data; // rechts
        WavAudio.akt_adr+=4;
        WavAudio.akt_pos+=4;
      }
      else {
        // end of wav file
        audio_buffer[n]=0;
        ret_wert=1;
      }
    }
  }

  return(ret_wert);
}

//--------------------------------------------------------------
// füllt erste hälfte vom audio buffer mit WAV daten
// return : 1=wav ende
//--------------------------------------------------------------
uint8_t P_FillBufferFirstWav(void)
{
  uint8_t ret_wert=0,lo,hi;
  uint32_t n;
  uint16_t wav_data;

  if(my_wav.channels==1) {
    for(n=0;n<(AUDIO_BUFFER_SIZE/2);n++) {
      if(WavAudio.akt_pos<my_wav.data_len) {
        // copy two bytes in audio buffer
        lo=my_wav.wav_ptr->table[WavAudio.akt_adr];
        hi=my_wav.wav_ptr->table[WavAudio.akt_adr+1];
        wav_data=(hi<<8)|lo;
        audio_buffer[n]=wav_data; // mono
        WavAudio.akt_adr+=2;
        WavAudio.akt_pos+=2;
      }
      else {
        // end of wav file
        audio_buffer[n]=0;
        ret_wert=1;
      }
    }
  }
  else {
    for(n=0;n<(AUDIO_BUFFER_SIZE/2);n+=2) {
      if(WavAudio.akt_pos<my_wav.data_len) {
        // copy four bytes in audio buffer
        lo=my_wav.wav_ptr->table[WavAudio.akt_adr];
        hi=my_wav.wav_ptr->table[WavAudio.akt_adr+1];
        wav_data=(hi<<8)|lo;
        audio_buffer[n]=wav_data; // links
        lo=my_wav.wav_ptr->table[WavAudio.akt_adr+2];
        hi=my_wav.wav_ptr->table[WavAudio.akt_adr+3];
        wav_data=(hi<<8)|lo;
        audio_buffer[n+1]=wav_data; // rechts
        WavAudio.akt_adr+=4;
        WavAudio.akt_pos+=4;
      }
      else {
        // end of wav file
        audio_buffer[n]=0;
        ret_wert=1;
      }
    }
  }

  return(ret_wert);
}

//--------------------------------------------------------------
void P_FillBufferFirstSample(void)
{
	uint8_t lo,hi;
	uint32_t n;
    int16_t l1,r1,l2,r2;
    uint8_t ch1=0,ch2=0;

    if((aktive_sample_1==0) && (aktive_sample_2==0)) {
    	for(n=0;n<(AUDIO_BUFFER_SIZE/2);n+=2) {
			audio_buffer[n]=0;
			audio_buffer[n+1]=0;
    	}
    	for(n=0;n<5;n++) {
    	  sample_aktpos[n]=sample_start[n];
    	}
    }
    else if((aktive_sample_1!=0) && (aktive_sample_2==0)) {
    	ch1=aktive_sample_1-1;
    	for(n=0;n<(AUDIO_BUFFER_SIZE/2);n+=2) {
    	    lo=sample_wav[ch1].table[sample_aktpos[ch1]];
    	    hi=sample_wav[ch1].table[sample_aktpos[ch1]+1];
    	    l1=(hi<<8)|lo;
    	    lo=sample_wav[ch1].table[sample_aktpos[ch1]+2];
    	    hi=sample_wav[ch1].table[sample_aktpos[ch1]+3];
    	    r1=(hi<<8)|lo;
    	    sample_aktpos[ch1]+=4;
    	    if(sample_aktpos[ch1]>sample_len[ch1]) {
    	      sample_aktpos[ch1]=sample_start[ch1];
    	    }
			audio_buffer[n]=(l1>>1);
			audio_buffer[n+1]=(r1>>1);
    	}
    }
    else {
    	ch1=aktive_sample_1-1;
    	ch2=aktive_sample_2-1;
    	for(n=0;n<(AUDIO_BUFFER_SIZE/2);n+=2) {
    	    lo=sample_wav[ch1].table[sample_aktpos[ch1]];
    	    hi=sample_wav[ch1].table[sample_aktpos[ch1]+1];
    	    l1=(hi<<8)|lo;
    	    lo=sample_wav[ch1].table[sample_aktpos[ch1]+2];
    	    hi=sample_wav[ch1].table[sample_aktpos[ch1]+3];
    	    r1=(hi<<8)|lo;
    	    sample_aktpos[ch1]+=4;
    	    if(sample_aktpos[ch1]>sample_len[ch1]) {
    	      sample_aktpos[ch1]=sample_start[ch1];
    	    }
    	    lo=sample_wav[ch2].table[sample_aktpos[ch2]];
    	    hi=sample_wav[ch2].table[sample_aktpos[ch2]+1];
    	    l2=(hi<<8)|lo;
    	    lo=sample_wav[ch2].table[sample_aktpos[ch2]+2];
    	    hi=sample_wav[ch2].table[sample_aktpos[ch2]+3];
    	    r2=(hi<<8)|lo;
    	    sample_aktpos[ch2]+=4;
    	    if(sample_aktpos[ch2]>sample_len[ch2]) {
    	      sample_aktpos[ch2]=sample_start[ch2];
    	    }
			audio_buffer[n]=(l1>>1)+(l2>>1);
			audio_buffer[n+1]=(r1>>1)+(r2>>1);
    	}
    }
}

//--------------------------------------------------------------
// füllt zweite hälfte vom audio buffer mit WAV daten
// return : 1=wav ende
//--------------------------------------------------------------
uint8_t P_FillBufferSecondWav(void)
{
  uint8_t ret_wert=0,lo,hi;
  uint32_t n;
  uint16_t wav_data;

  if(my_wav.channels==1) {
    for(n=(AUDIO_BUFFER_SIZE/2);n<AUDIO_BUFFER_SIZE;n++) {
      if(WavAudio.akt_pos<my_wav.data_len) {
        // copy two bytes in audio buffer
        lo=my_wav.wav_ptr->table[WavAudio.akt_adr];
        hi=my_wav.wav_ptr->table[WavAudio.akt_adr+1];
        wav_data=(hi<<8)|lo;
        audio_buffer[n]=wav_data; // mono
        WavAudio.akt_adr+=2;
        WavAudio.akt_pos+=2;
      }
      else {
        // end of wav file
        audio_buffer[n]=0;
        ret_wert=1;
      }
    }
  }
  else {
    for(n=(AUDIO_BUFFER_SIZE/2);n<AUDIO_BUFFER_SIZE;n+=2) {
      if(WavAudio.akt_pos<my_wav.data_len) {
        // copy four bytes in audio buffer
        lo=my_wav.wav_ptr->table[WavAudio.akt_adr];
        hi=my_wav.wav_ptr->table[WavAudio.akt_adr+1];
        wav_data=(hi<<8)|lo;
        audio_buffer[n]=wav_data; // links
        lo=my_wav.wav_ptr->table[WavAudio.akt_adr+2];
        hi=my_wav.wav_ptr->table[WavAudio.akt_adr+3];
        wav_data=(hi<<8)|lo;
        audio_buffer[n+1]=wav_data; // rechts
        WavAudio.akt_adr+=4;
        WavAudio.akt_pos+=4;
      }
      else {
        // end of wav file
        audio_buffer[n]=0;
        ret_wert=1;
      }
    }
  }

  return(ret_wert);
}

//--------------------------------------------------------------
void P_FillBufferSecondSample(void)
{
	uint8_t lo,hi;
	uint32_t n;
    int16_t l1,r1,l2,r2;
    uint8_t ch1=0,ch2=0;

    if((aktive_sample_1==0) && (aktive_sample_2==0)) {
    	for(n=(AUDIO_BUFFER_SIZE/2);n<AUDIO_BUFFER_SIZE;n+=2) {
			audio_buffer[n]=0;
			audio_buffer[n+1]=0;
    	}
    	for(n=0;n<5;n++) {
    	  sample_aktpos[n]=0;
    	}
    }
    else if((aktive_sample_1!=0) && (aktive_sample_2==0)) {
    	ch1=aktive_sample_1-1;
    	for(n=(AUDIO_BUFFER_SIZE/2);n<AUDIO_BUFFER_SIZE;n+=2) {
    	    lo=sample_wav[ch1].table[sample_aktpos[ch1]];
    	    hi=sample_wav[ch1].table[sample_aktpos[ch1]+1];
    	    l1=(hi<<8)|lo;
    	    lo=sample_wav[ch1].table[sample_aktpos[ch1]+2];
    	    hi=sample_wav[ch1].table[sample_aktpos[ch1]+3];
    	    r1=(hi<<8)|lo;
    	    sample_aktpos[ch1]+=4;
    	    if(sample_aktpos[ch1]>sample_len[ch1]) {
    	      sample_aktpos[ch1]=sample_start[ch1];
    	    }
			audio_buffer[n]=(l1>>1);
			audio_buffer[n+1]=(r1>>1);
    	}
    }
    else {
    	ch1=aktive_sample_1-1;
    	ch2=aktive_sample_2-1;
    	for(n=(AUDIO_BUFFER_SIZE/2);n<AUDIO_BUFFER_SIZE;n+=2) {
    	    lo=sample_wav[ch1].table[sample_aktpos[ch1]];
    	    hi=sample_wav[ch1].table[sample_aktpos[ch1]+1];
    	    l1=(hi<<8)|lo;
    	    lo=sample_wav[ch1].table[sample_aktpos[ch1]+2];
    	    hi=sample_wav[ch1].table[sample_aktpos[ch1]+3];
    	    r1=(hi<<8)|lo;
    	    sample_aktpos[ch1]+=4;
    	    if(sample_aktpos[ch1]>sample_len[ch1]) {
    	      sample_aktpos[ch1]=sample_start[ch1];
    	    }
    	    lo=sample_wav[ch2].table[sample_aktpos[ch2]];
    	    hi=sample_wav[ch2].table[sample_aktpos[ch2]+1];
    	    l2=(hi<<8)|lo;
    	    lo=sample_wav[ch2].table[sample_aktpos[ch2]+2];
    	    hi=sample_wav[ch2].table[sample_aktpos[ch2]+3];
    	    r2=(hi<<8)|lo;
    	    sample_aktpos[ch2]+=4;
    	    if(sample_aktpos[ch2]>sample_len[ch2]) {
    	      sample_aktpos[ch2]=sample_start[ch2];
    	    }
			audio_buffer[n]=(l1>>1)+(l2>>1);
			audio_buffer[n+1]=(r1>>1)+(r2>>1);
    	}
    }
}
