/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <string.h>
#include "playsound.h"

/*******************************************************************************
 * Defination
 ******************************************************************************/

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
static void I2S_SendWavData(uint8_t* DataBuffer, uint32_t len);
/********************************************************************************
 * Variables
 ******************************************************************************/
sound_card_t g_card;
static sai_data_format_t format;
static sai_user_config_t tx_config;
static sai_handler_t tx_handler;
static sgtl_handler_t codec_handler;
 /*******************************************************************************
 * Code
 ******************************************************************************/

 /*!
 * @brief Initialize the structure information for sai
 */
static void snd_card_config(void)
{
    /* SAI configuration */
    tx_config.bus_type = kSaiBusI2SLeft;
    tx_config.channel = 0;
    tx_config.slave_master = kSaiMaster;
    tx_config.sync_mode = kSaiModeAsync;
    tx_config.bclk_source = kSaiBclkSourceMclkDiv;
    tx_config.mclk_source = kSaiMclkSourceSysclk;
    tx_config.mclk_divide_enable = true;
    
    g_card.controller.handler = &tx_handler;
    tx_handler.direction = AUDIO_TX;
    tx_handler.instance = 0;
    tx_handler.fifo_channel = 0;
    g_card.controller.ops = &g_sai_ops;
#if USEDMA
    g_card.controller.dma_source = kDmaRequestMux0I2S0Tx;
#endif
    g_card.codec.handler = &codec_handler;
    g_card.codec.ops = &g_sgtl_ops;
    g_card.direction = AUDIO_TX;
}

/*!
 * @brief     Init sound card.
 *
 * This function will initialize controller(K70's sai moudle) and decoder(SGTL5000)
 * and config them with default values for play audio.
 * @note      8 bit is not supported in SGTL5000
 *
 */
void PLAYSOUND_Init(void)
{
        /* Configure the play audio format */
    format.bits = 16;
    format.sample_rate = 44100;
    format.mclk = 384 * format.sample_rate;
    format.words = 2;
    format.watermark = 4;

    snd_card_config();
    edma_init();
    snd_init(&g_card, &tx_config, NULL);
}

/*!
 * @brief Use sound card to play audio, need wave file data, only support standard Windows PCM uncompressed file.
 *
 * Before calling this function, we must make sure PLAYSOUND_Init has alrady called.
 * This function will play audio according to wave file data which acquired by a
 * user callback function.
 * Prototyoe of "GetData" function:
 * uint32_t APP_GetData(uint32_t  NumBtytesReq, uint8_t **ppData, uint32_t offset)
 *
 * @param[in] fpAppGetData       Pointer to a function which is called by PLAYSOUND_Play routine for getting data.
 *
 * @return   0:succ 1:fail
 */
int PLAYSOUND_Play(APP_GET_DATA_FUNC fpAppGetData)
{

    uint8_t *pData;
    uint32_t NumBytesRead = 0;
    uint32_t offset = 0;
    wave_file_t wavfile1;
    /* get header data */
    while (offset < WAVE_FILE_HEADER_SIZE)
    {
        NumBytesRead = fpAppGetData(WAVE_FILE_HEADER_SIZE-offset , &pData, offset);
        offset += NumBytesRead;
    }
    /* decode wave file */
    if (0 != PLAYSOUND_GetWaveFileInfo(pData, &wavfile1))
    {
        return 1;
    }
    /* reconfig sound card format */

    format.bits = wavfile1.header.bit_samp;
    format.sample_rate = wavfile1.header.samp_freq;
    format.words = wavfile1.header.channels;
    format.mclk = 384 * format.sample_rate;
    snd_data_format_configure(&g_card,&format);
    
    /* begin send data */
    while (offset < wavfile1.header.length)
    {
        /* try to get all data */
        NumBytesRead = fpAppGetData(wavfile1.header.length-offset, &pData, offset);
        I2S_SendWavData(pData, NumBytesRead);
        offset += NumBytesRead;
    }
    return 0;
}

/**
 * @brief Send I2S data
 *
 * This function should call PD layer funcion snd_trigger_tx.
 * @param DataBuffer        pointer for data buffer
 * @param len               number of bytes to write
 * @param pformat           pointer to a wave data format struct
 */
static void I2S_SendWavData(uint8_t* DataBuffer, uint32_t len)
{
    uint32_t count = 0;
    static bool first_copy = true;
    uint8_t *pData = DataBuffer;
    snd_state_t tx_status;
    if(first_copy)
    {
        first_copy = false;
        snd_wait_event(&g_card);
        snd_get_status(&tx_status, &g_card);
        memcpy(tx_status.input_address, pData, tx_status.size);
        snd_update_tx_status(&g_card, tx_status.size);
        snd_start_tx(&g_card);
        pData += tx_status.size;
        count += tx_status.size;
    }
    while(count < len)
    {
        snd_wait_event(&g_card);
        snd_get_status(&tx_status, &g_card);
        memcpy(tx_status.input_address, pData, tx_status.size);
        pData += tx_status.size;
        snd_update_tx_status(&g_card, tx_status.size);
        count += tx_status.size;
    }
}

/*!
 * @brief Decode wave file header, only support standard Windows PCM uncompressed file.
 *
 * This function decode Windows PCM wave file data and, if success, loaded file
 * header information into struct instance.
 * @param[in] pBuffer            Pointer to a wave file start address.
 * @param[in] pWave              Pointer to a wave_file struct.
 *
 * @return   0:succ 1:fail
 */
int PLAYSOUND_GetWaveFileInfo(uint8_t* pBuffer, wave_file_t *pWave)
{
    /* RIFF chunk */
    memcpy(pWave->header.riff, pBuffer, 4);
    pBuffer+=4;
    if (memcmp(pWave->header.riff, "RIFF", 4))
    {
        return 1;
    }
    /* SIZE : from here to file end */
    memcpy(&pWave->header.size, pBuffer, 4);
    pBuffer+=4;
    /* wave file flag */
    memcpy(pWave->header.wave_flag, pBuffer, 4);
    pBuffer+=4;
    if (memcmp(pWave->header.wave_flag, "WAVE", 4))
    {
        return 1;
    }
    /* fmt chunk */
    memcpy(pWave->header.fmt, pBuffer, 4);
    pBuffer+=4;
    if (memcmp(pWave->header.fmt, "fmt ", 4))
    {
        return 1;
    }
    /* fmt length */
    memcpy(&pWave->header.fmt_len, pBuffer, 4);
    pBuffer+=4;
    /* tag : PCM or not */
    memcpy(&pWave->header.tag, pBuffer, 4);
    pBuffer+=2;
    /* channels */
    memcpy(&pWave->header.channels, pBuffer, 4);
    pBuffer+=2;
    /* samp_freq */
    memcpy(&pWave->header.samp_freq, pBuffer, 4);
    pBuffer+=4;
    memcpy(&pWave->header.byte_rate, pBuffer, 4);
    pBuffer+=4;
    /* quantize bytes for per samp point */
    memcpy(&pWave->header.block_align, pBuffer, 4);
    pBuffer+=2;
    memcpy(&pWave->header.bit_samp, pBuffer, 4);
    pBuffer+=2;
    /* data chunk */
    memcpy(pWave->header.data_flag, pBuffer, 4);
    pBuffer+=4;
    if (memcmp(pWave->header.data_flag, "data ", 4))
    {
        return 1;
    }
    memcpy(&pWave->header.length, pBuffer, 4);
    pBuffer+=4;
    return 0;
}

/*******************************************************************************
 * EOF
 ******************************************************************************/

