//--------------------------------------------------------------
// File     : main.c
// Datum    : 05.12.2015
// Version  : 1.0
// Autor    : UB
// EMail    : mc-4u(@)t-online.de
// Web      : www.mikrocontroller-4u.de
// CPU      : STM32F746
// Board    : STM32F746-Discovery-Board
// IDE      : OpenSTM32
// GCC      : 4.9 2015q2
// Module   : CubeHAL
// Funktion : WAVE Player from USB-Drive
//            Load max 13 WAVE-Files from USB-Drive
//            Touch Buttons for max 5 Wave-Files
//            Plays max 2 Wave-Files simultaneously
//--------------------------------------------------------------


#include "stm32_ub_system.h"
#include "stm32_ub_lcd_480x272.h"
#include "stm32_ub_sdram.h"
#include "stm32_ub_font.h"
#include "stm32_ub_touch_480x272.h"
#include "stm32_ub_usb_msc_host.h"
#include "stm32_ub_fatfs.h"
#include "stm32f7_audio.h"

//--------------------------------------------------------------------------------------
void showScreeen(void);
void resetData(void);
void readAllWaveFiles(void);
uint32_t checkFileExtension(const char *filename);
uint32_t checkWavFormat(const char *filename);
uint32_t copyWave2SDRam(const char *filename);
void drawButtons(uint32_t buttons);
uint32_t checkButtons(void);


#define WAVE_DIR_NAME "" // search wave files in the root
char str_buffer[32];

// buffer for all WAVE-Files in SD-RAM
#define  WAVE_SDRAM_SIZE 1024*1024 // 1Mbyte
unsigned char SDR_WAVE_BUFFER[WAVE_SDRAM_SIZE]		__attribute__((section(".SDR_WAVE_Section")));
uint32_t akt_wave_adr=0;
// buffer for one WAVE Header in RAM
#define WAVE_HEADER_SIZE  50 // dont change
unsigned char WAVE_HEADER_BUFFER[WAVE_HEADER_SIZE];
UB_WAV dummy_sample;
// max wav files to load
#define MAX_WAVE_SAMPLES  13 // (space for max 13 files on screen)
UB_WAV wave_sample[MAX_WAVE_SAMPLES];
WAV_t wav_data[MAX_WAVE_SAMPLES];
uint8_t akt_wave_sample=0;
// wave format for all wave files
#define WAVE_CHANNELS  2 // stereo
#define WAVE_FRQ 44100   // 44,1 kHz


//--------------------------------------------------------------------------------------
int main(void)
{
  USB_MSC_HOST_STATUS_t usb_status,old_status=USB_MSC_HOST_NO_INIT;
  uint32_t buttons=0, old_buttons=0;

  // init System
  UB_System_Init();

  // init LCD
  UB_LCD_Init();
  UB_LCD_LayerInit_Fullscreen();
  UB_LCD_SetLayer_2();
  UB_LCD_FillLayer(RGB_COL_BLACK);

  // init Touch
  UB_Touch_Init();

  // init USB
  UB_USB_MSC_HOST_Init();

  // init FATfs
  UB_Fatfs_Init();

  // init Audio and play
  UB_AUDIO_OUT_Init(OUTPUT_DEVICE_HEADPHONE, 80, WAVE_FRQ);
  AUDIO_Play_Buffer();

  // print screen
  showScreeen();

  while(1) {
	  usb_status=UB_USB_MSC_HOST_Do();
	  if(usb_status!=old_status) {
		  // usb-drive connected or disconnected
		  old_status=usb_status;

		  if(usb_status==USB_MSC_DEV_CONNECTED) {
			  showScreeen();
			  resetData();
			  UB_Font_DrawString(400,10,"OK   ",&Arial_10x15,RGB_COL_BLACK,RGB_COL_GREEN);
			  readAllWaveFiles();
			  sprintf(str_buffer, "Files ok : %d ", akt_wave_sample);
			  UB_Font_DrawString(10,30,str_buffer,&Arial_10x15,RGB_COL_BLUE,RGB_COL_BLACK);
			  drawButtons(0xFFFF);
			  drawButtons(0); // redraw buttons
			  // init all samples (max 5)
			  if(akt_wave_sample>0) AUDIO_Init_Samples(0, &wav_data[0]);
			  if(akt_wave_sample>1) AUDIO_Init_Samples(1, &wav_data[1]);
			  if(akt_wave_sample>2) AUDIO_Init_Samples(2, &wav_data[2]);
			  if(akt_wave_sample>3) AUDIO_Init_Samples(3, &wav_data[3]);
			  if(akt_wave_sample>4) AUDIO_Init_Samples(4, &wav_data[4]);
		  }
		  else {
			  UB_Font_DrawString(400,10,"ERROR",&Arial_10x15,RGB_COL_WHITE,RGB_COL_RED);
		  }
	  }
	  else {
        if(akt_wave_sample>0) {
          buttons=checkButtons();
          if(buttons!=old_buttons) {
        	  // buttons changed
        	  old_buttons=buttons;
        	  drawButtons(buttons);
        	  AUDIO_Set_ActiveSample(buttons);
          }
        }
	  }
  }
}

//--------------------------------------------------------------------------------------
// show startup screen
//--------------------------------------------------------------------------------------
void showScreeen(void)
{
  UB_LCD_FillLayer(RGB_COL_BLACK);

  // Headline
  UB_Font_DrawString(10,10," WAVE Player V:1.0 ",&Arial_10x15,RGB_COL_WHITE,RGB_COL_BLACK);
  UB_Font_DrawString(240,10,"USB_FS @ CN13 :",&Arial_10x15,RGB_COL_MAGENTA,RGB_COL_BLACK);

  sprintf(str_buffer, "Frq : %d ", WAVE_FRQ);
  UB_Font_DrawString(240,30,str_buffer,&Arial_10x15,RGB_COL_BLUE,RGB_COL_BLACK);
}

//--------------------------------------------------------------------------------------
// reset all global data
//--------------------------------------------------------------------------------------
void resetData(void)
{
	akt_wave_adr=0;
	akt_wave_sample=0;
}

//--------------------------------------------------------------------------------------
// read some wave files (with the same settings)
// and copy them to the sdram
// (list all wave files at screen)
//--------------------------------------------------------------------------------------
void readAllWaveFiles(void)
{
  FRESULT fatfs_error;
  DIR mydir;
  FILINFO myfileinfo;
  uint8_t read_ok=0;
  uint32_t file_cnt=0;
  uint32_t ypos=70;


  if(UB_Fatfs_CheckMedia(USB_0)==FATFS_OK) { // check if drive ok
    if(UB_Fatfs_Mount(USB_0)==FATFS_OK) { // check if mount ok
      fatfs_error=f_opendir(&mydir, WAVE_DIR_NAME);
      if(fatfs_error==FR_OK) {
    	do { // read some files in a loop
    	  UB_USB_MSC_HOST_Do();
          fatfs_error=f_readdir(&mydir, &myfileinfo);
          if(fatfs_error!=FR_OK) read_ok=1;
          if(myfileinfo.fname[0] == 0) read_ok=1;
          if((myfileinfo.fattrib & AM_DIR) == 0) { // check if file
            sprintf(str_buffer, "%s/%s", WAVE_DIR_NAME, myfileinfo.fname);
            if(checkFileExtension(str_buffer)==1) { // check if "*.wav"
              if(checkWavFormat(str_buffer)==1) { // check wave settings
                if(copyWave2SDRam(str_buffer)==1) { // copy to SRDAM
                  sprintf(str_buffer, "Ok  : %s , Len : %d", myfileinfo.fname, (int)(wav_data[akt_wave_sample].file_len));
                  UB_Font_DrawString(10,ypos,str_buffer,&Arial_10x15,RGB_COL_CYAN,RGB_COL_BLACK);
                  ypos+=15;
                  akt_wave_sample++; // store new data in next sample
                  file_cnt++;
                  if(file_cnt>=MAX_WAVE_SAMPLES) read_ok=1;
                }
                else {
                  // buffer full
              	  sprintf(str_buffer, "Err : %s , out of space", myfileinfo.fname);
              	  UB_Font_DrawString(10,ypos,str_buffer,&Arial_10x15,RGB_COL_RED,RGB_COL_BLACK);
                  ypos+=15;
                  file_cnt++;
                  if(file_cnt>=MAX_WAVE_SAMPLES) read_ok=1;
                }
              }
              else {
            	  // wrong wav setting
            	  sprintf(str_buffer, "Err : %s , wrong format", myfileinfo.fname);
            	  UB_Font_DrawString(10,ypos,str_buffer,&Arial_10x15,RGB_COL_RED,RGB_COL_BLACK);
                  ypos+=15;
                  file_cnt++;
                  if(file_cnt>=MAX_WAVE_SAMPLES) read_ok=1;
              }
            }
          }
    	}
        while(read_ok==0);
	  }
      UB_Fatfs_UnMount(USB_0);
    }
  }
}




//--------------------------------------------------------------------------------------
// check if file is "*.wav"
//--------------------------------------------------------------------------------------
uint32_t checkFileExtension(const char *filename)
{
  uint32_t ret_wert=0;
  char * pch;

  pch=strrchr(filename,'.');
  if(pch==0) return(0);
  if(pch==filename) return(0);

  if (strcmp(".WAV", pch) == 0) {
    ret_wert=1; // ok
  }
  if (strcmp(".wav", pch) == 0) {
    ret_wert=1; // ok
  }

  return(ret_wert);
}

//--------------------------------------------------------------------------------------
// check if wav header and format settings ok
// store data in "dummy_data"
//--------------------------------------------------------------------------------------
uint32_t checkWavFormat(const char *filename)
{
	uint32_t ret_wert=0;
	FRESULT fatfs_error;
	FIL myFile;
	UINT bytes_read;
	uint8_t check;

	// set dummy table
	dummy_sample.table = WAVE_HEADER_BUFFER;
	dummy_sample.size = 0;

	UB_USB_MSC_HOST_Do();
	fatfs_error=f_open(&myFile, filename, FA_OPEN_EXISTING | FA_READ);
	if(fatfs_error==FR_OK) {
		// read wav header
		fatfs_error=f_read(&myFile, WAVE_HEADER_BUFFER, WAVE_HEADER_SIZE, &bytes_read);
		dummy_sample.size = bytes_read;
		if(fatfs_error==FR_OK) {
		  check=AUDIO_Fill_WAV_Data(&dummy_sample, &wav_data[akt_wave_sample]);
		  if(check==0) {
			 // check format
			 ret_wert=1; // ok
			 if(wav_data[akt_wave_sample].channels!=WAVE_CHANNELS) ret_wert=0;
			 if(wav_data[akt_wave_sample].samplerate!=WAVE_FRQ) ret_wert=0;
		  }
		}

		f_close(&myFile);
	}

	return ret_wert;
}

//--------------------------------------------------------------------------------------
// copy one file to sdram
//--------------------------------------------------------------------------------------
uint32_t copyWave2SDRam(const char *filename)
{
	uint32_t ret_wert=0;
	FRESULT fatfs_error;
	FIL myFile;
	uint8_t read_ok=0;
	UINT bytes_read;
	uint8_t *ptr;

	// check space in ram
	if((akt_wave_adr+wav_data[akt_wave_sample].file_len) > WAVE_SDRAM_SIZE) return 0;

	// set pointer to next free address
	ptr=&SDR_WAVE_BUFFER[akt_wave_adr];

	wave_sample[akt_wave_sample].table=&SDR_WAVE_BUFFER[akt_wave_adr];
	wave_sample[akt_wave_sample].size=wav_data[akt_wave_sample].file_len;

	wav_data[akt_wave_sample].wav_ptr=&wave_sample[akt_wave_sample];

	fatfs_error=f_open(&myFile, filename, FA_OPEN_EXISTING | FA_READ);
	if(fatfs_error==FR_OK) {
		do {
			UB_USB_MSC_HOST_Do();
			// read complete wave into sdram
			fatfs_error=f_read(&myFile, ptr, 255, &bytes_read);
			ptr+=bytes_read;
			ret_wert+=bytes_read;
			if(fatfs_error!=FR_OK) read_ok=1;
			if(bytes_read==0) read_ok=1;
		}
		while(read_ok==0);

		f_close(&myFile);
		ret_wert=1; // ok
	}
	else ret_wert=0;

	akt_wave_adr+=wav_data[akt_wave_sample].file_len;

	return ret_wert;
}

//--------------------------------------------------------------------------------------
// draw pixel
//--------------------------------------------------------------------------------------
void drawPixel(uint16_t x, uint16_t y, uint16_t c)
{
	UB_LCD_SetCursor2Draw(x,y);
	UB_LCD_DrawPixel(c);
}

//--------------------------------------------------------------------------------------
// draw one button
//--------------------------------------------------------------------------------------
void drawButton(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t c)
{
  uint16_t xp,yp;

  for(yp=y;yp<(y+h);yp++) {
	  for(xp=x;xp<(x+w);xp++) {
		  drawPixel(xp,yp,c);
	  }
  }
}

//--------------------------------------------------------------------------------------
// draw max 5 buttons
//--------------------------------------------------------------------------------------
void drawButtons(uint32_t buttons)
{
  uint16_t w=100,h=30;
  uint16_t c0=RGB_COL_GREY, c1=RGB_COL_BLUE;
  uint16_t c;
  static uint32_t old_buttons=0xFFFF;

  if(akt_wave_sample>0) {
	if((buttons&0x01) != (old_buttons&0x01)) {
	  c=c0;
	  if((buttons&0x01)!=0) c=c1;
	  drawButton(360,70,w,h,c);
	  UB_Font_DrawString(380,80,"Wave-1",&Arial_10x15,RGB_COL_BLACK,c);
	}
  }
  if(akt_wave_sample>1) {
	if((buttons&0x02) != (old_buttons&0x02)) {
	  c=c0;
	  if((buttons&0x02)!=0) c=c1;
	  drawButton(360,70+(1*(h+10)),w,h,c);
	  UB_Font_DrawString(380,80+(1*(h+10)),"Wave-2",&Arial_10x15,RGB_COL_BLACK,c);
	}
  }
  if(akt_wave_sample>2) {
	if((buttons&0x04) != (old_buttons&0x04)) {
	  c=c0;
	  if((buttons&0x04)!=0) c=c1;
	  drawButton(360,70+(2*(h+10)),w,h,c);
	  UB_Font_DrawString(380,80+(2*(h+10)),"Wave-3",&Arial_10x15,RGB_COL_BLACK,c);
	}
  }
  if(akt_wave_sample>3) {
	if((buttons&0x08) != (old_buttons&0x08)) {
	  c=c0;
	  if((buttons&0x08)!=0) c=c1;
	  drawButton(360,70+(3*(h+10)),w,h,c);
	  UB_Font_DrawString(380,80+(3*(h+10)),"Wave-4",&Arial_10x15,RGB_COL_BLACK,c);
	}
  }
  if(akt_wave_sample>4) {
	if((buttons&0x10) != (old_buttons&0x10)) {
	  c=c0;
	  if((buttons&0x10)!=0) c=c1;
	  drawButton(360,70+(4*(h+10)),w,h,c);
	  UB_Font_DrawString(380,80+(4*(h+10)),"Wave-5",&Arial_10x15,RGB_COL_BLACK,c);
	}
  }

  old_buttons=buttons;
}

//--------------------------------------------------------------------------------------
// check if button is pressed
//--------------------------------------------------------------------------------------
uint32_t checkButton(uint8_t n, uint16_t x, uint16_t y)
{
	uint16_t w=100,h=30;

	if(n==1) {
		if((x>360) && (x<(360+w))) {
			if((y>70) && (y<(70+h))) {
				return 0x01;
			}
		}
	}
	if(n==2) {
		if((x>360) && (x<(360+w))) {
			if((y>70+(1*(h+10))) && (y<(70+(1*(h+10))+h))) {
				return 0x02;
			}
		}
	}
	if(n==3) {
		if((x>360) && (x<(360+w))) {
			if((y>70+(2*(h+10))) && (y<(70+(2*(h+10))+h))) {
				return 0x04;
			}
		}
	}
	if(n==4) {
		if((x>360) && (x<(360+w))) {
			if((y>70+(3*(h+10))) && (y<(70+(3*(h+10))+h))) {
				return 0x08;
			}
		}
	}
	if(n==5) {
		if((x>360) && (x<(360+w))) {
			if((y>70+(4*(h+10))) && (y<(70+(4*(h+10))+h))) {
				return 0x10;
			}
		}
	}

	return 0;
}

//--------------------------------------------------------------------------------------
// check which button is pressed
//--------------------------------------------------------------------------------------
uint32_t checkButtons(void)
{
  uint32_t ret_wert=0;
  uint16_t x,y,max_button;
  uint32_t n,check;

  UB_Touch_Read();
  if(MultiTouch_Data.cnt==0) return 0;

  max_button=akt_wave_sample;
  if(max_button>5) max_button=5;

  // touch pos 1
  if(MultiTouch_Data.cnt>0) {
	  for(n=1;n<=max_button;n++) {
	    x=MultiTouch_Data.p[0].xp;
	    y=MultiTouch_Data.p[0].yp;
	    check=checkButton(n,x,y);
	    if(check!=0) {
	    	ret_wert|=check;
	    	break;
	    }
	  }
  }
  // touch pos 2
  if(MultiTouch_Data.cnt>1) {
	  for(n=1;n<=max_button;n++) {
	    x=MultiTouch_Data.p[1].xp;
	    y=MultiTouch_Data.p[1].yp;
	    check=checkButton(n,x,y);
	    if(check!=0) {
	    	ret_wert|=check;
	    	break;
	    }
	  }
  }


  return ret_wert;
}
