/**
 * audio_decoder_mp3.c: The mp3 decoder
 * 
 * Copyright: See COPYING file that comes with this distribution
 *
 * Author: Matthias Feistel <hitman_47@users.sourceforge.net>, (C) 2007
 */

#include <unistd.h>
#include <string.h>
#include <malloc.h>
#include <vdr/skins.h>  // for OSD error messages
#include "audio_decoder_mp3.h"
#include "config.h"
#include "settings.h"
#include "audio_playlist.h"
#include <sstream>  // MyUserAgent()

cMp3Decoder::cMp3Decoder( void ) :
	leftBuffer(0), rightBuffer(0), samplesInBuffer(0)
{
	mad_stream_init( &madStream );
	mad_frame_init( &madFrame );
	mad_synth_init( &madSynth );
	timeIndex = mad_timer_zero;
	
	mo_PCMBuffer = new cSimpleRingBuffer( PCM_BUFFER_SIZE );
	
	mp_mp3dataavailable = false;
}

cMp3Decoder::~cMp3Decoder( void )
{
	mp_mp3dataavailable = false;
	
	mad_stream_finish( &madStream );
	mad_frame_finish( &madFrame );
	mad_synth_finish( &madSynth );
	delete[]leftBuffer;
	delete[]rightBuffer;
	
	if( mo_PCMBuffer )
		DELETENULL( mo_PCMBuffer );
}

/// Functions copied from the vdr-PODCATCHER plugin:
int cMp3Decoder::FillReadBuffer( void )
{
	int unprocessedBytesInBuffer = 0;
	int bytesToRead = 0;
	int bytesRead = 0;
	
	if( madStream.next_frame != NULL )
	{
		unprocessedBytesInBuffer = madStream.bufend - madStream.next_frame;
		memmove( readBuffer, madStream.next_frame, unprocessedBytesInBuffer );
	}
	else
	{
		unprocessedBytesInBuffer = 0;
	}
	
	bytesToRead = readBufferSize - unprocessedBytesInBuffer;
	
	
	// Read data pieces from source:
	bytesRead = GetDataFromSource( (readBuffer + unprocessedBytesInBuffer ),
			bytesToRead );
	
	return bytesRead + unprocessedBytesInBuffer;
}

/*
 the method is based on the code of the madplay project:
 madplay - MPEG audio decoder and player
 Copyright (C) 2000-2004 Robert Leslie
 clip() & audio_linear_round() in audio.c
 */
signed int cMp3Decoder::ScaleTo16Bit( mad_fixed_t sample )
{
	// round
	sample += (1L << (MAD_F_FRACBITS - 16));
	
	
	// clip
	if( sample >= MAD_F_ONE )
	{
		sample = MAD_F_ONE - 1;
	}
	else if( sample < -MAD_F_ONE )
	{
		sample = -MAD_F_ONE;
	}
	// quantize
	return sample >> (MAD_F_FRACBITS + 1- 16);
}

void cMp3Decoder::DecodeNextChunk( void )
{
	int decodeResult = 1;
	
	do
	{
		// readBuffer wird hier gefuellt.
		int bytesInBuffer = FillReadBuffer( );
		
		if( bytesInBuffer > 0 )
		{
			mad_stream_buffer( &madStream, readBuffer, bytesInBuffer );
			
			
			// decodes the next frame's header and subband samples
			decodeResult = mad_frame_decode( &madFrame, &madStream );
		}
		else
		{
			break;
		}
		
	} while( decodeResult && MAD_RECOVERABLE( madStream.error ) );
	
	if( decodeResult == 0 )
	{
		// synthesizes PCM samples from subband samples
		mad_synth_frame( &madSynth, &madFrame );
		
		sampleRate = madFrame.header.samplerate;
		mad_timer_add( &timeIndex, madFrame.header.duration );
		
		Resample( madSynth.pcm );
		
		mp_mp3dataavailable = true;
	}
	
	if( decodeResult == -1 )
	{
		// Following code suppresses stuttering like a "jumping cd" if no data is present:
		// synthesizes PCM samples from subband samples
		mad_synth_frame( &madSynth, &madFrame );
		
		sampleRate = madFrame.header.samplerate;
		mad_timer_add( &timeIndex, madFrame.header.duration );
		
		mad_frame_mute( &madFrame );
		
////		cCondWait::SleepMs( DEC_FAILURETIMEOUTINTERVAL );
		
		mp_mp3dataavailable = false;
	}
	
}

void cMp3Decoder::Resample( mad_pcm &pcm )
{
	mad_fixed_t *left = madSynth.pcm.samples[0];
	mad_fixed_t *right = madSynth.pcm.samples[1];
	unsigned int numberOfChannels = madSynth.pcm.channels;
	unsigned int nsamples = madSynth.pcm.length;
	
	int
			resampleRatio = resampler.Init( madSynth.pcm.samplerate,
					PCM_SAMPLERATE );
	int resampledBufferSize = resampleRatio * nsamples;
	
	delete[]leftBuffer;
	delete[]rightBuffer;
	
	leftBuffer = new mad_fixed_t[resampledBufferSize];
	leftBufferReadPtr = leftBuffer;
	
	samplesInBuffer = resampler.Resample( left, nsamples, leftBuffer );
	samplesInBuffer = nsamples;
	
	if( numberOfChannels == 2 )
	{
		rightBuffer = new mad_fixed_t[resampledBufferSize];
		rightBufferReadPtr = rightBuffer;
		resampler.Init( madSynth.pcm.samplerate, PCM_SAMPLERATE );
		resampler.Resample( right, nsamples, rightBuffer );
	}
	else
	{
		rightBuffer = 0;
		rightBufferReadPtr = leftBuffer;
	}
	
}
//:PODCATCHER


/*
 the code in the Action() method is based on the madlld.c source by:
 (c) 2001-2004 Bertrand Petit
 URL: http://www.bsd-dk.dk/%7Eelrond/audio/madlld
 */
unsigned long cMp3Decoder::GetMp3Data( uint8_t *data, const unsigned long count )
{
	unsigned char outputBuffer[OUTPUT_BUFFER_SIZE], *outputPtr = outputBuffer;
	unsigned long outputSize = 0;
	
	if( mo_PCMBuffer )
	{
		
		// Decode mp3data and put data on buffer, only when it is free: 
		if( mo_PCMBuffer->Free( ) > mo_PCMBuffer->Size( ) / 8 )
		{
			
			
			// Load next bytes from http-source and give it to mad:
			DecodeNextChunk( );
			
			
			// copied from vdr-daap:
			for( int i = 0; i < madSynth.pcm.length; i++ )
			{
				signed short sample = 0;
				
				
				// left channel
				sample = ScaleTo16Bit( madSynth.pcm.samples[0][i] );
				// store to buffer and convert to unsigned
				*(outputPtr++) = sample >> 8;
				*(outputPtr++) = sample & 0xFF;
				
				
				// right channel
				if( 2 == MAD_NCHANNELS( &madFrame.header ) )
					sample = ScaleTo16Bit( madSynth.pcm.samples[1][i] );
				
				
				// store to buffer and convert to unsigned
				*(outputPtr++) = sample >> 8;
				*(outputPtr++) = sample & 0xFF;
			}
			
			outputSize = outputPtr - outputBuffer;
			outputPtr = outputBuffer;
			
			
			// Now, we have PCM-data to output to the dvbdevice:

			if( outputSize )
			{
				mo_PCMBuffer->Put( outputPtr, outputSize );
			}
			
		}

		return mo_PCMBuffer->Get( data, count );

		
	}
	
	data = 0;
	return 0;
}

bool cMp3Decoder::IsMp3DataAvailable( void )
{
	return mp_mp3dataavailable;
}

int cMp3Decoder::GetIndex(int &Current, int &Total, bool SnapToIFrame)
{
	Current = 0;
	Total = 0;
	
	Current = timeIndex.seconds;
////	Total = cPlaylist< cMetaDataPlaylist_Container >::getSingleton().begin( )->duration;

	return 0;
}
