/*******************************************************************************
 *
 *      A U D I O   R E C O R D E R
 *
 *      Copyright A Levido 2013 - All Rights Reserved
 *
 ******************************************************************************/
#include "audio.h"
#include "ff.h"
#include "system.h"
#include "menu.h"

#define AU_BUFFER_SIZE          32768//48128           //bytes
#define FILE_SIZE_OFFSET            4
#define DATA_SIZE_OFFSET           40

UINT32 bufferA[AU_BUFFER_SIZE / 4];
UINT32 bufferB[AU_BUFFER_SIZE / 4];
UINT32 bufferX[1];
extern FIL file;
extern BOOL inInterruptHandler;
extern jmp_buf errJump, intJump;

volatile UINT32 lastA, lastB, writingA;
volatile UINT32 dmaSuspend;
volatile UINT32 Abusy, Bbusy;

void stopPlay();
void stopRec(BOOL noRecurse);

/* Initialise *****************************************************************/
void auInit(void)
{
    // Initialise SPI for codec audio
    SpiChnConfigure(SPI_CHANNEL2, SPI_CONFIG_SLVEN | SPI_CONFIG_SSEN |
            SPI_CONFIG_CKP_HIGH | SPI_CONFIG_MODE32 | SPI_CONFIG_FSP_HIGH |
            SPI_CONFIG_FSP_IN | SPI_CONFIG_FRMEN | SPI_CONFIG_FRM_CNT1);

    DmaEnable(TRUE);

    // Initialise DMA for playback
    DmaChnOpen(DMA_CHANNEL0, DMA_CHN_PRI2, DMA_OPEN_CHAIN_LOW);
    DmaChnSetEventControl(DMA_CHANNEL0, DMA_EV_START_IRQ_EN |
            DMA_EV_START_IRQ(INT_SOURCE_SPI_TX(2)));

    DmaChnOpen(DMA_CHANNEL1, DMA_CHN_PRI2, DMA_OPEN_CHAIN_HI);
    DmaChnSetEventControl(DMA_CHANNEL1, DMA_EV_START_IRQ_EN |
            DMA_EV_START_IRQ(INT_SOURCE_SPI_TX(2)));

    // Initialise DMA for record
    DmaChnOpen(DMA_CHANNEL2, DMA_CHN_PRI3, DMA_OPEN_CHAIN_LOW);
    DmaChnSetEventControl(DMA_CHANNEL2, DMA_EV_START_IRQ_EN |
            DMA_EV_START_IRQ(INT_SOURCE_SPI_TX(2)));

    DmaChnOpen(DMA_CHANNEL3, DMA_CHN_PRI3, DMA_OPEN_CHAIN_HI);
    DmaChnSetEventControl(DMA_CHANNEL3, DMA_EV_START_IRQ_EN |
            DMA_EV_START_IRQ(INT_SOURCE_SPI_TX(2)));

    DmaChnClrEvFlags(DMA_CHANNEL0, DMA_EV_ALL_EVNTS);
    DmaChnSetEvEnableFlags(DMA_CHANNEL0, DMA_EV_BLOCK_DONE);
    DmaChnClrEvFlags(DMA_CHANNEL1, DMA_EV_ALL_EVNTS);
    DmaChnSetEvEnableFlags(DMA_CHANNEL1, DMA_EV_BLOCK_DONE);
    DmaChnClrEvFlags(DMA_CHANNEL2, DMA_EV_ALL_EVNTS);
    DmaChnSetEvEnableFlags(DMA_CHANNEL2, DMA_EV_BLOCK_DONE);
    DmaChnClrEvFlags(DMA_CHANNEL3, DMA_EV_ALL_EVNTS);
    DmaChnSetEvEnableFlags(DMA_CHANNEL3, DMA_EV_BLOCK_DONE);
    INTSetVectorPriority(INT_DMA_0_VECTOR, INT_PRIORITY_LEVEL_7);
    INTSetVectorSubPriority(INT_DMA_0_VECTOR, INT_SUB_PRIORITY_LEVEL_3);
    INTSetVectorPriority(INT_DMA_1_VECTOR, INT_PRIORITY_LEVEL_7);
    INTSetVectorSubPriority(INT_DMA_1_VECTOR, INT_SUB_PRIORITY_LEVEL_3);
    INTSetVectorPriority(INT_DMA_2_VECTOR, INT_PRIORITY_LEVEL_7);
    INTSetVectorSubPriority(INT_DMA_0_VECTOR, INT_SUB_PRIORITY_LEVEL_3);
    INTSetVectorPriority(INT_DMA_3_VECTOR, INT_PRIORITY_LEVEL_7);
    INTSetVectorSubPriority(INT_DMA_1_VECTOR, INT_SUB_PRIORITY_LEVEL_3);
}


/* Play ***********************************************************************/
void auStartPlay(void)
{
    FRESULT fres;
    UINT sizeA, sizeB;

    lastA = FALSE;
    lastB = FALSE;

    /* Load buffers */
    fres = f_read(&file, bufferA, AU_BUFFER_SIZE, &sizeA);
    if(fres) throwError(fres, 500);
    if(sizeA < AU_BUFFER_SIZE) { lastA = TRUE; }

    fres = f_read(&file, bufferB, AU_BUFFER_SIZE, &sizeB);
    if(fres) throwError(fres, 501);
    if(sizeB < AU_BUFFER_SIZE) { lastB = TRUE; }

    DmaChnSetTxfer(DMA_CHANNEL0, bufferA, (void*)SpiChnBuffer(SPI_CHANNEL2),
    sizeA ,4 , 4);
    DmaChnSetTxfer(DMA_CHANNEL1, bufferB, (void*)SpiChnBuffer(SPI_CHANNEL2),
    sizeB ,4 , 4);
    DmaChnSetTxfer(DMA_CHANNEL2, (void*)SpiChnBuffer(SPI_CHANNEL2), bufferX,
    4, 4, 4);
    DmaChnSetTxfer(DMA_CHANNEL3, (void*)SpiChnBuffer(SPI_CHANNEL2), bufferX,
    4, 4, 4);

    DCH1INTCLR = 0xff;
    DCH0INTCLR = 0xff;
    INTClearFlag(INT_DMA1);
    INTClearFlag(INT_DMA0);
    INTEnable(INT_DMA0, INT_ENABLED);
    INTEnable(INT_DMA1, INT_ENABLED);

    // Start Data Pump
    SpiChnEnable(2, 1);
    DmaChnEnable(DMA_CHANNEL0);
    DmaChnEnable(DMA_CHANNEL2);
    cdcSetDACMute(0);
}
void __ISR(_DMA_0_VECTOR, IPL7AUTO) DMA0Handler(void)
{
    FRESULT fres;
    UINT sizeA;
    UINT32 errcode;

    inInterruptHandler = TRUE;
    if(errcode = setjmp(intJump)){
        enqueueHighPriorityEvent(ERROR, errcode, 0, 0);
    } else {
        RED_LED_ON;
        if(lastB == TRUE) { auStopPlay(TRUE); }
        else{
            fres = f_read(&file, bufferA, AU_BUFFER_SIZE, &sizeA);
            if(fres){
                stopPlay();
                throwError(fres, 502);
            }
            if(sizeA < AU_BUFFER_SIZE){
                lastA = TRUE;
                DmaChnSetTxfer(DMA_CHANNEL0, bufferA,
                        (void*)SpiChnBuffer(SPI_CHANNEL2), sizeA ,4 , 4);
            }
        }
        RED_LED_OFF;
    }
    inInterruptHandler = FALSE;
    DCH1INTCLR = 0xff;
    DCH0INTCLR = 0xff;
    INTClearFlag(INT_DMA1);
    INTClearFlag(INT_DMA0);
}

void __ISR(_DMA_1_VECTOR, IPL7AUTO) DMA1Handler(void)
{
    FRESULT fres;
    UINT sizeB;
    UINT32 errcode;

    inInterruptHandler = TRUE;
    if(errcode = setjmp(intJump)){
        enqueueHighPriorityEvent(ERROR, errcode, 0, 0);
    } else {
        RED_LED_ON;
        if(lastA == TRUE) { auStopPlay(TRUE); }
        else{
            fres = f_read(&file, bufferB, AU_BUFFER_SIZE, &sizeB);
            if(fres){
                stopPlay();
                throwError(fres, 503);
            }
            if(sizeB < AU_BUFFER_SIZE){
                lastB = TRUE;
                DmaChnSetTxfer(DMA_CHANNEL0, bufferB,
                        (void*)SpiChnBuffer(SPI_CHANNEL2), sizeB ,4 , 4);
            }
        }
        RED_LED_OFF;
    }
    inInterruptHandler = FALSE;
    DCH1INTCLR = 0xff;
    DCH0INTCLR = 0xff;
    INTClearFlag(INT_DMA1);
    INTClearFlag(INT_DMA0);
}

void stopPlay()
{
    cdcSetDACMute(1);
    f_close(&file);
    DmaResume(0);
    DmaChnDisable(DMA_CHANNEL0);
    DmaChnDisable(DMA_CHANNEL1);
    DmaChnDisable(DMA_CHANNEL2);
    DmaChnDisable(DMA_CHANNEL3);
    SpiChnEnable(2, 0);
    INTEnable(INT_DMA0, INT_DISABLED);
    INTEnable(INT_DMA1, INT_DISABLED);
}

void auStopPlay(BOOL atEnd)
{
    stopPlay();
    enqueueEvent(AUDIO, atEnd ? PLAY_STOP_ATEND : PLAY_STOP, 0, 0);
}

/* Record *********************************************************************/
void auStartRec(void)
{

    lastA = FALSE;
    lastB = FALSE;

    DmaChnSetTxfer(DMA_CHANNEL0, bufferX, (void*)SpiChnBuffer(SPI_CHANNEL2),
    4, 4, 4);
    DmaChnSetTxfer(DMA_CHANNEL1, bufferX, (void*)SpiChnBuffer(SPI_CHANNEL2),
    4, 4, 4);
    DmaChnSetTxfer(DMA_CHANNEL2, (void*)SpiChnBuffer(SPI_CHANNEL2), bufferA,
    4, AU_BUFFER_SIZE, 4);
    DmaChnSetTxfer(DMA_CHANNEL3, (void*)SpiChnBuffer(SPI_CHANNEL2), bufferB,
    4, AU_BUFFER_SIZE, 4);

    DCH2INTCLR = 0xFF;
    DCH3INTCLR = 0xFF;
    INTClearFlag(INT_DMA3);
    INTClearFlag(INT_DMA2);
    INTEnable(INT_DMA2, INT_ENABLED);
    INTEnable(INT_DMA3, INT_ENABLED);

    // Start Data Pump
    writingA = TRUE;
    Abusy = FALSE;
    Bbusy = FALSE;
    SpiChnEnable(2, 1);
    DmaChnEnable(DMA_CHANNEL0);
    DmaChnEnable(DMA_CHANNEL2);
 
}
void __ISR(_DMA_2_VECTOR, IPL7AUTO) DMA2Handler(void)
{
    FRESULT fres;
    UINT sizeA;
    UINT32 errcode, write_size;

    INTClearFlag(INT_DMA2);

    inInterruptHandler = TRUE;
    if(errcode = setjmp(intJump)){
        enqueueHighPriorityEvent(ERROR, errcode, 0, 0);
    } else {
        while(Bbusy);
        Abusy = TRUE;
        write_size = AU_BUFFER_SIZE;
        RED_LED_ON;
        if( file.fptr > 0 && file.fptr < 1024 ) {
            write_size -= file.fptr;
            fres = f_write(&file, (const BYTE*)bufferA + file.fptr, write_size, &sizeA);
        } else {
            fres = f_write(&file, bufferA, write_size, &sizeA);
        }
        if(fres){
            g_disable_error_throw = TRUE;
            stopRec(FALSE);
            g_disable_error_throw = FALSE;
            Abusy = FALSE;
            RED_LED_OFF;
            throwError(fres, 504);
        }
        writingA = FALSE;
        if(sizeA < write_size){
            stopRec(FALSE);
        }
        Abusy = FALSE;
        RED_LED_OFF;
    }
    inInterruptHandler = FALSE;
    DCH2INTCLR = 0xFF;
//    INTClearFlag(INT_DMA3);
}

void __ISR(_DMA_3_VECTOR, IPL7AUTO) DMA3Handler(void)
{
    FRESULT fres;
    UINT sizeB;
    UINT32 errcode, write_size;

    INTClearFlag(INT_DMA3);

    inInterruptHandler = TRUE;
    if(errcode = setjmp(intJump)){
        enqueueHighPriorityEvent(ERROR, errcode, 0, 0);
    } else {
        while(Abusy);
        Bbusy = TRUE;
        write_size = AU_BUFFER_SIZE;
        RED_LED_ON;
        if( file.fptr > 0 && file.fptr < 1024 ) {
            write_size -= file.fptr;
            fres = f_write(&file, (const BYTE*)bufferB + file.fptr, write_size, &sizeB);
        } else {
            fres = f_write(&file, bufferB, write_size, &sizeB);
        }
        if(fres){
            g_disable_error_throw = TRUE;
            stopRec(FALSE);
            g_disable_error_throw = FALSE;
            Bbusy = FALSE;
            RED_LED_OFF;
            throwError(fres, 505);
        }
        writingA = TRUE;
        if(sizeB < write_size){
            stopRec(FALSE);
        }
        Bbusy = FALSE;
        RED_LED_OFF;
    }
    inInterruptHandler = FALSE;
    DCH3INTCLR = 0x0FF;
//    INTClearFlag(INT_DMA2);
}

void stopRec(BOOL noRecurse)
{
    FRESULT fres;
    unsigned long dword;
    unsigned int n;
    DmaResume(0);
    DmaChnDisable(DMA_CHANNEL0);
    DmaChnDisable(DMA_CHANNEL1);
    DmaChnDisable(DMA_CHANNEL2);
    DmaChnDisable(DMA_CHANNEL3);
    SpiChnEnable(2, 0);
    INTEnable(INT_DMA2, INT_DISABLED);
    INTEnable(INT_DMA3, INT_DISABLED);
   
    /* finish file and close it */
    dword = f_tell(&file);
    dword -= 8;
    fres = f_lseek(&file, FILE_SIZE_OFFSET);
    if(fres && !noRecurse) throwError(fres, 506);
    fres = f_write(&file, &dword, 4, &n);
    if(fres && !noRecurse) throwError(fres, 507);
    dword -= 36;
    fres = f_lseek(&file, DATA_SIZE_OFFSET);
    if(fres && !noRecurse) throwError(fres, 508);
    fres = f_write(&file, &dword, 4, &n);
    if(fres && !noRecurse) throwError(fres, 509);
    fres = f_close(&file);
    if(fres && !noRecurse) throwError(fres, 510);
    f_close(&file);
    enqueueEvent(AUDIO, REC_STOP, 0, 0);
}

void auStopRec(BOOL noRecurse)
{
    FRESULT fres;
    UINT n;

    dmaSuspend = DmaSuspend();
    SpiChnEnable(2, 0);
    if(writingA){
        fres = f_write(&file, bufferA, DCH2DPTR, &n);
        if(fres && !noRecurse){
            stopRec(FALSE);
            throwError(fres, 511);
        }
    }
    else {
        fres = f_write(&file, bufferB, DCH3DPTR, &n);
        if(fres && !noRecurse){
            stopRec(FALSE);
            throwError(fres, 512);
        }
    }
    stopRec(noRecurse);
    DmaResume(0);

}

void auPause(void)
{
    dmaSuspend = DmaSuspend();
    SpiChnEnable(2, 0);
}

void auResume(void)
{
    SpiChnEnable(2, 1);
    DmaResume(0);
}