/******************************************************************************
Copyright (c) 2006 Analog Devices, Inc.  All Rights Reserved.  This software is
proprietary and confidential to Analog Devices, Inc. and its licensors.
*******************************************************************************

$Revision: 1.2 $
$Date: 2008/06/10 00:19:39 $
$LastChangedBy: dpowell $

Project:   	AAC decoder
Title:      AAC pplication
Author:     CFJ

Description:
            AAC decoder API

References:
            None

******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cycles.h>
#include <cplb.h>
#include <math.h> /* countones() */

#include "codec.h"
#include "riff_format.h"
#include "adac_buffer.h"  
//#include <audio_codec.h>

#include "adi_heaacv2_decoder.h"
#include "circular_buffer.h"
#include "adi_aaci_indexer.h"

#include "adi_mp4ad_parser.h"
#include "mp4ad_parsing_process.h"


#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__) /* BF548 EZKit */
#include <mediaplayer.h>
lcd_taginfo_t *aactag;
#endif
/******************************************************************************
**          #defines
******************************************************************************/
#define FILE_NAME_LEN 512
#define PARAM_LINE_BUFFER_LEN (2*FILE_NAME_LEN + 2)
#define NUM_OUTPUT_PCM_CHANNELS	(2)

#define ADI_AACD_INPUT_BUFFER_LENGTH 	5*ADI_AACD_MIN_BITSTREAM_BUFFER_NUMBYTES+4

#define ADI_MP4AD_METADATA_SONGTITLE_NUMBYTES	(256)
#define ADI_MP4AD_METADATA_ARTIST_NUMBYTES		(256)
#define ADI_MP4AD_METADATA_ALBUM_NUMBYTES		(256)
#define ADI_MP4AD_METADATA_YEAR_NUMBYTES		(8)
#define ADI_MP4AD_METADATA_GENRE_NUMBYTES		(256)
#define ADI_MP4AD_METADATA_COMMENTS_NUMBYTES	(256)

#define PARSER_INPUT_BUF_SIZE_BYTES             (5000)
#define PARSER_INDEXING_SIZE_BYTES              (1500000)

/******************************************************************************
**          Typedefs/Enumerations
******************************************************************************/
typedef unsigned char	Byte;

/******************************************************************************
**          Local variables
******************************************************************************/

section("aacd_data_in")
unsigned char input_buffer[ADI_AACD_INPUT_BUFFER_LENGTH]; //input buffer
section("aacd_data_out")
short  aacd_outsample[2*ADI_AACD_MAX_OUTPUT_PCM_SAMPLES_PER_CHAN];
section("adi_slow_noprio_rw") adi_aacd_audio_data_t enc_input_buffer[2*ADI_AACD_MAX_OUTPUT_PCM_SAMPLES_PER_CHAN];



// AAC-LC Decoder State Memory
static section ("state_fastb0_prio4")   Byte	STATE_on_chip_block0[   ADI_AACD_FASTB0_PRIO4_STATE_MEM_NUMBYTES ];
static section ("state_fastb1_prio6")   Byte	STATE_on_chip_block1[   ADI_AACD_FASTB1_PRIO6_STATE_MEM_NUMBYTES ];
static section ("state_fast_prio7")     Byte	STATE_on_chip[          ADI_AACD_FAST_PRIO7_STATE_MEM_NUMBYTES ];
static section ("scratch_fastb0_prio0") Byte	SCRATCH_on_chip_block0[ ADI_AACD_FASTB0_PRIO0_SCRATCH_MEM_NUMBYTES ];
static section ("scratch_fastb1_prio1") Byte	SCRATCH_on_chip_block1[ ADI_AACD_FASTB1_PRIO1_SCRATCH_MEM_NUMBYTES ];
static section ("scratch_fast_prio2")   Byte	SCRATCH_off_chip[       ADI_AACD_FAST_PRIO2_SCRATCH_MEM_NUMBYTES ];
static section ("scratch_fast_prio3")   Byte	SCRATCH_FAST_PRIO3[     ADI_AACD_FAST_PRIO3_SCRATCH_MEM_NUMBYTES ];

// memory & variables needed by the indexing module
static                                Byte	INDEXER_on_chip_block0[   ADI_AACI_FASTB0_PRIO0_STATE_MEM_NUMBYTES ];

// MP4 parser data
static                                Byte	PARSER_STATE_on_chip_block0[ ADI_MP4AD_FASTB0_PRIO0_STATE_MEM_NUMBYTES];
static section("adi_slow_noprio_rw")  adi_mp4ad_metadata_t	PARSER_STATE_text_fields;
static section("adi_slow_noprio_rw")  char	PARSER_METADATA_SONGTITLE[ ADI_MP4AD_METADATA_SONGTITLE_NUMBYTES + 1];
static section("adi_slow_noprio_rw")  char	PARSER_METADATA_ARTIST[ ADI_MP4AD_METADATA_ARTIST_NUMBYTES  + 1];
static section("adi_slow_noprio_rw")  char	PARSER_METADATA_ALBUM[ ADI_MP4AD_METADATA_ALBUM_NUMBYTES  + 1];
static section("adi_slow_noprio_rw")  char	PARSER_METADATA_YEAR[ ADI_MP4AD_METADATA_YEAR_NUMBYTES  + 1];
static section("adi_slow_noprio_rw")  char	PARSER_METADATA_GENRE[ ADI_MP4AD_METADATA_GENRE_NUMBYTES  + 1];
static section("adi_slow_noprio_rw")  char	PARSER_METADATA_COMMENTS[ ADI_MP4AD_METADATA_COMMENTS_NUMBYTES  + 1];

static section("adi_slow_noprio_rw")  Byte  parser_input_buffer[PARSER_INPUT_BUF_SIZE_BYTES];
static section("adi_slow_noprio_rw")  adi_mp4ad_container_info_t	PARSER_STATE_atoms;
static section("adi_slow_noprio_rw")  Byte	PARSER_STATE_atoms_indexing[PARSER_INDEXING_SIZE_BYTES];

adi_aacd_output_status_t dec_output_status;
adi_aacd_config_t requested_dec_config;
adi_aacd_frame_properties_t frame_properties = {};
adi_aacd_stream_properties_t stream_properties = {};
	

/******************************************************************************
**          Local function declaration
******************************************************************************/
void print_usage(void);
void setup_memory(adi_aacd_mem_t *memory_setup);
void describe_decoder_error(int decoderResult);
void display_decoder_config_params(adi_aacd_config_t *dec_config);






/*
**
** Function:            AAC_Decode
**
** Description:         Function that decodes the given AAC file
**
** Arguments:			filename
**
** Outputs:             none
**
** Return value:        AAC decoder status
**
*/
section("adi_slow_noprio_code")
int AAC_Decode(char *filename)
{
	
#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__) /* BF548 EZKit */
MPPMESSAGE	MessageToApp;
extern OS_EVENT *mgr_application_Q;

/* no tag display by default */		
MessageToApp.uMID = MSGID_LCD_TAG;
MessageToApp.Param1 = NO_TAG_DISPLAY;
aactag->file_type = AAC_TYPE;
#endif	

    int infile_len;
	FILE *filein;
	int ch,fb;
	//int *pFifoBuffer;
	
	short *pFifoBuffer;

    int dec_nNumSamples, dec_nNumChans;
    long long totalsamples;
    int i, j, printed = 0;
    bool audio_enabled = 0;
  
	adi_aacd_t *adi_aac_dec_instance;
	adi_aacd_bitstream_data_t *input_bitstream = NULL;
	adi_aacd_audio_data_t *dec_output_pcm_channels[NUM_OUTPUT_PCM_CHANNELS];
	adi_aacd_mem_t memory_setup;
    cbuffer_t circ_buffer;
//    wavefile_t riff_output;
//    generic_buffers heaac_buffers;
	int adi_aacd_initResult;
	int adi_aacd_decoderResult;

    	int sampleRate;
	
    int total_bytes_processed;
    int FrameNum;
    int last_timing_point; 
    int framenum_timing_pt;
	int all_frames_processed;
	int isAdtsFile, isAdifFile, isMp4File;
	int Eos=0; /* flag to indicate end of stream in the input file is reached */
	int num_bytes_added;

    // variables associated with the playback controller and ADTS indexer
	adi_aaci_t	*adi_aaci_instance = NULL;		// Indexer instance
	adi_aaci_mem_t adi_aaci_mem;
	adi_aaci_config_t adi_aaci_config;
	adi_aaci_return_code_t adi_aaci_result;
	int	seek_time_millisecs;		// Seek time in millisecs
	int	curr_time_millisecs;		// Current time in millisecs
	int curr_stream_offset;			// Current file offset in stream
	int ok_to_seek;                 // flag to indicate when ok to perform a seek for FF
//	FILE *playback_cmd_stream_ptr;
	//adi_playback_command_type_t curr_command=ADI_PLAYBACK_CMD_STOP;

    // variables associated with the MP4 parser
    adi_mp4ad_config_t requested_parser_config = {};
    adi_mp4ad_mem_t parser_memory;
    cbuffer_t parser_buffer;
    adi_mp4ad_instance_t* adi_mp4ad_parser_instance;
    adi_mp4ad_return_code_t parser_error = ADI_MP4AD_NEED_MORE_INPUT;
    adi_mp4ad_output_status_t parser_output_status;
    mp4_file_index_t current_file_access_unit_state;
    int parser_consumed_bytes;
    int result,offset;
    int next_input_bytecount;
    
    adi_mp4ad_metadata_t* metadata;
/////////////////////
	// Assign some default values
    total_bytes_processed = 0;
    all_frames_processed=0;

	FrameNum = 0;
    last_timing_point  = 0;
    framenum_timing_pt = 0; 
 	dec_nNumSamples = 0;
 	totalsamples = 0;
 	sampleRate = 0;

//	infile_len = 0;

	offset =0;

///////////////////////////
    // print out version details of the modules
    /*printf("Module information\n~~~~~~~~~~~~~~~~~~~~~~\n");
    printf("  %s\n",adi_aacd_module_info.version_text);
    printf("  %s\n",adi_mp4ad_module_info.version_text);
    printf("~~~~~~~~~~~~~~~~~~~~~~\n\n");*/

	// Open the AAC file
	filein = fopen(filename,"rb");

	if (filein == NULL)
	{
		printf("unable to read input file\n");
		return CODEC_FILE_ERROR;
	}


	initialise_cbuffer(&circ_buffer, input_buffer, ADI_AACD_INPUT_BUFFER_LENGTH);
	adi_aacd_get_default_config(&requested_dec_config);
    // initialise codec Configuration
    requested_dec_config.output_chan_config = ADI_AACD_2_0_CHAN_CONFIG;
	requested_dec_config.decoding_mode = ADI_AACD_DECODING_MODE_HEAACv2;

	requested_dec_config.use_circular_bitstream_buffer = 1;
	requested_dec_config.circular_buffer_base = (adi_aacd_bitstream_data_t*) (circ_buffer.base);
	requested_dec_config.circular_buffer_length = circ_buffer.length;

	//// Assign empty structures for information to be passed back from the decoder
	requested_dec_config.frame_properties_ptr = &frame_properties;
	requested_dec_config.stream_properties_ptr = &stream_properties;

    requested_dec_config.selected_program_id = -1;   // Select the first available program


/////////////////////
	// Instantiate codec
	// Create the instance
	setup_memory(&memory_setup);
    adi_aac_dec_instance = adi_aacd_create (&memory_setup);
	if (adi_aac_dec_instance == NULL)
	{
		printf("Cannot create the decoder instance\n");
		return(CODEC_ALGORITHM_ERROR);
	}

/////////////////////
    // Create the MP4 parser instance
    parser_memory.state.fastb0.prio0 = 	PARSER_STATE_on_chip_block0;
    if ((adi_mp4ad_parser_instance = adi_mp4ad_create (&parser_memory)) == NULL)
    {
        printf("Cannot create the MP4 parser instance.\n");
        return(CODEC_ALGORITHM_ERROR);
    }

	// Instantiate indexer and playback controller
	adi_aaci_mem.state.fastb0.prio0 = 	INDEXER_on_chip_block0;
	// Create the indexer instance
	adi_aaci_instance = adi_aaci_create (&adi_aaci_mem);
	if (adi_aaci_instance == NULL)
	{
		printf("Cannot create the indexer instance\n");
		return(CODEC_ALGORITHM_ERROR);
	}
#if 0
    // open the playback command file stream
    playback_cmd_stream_ptr = adi_playback_controller_open( "seek.txt" );
    // initialise the playback subsystem
    adi_playback_controller_init();
#endif
	// Output pointers
//    heaac_buffers.buffer = (void **)dec_output_pcm_channels;
//    heaac_buffers.buffer_count = 2;
//    heaac_buffers.buffer_size = ADI_AACD_MAX_OUTPUT_PCM_SAMPLES_PER_CHAN*2; // measured in bytes
//    heaac_buffers.element_step = 1;
    dec_output_pcm_channels[0] = (adi_aacd_audio_data_t *) &(aacd_outsample[0*ADI_AACD_MAX_OUTPUT_PCM_SAMPLES_PER_CHAN]);
	dec_output_pcm_channels[1] = (adi_aacd_audio_data_t *) &(aacd_outsample[1*ADI_AACD_MAX_OUTPUT_PCM_SAMPLES_PER_CHAN]);

   	// Fill buffer initially
	fill_cbuffer(&circ_buffer, filein);

/////////////////////
	// Probe input for container type
    // Check if the bitstream is ADIF/ADTS
	isAdtsFile = 0;
	isAdifFile = 0;
	isMp4File = 0;
	requested_dec_config.container_format = ADI_AACD_RAW;
	input_bitstream = (adi_aacd_bitstream_data_t *)CBUFFER_PTR(circ_buffer);
    adi_aacd_decoderResult = adi_aacd_input_is_adts(input_bitstream,circ_buffer.data_length);
    if (adi_aacd_decoderResult==ADI_AACD_SUCCESS)
    {
	    requested_dec_config.container_format = ADI_AACD_ADTS;
	    isAdtsFile = 1;

    /////////////////////
    	// Initialise the ADTS Indexer
		// The first index point is time zero at end of the header
		curr_time_millisecs = 0;
		curr_stream_offset = 0;
		adi_aaci_config.circular_buffer_base = (adi_aaci_bitstream_data_t*) (circ_buffer.base);
		adi_aaci_config.circular_buffer_length = ADI_AACD_INPUT_BUFFER_LENGTH;
		adi_aaci_config.use_circular_bitstream_buffer = requested_dec_config.use_circular_bitstream_buffer;
		adi_aaci_result =  adi_aaci_init (adi_aaci_instance, &adi_aaci_config);
		if (ADI_AACI_SUCCESS != adi_aaci_result)
		{
			printf ("Cannot initialise indexer\n");
			return(CODEC_ALGORITHM_ERROR);
		}
    }
    else if (adi_aacd_decoderResult==ADI_AACD_INVALID_HEADER)
    {
        adi_aacd_decoderResult = adi_aacd_input_is_adif(input_bitstream,circ_buffer.data_length);
        if (adi_aacd_decoderResult==ADI_AACD_SUCCESS)
        {
    	    requested_dec_config.container_format = ADI_AACD_ADIF;
    	    isAdifFile = 1;
        }
        else if (adi_aacd_decoderResult==ADI_AACD_NEED_MORE_INPUT)
        {
            printf ("There was not enough data in the input buffer to check if the format is ADIF\n");
    		return(CODEC_ALGORITHM_ERROR);
        }
    }
    else if (adi_aacd_decoderResult==ADI_AACD_NEED_MORE_INPUT)
    {
        printf ("There was not enough data in the input buffer to check if the format is ADTS\n");
		return(CODEC_ALGORITHM_ERROR);
	}

    /////////////////////
    // Perform MP4 parsing if the bitstream is in neither ADTS nor ADIF format
    if (requested_dec_config.container_format == ADI_AACD_RAW)
    {
        // Initialize parser input buffer
        initialise_cbuffer(&parser_buffer, parser_input_buffer, PARSER_INPUT_BUF_SIZE_BYTES);

		// Stream initialisation for parser instance
		requested_parser_config.circular_buffer_base = parser_input_buffer;
		requested_parser_config.circular_buffer_length = PARSER_INPUT_BUF_SIZE_BYTES;
		requested_parser_config.use_circular_bitstream_buffer = requested_dec_config.use_circular_bitstream_buffer;

		// Set memory pointers for parsed metadata
		PARSER_STATE_text_fields.song_title.tag_data = PARSER_METADATA_SONGTITLE;
		PARSER_STATE_text_fields.artist.tag_data = PARSER_METADATA_ARTIST;
		PARSER_STATE_text_fields.album.tag_data = PARSER_METADATA_ALBUM;
		PARSER_STATE_text_fields.year.tag_data = PARSER_METADATA_YEAR;
		PARSER_STATE_text_fields.genre.tag_data = PARSER_METADATA_GENRE;
		PARSER_STATE_text_fields.comments.tag_data = PARSER_METADATA_COMMENTS;
		// Set maximum string lengths for parsed metadata
		PARSER_STATE_text_fields.song_title.max_num_bytes = ADI_MP4AD_METADATA_SONGTITLE_NUMBYTES;
		PARSER_STATE_text_fields.artist.max_num_bytes = ADI_MP4AD_METADATA_ARTIST_NUMBYTES;
		PARSER_STATE_text_fields.album.max_num_bytes = ADI_MP4AD_METADATA_ALBUM_NUMBYTES;
		PARSER_STATE_text_fields.year.max_num_bytes = ADI_MP4AD_METADATA_YEAR_NUMBYTES;
		PARSER_STATE_text_fields.genre.max_num_bytes = ADI_MP4AD_METADATA_GENRE_NUMBYTES;
		PARSER_STATE_text_fields.comments.max_num_bytes = ADI_MP4AD_METADATA_COMMENTS_NUMBYTES;

		requested_parser_config.text_fields_ptr = &PARSER_STATE_text_fields;
		requested_parser_config.container_info_ptr = &PARSER_STATE_atoms;
		requested_parser_config.index_mem_ptr = PARSER_STATE_atoms_indexing;
		requested_parser_config.index_mem_num_bytes = PARSER_INDEXING_SIZE_BYTES;

        parser_error = adi_mp4ad_init (
    							adi_mp4ad_parser_instance,
    							&requested_parser_config);
    
        switch (parser_error)
    	{
    		case ADI_MP4AD_SUCCESS:
    		{
    		    // successful initialisation of parser
    			break;
    		}
    		case ADI_MP4AD_INVALID_CONFIG_PARAMS:
    		{
    			printf("Parser config params were changed - continuing on\n");
    			break;
    		}
    		case ADI_MP4AD_TERMINAL_FAILURE:
    		{
    			printf("Cannot initialise the parser with invalid configuration parameters - will not decode this file\n");
    			return(CODEC_ALGORITHM_ERROR);
    		}
    	}

		// Check the length of infile
		fseek(filein,0,SEEK_END);
		infile_len = ftell(filein);

	    // parse input file from beginning to end
    	fseek(filein, 0, SEEK_SET);
        // fill parser buffer before parsing
      	fb = fill_cbuffer(&parser_buffer, filein);
      	
      	//printf("fill pb %d\n",fb);

        next_input_bytecount=parser_buffer.data_length;
            
        while ((parser_buffer.data_length) || (ftell(filein) < infile_len)) // while end of file not reached
        {
            result = perform_mp4ad_parsing_loop(adi_mp4ad_parser_instance,
                                                    &parser_buffer,
                                                    &parser_output_status,
                                                    NULL);
			//printf("result=%d\n",result);                                                  
            if (result < 0)
            {
                printf("MP4 container parsing error (%d returned by perform_mp4ad_parsing_loop).\n", result);
                return(CODEC_ALGORITHM_ERROR);
    	    }
    	    else if (result > 0)
            // skip_large_atom
            {
            	offset += result;
                // Move the file read pointer to the start of the next atom
                // number of bytes to skip in the file is returned by perform_mp4ad_parsing_loop
                if (fseek (filein, result, SEEK_CUR) !=0)
                printf("fseek error !\n");
                
                //fseek(filein, offset, SEEK_SET);

                next_input_bytecount += result; // move count forward by the expected number of bytes
            
                // Flush the input buffer
                flush_cbuffer(&parser_buffer);
            }

            // fill parser buffer with more data
        	num_bytes_added = fill_cbuffer(&parser_buffer, filein);
        	//printf("num_bytes_added %d\n",num_bytes_added);

            next_input_bytecount += num_bytes_added;
            
            // break out of loop, if there are some bytes left that cannot be 
            // processed but could not get anymore from the file
            if ((result == 0) && (num_bytes_added == 0) && (parser_buffer.data_length))
            {
                printf("ERROR: Could not process remaining %d bytes in the file -> treat it as corrupted\n",parser_buffer.data_length);
                return(CODEC_ALGORITHM_ERROR);
            }
        }

        // Raise warning if the length of the last container box appears to be
        // too big as signalled in the file, which could indicate a corrupted
        // file with missing data
        if (next_input_bytecount > infile_len)
        {
            printf("ERROR: The file appears to be missing data -> treat it as corrupted\n");
            return(CODEC_ALGORITHM_ERROR);
        }

		if (copy_mp4ad_config(&requested_dec_config, &requested_parser_config, stdout) == 0)
		{
			isMp4File = 1;

            // setup mp4 parser file accessing helper module
            init_mp4ad_file_access(&current_file_access_unit_state, adi_mp4ad_parser_instance, filein);

            // Flush the Decoder input buffer
            flush_cbuffer(&circ_buffer);
            input_bitstream = NULL;
		}
		else
		{
		    printf("The input file does not contain MPEG-4 AAC-LC or HE-AAC audio\n");
		}
		
		//metadata = &requested_parser_config->text_fields_ptr;
 
#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__)/* BF548 EZKit */
				
		/* get the tag info for lcd display */
		aactag->song_title = PARSER_STATE_text_fields.song_title.tag_data;
		aactag->song_artist = PARSER_STATE_text_fields.artist.tag_data;
		aactag->song_album = PARSER_STATE_text_fields.album.tag_data;
		aactag->song_track_time = NULL;
		MessageToApp.Param1 = TAG_DISPLAY;
#endif	    
		
    }

    //if (requested_dec_config.container_format == ADI_AACD_RAW)
    if (!(isAdifFile || isAdtsFile || isMp4File))
    {
		printf("This application only accepts AAC bitstreams encapsulated in ADIF, ADTS or MP4 containers\n");
		return(CODEC_ALGORITHM_ERROR);
    }

    curr_time_millisecs = 0;
    curr_stream_offset = 0;
    Eos = 0;
    
    
    
    
#if defined(__ADSP_MOAB__) || defined(__ADSP_KOOKABURRA__)/* BF548 EZKit */
	   /* Tag to be displayed at MOAB LCD if available */
    	MessageToApp.Param2 = aactag;
	    OSQPost(mgr_application_Q, &MessageToApp);
#endif

/////////////////////
    // initialise codec

    // always setup for 2 channel output (over-ride settings copied from MP4 parser in copy_mp4ad_config)
    // since MP4 files with v2 bitstream may only indicate a single channel
    requested_dec_config.output_chan_config = ADI_AACD_2_0_CHAN_CONFIG;

	//display_decoder_config_params(&requested_dec_config);
	adi_aacd_initResult= adi_aacd_init (adi_aac_dec_instance,&requested_dec_config);
    adi_aacd_decoderResult=adi_aacd_initResult;

	switch (adi_aacd_initResult)
	{
		case ADI_AACD_SUCCESS:
		{
			break;
		}
		case ADI_AACD_INVALID_CONFIG_PARAMS:
		{
			printf("There were some invalid configuration parameters.\n");
			printf("The following are the actual config params used by the decoder.\n");
			display_decoder_config_params(&requested_dec_config);
			break;
		}
		default:
			printf("Unprecedented error code (%d) returned by adi_aacd_init.\n",adi_aacd_initResult);
			return(CODEC_ALGORITHM_ERROR);

	}

/////////////////////
	// Main loop
	while(!all_frames_processed)
	{

	/////////////////////
	    // Refill input buffer (Push model => refill every time through loop)
	    if (circ_buffer.data_length < circ_buffer.length) 
	    {
			// Use adi_mp4ad_get_next_audio_data_unit() for MP4 streams
			if (isMp4File)
			{
       		    current_file_access_unit_state.end_of_stream_encountered_flag = Eos;
                current_file_access_unit_state.curr_stream_offset = curr_stream_offset;

                // record num input bytes before adding from mp4 file 
                num_bytes_added = circ_buffer.data_length;
    
	    	    int num_bytes_to_add = circ_buffer.length - circ_buffer.data_length;
                int result = demultiplex_audio_data_from_mp4_file(
                                &current_file_access_unit_state,      
                                &circ_buffer,
                                (adi_aacd_decoderResult == ADI_AACD_NEED_MORE_INPUT),
                                num_bytes_to_add,
                                NULL);

                // compute num bytes added to input buffer (new data len - old data len)
                num_bytes_added = circ_buffer.data_length - num_bytes_added;
                
                curr_stream_offset = current_file_access_unit_state.curr_stream_offset;
                Eos = current_file_access_unit_state.end_of_stream_encountered_flag;
    
                if (result < 0)
                {
                    printf("Fatal error (%d) encountered when calling demultiplex_audio_data_from_mp4_file\n",result);
                    return(CODEC_ALGORITHM_ERROR);
                }
                else if (result > 0)
                {
                    printf("Calling resynch because some data had to be flushed from the decoder input buffer...\n");
        			adi_aacd_resynch(adi_aac_dec_instance);
        			num_bytes_added = circ_buffer.data_length;
                }
                
    		}

    	    else // Fill buffer for non MP4 streams
    	    {
        		num_bytes_added = fill_cbuffer(&circ_buffer,filein);
        		if ( (num_bytes_added == 0) && (adi_aacd_decoderResult==ADI_AACD_NEED_MORE_INPUT) )
        		{
        		    // could not fill up circular buffer, assume End of Stream
        		    Eos = 1;
        		}
        	}
        }
        
        if (Eos == 1) 
        {
            // stop if decoder cannot process the input data but there is no more data
            all_frames_processed = 1;
	        printf( "\nAll frames have been decoded\n");
		}

	/////////////////////
		// Decode this frame
    	input_bitstream = (adi_aacd_bitstream_data_t *)CBUFFER_PTR(circ_buffer);
		adi_aacd_decoderResult = adi_aacd_decoder (
            adi_aac_dec_instance,
			circ_buffer.data_length,
			input_bitstream,
			&dec_output_status,
			dec_output_pcm_channels);
			
        total_bytes_processed += dec_output_status.num_input_bytes_consumed;
		advance_cbuffer(&circ_buffer, dec_output_status.num_input_bytes_consumed);

		// update the stream offset for the next frame for indexing
		curr_stream_offset += dec_output_status.num_input_bytes_consumed;
		


    /////////////////////
        /* parse return error codes */
		switch (adi_aacd_decoderResult)
		{
			case ADI_AACD_NEED_MORE_INPUT:
			{
                /* check how many bytes are left in the input buffer after calling the decoder */
        		if (circ_buffer.data_length == ADI_AACD_INPUT_BUFFER_LENGTH)
        		{
        		    printf("\nThe input buffer is not large enough for this bitstream.\n");
        		    all_frames_processed = 1; // can't continue - get out of the while loop
        		}
				break;
			}
        	case ADI_AACD_TIMEOUT_REACHED:
			{
				break;
			}
        	case ADI_AACD_SUCCESS:
			{
				break;
			}
        	case ADI_AACD_OPERATION_FAILED:
    		{
    			printf("\nGeneral error - application needs to resynch\n");
				/* flush input buffer and try to resynch the decoder */
        		flush_cbuffer(&circ_buffer);
        		adi_aacd_resynch(adi_aac_dec_instance);
        		if (isMp4File)
        		{
            		init_mp4ad_file_access(&current_file_access_unit_state, adi_mp4ad_parser_instance, filein);
            	}
				/* FIXME - add proper resynch code for ADTS/ADIF - resynch input pointer to next frame */
    			break;
    		}
			default:
			{
			    // all fatal errors ...
				describe_decoder_error(adi_aacd_decoderResult);
       		    all_frames_processed = 1; // can't continue - get out of the while loop
				break;
			}
		}

		if (dec_output_status.num_output_samples_per_chan == 0)
		    continue; /* no output - loop again for next frame */

	///////////////////
		/* Write HE-AAC Decoder output */
		dec_nNumSamples = dec_output_status.num_output_samples_per_chan;
		dec_nNumChans = countones(dec_output_status.active_output_chans);
		
		sampleRate = frame_properties.sample_rate;

		do
		{
				//pFifoBuffer = (int*)usr_getpcmbuffer(dec_nNumSamples<<3);
				pFifoBuffer = (short*)usr_getpcmbuffer(ADI_AACD_MAX_OUTPUT_PCM_SAMPLES_PER_CHAN);

		} 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
	
		if (dec_nNumSamples)
		{

				// Duplicate the left channel if we're playing back in mono
				const int secondChannel = (dec_nNumChans - 1);
			
	
				for(i=0; i<dec_nNumSamples; i++)
				{
					pFifoBuffer[2*i] 	= dec_output_pcm_channels[0][i];
					pFifoBuffer[2*i+1]	= dec_output_pcm_channels[secondChannel][i];
				}
				usr_setsamplecount( dec_nNumSamples<<1 );		
		
				// 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,sampleRate);
				}				

				
		}



    /////////////////////
    	// Indexing

        // for the MP4 file formats, we need to keep track of how many frames are
        // left to decode in the current chunk (see demultiplex_audio_data_from_mp4_file)
		current_file_access_unit_state.num_frames_to_read--;

    	// Update the index timing point for the next frame to be decoded
    	curr_time_millisecs = last_timing_point 
    	    + (int)( (float)(FrameNum-framenum_timing_pt+1) * 
    	        ( (1000.0 * (float)dec_output_status.num_output_samples_per_chan) / (float)frame_properties.sample_rate )
    	    );


	/////////////////////
	// provide progress messages to user (print curr timestamp)
		totalsamples += dec_nNumSamples;
		FrameNum++;
		

		if(!printed)
		{
		    //printf( "    num_channels1 = %d\n"            ,requested_dec_config.container_info.num_channels);
		    //printf( "    sampling_frequency1 = %d\n"      ,requested_dec_config.container_info.sampling_frequency);
		    printf( "    num_channels = %d\n"            ,frame_properties.num_channels);
		    printf( "    sampling_frequency = %d\n"      ,frame_properties.sample_rate);  
		    printed = 1; 
    	}			
		
	}

	// inform that there's no more data to be written for output
	usr_nomore_data();

	// Check if the audio is enabled. If so, wait for all the output buffers to empty
	if (audio_enabled)
	{
    	usr_wait_buffers_emptied();
	}

	usr_mute_output(true,sampleRate);

	fclose(filein);
//	fclose(playback_cmd_stream_ptr);
	fflush(stdout);	

    return CODEC_SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////
// utility function to encapsulate the allocation of memory structures
// required by the ADI AAC Decoder
void setup_memory(adi_aacd_mem_t *memory_setup)
{
    // Memory sections
    // State memory
    memory_setup->state.fastb0.prio4 = STATE_on_chip_block0;
    memory_setup->state.fastb1.prio6 = STATE_on_chip_block1;
    memory_setup->state.fast.prio7   = STATE_on_chip;
    // Scratch memory
    memory_setup->scratch.fastb0.prio0 = SCRATCH_on_chip_block0;
    memory_setup->scratch.fastb1.prio1 = SCRATCH_on_chip_block1;
    memory_setup->scratch.fast.prio2   = SCRATCH_off_chip;
    memory_setup->scratch.fast.prio3   = SCRATCH_FAST_PRIO3;

}
/*
**
** Function:            describe_decoder_error
**
** Description:         utility function to encapsulate the reporting of errors to stdout
**
** Arguments:			decoderResult
**
** Outputs:             none
**
** Return value:        none
**
*/
void describe_decoder_error(int decoderResult)
{
    switch(decoderResult)
    {
		case ADI_AACD_INVALID_BITSTREAM:
		{
			printf("\nUnsupported bitstream encountered - decoding stopped.\n");
			break;
		}
		case ADI_AACD_INVALID_HEADER:
		{
			printf("\nFound corrupted/inconsistent bitstream headers - decoding stopped.\n");
			break;
		}
		case ADI_AACD_TERMINAL_FAILURE:
		{
			printf("\nGeneral critical error - decoding stopped.\n");
			break;
		}
		default:
		{
			printf("\nPossible unknown Error (%d) encountered\n",decoderResult);
			break;
		}

	}
	return;
}

/*
**
** Function:            display_decoder_config_params
**
** Description:         function to display the contents of the decoder config object
**
** Arguments:			decoder configuration object
**
** Outputs:             none
**
** Return value:        none
**
*/
void display_decoder_config_params(adi_aacd_config_t *dec_config)
{
    int i;

	printf("The following configuration parameters are being used to initialise the decoder.\n");

    printf("    decoding_mode                 = %d (%s)\n",dec_config->decoding_mode,
        ( (dec_config->decoding_mode==ADI_AACD_DECODING_MODE_AACLC) ? "AACLC" :
          (dec_config->decoding_mode==ADI_AACD_DECODING_MODE_HEAACv1) ? "HEAAC v1" :
          (dec_config->decoding_mode==ADI_AACD_DECODING_MODE_HEAACv2) ? "HEAAC v2" :
          "UNKOWN" ) );

    printf("    use_circular_bitstream_buffer = %d\n",dec_config->use_circular_bitstream_buffer);
    printf("    circular_buffer_base          = %#08x\n",dec_config->circular_buffer_base);
    printf("    circular_buffer_length        = %d\n",dec_config->circular_buffer_length);
    printf("    frame_properties_ptr          = %#08x\n",dec_config->frame_properties_ptr);
    printf("    stream_properties_ptr         = %#08x\n",dec_config->stream_properties_ptr);
    printf("    output_chan_config            = %d\n",dec_config->output_chan_config);
    for (i=0;i<ADI_AACD_MAX_NUM_OUTPUT_CHANS;i++)
    {
        printf("    output_chan_stride[%d]         = %d\n",i,dec_config->output_chan_stride[i]);
    }

    printf("    container_format              = %d\n",dec_config->container_format);
    if (dec_config->container_format == ADI_AACD_RAW)
    {
        printf( "\n    --------------\n");
        printf( "    CONTAINER INFO\n    --------------\n");
        printf( "    container_format = %d\n"        ,dec_config->container_format );
        printf( "    selected_program_id = %d\n"     ,dec_config->selected_program_id );
        printf( "    channel_configuration = %d\n"   ,dec_config->container_info.channel_configuration );
        printf( "    num_channels = %d\n"            ,dec_config->container_info.num_channels);
        printf( "    sampling_frequency_index = %d\n",dec_config->container_info.sampling_frequency_index);
        printf( "    sampling_frequency = %d\n"      ,dec_config->container_info.sampling_frequency);
        printf( "    sbr_sampling_frequency = %d\n"  ,dec_config->container_info.sbr_sampling_frequency);
        printf( "    frame_length_flag = %d\n"       ,dec_config->container_info.frame_length_flag);
        printf( "    num_front_channel_elements = %d\n"  ,dec_config->container_info.num_front_channel_elements);
        printf( "    num_side_channel_elements = %d\n"   ,dec_config->container_info.num_side_channel_elements);
        printf( "    num_back_channel_elements = %d\n"   ,dec_config->container_info.num_back_channel_elements);
        printf( "    num_coupled_channel_elements = %d\n",dec_config->container_info.num_coupled_channel_elements);
        printf( "    num_lfe_channel_elements = %d\n"    ,dec_config->container_info.num_lfe_channel_elements);
        printf( "    --------------\n");

    }
    printf("\n");
}

