/******************************************************************************
Copyright (c) 2006 Analog Devices, Inc.  All Rights Reserved.  This software is
proprietary and confidential to Analog Devices, Inc. and its licensors.
*******************************************************************************

$Revision: 1.1 $
$Date: 2008/03/17 14:14:37 $
$LastChangedBy: dpowell $

Project:    HE-AAC Decoder
Title:      riff_header.c
Author:     DWP

Description:
            This file is intended to provide a common means to access the header
            information contained in a RIFF file.

References:
            Wave File Format [http://www.sonicspot.com/guide/wavefiles.html]

******************************************************************************/
#include "riff_header.h"

/******************************************************************************
**          #defines
******************************************************************************/
//#define DEBUG 

/******************************************************************************
**          Typedefs/Enumerations
******************************************************************************/


/******************************************************************************
**          Local variables
******************************************************************************/


/******************************************************************************
**          Local function declaration
******************************************************************************/


/******************************************************************************
**          Function Definitions
******************************************************************************/

/*
**
** Function:            parse_riff_header
**
** Description:         Examines the given file to ensure that it contains the
**                      expected RIFF container header
**
** Arguments:
**  fd                  file descriptor of the RIFF file
**  rh                  Structure to hold parsed RIFF header data
**
** Outputs:             
**  rh                  parsed RIFF header data is stored in the rh Structure
**                      on success the file descriptor is advanced beyond the header
**                      on failure, fd pointer is rewound to original location.
**                      DEBUG MODE
**                        prints descriptive text to stdout 
**
** Return value:        
**   0                  Success
**  -1                  Indicates that the first four bytes did not contain the
**                      characters 'R','I','F','F'
**  -2                  Indicates that the file size did not agree with that 
**                      stated in the container header
**  -3                  Indicates that the four bytes at offset 8 did not contain the
**                      characters 'W','A','V','E'
**
** Assumptions:         
**                      sizeof(unsigned int) == 4 bytes
**                      fd can successfully perform fseek(fd,0,SEEK_END) call
**                      fd is currently pointing to RIFF header in file
**
*/
int parse_riff_header(FILE *fd, riffheader_t *rh) 
{
    long int save_point, file_size;
    save_point = ftell(fd);
    
    fseek(fd,0L,SEEK_END);
    file_size = ftell(fd);
    fseek(fd, save_point, SEEK_SET);
    
    ////Riff Header
    //riff_id == 'R','I','F','F'
   	fread((rh->riffid), 1, (RIFF_ID_SZ), fd);

   	if ((rh->riffid[0] != 'R') ||
   	    (rh->riffid[1] != 'I') ||
   	    (rh->riffid[2] != 'F') ||
   	    (rh->riffid[3] != 'F')  )
   	{
        fseek(fd,save_point,SEEK_SET);
   	    return (-1);
   	}
    //bytes_following == (file_size - 8)
   	fread(&(rh->riffsize), sizeof(unsigned int), 1, fd);
   	if (rh->riffsize != (file_size - 8) )
   	{
        fseek(fd,save_point,SEEK_SET);
   	    return -2;
   	}

    //riff_type == 'W','A','V','E'
   	fread((rh->rifftype), 1, (RIFF_TYPE_SZ), fd);
   	if ((rh->rifftype[0] != 'W') ||
   	    (rh->rifftype[1] != 'A') ||
   	    (rh->rifftype[2] != 'V') ||
   	    (rh->rifftype[3] != 'E')  )
   	{
        fseek(fd,save_point,SEEK_SET);
   	    return -3;
   	}
#ifdef DEBUG
fprintf(stdout,"riffid : %c%c%c%c\n",rh->riffid[0],rh->riffid[1],rh->riffid[2],rh->riffid[3]);
fprintf(stdout,"riffsize : %ld\n",rh->riffsize);
fprintf(stdout,"rifftype : %c%c%c%c\n",rh->rifftype[0],rh->rifftype[1],rh->rifftype[2],rh->rifftype[3]);
#endif //DEBUG   	    
   	return 0;
}

/*
**
** Function:            parse_chunk_header
**
** Description:         Examines the file and parses the next chunk header. If a chunk is 
**                      recognized it is parsed by the helper function and and saved into 
**                      memory from the heap and can be located via the chunkdata pointer 
**                      in the chunkheader_t structure.
**                      Currently this function recognizes the following chunks and parses
**                      those with accompanying helper functions
**                      [data]
**                      [fmt ] parse_chunk_format()
**
** Arguments:
**  fd                  file descriptor of the RIFF file
**  ch                  Structure to hold parsed RIFF Chunk data
**
** Outputs:             Fills the chunkheader_t structure and, if recognized, allocates further
**                      memory on the heap to hold the chunk specific header data.
**                      DEBUG MODE
**                        prints descriptive text to stdout 
**
** Return value:        
**   0                  Successful Parse
**   return > 0         Return code of helper function 
**  -1                  EOF, if unable to read first eight bytes of expected chunk header
**  -2                  Source Code modified incorrectly
**  -255                Insufficient memory to allocate for chunk header data.
**
** Assumptions:         
**                      sizeof(unsigned int) == 4 bytes
**                      fd can successfully perform fseek rewind call
**                      fd is currently pointing to Chunk header start in file
**
*/
int parse_chunk_header(FILE *fd, chunkheader_t *ch)
{
    long int save_point;
    int i,n;
    char buf[CHUNK_ID_SZ+1];
    int invalid_hdr = -2;
    save_point = ftell(fd);

    // read ID
    for( i = 0 ; i <=CHUNK_ID_SZ ; buf[i++] = '\0');
   	n = fread(buf, 1, (CHUNK_ID_SZ), fd); 
   	if (n != CHUNK_ID_SZ) 
   	{
       fseek(fd,save_point,SEEK_SET);
        return -1;
   	}
//    fprintf(stdout,"parse_chunk_header : n = %d, ChunkID [%s] @ 0x%x found!\n", n, buf, ftell(fd)-4);
    strncpy(ch->chunkid, buf, CHUNK_ID_SZ+1);

    // read Size
    n = fread(&(ch->chunksize), sizeof(unsigned int), 1, fd);
    if (n != 1)
    {
//    fprintf(stdout,"parse_chunk_header : ChunkSize error (n = %d) rewind to %ld\n", n, save_point);
        fseek(fd,save_point,SEEK_SET);
        return -1;
   	}
//    fprintf(stdout,"parse_chunk_header : ChunkID [%s] length is %u\n", buf, ch->chunksize);
   	
   	// process fmt chunk
   	if (!strncmp("fmt ",buf, CHUNK_ID_SZ))
   	{
   	    ch->chunkdata = (void *)malloc(sizeof(chunkformat_t ));
   	    if (ch->chunkdata == NULL)
   	    {
//            fprintf(stderr,"parse_chunk_header : Out Of Memory!\n");
   	        return -255;
   	    }
//    fprintf(stdout,"parse_chunk_header : Processing FMT  @ 0x%x \n", ftell(fd));
        invalid_hdr = parse_chunk_format(fd, (chunkformat_t *)(ch->chunkdata));
//    fprintf(stdout,"parse_chunk_header : Processing FMT returned %d @ 0x%x \n", invalid_hdr, ftell(fd));
   	}
    
   	// process data chunk
   	if (!strncmp("data",buf, CHUNK_ID_SZ))
   	{
   	    invalid_hdr = 0;
   	}
    
#ifdef DEBUG
fprintf(stdout,"chunkid : %s\n",buf);
fprintf(stdout,"chunksize : %d\n",ch->chunksize);
#endif //DEBUG

    return invalid_hdr;
}

/*
**
** Function:            parse_chunk_format
**
** Description:         Helper Function to parse and fill the Chunk Header Data for
**                      a "fmt " chunk. Also allocates any extra memory required for 
**                      the optional ExtraFormatBytes at the end of the header.
**
** Arguments:
**  fd                  file descriptor of the RIFF file
**  cf                  Structure to hold parsed RIFF Chunk fmt
**
** Outputs:             fills the chunkformat_t structure with the data from the file 
**                      and advances the file descriptor beyond the header information.
**                      DEBUG MODE
**                        prints descriptive text to stdout 
**
** Return value:        
**   0                  Successful Parse
**  -1                  EOF, if unable to read full expected chunk header
**  -255                Insufficient memory to allocate for chunk header data.
**
** Assumptions:         
**                      Intel Big-Endian byte ordering
**                      fd is currently pointing to Chunk start + 8 bytes
**                      User will free heap memory allocated for extraformatdata if exists
**
*/
int parse_chunk_format(FILE *fd, chunkformat_t *cf)
{
    int n;

	n = fread(&(cf->audioformat), 2, 1, fd);              // 1: PCM, otherwise compressed
	if (n < 1) return -1;

	n = fread(&(cf->numchannels), 2, 1, fd);              // 1: mono, 2: stereo, etc.
	if (n < 1) return -1;

	n = fread(&(cf->samplerate), 4, 1, fd);               // bits/sec for single channel
	if (n < 1) return -1;

	n = fread(&(cf->byterate), 4, 1, fd);
	if (n < 1) return -1;

	n = fread(&(cf->blockalign), 2, 1, fd);
	if (n < 1) return -1;

	n = fread(&(cf->bitspersample),  2, 1, fd);
	if (n < 1) return -1;

	if (cf->audioformat != 1)
	{
	    fread(&(cf->extraformatsize), 2, 1, fd);
    	if (n < 1) return -1;
        if (cf->extraformatsize > 0)
        {
/*          cf->extraformatdata = (char *)(malloc(cf->extraformatsize));
 *          if ( cf->extraformatdata == NULL)
 *          {
//              fprintf(stderr,"parse_chunk_format : Out Of Memory (%hu)!\n", cf->extraformatsize);
 *              return -255;
 *          }
 *          n = fread((cf->extraformatdata), 1, cf->extraformatsize, fd);
 *          if (n < cf->extraformatsize) return -1;
 */
            //skip the extra data
            fseek(fd, cf->extraformatsize, SEEK_CUR);
      	}
 
    }
	else
	{
	    cf->extraformatsize = 0;
    }
    
#ifdef DEBUG
    fprintf(stderr,"\tVerifyChunk_Format->audioformat %hu\n", cf->audioformat);
    fprintf(stderr,"\tVerifyChunk_Format->numchannels %hu\n", cf->numchannels);
    fprintf(stderr,"\tVerifyChunk_Format->samplerate %u\n", cf->samplerate);
    fprintf(stderr,"\tVerifyChunk_Format->byterate %u\n", cf->byterate);
    fprintf(stderr,"\tVerifyChunk_Format->blockalign %hu\n", cf->blockalign);
    fprintf(stderr,"\tVerifyChunk_Format->bitspersample %hu\n", cf->bitspersample);
    fprintf(stderr,"\tVerifyChunk_Format->extraformatsize %hu\n", cf->extraformatsize);
/*
 *    if (cf->extraformatsize > 0) 
 *    {
 *        fprintf(stderr,"\tVerifyChunk_Format->extraformatdata");
 *        for (n = 0 ; n < cf->extraformatsize ; n++)
 *        {
 *            if ((n%16) == 0) fprintf(stderr,"\n\t0x%03x ", (n/16));
 *            fprintf(stderr,"%02x ", (cf->extraformatdata[n])&0xFF );
 *        }
 *        fprintf(stderr,"\n");
 *    }
 */ 
#endif //DEBUG
    return 0;
}

/*
**
** Function:            SkipChunk
**
** Description:         Advance the File Descriptor to the beginning of the next chunk, 
**                      assuming that the current position is currently offset 8 bytes from
**                      the beginning of the current chunk. This is necessary as chunks 
**                      must be aligned on word boundaries, and so '\0' bytes are inserted 
**                      after chunks in bitstream to achieve this.
**                      Assumes word size of 4;
**
** Arguments:
**  fd                  file descriptor of the RIFF file
**  ch                  RIFF Chunk Header containing size of chunk to skip
**
** Outputs:             Advanced file descriptor
**
** Assumptions:         
**                      alignment of RIFF file at 4 bytes
**                      fd is currently pointing to Chunk start + 8 bytes
**
*/
void SkipChunk(FILE *fd, chunkheader_t *ch) 
{
    int fill;
    // not aligned on four byte boundary
    fill = 4 - (ch->chunksize & 3);
    if (fill != 4)
    {
//            fprintf(stderr,"\tSkipChunk %d + %u\n", fill, ch->chunksize);
        fseek(fd,(ch->chunksize)+fill,SEEK_CUR);
    }
    else
    {
//            fprintf(stderr,"\tSkipChunk %u\n", ch->chunksize);
        fseek(fd,(ch->chunksize),SEEK_CUR);
    }
}

//////////////////////////////////////////////////////////////////////////////////////
int write_generic_buffer16(wavefile_t *riff, generic_buffers *data, int num_elements)
{
    int element_count, ch, inc, sample;
    short *buf = (short *)riff->temporary_buffer;
    short *src[MAX_CH];
    
    // ensure we dont copy beyond the generic buffer length
    element_count = (num_elements > (data->buffer_size/2))? data->buffer_size/2 :num_elements;

    // ensure our temporary_buffer is big enough
    if ( element_count*(data->buffer_count) > riff->buffer_len)
    {
        //element_count = temporary_buffer_size/(data->buffer_count);
        return -1;
    }
    
    inc = data->element_step;
    
    for( ch = 0; ch < data->buffer_count ; ch++)
    {
        src[ch] = (short *)(data->buffer[ch]);
    }
    
    for (sample = 0 ; sample < element_count ; sample ++)
    {
        for( ch = 0; ch < riff->wavedata.numchannels ; ch++)
        {
            *buf++ = *src[ch];
            src[ch] += inc;
        }
    }
    
    element_count *= riff->wavedata.numchannels;
   	ch = fwrite(((short *)riff->temporary_buffer), sizeof(short), element_count, riff->fp);
	return ch;

}
            
//////////////////////////////////////////////////////////////////////////////////////
void write_riff_header(wavefile_t *riff)
{
    long int file_len = 0, data_size, save_point;
    int count;
    
    //return to file beginning
    fseek(riff->fp, 0L, SEEK_SET);
    //RIFF WAVE Header
    fwrite("RIFF"   , 1, 4, riff->fp);
    fwrite(&file_len, 4, 1, riff->fp); //temp set to 0
    fwrite("WAVE"   , 1, 4, riff->fp);

    // set the dependant variables
    //// number of bytes per sample
    riff->wavedata.blockalign = (riff->wavedata.bitspersample>>3) * riff->wavedata.numchannels;;
    //// number of bytes per second
    riff->wavedata.byterate = riff->wavedata.samplerate * riff->wavedata.blockalign ;

    //RIFF fmt chunk header
    fwrite(&riff->metadata.chunkid      , 1, 4, riff->fp);
    fwrite(&riff->metadata.chunksize    , 4, 1, riff->fp);
    fwrite(&riff->wavedata.audioformat  , 2, 1, riff->fp);    
    fwrite(&riff->wavedata.numchannels  , 2, 1, riff->fp);
    fwrite(&riff->wavedata.samplerate   , 4, 1, riff->fp);
    fwrite(&riff->wavedata.byterate     , 4, 1, riff->fp);
    fwrite(&riff->wavedata.blockalign   , 2, 1, riff->fp);
    fwrite(&riff->wavedata.bitspersample, 2, 1, riff->fp);
    if (riff->wavedata.extraformatsize)
    {
        //return -1; // unable to handle extraformatsize
    }
//    fwrite(&file_len, 4, 1, riff->fp); //set extraformatsize to 0
    
    //RIFF data chunk header
    fwrite("data", 1, 4, riff->fp);
    data_size = 0;
    fwrite(&data_size, 4, 1, riff->fp); // temp set to 0;
    
    fseek(riff->fp, 0L, SEEK_END); // Go to end of file
	file_len=ftell(riff->fp);//finding the entire file size in terms of bytes

    // write in file size to RIFF WAVE header
    fseek(riff->fp, 4L, SEEK_SET);
	file_len -= 8; 
    fwrite(&file_len, 4, 1, riff->fp);
    
    // write in the data chunk size (assuming there is only one)
    fseek(riff->fp, 40L, SEEK_SET);
	data_size = file_len - (44-8); // number of bytes after this
    fwrite(&data_size, 4, 1, riff->fp); 
}

//////////////////////////////////////////////////////////////////////////////////////
void init_riff_file(wavefile_t *riff, FILE *fp, unsigned char *buffer, int buffer_len)
{
    // save file pointer;
    riff->fp = fp;
    
    // save buffer for use later
    riff->temporary_buffer = buffer;
    riff->buffer_len = buffer_len;
    
    // initialise the known wave data
    riff->metadata.chunkid[0] = 'f'; 
    riff->metadata.chunkid[1] = 'm'; 
    riff->metadata.chunkid[2] = 't'; 
    riff->metadata.chunkid[3] = ' '; 
    riff->metadata.chunksize = 16; 
    // hack to tie together the two structs rather than mallocing the wavedata
    riff->metadata.chunkdata = &(riff->wavedata) ;

    riff->wavedata.audioformat     = 1; // PCM    
    riff->wavedata.numchannels     = 0; // unknown
    riff->wavedata.samplerate      = 0; // unknown
    riff->wavedata.byterate        = 0; // unknown
    riff->wavedata.blockalign      = 0; // unknown 
    riff->wavedata.bitspersample   = 16;// 16 bit
    riff->wavedata.extraformatsize = 0;
    riff->wavedata.extraformatdata = NULL;
    
    write_riff_header(riff);
}

    
/*
**
** EOF: $HeadURL: svn://dingofiles.spd.analog.com/audio/trunk/common/components/riff/public/riff_header.c $
**
*/
