/*****************************************************************************
Copyright(c) 2005 Analog Devices, Inc.  All Rights Reserved. This software is 
proprietary and confidential to Analog Devices, Inc. and its licensors.
******************************************************************************

$RCSfile: MP3_decode.c,v $
$Revision: 1.2 $
$Date: 2008/06/06 05:57:52 $

Project:	Audio Kit
Title:		MP3 decode
Author(s):	
Revised by: 

Description:
			High level MP3 decoding functions

References:
			None

******************************************************************************
Tab Setting:			4

Target Processor:		ADSP-BF5xx
Target Tools Revision:	ADSP VisualDSP++ v4.0
******************************************************************************

Modification History:
====================
$Log: MP3_decode.c,v $
Revision 1.2  2008/06/06 05:57:52  randreol
Changes to allow for Kookaburra port

Revision 1.1  2008/03/17 14:14:39  gstephan
Updates for SDK 3.00

Revision 1.5  2007/04/02 06:37:39  randreol
Support for new MP3 library

Revision 1.4  2006/12/12 04:46:24  randreol
New decoder timeout

Revision 1.3  2006/12/04 06:29:38  randreol
Changed media path

Revision 1.2  2006/11/15 06:39:59  randreol
Bring code inline with VDSP November release

Revision 1.2  2006/05/10 05:04:11  dwu
Fixed a circular buffer bug.
Added code to measure USB read times.

Revision 1.1.1.1  2006/05/09 04:01:29  dwu
USB based audio demonstration




*****************************************************************************/

#include "system.h"
#include "codec.h"
//#include "services_install.h"
#include "adac_buffer.h"
#include "SDK-ezkitutilities.h"
#include "adi_mp3_dec.h"
#include "adi_id3_parser.h"

#include <stdio.h>					// stdio header
#include <string.h>					// string header
#include <stdlib.h>
#include <math.h>
#include <cycle_count.h> 			// for basic cycle counting

#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__) /* UCOS used */
#include <mediaplayer.h>
lcd_taginfo_t *mp3tag;
#endif

// User-defined clientdata structure
typedef struct
{
    unsigned char * pAddress;
    unsigned long nNumBytes;
    unsigned long nTotalBytes;    
	unsigned char * pBufBase;	
	unsigned char * pBufRead;
	unsigned long nBufSize;    
    unsigned long nBufBytes;
	void 	*pSeekStruct;
    void 	*pFrameProp;
    void 	*pConfigIn;
    unsigned long nNumBytesUsed;
} MP3_MemState;

// check for sync for 128 kb 
#define ADI_MP3_DEC_MAX_BYTES_TO_SEARCH_FOR_SYNC (128*1024)

/* id3 */

#define ADI_ID3_PARSER_SONG_TITLE_LENGTH	50
#define ADI_ID3_PARSER_ARTIST_LENGTH		50
#define ADI_ID3_PARSER_ALBUM_LENGTH			50
#define ADI_ID3_PARSER_YEAR_LENGTH			50
#define ADI_ID3_PARSER_COMMENTS_LENGTH		50
#define ADI_ID3_PARSER_GENRE_LENGTH			50
#define ADI_ID3_PARSER_TRACK_TIME_LENGTH	50
#define ADI_ID3_PARSER_MODIFIED_LENGTH	50
#define ADI_ID3_PARSER_RECORDING_TIME_LENGTH	50
#define ADI_ID3_PARSER_RECORDING_DATE_LENGTH	50

/*****************************************************************************
	MP3 decoder related variables
 *****************************************************************************/
// State memory blocks
section("State_Data0")	  int OnchipBlock0[ADI_MP3_DEC_STATE_ON_CHIP_BLOCK0];
section("State_Data1")	  int OnchipBlock1[ADI_MP3_DEC_STATE_ON_CHIP_BLOCK1];
section("State_Data")	  int Onchip[ADI_MP3_DEC_STATE_ON_CHIP];
section("State_SlowData") int AnyWhere[ADI_MP3_DEC_STATE_OFF_CHIP];

// Scratch memory blocks
#pragma align 8192
section("Scratch_Data0")  	  int OnchipBlock0Scratch[ADI_MP3_DEC_SCRATCH_ON_CHIP_BLOCK0];
section("Scratch_Data1")      int OnchipBlock1Scratch [ADI_MP3_DEC_SCRATCH_ON_CHIP_BLOCK1];
section("Scratch_Data")       int OnchipScratch [ADI_MP3_DEC_SCRATCH_ON_CHIP];
section("Scratch_SlowData")   int AnyWhereScratch[ADI_MP3_DEC_SCRATCH_OFF_CHIP];


section("Scratch_Data0")
adi_mp3_dec_bitstream_data_t mp3_input_buffer[ADI_MP3_DEC_MIN_BITSTREAM_BUFSIZE];
section("Scratch_SlowData")
adi_mp3_dec_audio_data_t mp3_output_buffer[ADI_MP3_DEC_MAX_OUTBUFSIZE];

// State and scratch memory block allocation objects
adi_mp3_dec_memory_pool_t	mp3state;
adi_mp3_dec_memory_pool_t	mp3scratch;

adi_mp3_dec_instance_t	*mp3_dec_handle;
adi_mp3_dec_config_item_t Config_Item;
adi_mp3_dec_config_t	mp3_requested_config;
adi_mp3_dec_config_t    mp3_actual_decoder_config;

adi_mp3_dec_bitstream_data_t *mp3_curr_input_bitstream_ptr;
adi_mp3_dec_output_status_t mp3_dec_output_status;
adi_mp3_dec_audio_data_t *output_pcm_channels[ADI_MP3_DEC_MAX_NUM_CH];
adi_mp3_dec_frame_properties_t mp3_frame_properties;



// User defined USB input buffer size in bytes
#define USB_INPUT_BUFFER_SIZE	65536	// must be even number
#define HDR_LENGTH 46

#define ADI_MP3_DEC_MAX_NUM_TAGS	6
char *pForSongInfo[ADI_MP3_DEC_MAX_NUM_TAGS];
int nSumSamples = 0;

//======================================================
// ID3 parser
//======================================================
char song_title[ADI_ID3_PARSER_SONG_TITLE_LENGTH] ;
char artist[ADI_ID3_PARSER_ARTIST_LENGTH] ;
char album[ADI_ID3_PARSER_ALBUM_LENGTH] ;
char year[ADI_ID3_PARSER_YEAR_LENGTH] ;
char comments[ADI_ID3_PARSER_COMMENTS_LENGTH] ;
char genre[ADI_ID3_PARSER_GENRE_LENGTH] ;
char track_time[ADI_ID3_PARSER_TRACK_TIME_LENGTH] ;
char modified[ADI_ID3_PARSER_MODIFIED_LENGTH];
char recording_time[ADI_ID3_PARSER_RECORDING_TIME_LENGTH];
char recording_date[ADI_ID3_PARSER_RECORDING_DATE_LENGTH];

// =========================================================
//	USB data buffers
// =========================================================
// User defined USB input buffer
//#pragma align 2
section("Scratch_SlowData")
unsigned char USB_Input_Buffer[USB_INPUT_BUFFER_SIZE];


/* ID3 objects*/

id3_t			id3_instance;
id3_config_t	id3_config;

MP3_MemState	ClientData;

FILE *filein;

/* local function prototypes */
int adi_mp3_fill_buf(char **start_read_ptr,
					 char *updated_read_ptr,
					 char *base,
					 int  buf_len,
					 int  use_circular_buf,
					 int  *end_of_stream_reached);

void print_id3_info(id3_t *id3_instance);




/*****************************************************************************
	Function definitions
 *****************************************************************************/
// User callback function for pulling data to the decoder
unsigned int UserCallback_PullData(unsigned char *pData,unsigned long nNumBytes,void * pClientData)
{
    MP3_MemState *pMemState = (MP3_MemState *)pClientData;
    unsigned int bytes_read, bytes_copied;
    unsigned int sum_bytes_copied, BytesToGo;
    FILE *fptr;
    int i;
    int curr_file_position, new_file_position;

    fptr = (FILE*)pMemState->pAddress;

    //printf("\n pull data \n");

    // skip data instead of copying data
    if (pData == NULL)
    {
        curr_file_position = pMemState->nTotalBytes - (pMemState->nNumBytes + pMemState->nBufBytes);
        new_file_position = curr_file_position + nNumBytes;

        // seek to beginning
        fseek(fptr,0,SEEK_SET);
        // seek to new position
        fseek(fptr,new_file_position,SEEK_SET);

        // update bytes remain in file
        pMemState->nNumBytes = pMemState->nTotalBytes - ftell(fptr);

        // flush the input buffer and refill
        bytes_read = fread(pMemState->pBufBase,1,pMemState->nBufSize,fptr);
        pMemState->nBufBytes = bytes_read;
        pMemState->pBufRead = pMemState->pBufBase;
        pMemState->nNumBytes -= bytes_read;

        return nNumBytes;
    }

    // otherwise copy data as usual
	BytesToGo = nNumBytes;
	sum_bytes_copied = 0;

	while( (BytesToGo > 0) && ((pMemState->nBufBytes != 0) || (pMemState->nNumBytes != 0)) )
	{

    	if(pMemState->nBufBytes == 0)					// buffer empty
    	{												// fill buffer
    		bytes_read = fread(pMemState->pBufBase,1,pMemState->nBufSize,fptr);
    		pMemState->nNumBytes -= bytes_read;			// bytes in file
    		pMemState->pBufRead = pMemState->pBufBase;  // buffer read pointer
    		pMemState->nBufBytes = bytes_read;			// bytes in buffer
    	}


    	for(i=0;i<min(pMemState->nBufBytes, BytesToGo);i++)
    		pData[i] = pMemState->pBufRead[i];

    	bytes_copied = min(pMemState->nBufBytes, BytesToGo);
    	sum_bytes_copied += bytes_copied;
    	pData += bytes_copied;
    	BytesToGo -= bytes_copied;

    	pMemState->pBufRead += bytes_copied;
    	pMemState->nBufBytes -= bytes_copied;

    }

    pMemState->pAddress = (unsigned char*)fptr;

    pMemState->nNumBytesUsed += sum_bytes_copied;

    return sum_bytes_copied;
}

// Function to print the ID3 fields of interest
section("MP3_slowcode") void print_id3_info(id3_t *id3_instance)
{
	printf("\nSong title: %s",song_title);
	printf("\nArtist:     %s",artist);
	printf("\nAlbum:      %s",album);
	printf("\nYear:       %s",year);
	printf("\nGenre:      %s",genre);
	printf("\nComments:   %s",comments);
	printf("\nTrack_time (msec): %s",track_time);
	printf("\nRecording time: %s",recording_time);
	printf("\nModified: %s",modified);
	printf("\nRecording date: %s\n",recording_date);
	fflush(stdout);
}

// Function to initialize the ID3 tag memory
section("MP3_slowcode") void allocate_memory_for_id3_tags(id3_tag_t *id3_field_ptr)
{
	id3_field_ptr->songtitle.tag_data   = song_title;
    id3_field_ptr->songtitle.max_length = ADI_ID3_PARSER_SONG_TITLE_LENGTH;

	id3_field_ptr->artist.tag_data = artist;
	id3_field_ptr->artist.max_length = ADI_ID3_PARSER_ARTIST_LENGTH;

	id3_field_ptr->album.tag_data = album;
	id3_field_ptr->album.max_length = ADI_ID3_PARSER_ALBUM_LENGTH;

	id3_field_ptr->year.tag_data = year;
    id3_field_ptr->year.max_length = ADI_ID3_PARSER_YEAR_LENGTH;

	id3_field_ptr->comments.tag_data = comments;
	id3_field_ptr->comments.max_length = ADI_ID3_PARSER_COMMENTS_LENGTH;

	id3_field_ptr->genre.tag_data = genre;
	id3_field_ptr->genre.max_length = ADI_ID3_PARSER_GENRE_LENGTH;

	id3_field_ptr->track_time.tag_data = track_time;
	id3_field_ptr->track_time.max_length = ADI_ID3_PARSER_TRACK_TIME_LENGTH;

    id3_field_ptr->modified.tag_data = modified;
	id3_field_ptr->modified.max_length = ADI_ID3_PARSER_MODIFIED_LENGTH;

	id3_field_ptr->recording_time.tag_data = recording_time;
	id3_field_ptr->recording_time.max_length = ADI_ID3_PARSER_RECORDING_TIME_LENGTH;

	id3_field_ptr->recording_date.tag_data = modified;
	id3_field_ptr->recording_date.max_length = ADI_ID3_PARSER_RECORDING_DATE_LENGTH;
}


// Function to fill the input buffer for the decoder, used for push API
section("MP3_slowcode")
int adi_mp3_fill_buf(char **start_read_ptr,
					 char *updated_read_ptr,
					 char *base,
					 int  buf_len,
					 int  use_circular_buf,
					 int *end_of_stream_reached)
{
		int space, bytes_copied;
	int bytes_req, bytes_avaialble;
	char *buf_end, *read, *write;

	if(use_circular_buf == 0)
	{
		printf("\n\tPlease use circular buffering \n");
		exit(0);
	}

	buf_end = base + buf_len;

	/* at present, bytes_avaialble in the buffer */
	if(updated_read_ptr < *start_read_ptr)
	{
		bytes_avaialble = *start_read_ptr - updated_read_ptr;
	}
	else
	{
		bytes_avaialble  = (buf_end - updated_read_ptr);
		bytes_avaialble += (*start_read_ptr - base);
	}

	if(bytes_avaialble == buf_len)
	{
		bytes_avaialble = 0;
	}

	read  = updated_read_ptr;
	write = *start_read_ptr;

	/* find the space available in the inbuf and copy the data into buffer */
	if(read > write)
	{
		space = read - write;
#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__) 
		bytes_copied = fread ((unsigned char*)write, 1, space, filein);
#else		
		bytes_copied = UserCallback_PullData((unsigned char*)write, space, &ClientData);
#endif
		bytes_req = space;
	}
	else
	{
		space = buf_end - write;
#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__)
		bytes_copied = fread ((unsigned char*)write, 1, space, filein);
#else		
		bytes_copied = UserCallback_PullData((unsigned char*)write, space, &ClientData);
#endif		
		bytes_req = space;

		space = read - base;
#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__)
		bytes_copied += fread ((unsigned char*)base, 1, space, filein);
#else		
		bytes_copied += UserCallback_PullData((unsigned char*)base, space, &ClientData);
#endif		
		bytes_req += space;
	}

	if(bytes_copied < bytes_req)
	{
		*end_of_stream_reached = 1;
	}
	
	bytes_avaialble += bytes_copied;
	*start_read_ptr = updated_read_ptr;
	return bytes_avaialble;
}

// Function that decodes the given file
int MP3_Decode_File(char *filename)
{

	int dec_return, ch, nDecodedSamples = 0;
	int id3_parser_ret,id3_available_bytes,id3_set_pos;
	short *pFifoBuffer;
    int nNumSamples, nNumChans;
    int i, j, printed = 0;
    bool audio_enabled = 0;
    int set_once = 1;
    unsigned int total_samples_per_chan;
    int end_of_stream_reached;
    int mp3_num_input_bytes;

#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__) /* BF548 EZKit */
MPPMESSAGE	MessageToApp;
extern OS_EVENT *mgr_application_Q;
#endif



   
    
	// cycle counting
	cycle_t start_count;
	cycle_t final_count;
	cycle_t min_frame_cycles;	// decode time
	cycle_t max_frame_cycles;
	cycle_t max_usb_cycles;
	int block_size_at_max_frame_cycle = 0;		

	// initialise statistics
	max_frame_cycles = 0;
	min_frame_cycles = (cycle_t)10*600*1000000;	
	max_usb_cycles = 0;

	// =========================================================
	//	init state & scratch pools
	// =========================================================
	mp3state.onchip_block0 = OnchipBlock0;
	mp3state.onchip_block1 = OnchipBlock1;
	mp3state.onchip        = Onchip;
	mp3state.offchip       = AnyWhere;

	mp3scratch.onchip_block0 = OnchipBlock0Scratch;
	mp3scratch.onchip_block1 = OnchipBlock1Scratch;
	mp3scratch.onchip        = OnchipScratch;
	mp3scratch.offchip       = AnyWhereScratch;
	
		/* id3 memory allocation */
    allocate_memory_for_id3_tags(&id3_instance.fields);	

	mp3_dec_handle = adi_mp3_dec_create(&mp3state, &mp3scratch);
	if((void *)0 == mp3_dec_handle)
	{
		printf("\n Unable to create the decoder........\n");
		return CODEC_MEMORY_ALLOCATION_ERROR;
	}

    for(i=0;i<ADI_MP3_DEC_MAX_NUM_TAGS;i++)
		pForSongInfo[i] = NULL;
	
	// Open the mp3 file
	filein = fopen(filename,"rb");
		
	if (filein == NULL)
	{
		printf("unable to read/create input/output file %s\n", filename);
		return CODEC_FILE_ERROR;
	}
	
    fseek(filein,0,SEEK_END);
    ClientData.nTotalBytes = ftell(filein);
    ClientData.nNumBytes = ClientData.nTotalBytes;
    fseek(filein,0,SEEK_SET);
    ClientData.pAddress = (unsigned char*)filein;

    ClientData.pBufBase = (unsigned char *)USB_Input_Buffer;
    ClientData.pBufRead = ClientData.pBufBase;
    ClientData.nBufSize = USB_INPUT_BUFFER_SIZE;
    ClientData.nBufBytes = 0;
	ClientData.nNumBytesUsed = 0;
	
   	ClientData.pFrameProp = &mp3_frame_properties;
	ClientData.pConfigIn  = &mp3_requested_config;
	
	//=======================================================
	// ID3 parser
	//=======================================================
	/* id3 config init */
	id3_config.input_buf_base = (char *)&mp3_input_buffer;
	id3_config.input_buf_length	 = ADI_MP3_DEC_MIN_BITSTREAM_BUFSIZE;
	id3_config.use_circular_buffer = 1;
	id3_config.curr_input_bitstream_ptr = id3_config.input_buf_base;
	id3_config.no_more_data_with_app = 0;

	adi_id3_parser_init(&id3_instance , &id3_config);

	/* id3 parser */
	id3_available_bytes = adi_mp3_fill_buf(&id3_config.curr_input_bitstream_ptr,
									id3_instance.inbuf_struct.read,
									id3_config.input_buf_base,
									id3_config.input_buf_length,
									id3_config.use_circular_buffer,
									&id3_config.no_more_data_with_app);

	/* Parse the bitstream for id3 tags */
	while(1)
	{
		parse_id3:

		id3_parser_ret = adi_id3_parser(&id3_instance,
									id3_available_bytes,
									id3_config.curr_input_bitstream_ptr,
									id3_config.no_more_data_with_app);

		if(id3_parser_ret == ADI_ID3_PARSER_TAG_CORRUPTED)
		{
			printf("\n Tags corrupted...id3 pasrer not complete..!\n");
			break;
		}

		if(id3_parser_ret == ADI_ID3_PARSER_STREAM_COMPLETE)
		{
			printf("\n Stream complete...id3 pasrer not complete..!\n");
			break;
		}


		if(id3_parser_ret == ADI_ID3_PARSER_SUCCESS)
		{
			break;
		}

		if(id3_parser_ret == ADI_ID3_PARSER_NEED_DATA)
		{
			id3_available_bytes = adi_mp3_fill_buf(&id3_config.curr_input_bitstream_ptr,
										id3_instance.inbuf_struct.read,
										id3_config.input_buf_base,
										id3_config.input_buf_length,
										id3_config.use_circular_buffer,
										&id3_config.no_more_data_with_app);
			goto parse_id3;
		}

		if(id3_parser_ret == ADI_ID3_PARSER_FAIL)
		{
			int total_bytes,bytes;

			total_bytes = ClientData.nTotalBytes;
			if(total_bytes < 128)
			{
				fseek(filein,0,SEEK_SET);
			}
			else
			{
				fseek(filein,-128,SEEK_END);
			}
			bytes = ftell(filein);
			ClientData.nNumBytes = total_bytes - bytes; // should be 128 for a valid file
			ClientData.nBufBytes = 0;

			//id3_instance.inbuf_struct.read += id3_config.input_buf_length -3;

			id3_config.curr_input_bitstream_ptr = id3_config.input_buf_base;
			id3_instance.inbuf_struct.read = id3_config.input_buf_base;

			id3_available_bytes = adi_mp3_fill_buf(&id3_config.curr_input_bitstream_ptr,
										id3_instance.inbuf_struct.read,
										id3_config.input_buf_base,
										id3_config.input_buf_length,
										id3_config.use_circular_buffer,
										&id3_config.no_more_data_with_app);

			goto parse_id3;
		}

		if(id3_parser_ret == ADI_ID3_PARSER_NO_TAG_FOUND)
		{
			printf("\n No tag found\n");
			break;
		}
	}// end of while
	
	if (id3_instance.ID3_ver)
	{
	    //printf("\n\tFound ID3 Tag Ver %1d \n",id3_instance.ID3_ver);
	    /* Display ID3 Tag info */
	    print_id3_info(&id3_instance);
	    
#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__)/* BF548 EZKit */

		/* get the tag info for lcd display */
		mp3tag->song_title = id3_instance.fields.songtitle.tag_data;
		mp3tag->song_artist = id3_instance.fields.artist.tag_data;
		mp3tag->song_album = id3_instance.fields.album.tag_data;
		mp3tag->song_track_time = id3_instance.fields.track_time.tag_data;
		mp3tag->file_type = MP3_TYPE;
 
	   /* ID3 Tag to be displayed at MOAB LCD */
	    MessageToApp.uMID = MSGID_LCD_TAG;
	    MessageToApp.Param1 = TAG_DISPLAY;
    	MessageToApp.Param2 = mp3tag;
	    OSQPost(mgr_application_Q, &MessageToApp);
#endif
	
	}

	
	//print_id3_info(&id3_instance);

	if(id3_instance.tag_size >0)
	{
		id3_set_pos = id3_instance.tag_size <= ClientData.nTotalBytes?
					id3_instance.tag_size:ClientData.nTotalBytes;
	}
	else
		id3_set_pos = 0;


	if((id3_instance.ID3_ver == 2) || (id3_instance.ID3_ver == 3)
		||(id3_instance.ID3_ver == 4))
	{
		fseek(filein, id3_set_pos , SEEK_SET);

		//fseek(filein, id3_instance.total_bytes_processed , SEEK_SET);
		ClientData.nNumBytes = ClientData.nTotalBytes - id3_set_pos;
		ClientData.nBufBytes = 0;
		ClientData.nNumBytesUsed = id3_set_pos;
	}
	else
	{
		fseek(filein, 0, SEEK_SET);
		ClientData.nNumBytes = ClientData.nTotalBytes - 0;
		ClientData.nBufBytes = 0;
	}
	//=======================================================
	// ID3 parser end
	//=======================================================
			

	// =========================================================
	//	init bitstream ptrs and status output
	// =========================================================
	mp3_num_input_bytes = 0;
	mp3_curr_input_bitstream_ptr = mp3_input_buffer;
	for(ch = 0; ch<ADI_MP3_DEC_MAX_NUM_CH; ch++)
		output_pcm_channels[ch] = &mp3_output_buffer[ch];


	// =========================================================
	//	init application config
	// =========================================================

	mp3_requested_config.noMoreDataWithApp   = 0;

	mp3_requested_config.input_buffer_length    = ADI_MP3_DEC_MIN_BITSTREAM_BUFSIZE;
	mp3_requested_config.input_buffer_base_addr = mp3_input_buffer;
	mp3_requested_config.frame_properties = &mp3_frame_properties;
	mp3_requested_config.max_bytes_to_search_for_sync = ADI_MP3_DEC_MAX_BYTES_TO_SEARCH_FOR_SYNC;


	mp3_dec_output_status.frame_properties_ptr = &mp3_frame_properties;

	// must be "1"
	mp3_requested_config.use_circular_buffer = 1;

	end_of_stream_reached = 0;

	dec_return = adi_mp3_dec_init(mp3_dec_handle,
								(const adi_mp3_dec_config_t*)&mp3_requested_config,
								&mp3_actual_decoder_config);

	if(ADI_MP3_DEC_INVALID_CONFIG_PARAMS == dec_return)
	{
		printf("\n Invalid config parameters........\n");
	}
	

	// Decoding loop
	while(1)
	{
		
		// Get an output PCM buffer
		do
		{
			pFifoBuffer = (short*)usr_getpcmbuffer(ADI_MP3_DEC_MAX_OUTBUFSIZE);
		} while(pFifoBuffer == NULL);
		
#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__)/* BF548 EZKit */
		adi_sem_Pend( PauseSemaphoreHandle, ADI_SEM_TIMEOUT_FOREVER );
	
        /* continue playing this file until the user presses Stop button or reach end of file */
        /* IF (STOP button pressed) */
        if(StopButton)
        {
           break;
        }
#endif

		
decode_again:
	    START_CYCLE_COUNT(start_count)
	   	// Decode the next frame
		dec_return = adi_mp3_decoder(mp3_dec_handle,
									mp3_num_input_bytes,
									mp3_curr_input_bitstream_ptr,
									&mp3_dec_output_status,
									output_pcm_channels);
		STOP_CYCLE_COUNT(final_count,start_count)
		
		mp3_num_input_bytes = 0;
			
		if(dec_return == ADI_MP3_DEC_NEED_MORE_DATA)
		{
			mp3_num_input_bytes = adi_mp3_fill_buf((char**)&mp3_curr_input_bitstream_ptr,
											(char*)mp3_dec_output_status.dec_read_ptr,
											(char*)mp3_requested_config.input_buffer_base_addr,
											mp3_requested_config.input_buffer_length,													
											mp3_requested_config.use_circular_buffer,
											&end_of_stream_reached);

			if(end_of_stream_reached == 1)
			{
				adi_mp3_dec_reconfigure(mp3_dec_handle,
										NOMORE_DATA_WITHAPP,
										1,
										&mp3_actual_decoder_config);
			}
			goto decode_again;
		}
		else if( dec_return == ADI_MP3_DEC_INVALID_FILE)
		{
			printf("\nInvalid MP3 file....\n");
			nNumChans = 0;
			break;
		}
		else if (dec_return == ADI_MP3_DEC_STREAM_COMPLETE)
		{
			printf("\nStream complete....!\n");
			break;
		}
		
		if ( (dec_return == ADI_MP3_DEC_SUCCESS) || (dec_return == ADI_MP3_DEC_TIMEOUT_REACHED) )
		{
            nNumSamples = mp3_dec_output_status.num_output_samples_per_channel;
			nNumChans = mp3_dec_output_status.frame_properties_ptr->num_output_channels;

			min_frame_cycles = min(min_frame_cycles, final_count);
			
			if (final_count > max_frame_cycles)
			{
				block_size_at_max_frame_cycle = nNumSamples*nNumChans;
				max_frame_cycles = final_count;
		}

			nDecodedSamples += nNumSamples;

			if (nNumChans == 0) break; // no more data for MP3
	
			if (nNumSamples)
			{
				// If only single channel, duplicate the second channel
				if (nNumChans == 1)
				{
					for(i=0;i<nNumSamples;i++)
					{
						pFifoBuffer[2*i] = output_pcm_channels[0][i];
						pFifoBuffer[2*i+1] = output_pcm_channels[0][i];
					}
					nNumChans *= 2;
				}
				else
				{
					memcpy(pFifoBuffer,output_pcm_channels[0],nNumChans*nNumSamples*2);
				}
			}

			// Set the number of samples written
			usr_setsamplecount(nNumSamples*nNumChans);		
								
			// Unmute the output if all the output buffers have been filled
			if ( (!audio_enabled) && (usr_get_freebuffercount() <= 1) )
			{
				audio_enabled = true;
				usr_mute_output(false, (u32)mp3_frame_properties.nSampleRate );
			}
			
		}
		else
		{
			printf("\n Unknown error type...should never happen\n\n");
		}
		// Print out the tag information
	    if (!printed)
	    {
	    	printf("\nSampling rate = %.1fkHz\n",(float)(mp3_frame_properties.nSampleRate/1000));
	    	printf("Bit rate = %dkbps\n",mp3_frame_properties.iBitRate);
	    	printf("Number of Channels = %d\n",mp3_frame_properties.num_output_channels);
			printf("Layer Number = %d\n",mp3_frame_properties.nLayerNum);

	    	printf("\n");
	    	printed = 1;
	    }


	}
	
	// inform that there's no more data to be written for output
	usr_nomore_data();	
	
	// wait until all buffer contents have been played out
	if (audio_enabled)
	{
	    usr_wait_buffers_emptied();
	}   
	
	
	usr_mute_output(true, (u32)mp3_frame_properties.nSampleRate );
	
	fclose(filein);
	
	fflush(stdout);
	
#ifdef EXTRA_DEBUG_INFO	
	printf("Max Cycles %llu msec\n", max_frame_cycles);
	printf("Min Cyles %llu msec\n", min_frame_cycles);
#endif		
	
	return CODEC_SUCCESS;
}
