/*********************************************************************************************************                                               
*                                          ACH Test Suite
*                             COPYRIGHT(c) 2019 Amped RF Technology (ART)
*                                        All Rights Reserved
*
* File    : test_voice_record.c
* Version : V1.0.0
* Brief   : ACH Test Suite
**********************************************************************************************************/

#include "ach118x.h"
#include <stdio.h>
#include <math.h>
#include "GKI_spi.h"
#include "GKI_uart.h"
#include "GKI_queue.h"
#include "md25d80.h"
#include "stdlib.h"
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "timers.h"
#include "menu.h"
#include "ach118x_file.h"
#ifdef ENABLE_VoiceRecord

#define SAMPLE_RATE 8000

#define MODE_PLAYBACK   0
#define MODE_RECORD     1
#define MODE_TEST       3

#define CTRL_IDLE       0
#define CTRL_RUN        1
#define CTRL_STOP       2

uint32_t RecordingByteMax=0;

//-----------------------------------------------------
// volume control
//-----------------------------------------------------
#define VOLUME_MIN      00
#define VOLUME_MAX      80
#define VOLUME_DEFAULT  00
int volume = VOLUME_DEFAULT;
void setvolume(char ch)
{
    if (ch=='+' && volume<VOLUME_MAX) {
        printf("Volume set to %d\n", ++volume);
        codec_set_headset_volume(volume<<2);
    }
    else if (ch=='-' && volume>VOLUME_MIN) {
        printf("Volume set to %d\n", --volume);
        codec_set_headset_volume(volume<<2);
    }
}



//-------------------------------------------------------------------------------
//     Structures to hold Wave file attributes
//-------------------------------------------------------------------------------
typedef struct {
    char        ID[4];              // "RIFF" - Resource Interchange File Format
    uint32_t    Size;               // 36 + SubChunk2Size
    char        Format[4];          // "WAVE"
} WAV_CHUNK_t;

typedef struct {
    char        ID[4];              // "RIFF" - Resource Interchange File Format
    uint32_t    Size;               // 36 + SubChunk2Size
} WAV_SUBCHUNK_t;

typedef struct {
    uint16_t    AudioFormat;        // PCM = 1
    uint16_t    NumChannels;        // Mono = 1, Stereo = 2, etc.
    uint32_t    SampleRate;         // 8000, 44100, etc.
    uint32_t    ByteRate;           // SampleRate * NumChannels * BitsPerSample/8
    uint16_t    BlockAlign;         // NumChannels * BitsPerSample/8
    uint16_t    BitsPerSample;      // 8 bits = 8, 16 bits = 16, etc.
} WAV_FMT_t;


typedef struct {
    WAV_CHUNK_t     Chunk;
    WAV_SUBCHUNK_t  FmtChunk;
    WAV_FMT_t       FmtData;
    WAV_SUBCHUNK_t  DataChunk;
    uint16_t             DataSamples[];
} WaveFile_t;
WaveFile_t *pWaveFile = NULL;




//-----------------------------------------
//   Private function prototypes
//-----------------------------------------
int  LoadMusic(void);
void vTaskMenu(void *pvParam);
void vTaskSw(void *pvParam);
void FileFeedStop(void);
int  ReadMusicFile(uint32_t Length);
void FlashTest(void);
char *DevTxt(void *);
void TestMem(void);
uint32_t FlashSize(void);
void ListFiles(void);
void PlayFile(void);
void ShowDetail(void);
void ShowWaveFile(int);
void ShowFileEntry(FileEntry_t *f);
void RecordingStart(void);
void RecordingStop(void);
void RecordingPlayback(void);
void RecordingSave(void);
void RecordingTest(void);
void PlayWaveFile(int);
void SampleRangeInit(void);
void SampleRangeLog(int16_t samp);


//---------------------------------------
// SPI and MHz settings
//---------------------------------------
#define FLASH_SPI       SPI2
#define CPU_MHZ         200
#define SPI_DIV         10

#define FEED_PERIOD_MS      450
#define IRQ_I2S_BUF_SIZE    512
#define FILE_BUF_SIZE       2048
#define CR                  13
#define LF                  10
#define MAX_FILE_SIZE       (MD25D80_SIZE-256)
#define MAX_SAMPLE_SIZE     (MAX_FILE_SIZE/4)


// Convert 8-bit to 16-bit sample
#define ConvertTo16(x)  ((int16_t)(x - 0x80) << 8)


//------------------------------------------
// Globals
//------------------------------------------
//int PlayCountdown=0;

uint8_t *AudioBuf=NULL;
uint32_t AudioBufPnt=0;
uint32_t AudioBufLength=0;
uint32_t AudioMode=MODE_PLAYBACK;
uint32_t AudioCtrl=CTRL_RUN;

int16_t SampLowest, SampHighest;


uint32_t MusicDataBase   = 0;
uint32_t MusicDataSize   = 0;
uint32_t MusicBlockPnt   = 0;
uint32_t MusicBlockAlign = 0;
uint32_t MusicBlockSize  = 0;
uint32_t ErrorFileQueueEmpty   = 0;
uint32_t ErrorFileQueueOverrun = 0;
uint32_t IrqCount=0;
TimerHandle_t FeedTimerHandle=NULL;
void * pvTimerID = (void *)123;
void vLoadMusicTask(void *);
QueueHandle_t xQueueFeedTimeout=NULL;
uint32_t gFileFeedActive=0;

FileEntry_t wavefile={0};


//----------------------
//  Define sample data
//----------------------
typedef struct {
    uint8_t lft;
    uint8_t rgt;
} b8_t;
typedef struct {
    int16_t lft;
    int16_t rgt;
} b16_t;
typedef union {
    b8_t    b8;
    b16_t   b16;
} SAMPLE_t;



//------------------------------------------------------------------------
// menu definitions
//------------------------------------------------------------------------
extern MenuTypedef MainMenu;
extern MenuTypedef RecordMenu;

const MenuItemTypedef MainMenuItems[]={
    //{ITEM_TYPE_NORMAL,  1, "List wave files",           (void *)&ListFiles},
    {ITEM_TYPE_NORMAL,  1, "Playback",            (void *)&PlayFile},
    //{ITEM_TYPE_NORMAL,  1, "Show wave file detail",     (void *)&ShowDetail},
    {ITEM_TYPE_MENU,    1, "Record",                    &RecordMenu},
    {0,0,0}
};
MenuTypedef MainMenu={"Main Menu", (MenuItemTypedef *)&MainMenuItems};
//------------------------------------------------------------------------
extern MenuTypedef RecordMenu;
const MenuItemTypedef RecordMenuItems[]={
    {ITEM_TYPE_NORMAL,  0, "Start",           (void *)&RecordingStart},
    {ITEM_TYPE_NORMAL,  0, "Stop",            (void *)&RecordingStop},
    {ITEM_TYPE_NORMAL,  0, "Playback",        (void *)&RecordingPlayback},
    //{ITEM_TYPE_NORMAL,  0, "Save",            (void *)&RecordingSave},
    //{ITEM_TYPE_NORMAL,  0, "Test",            (void *)&RecordingTest},
    {0,0,0}
};
MenuTypedef RecordMenu={"Record", (MenuItemTypedef *)&RecordMenuItems};
//------------------------------------------------------------------------


//------------------------------------
// Select file from director.
// ty
//  0 executable
//  1 any
//------------------------------------
int SelectWaveFile(void)
{
    int i, n, num, ch, attr;
    uint8_t nselect[ENTRYMAX];
    int repaintmenu=true;

    while (1) {
        if (repaintmenu) {
            for (n=1, num=0; n<ENTRYMAX; n++) {
                if (Directory[n].EntryType==ENTRY_UNUSED) {
                    break;
                }
                if (Directory[n].EntryType==ENTRY_VALID) {
                    if (strstr(Directory[n].FileName, ".wav")) {
                        nselect[num] = n;
                        ShowMenuItemNum(num++);
                        ShowFileEntry(&Directory[n]);
                    }
                }
            }
            repaintmenu = false;
        }
        if (num==0) {
            putstring_crlf("No Files Found");
            return -1;
        }

        putchar('#');
        do {
            ch = CharIsReady() ? getchar() : 0;
            if (ch==0x0A)
                ch = 0;
        } while (ch==0);
        crlf();


        // Return from menu on CR
        if (ch==0x0D) {
            return -1;                     // go back one menu
        }
        // Show menu on space
        else if (ch==' ') {
            repaintmenu = true;            // repaint menu
            continue;
        }

        // Get item index
        i = GetMenuItemNum(ch);

        // Exit on Invalid keypress
        if (i<0 || i>=num) {
            InvalidSelection();
            repaintmenu = true;            // repaint menu
            continue;
        }
        else {
            return nselect[i];
        }
    }
}

//------------------------------------
void ListFiles(void)
{
    for (int n=0; n>=0; ) {
        n = NextFile(n, "wav");
        if (n>0) {
            ShowFileEntry(&Directory[n]);
        }
    }
    crlf();
}
//------------------------------------
void PlayFile(void)
{
    int n = SelectWaveFile();
    if (n > 0) {
        printf("Playing %s\r\n", Directory[n].FileName);
        PlayWaveFile(n);
    }
    crlf();
}
//------------------------------------
void ShowDetail(void)
{
}


//------------------------------------------
// Show File Entry
//------------------------------------------
void ShowFileEntry(FileEntry_t *f)
{
    printf("%-20s %-8d\r\n",
           f->FileName,
           f->FileSize);
}


//------------------------------------------
// Erase sector when sector is not empty
//------------------------------------------
void FlashSectorErase(uint32_t EraseAddr)
{
    uint8_t *pBuf;

    // Can't allocate memory, so erase anyway
    if ((pBuf = malloc(M25D80_SECTOR_SIZE))==0) {
        MD25D80_4KB_SectorErase(FLASH_SPI, EraseAddr);
        return;
    }

    // Erase only if not empty
    MD25D80_ReadData(FLASH_SPI, EraseAddr, pBuf, M25D80_SECTOR_SIZE);
    for (int i=0; i<M25D80_SECTOR_SIZE; i++) {
        if (pBuf[i] != 0xFF) {
            MD25D80_4KB_SectorErase(FLASH_SPI, EraseAddr);
            break;
        }
    }
    free(pBuf);
}




//------------------------------------------
// Return device name
//------------------------------------------
char *DevTxt(void *SPIx)
{
    uint32_t addr = (uint32_t)SPIx;
    switch ((uint32_t)SPIx) {
    case (uint32)SPI0: return "SPI0";
    case (uint32)SPI1: return "SPI1";
    case (uint32)SPI2: return "SPI2";
    default: return "Dev?";
    }
}


//-----------------------------------------------------
// Show Flash IDs
//-----------------------------------------------------
void RevealFlashIDs(void)
{
    uint8_t ElectSignature;
    uint8_t REMS[2];
    uint8_t RDID[3];

    MD25D80_ReadDeviceID(FLASH_SPI, &ElectSignature);
    MD25D80_ReadManufactureID(FLASH_SPI, REMS);
    MD25D80_ReadIdentification(FLASH_SPI, RDID);

    //printf("Signature: 0x%02X\r\n", ElectSignature);
    printf("Flash IDs: %02X %02X %02X %02X %02X\r\n",
           ElectSignature, REMS[1], RDID[0], RDID[1], RDID[2]);
    //printf("Device:    0x%02X\r\n", REMS[1]);
    //printf("Manuf:     0x%02X\r\n", RDID[0]);
    //printf("MemType:   0x%02X\r\n", RDID[1]);
    //printf("Density:   0x%02X\r\n", RDID[2]);
}


//-----------------------------------------------------
// Wait to receive WaitCh up to TimeoutMS
//-----------------------------------------------------
int WaitFor(uint8_t WaitCh, uint32_t TimeoutMS)
{
    uint8_t ch;
    uint32_t t0,t1,ms;
    t0 = xTaskGetTickCount();
    do {
        ch = CharIsReady() ? getchar() : 0;
        t1 = xTaskGetTickCount();
        ms = (t1-t0) / portTICK_PERIOD_MS;
        if (ch == WaitCh)
            return true;
    } while (ms<TimeoutMS);
    return false;
}



//------------------------------------------------------------------------
// Main menu
//------------------------------------------------------------------------
void vTaskMenu(void *pvParam)
{
    int ch;
    uint32_t pWavFile, WavFileSize;

    //--------------------------------
    //  Set and show speed settings
    //--------------------------------
    printf("SystemCoreClock %d MHz\r\n", SystemCoreClock/1000000);
    uint32_t baudr, Hz, MHz, kHz;
    //GKI_SpiDivInit(FLASH_SPI, SPI_DIV);

    GKI_SpiDivPolPhaInit(FLASH_SPI, SPI_DIV, 0, 0);

    baudr = FLASH_SPI->BAUDR;
    Hz = SystemCoreClock / 2 / baudr;
    MHz = Hz / 1000000;
    kHz = (Hz / 1000) % 1000;
    printf("Flash: %s (%d.%01d MHz, Div=%d)\r\n", DevTxt(FLASH_SPI), MHz, kHz/100, baudr);

    // Reset flash
    MD25D80_PowerDown(FLASH_SPI);
    MD25D80_ReleasePowerDown(FLASH_SPI);
    SPI_Cleanup(FLASH_SPI);
    RevealFlashIDs();

    //-------------------------
    //   Do menu forever
    //-------------------------
    printf("\nVolume is %d. Press +/- to change volume. (%d max)\n", volume, VOLUME_MAX);
    menu_start(&MainMenu);
}




//==============================================================
// stop file feed
//==============================================================
void FileFeedStop(void)
{
    TIM1->ControlReg = 0;
    NVIC_DisableIRQ(TIM_IRQn);
}




//==============================================================
// Check wave header
//==============================================================
int IsWaveHeaderValid(void)
{
    if (pWaveFile==NULL)
        return 0;
    else if (pWaveFile->FmtData.AudioFormat != 1)
        return 0;
    else if ((pWaveFile->FmtData.NumChannels) != 1 && (pWaveFile->FmtData.NumChannels != 2))
        return 0;
    else if ((pWaveFile->FmtData.BitsPerSample) != 8 && (pWaveFile->FmtData.BitsPerSample != 16))
        return 0;
    else {
        switch (pWaveFile->FmtData.SampleRate) {
        case  8000:  return true;
        case 11025:  return true;
        case 12000:  return true;
        case 16000:  return true;
        case 22050:  return true;
        case 24000:  return true;
        case 32000:  return true;
        case 44100:  return true;
        case 48000:  return true;
        case 88200:  return true;
        case 96000:  return true;
        case 192000: return true;
        default:     return false;
        }
    }
}


//==============================================================
//==============================================================
void ShowWaveHeader(void)
{
    printf("NumChannels   %d\r\n", pWaveFile->FmtData.NumChannels);
    printf("SampleRate    %d\r\n", pWaveFile->FmtData.SampleRate);
    printf("BitsPerSample %d\r\n", pWaveFile->FmtData.BitsPerSample);
    printf("BlockAlign    %d\r\n", pWaveFile->FmtData.BlockAlign);
    uint32_t playtimeSec  = (pWaveFile->DataChunk.Size / pWaveFile->FmtData.BlockAlign) / pWaveFile->FmtData.SampleRate;
    uint32_t playtimeFrac = (pWaveFile->DataChunk.Size / pWaveFile->FmtData.BlockAlign) % pWaveFile->FmtData.SampleRate;
    printf("PlayTime      %d.%d sec\r\n", playtimeSec, 10*playtimeFrac/pWaveFile->FmtData.SampleRate);
    printf("DataSize      %d bytes\r\n", pWaveFile->DataChunk.Size);
}


//==============================================================
// wave player
// n = 0 - Play currently loaded wave file
//     1 = Load file and play
//==============================================================
void PlayWaveFile(int n)
{
    // Reset any currently playing file
    TIM1->ControlReg = 0;
    AUDIO_I2S->Ch0.IMR |= I2S_IMR_TXFEM;    // halt interrupts

    // Load wave file
    if (n==0) {
        if (pWaveFile==NULL) {
            printf("No wave file loaded\r\n");
            return;
        }
    }
    else
    {
        FileEntry_t *f = &Directory[n];
        if (pWaveFile) {
            free(pWaveFile);
        }
        pWaveFile = malloc(f->FileSize);
        if (pWaveFile == NULL) {
            printf("Wave file too large to play\r\n");
            return;
        }
        MD25D80_SafeReadData(FLASH_SPI,
                             f->FileStartSector * SECTOR_SIZE,
                             (uint8_t *)pWaveFile,
                             f->FileSize);
    }

    // Check for wave header validity
    if (!IsWaveHeaderValid()) {
        printf("Wave header invalid\r\n");
        return;
    }

    // Check for wave header validity
    if (pWaveFile->DataChunk.Size==0) {
        printf("Nothing to playback\r\n");
        return;
    }

    //==============================================================
    // Show WAV statistics
    //==============================================================
    //ShowWaveHeader();

    //==============================================================
    // Setup vars for ISR
    //==============================================================
    AudioBuf        = (void *)pWaveFile->DataSamples;
    AudioBufPnt     = 0;
    AudioBufLength  = pWaveFile->DataChunk.Size;
    AudioMode       = MODE_PLAYBACK;
    AudioCtrl       = CTRL_RUN;
    SampleRangeInit();

    //==============================================================
    // Set I2S with Sample Rate
    //==============================================================
    if (util_ConfigAudioI2S_SampleRate(pWaveFile->FmtData.SampleRate) < 0) {
        printf("Invalid sample rate\r\n");
        return;
    }

    //==============================================================
    // Set Codec
    //==============================================================
    codec_init();
    if (codec_config_samplerate(pWaveFile->FmtData.SampleRate) < 0) {
        printf("Cannot set CODEC\r\n");
        return;
    }

    //==============================================================
    // Set Volume
    //==============================================================
    codec_set_master_volume(PLAYBACK_MASTER_VOLUME_MAX);
    codec_set_headset_volume(volume<<2);
    
    //==============================================================
    // Turn on I2S interrupts
    //==============================================================
    REG32(SYSCFG, SYSCFG_SYS_LEV_INTR_MASK_REG) |= I2S1_IRQ_MASK;
    NVIC_EnableIRQ(I2S1_IRQn);      // Enable I2S1 interrupt
    //==============================================================
    I2S1->CER = 0;
    I2S1->CCR = I2S_CCR_WSS_32 | I2S_CCR_SCLKG_NONE;

    //==============================================================
    I2S1->Ch0.TFCR  = 3;            // Set Transmit Ch Empty Trigger level

    //==============================================================
    // I2S config
    //==============================================================
    I2S1->IER  = I2S_IER_IEN;       // DW_apb_i2s Enable
    I2S1->ITER = I2S_ITER_TXEN;     // I2S Transmit Block Enable
    I2S1->CER  = I2S_CER_CLKEN;     // Clock generation enable

    //==============================================================
    // Interrupt on tx empty
    //==============================================================
    I2S1->Ch0.IMR &= ~I2S_IMR_TXFEM;
}


//==============================================================
//==============================================================
void SampleRangeInit(void) {
    SampLowest  = 0x7FFF;
    SampHighest = 0x8000;
}


//==============================================================
//==============================================================
void SampleRangeLog(int16_t samp)
{
    if (samp<SampLowest)
        SampLowest = samp;
    if (samp>SampHighest)
        SampHighest = samp;
}


//==============================================================
// I2S Interrupt Service Routine
//==============================================================
void I2S1_IRQHandler(void)
{
    int i, n;
    SAMPLE_t *sample;
    int16_t samp16;
    int16_t sampLft, sampRgt;

    // Playback
    if (AudioMode==MODE_PLAYBACK)
    {
        //--------------------------
        // Get FIFO trigger level
        //--------------------------
        i = I2S1->Ch0.TFCR;     // Samples already in FIFO
        n = 16 - i;             // Number of samples to fill FIFO

        //--------------------------------------------
        //   Load samples from FileBuf into I2S FIFO
        //--------------------------------------------
        for (i=0; i<n; i++) {
            sample = (void *)&AudioBuf[AudioBufPnt];

            SampleRangeLog(sample->b16.lft);

            if (pWaveFile->FmtData.BitsPerSample==8) {
                I2S1->Ch0.LFT.THR = samp16 = ConvertTo16(sample->b8.lft);
                if (pWaveFile->FmtData.NumChannels==2)
                    samp16 = ConvertTo16(sample->b8.rgt);
                I2S1->Ch0.RGT.THR = samp16;
            }
            else { // Assume 16 bit
                I2S1->Ch0.LFT.THR = sample->b16.lft;
                I2S1->Ch0.RGT.THR = (pWaveFile->FmtData.NumChannels==1 ? sample->b16.lft : sample->b16.rgt);
            }

            AudioBufPnt += pWaveFile->FmtData.BlockAlign;
            if (AudioBufPnt >= AudioBufLength || AudioCtrl==CTRL_STOP) {
                AudioCtrl = CTRL_IDLE;
                AUDIO_I2S->Ch0.IMR |= I2S_IMR_TXFEM;    // halt I2S interrupts
                //printf("Playback stopped. %d samples (%d %d)\r\n",
                //       AudioBufPnt/2, SampLowest, SampHighest);
                break;
            }
        }
    }

    // Recording
    else if (AudioMode==MODE_RECORD) {
        n = I2S1->Ch0.RFCR;     // Number of samples in FIFO
        while (n--) {
            // Assume one channel at 16 bits
            sampLft = I2S1->Ch0.LFT.THR;
            sampRgt = I2S1->Ch0.RGT.THR;
            *(uint16_t *)&AudioBuf[AudioBufPnt] = sampLft;
            SampleRangeLog(sampLft);
            AudioBufPnt += pWaveFile->FmtData.BlockAlign;
            if (AudioBufPnt >= RecordingByteMax || AudioCtrl==CTRL_STOP) {
                AudioCtrl = CTRL_IDLE;
                AUDIO_I2S->Ch0.IMR |= I2S_IMR_RXDAM;    // halt I2S interrupts
                //AUDIO_I2S->IER  = 0;                    // DW_apb_i2s disable
                //AUDIO_I2S->IRER = 0;                    // Receiver block disable
                pWaveFile->DataChunk.Size = AudioBufPnt;
                pWaveFile->Chunk.Size = 36 + pWaveFile->DataChunk.Size;
                printf("Recording stopped. %d samples (%d %d)\r\n",
                       AudioBufPnt/2, SampLowest, SampHighest);
                break;
            }
        }
    }

    // Recording TEST
    else if (AudioMode==MODE_TEST) {
        n = I2S1->Ch0.RFCR;     // Number of samples in FIFO
        while (n--) {
            // Assume one channel at 16 bits
            sampLft = I2S1->Ch0.LFT.THR;
            sampRgt = I2S1->Ch0.RGT.THR;
            SampleRangeLog(sampLft);
        }
    }
}


//=================================================================================
//=================================================================================
// test_wave_player()
//=================================================================================
//=================================================================================
void test_application(void)
{
    // Set clock mhz
    util_SetMainFreqMHz(CPU_MHZ);

    // UART Config
    UART_InitTypeDef uartStruct;
    uartStruct.UART_BaudRate = 115200;
    uartStruct.UART_HardwareFlowControl = FLOWCON_DISABLE;
    uartStruct.UART_Parity = NONE_PARITY;
    uartStruct.UART_StopBits = UART_STOP_1;
    uartStruct.UART_WordLength = UART_LEN_8;
    UART_Init((UART_Typedef *)UART0_REG_BASE, &uartStruct);
    codec_init();
    
    xQueueFeedTimeout = xQueueCreate(1,1);
    xTaskCreate( vTaskMenu, "MainMenu", 500, NULL, 5, NULL );

    vTaskStartScheduler();
    printf("RTOS Failed\r\n");
}


//==============================================================
// Report flash size in bytes
//==============================================================
uint32_t FlashSize(void)
{
    uint8_t ReadBuf[4]={0};
    MD25D80_ReadIdentification(FLASH_SPI, ReadBuf);
    switch (ReadBuf[2]) {
    case 0x13:  return 1024*512;    //
    case 0x15:  return 1024*2048;   // MX25R1635F
    default:    return 1024*512;
    }
}


//==============================================================
// Record voice start
//==============================================================
void RecordingStart(void)
{
    uint16_t MaxSeconds = 25;
    uint32_t MaxDataSize = 2 * SAMPLE_RATE * MaxSeconds;
    uint32_t FileAllocMax;

    // Clear previous allocation
    if (pWaveFile) {
        free(pWaveFile);
    }
    FileAllocMax = xPortGetMinimumEverFreeHeapSize()-1024;
    if (FileAllocMax==NULL) {
        printf("Cannot start\r\n");
    }

    //==============================================================
    // Setup recording wave file
    //==============================================================
    //    Length = MIN(WavData.Size, xPortGetMinimumEverFreeHeapSize()-1024);
    pWaveFile = malloc(sizeof(WaveFile_t) + MaxDataSize);
    if (pWaveFile==NULL) {
        printf("Cannot allocate memory\r\n");
        return;
    }
    memset(pWaveFile, 0, FileAllocMax);
    memcpy(pWaveFile->Chunk.ID, "RIFF", 4);
    pWaveFile->Chunk.Size                            = 0;    // 36 + SubChunk2Size
    memcpy(pWaveFile->Chunk.Format, "WAVE", 4);
    memcpy(pWaveFile->FmtChunk.ID, "fmt ", 4);
    pWaveFile->FmtChunk.Size                         = 16;
    pWaveFile->FmtData.AudioFormat                   = 1;
    pWaveFile->FmtData.NumChannels                   = 1;
    pWaveFile->FmtData.SampleRate                    = SAMPLE_RATE;
    pWaveFile->FmtData.ByteRate                      = SAMPLE_RATE * 2;
    pWaveFile->FmtData.BlockAlign                    = 2;
    pWaveFile->FmtData.BitsPerSample                 = 16;
    memcpy(pWaveFile->DataChunk.ID, "data", 4);
    pWaveFile->DataChunk.Size                        = 0;

    // Setup vars for ISR
    RecordingByteMax = FileAllocMax - sizeof(WaveFile_t);
    AudioBuf = (void *)pWaveFile->DataSamples;
    AudioBufLength = 0;
    AudioBufPnt = 0;
    AudioMode = MODE_RECORD;
    AudioCtrl = CTRL_RUN;
    SampleRangeInit();

#if 0
    // Fill with 4000 samples of 8000 bps, 2000Hz tone
    uint16_t Sample2000Hz[4]={0x0000, 0x5a83, 0x0000, 0xa57d};
    uint32_t SampleNum=0;
    uint16_t *pSamples = pWaveFile->DataSamples;
    for (SampleNum=0; SampleNum<4000; SampleNum++) {
        *pSamples++ = Sample2000Hz[SampleNum & 3];
    }
    pWaveFile->DataChunk.Size = SampleNum*2;
    pWaveFile->Chunk.Size = 36 + pWaveFile->DataChunk.Size;
#else

    //==============================================================
    // Set I2S with Sample Rate
    //==============================================================
    if (util_ConfigAudioI2S_SampleRate(pWaveFile->FmtData.SampleRate) < 0) {
        printf("Invalid sample rate\r\n");
        return;
    }

    //==============================================================
    // Set Codec
    //==============================================================
    codec_init();
    if (codec_config_samplerate(pWaveFile->FmtData.SampleRate) < 0) {
        printf("Cannot set CODEC\r\n");
        return;
    }
    // do it again
    codec_config_samplerate(pWaveFile->FmtData.SampleRate);


    //==============================================================
    // Turn on I2S interrupts
    //==============================================================
    REG32(SYSCFG, SYSCFG_SYS_LEV_INTR_MASK_REG) |= I2S1_IRQ_MASK;
    NVIC_EnableIRQ(I2S1_IRQn); // Enable I2S1 interrupt
    //==============================================================
    I2S1->CER = 0;
    I2S1->CCR = I2S_CCR_WSS_32 | I2S_CCR_SCLKG_NONE;

    //==============================================================
    I2S1->Ch0.RFCR  = 3;            // Set Receive Ch Full Trigger level

    //==============================================================
    // I2S config
    //==============================================================
    I2S1->IER  = I2S_IER_IEN;       // DW_apb_i2s Enable
    I2S1->IRER = I2S_ITER_TXEN;     // Receiver block enable
    I2S1->CER  = I2S_CER_CLKEN;     // Clock generation enable

    //==============================================================
    // Interrupt on rx Data Available
    //==============================================================
    I2S1->Ch0.IMR &= ~I2S_IMR_RXDAM;
#endif
}

//==============================================================
// Record voice test
//==============================================================
void RecordingTest(void)
{
    RecordingStart();
    AudioMode = MODE_TEST;
    while (1) {
        SampleRangeInit();
        vTaskDelay(1000);
        printf("%6d %6d %6d\r\n", SampLowest, SampHighest, SampHighest-SampLowest);
    }
}

//==============================================================
// Record voice stop
//==============================================================
void RecordingStop(void)
{
    AudioCtrl = CTRL_STOP;
}

//==============================================================
// Record voice stop
//==============================================================
void RecordingPlayback(void)
{
    PlayWaveFile(0);
}

//==============================================================
// Record voice stop
//==============================================================
void RecordingSave(void)
{
    printf("Not Implemented\r\n");
}


#endif // ENABLE_VoiceRecord
/************************ (C) COPYRIGHT ART *****END OF FILE****/
