//******************************************************************************
//
// wavfile.c - This file supports reading audio data from a .wav file and
// reading the file format.
//
// Copyright (c) 2012-2017 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
//
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 2.1.4.178 of the Tiva Utility Library.
//
//******************************************************************************

#include <stdint.h>
#include "inc/hw_types.h"
#include "third_party/fatfs/src/ff.h"
#include "third_party/fatfs/src/diskio.h"
#include "wavfile.h"

//******************************************************************************
//
// The flag values for the ui32Flags member of the tWavFile structure.
//
//******************************************************************************
#define WAV_FLAG_FILEOPEN       0x00000001

//******************************************************************************
//
// Basic wav file RIFF header information used to open and read a wav file.
//
//******************************************************************************
#define RIFF_CHUNK_ID_RIFF      0x46464952
#define RIFF_CHUNK_ID_FMT       0x20746d66
#define RIFF_CHUNK_ID_DATA      0x61746164
#define RIFF_TAG_WAVE           0x45564157
#define RIFF_FORMAT_UNKNOWN     0x0000
#define RIFF_FORMAT_PCM         0x0001
#define RIFF_FORMAT_MSADPCM     0x0002
#define RIFF_FORMAT_IMAADPCM    0x0011

//******************************************************************************
//
// This function returns the format of a wav file that has been opened with
// the WavOpen() function.
//
// \param psWavData is the structure that was passed to the WavOpen() function.
// \param psWavHeader is the structure to fill with the format of the wav file.
//
// This function is used to get the audio format of a file that was opened
// with the WavOpen() function.  The \e psWavData parameter should be the
// same structure that was passed to the WavOpen() function.  The
// \e psWavHeader function will be filled with the format of the open file if
// the \e psWavData is a valid open file.  If this function is called with
// an invalid \e psWavData then the results will be undetermined.
//
// \return None.
//
//******************************************************************************
void
WavGetFormat(tWavFile *psWavData, tWavHeader *psWavHeader)
{
    //
    // Only return data if the file is open.
    //
    psWavHeader->ui32DataSize = psWavData->sWavHeader.ui32DataSize;
    psWavHeader->ui16NumChannels = psWavData->sWavHeader.ui16NumChannels;
    psWavHeader->ui32SampleRate = psWavData->sWavHeader.ui32SampleRate;
    psWavHeader->ui32AvgByteRate = psWavData->sWavHeader.ui32AvgByteRate;
    psWavHeader->ui16BitsPerSample = psWavData->sWavHeader.ui16BitsPerSample;
}

//******************************************************************************
//
// This function is called to open and determine if a file is a valid .wav
// file.
//
// \param pcFileName is the null terminated string for the file to open.
// \param psWavData is the structure used to hold the file state information.
//
// This function is used to open a file and determine if it is a valid .wav
// file.  The \e pcFileName will be opened and read to look for a valid .wav
// file header and prepared for calling the WavRead() or WavGetFormat()
// functions.  When an application is done with the .wav file it should call
// the WavClose() function to free up the file.  The function will return
// zero if the function successfully opened a .wav file and a non-zero value
// indicates that the file was a valid .wav file or the file could not be
// opened.
//
// \return A value of zero indicates that the file was successfully opened and
// any other value indicates that the file was not opened.
//
//******************************************************************************
int
WavOpen(const char *pcFileName, tWavFile *psWavData)
{
    unsigned char pucBuffer[16];
    uint32_t *pui32Buffer;
    uint16_t *pui16Buffer;
    uint32_t ui32ChunkSize;
    uint32_t ui32Count;

    //
    // Create some local pointers using in parsing values.
    //
    pui32Buffer = (uint32_t *)pucBuffer;
    pui16Buffer = (uint16_t *)pucBuffer;

    //
    // Open the file as read only.
    //
    if(f_open(&psWavData->i16File, pcFileName, FA_READ) != FR_OK)
    {
        return(-1);
    }

    //
    // File is open.
    //
    psWavData->ui32Flags = WAV_FLAG_FILEOPEN;

    //
    // Read the first 12 bytes.
    //
    if(f_read(&psWavData->i16File, pucBuffer, 12, (UINT *)&ui32Count) != FR_OK)
    {
        return(-1);
    }

    //
    // Look for RIFF tag.
    //
    if((pui32Buffer[0] != RIFF_CHUNK_ID_RIFF) ||
       (pui32Buffer[2] != RIFF_TAG_WAVE))
    {
        return(-1);
    }

    //
    // Read the next chunk header.
    //
    if(f_read(&psWavData->i16File, pucBuffer, 8, (UINT *)&ui32Count) != FR_OK)
    {
        return(-1);
    }

    //
    // Now look for the RIFF ID format tag.
    //
    if(pui32Buffer[0] != RIFF_CHUNK_ID_FMT)
    {
        return(-1);
    }

    //
    // Read the format chunk size and insure that it is 16.
    //
    ui32ChunkSize = pui32Buffer[1];

    if(ui32ChunkSize > 16)
    {
        return(-1);
    }

    //
    // Read the next chunk header.
    //
    if(f_read(&psWavData->i16File, pucBuffer, ui32ChunkSize,
              (UINT *)&ui32Count) != FR_OK)
    {
        return(-1);
    }

    //
    // Save the audio format data so that it can be returned later if
    // requested.
    //
    psWavData->sWavHeader.ui16Format = pui16Buffer[0];
    psWavData->sWavHeader.ui16NumChannels =  pui16Buffer[1];
    psWavData->sWavHeader.ui32SampleRate = pui32Buffer[1];
    psWavData->sWavHeader.ui32AvgByteRate = pui32Buffer[2];
    psWavData->sWavHeader.ui16BitsPerSample = pui16Buffer[7];

    //
    // Only mono and stereo supported.
    //
    if(psWavData->sWavHeader.ui16NumChannels > 2)
    {
        return(-1);
    }

    //
    // Read the next chunk header.
    //
    if(f_read(&psWavData->i16File, pucBuffer, 8, (UINT *)&ui32Count) != FR_OK)
    {
        return(-1);
    }

    //
    // Now make sure that the file has a data chunk.
    //
    if(pui32Buffer[0] != RIFF_CHUNK_ID_DATA)
    {
        //
        // if not equal to RIFF_CHUNK_ID_DATA="data" then calculate the SubChunk2Size.
        // SubChunk2Size = SubChunk - 36.
        // Refer to http://soundfile.sapp.org/doc/WaveFormat/
        psWavData->sWavHeader.ui32DataSize = ui32ChunkSize - 36;;
    }
    else
    {
        //
        // Save the SubChunk2Size of the data.
        //
        psWavData->sWavHeader.ui32DataSize = pui32Buffer[1];

    }

    return(0);
}

//******************************************************************************
//
// This is used to close a .wav file that was opened with WavOpen().
//
// \param psWavData is the file structure that was passed into the WavOpen()
// function.
//
// This function should be called when a function has completed using a .wav
// file that was opened with the WavOpen() function.  This will free up any
// file system data that is held while the file is open.
//
// \return None.
//
//******************************************************************************
void
WavClose(tWavFile *psWavData)
{
    if(psWavData->ui32Flags & WAV_FLAG_FILEOPEN)
    {
        //
        // Close out the file.
        //
        f_close(&psWavData->i16File);

        //
        // Mark file as no longer open.
        //
        psWavData->ui32Flags &= ~WAV_FLAG_FILEOPEN;
    }
}

//******************************************************************************
//
// This function is used to read audio data from a file that was opened with
// the WavOpen() function.
//
// \param psWavData is the file structure that was passed into the WavOpen()
// function.
// \param pucBuffer is the buffer to read data into.
// \param ui32Size is the amount of data to read in bytes.
//
//
// This function handles reading data from a .wav file that was opened with
// the WavOpen() function.   The function will return the actual number of
// of bytes read from the file.
//
// \return This function returns the number of bytes read from the file.
//
//******************************************************************************
uint16_t
WavRead(tWavFile *psWavData, unsigned char *pucBuffer, uint32_t ui32Size)
{
    uint32_t ui32Count;

    //
    // Read in another buffer from the file.
    //
    if(f_read(&psWavData->i16File, pucBuffer, ui32Size,
              (UINT *)&ui32Count) != FR_OK)
    {
        return(0);
    }

    return(ui32Count);
}
