// loop_spi.c : gapless looping + playing from SPI rom

// based on sp_spi.c: plug-in for playing prerecorded ogg files from Intel,
// Eon, Winbond or equivalent 128Kbyte...16Mbyte SPI serial flash eeprom. 
// For this example, a serial flash chip is connected to SI, SO, SCLK, XCS.
// LEDS are connected to GPIO0[12] (NFCLE) and GPIO0[13] (NFALE) pins


#include <stdlib.h>
#include <stdio.h>
#include <vs1000.h>
#include <codec.h>
#include <minifat.h>
#include <player.h>
#include <audio.h>
#include <usb.h>
#include <vsNand.h>
#include <mappertiny.h>

// FAT image is expected to start at sector 32 (16K after chip start)
const u_int16 fatStartSector = 32;


//------- 24-BIT ADDRESSABLE SPI EEPROM ReadDiskSector DRIVER -----------
#define SPI_EEPROM_COMMAND_READ_STATUS_REGISTER  0x05
#define SPI_EEPROM_COMMAND_READ  0x03
//macro to set SPI to MASTER; 8BIT; FSYNC Idle => xCS high
#define SPI_MASTER_8BIT_CSHI   PERIP(SPI0_CONFIG) = \
        SPI_CF_MASTER | SPI_CF_DLEN8 | SPI_CF_FSIDLE1
//macro to set SPI to MASTER; 8BIT; FSYNC not Idle => xCS low
#define SPI_MASTER_8BIT_CSLO   PERIP(SPI0_CONFIG) = \
        SPI_CF_MASTER | SPI_CF_DLEN8 | SPI_CF_FSIDLE0
//macro to set SPI to MASTER; 16BIT; FSYNC not Idle => xCS low
#define SPI_MASTER_16BIT_CSLO  PERIP(SPI0_CONFIG) = \
        SPI_CF_MASTER | SPI_CF_DLEN16 | SPI_CF_FSIDLE0
void InitSpi(u_int16 clockDivider) {
  SPI_MASTER_8BIT_CSHI;
  PERIP(SPI0_FSYNC) = 0;        // Frame Sync is used as an active low xCS
  PERIP(SPI0_CLKCONFIG) = SPI_CC_CLKDIV * (clockDivider-1);   // Spi clock divider = 1
  PERIP(GPIO1_MODE) |= 0x1f;    // Set SPI pins to be peripheral controlled
}  
void EESingleCycleCommand(u_int16 cmd){
  SPI_MASTER_8BIT_CSHI; 
  SPI_MASTER_8BIT_CSLO;
  SpiSendReceive(cmd);
  SPI_MASTER_8BIT_CSHI;
}
/// Wait for not_busy (status[0] = 0) and return status
u_int16 EEWaitGetStatus(void) {
  u_int16 status;
  SPI_MASTER_8BIT_CSHI;
  SPI_MASTER_8BIT_CSLO;
  SpiSendReceive(SPI_EEPROM_COMMAND_READ_STATUS_REGISTER);
  while ((status = SpiSendReceive(0)) & 0x01)
    ; //Wait until ready
  SPI_MASTER_8BIT_CSHI;
  return status;
}
/// Read a block from EEPROM
/// \param blockn number of 512-byte sector 0..32767
/// \param dptr pointer to data block
u_int16 EEReadBlock(u_int16 blockn, u_int16 *dptr) {
  EEWaitGetStatus();                    // Wait until EEPROM is not busy
  SPI_MASTER_8BIT_CSLO;                  // Bring xCS low
  SpiSendReceive(SPI_EEPROM_COMMAND_READ);
  SpiSendReceive(blockn>>7);            // Address[23:16] = blockn[14:7]
  SpiSendReceive((blockn<<1)&0xff);     // Address[15:8]  = blockn[6:0]0
  SpiSendReceive(0);                    // Address[7:0]   = 00000000
  SPI_MASTER_16BIT_CSLO;                 // Switch to 16-bit mode
  { int n;
    for (n=0; n<256; n++){
      *dptr++ = SpiSendReceive(0);      // Receive Data
    }
  }
  SPI_MASTER_8BIT_CSHI;                  // Bring xCS back to high
  return 0;
}
// This function will replace ReadDiskSector() functionality
auto u_int16 MyReadDiskSector(register __i0 u_int16 *buffer,
			      register __a u_int32 sector) {
  EEReadBlock(sector+fatStartSector, buffer);
  return 0;
}
//-----------------End of SPI EEPROM driver ------------------






// ------------ Gapless looping functionality --------------
// Expose some ROM functions...
auto s_int16 CodVorbisGoTo(register struct CodecVorbis *cv,
			   register u_int16 seconds);
extern struct Codec *cod;
struct Codec *CodVorbisCreate(void);
extern struct CodecServices cs;
extern u_int16 ogg[];
extern s_int16 vFirstFrame;
// PlayCurrentFile with gapless looping
u_int16 MyPlayCurrentFile(void) {
  register enum CodecError ret = ceFormatNotFound;
  LoadCheck(NULL, 0); // set higher clock
  if ((cod = CodVorbisCreate())) {
    const char *eStr;
    ret = cod->Decode(cod, &cs, &eStr); // play Ogg Vorbis
    // if played to the end, start over
    while (cs.fileLeft < 5) {
      LoadCheck(NULL, 0);    // set higher clock
      CodVorbisGoTo(cod, 0); // go to beginning of file
      ogg[1] = 0;            // ogg.headerTypeFlag: reset lastFrame
      ogg[14] = 0;           // ogg.cont: set vorbis continuation
      vFirstFrame = 1;       // first ogg frame in file
      cs.playTimeSamples = 0; 
      cs.playTimeSeconds = 0;
      ret = cod->Decode(cod, &cs, &eStr); // play Ogg Vorbis
    }
    cod->Delete(cod);
  }
  return ret;
}




void main(void) {
  InitAudio();
  PERIP(INT_ENABLEL) = INTF_RX | INTF_TIM0;
  PERIP(INT_ENABLEH) = INTF_DAC;


  // Initialize SPI and hook in own disk read function.
  InitSpi(1);
  SetHookFunction((u_int16)ReadDiskSector, MyReadDiskSector);

  
  PlayerVolume();
  if (InitFileSystem()) {
    // File System Not Found, play low sine wave as an "error message"
    register u_int16 *g = (u_int16*) g_yprev0; // get ptr to sintest params
    *g++ = 4; *g++ = 44100; *g++ = 0x5050; *g++ = 120; *g++ = 200;
    SinTest(); //Exit to SinTest: Play weird tone at low volume
    
  } else {
    
    // FAT File System Found, play files from it.
    // The code below is an example playing routine. It's provided
    // here as an example of one possible way of implementing the player
    // main routine.
    
    minifatInfo.supportedSuffixes = supportedFiles;
    player.totalFiles = OpenFile(0xffffU);        
    player.currentFile = 1;
    
    while (1) { // player loop
      
      // "Previous" file selected at the first file
      if (player.currentFile < 0) {
	player.currentFile += player.totalFiles;
      }
      // "Next" file selected at or after the last file
      if (player.currentFile >= player.totalFiles){
	player.currentFile -= player.totalFiles;
      }
      
      // Try to open file number player.currentFile
      if (OpenFile(player.currentFile) < 0) {        
	// File opened. (OpenFile returns negative number for ok
	// or number of files if not ok)
	
	OpenFile(player.currentFile);
	
	player.ffCount = 0;
	cs.cancel = 0;
	cs.goTo = -1;
	cs.fileSize = cs.fileLeft = minifatInfo.fileSize;
	cs.fastForward = 1; /* reset play speed to normal */
	MyPlayCurrentFile(); 	  
	
	player.currentFile += player.nextStep;	
	player.nextStep = 0; // This can be changed by keyboard-handler
	
      } else { // Could not OpenFile.
	player.currentFile = 0; // Revert to playing the first file
      }
    } //end of player loop
  }             
} 


