/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  The functions in this module implements the specific behavior
*                for the advanced audio converter class for configuration of
*				 hardware accelerated codec.
*
*/


// INCLUDE FILES
#include "MP3AudioPlayControllerDecoder.h"
#include "DebugMacros.h"
#include <mmfpaniccodes.h>
#include <Mmfcodec.h>

const TUint KRawMp3MaxFrameSize  = 1441;
const TUint KSizeOfInBuffer   = 2*KRawMp3MaxFrameSize;
const TInt 	KRawMp3FrameHeaderSize = 5;

class TAudioFrameInfo
    {
	public:
		TInt iMode;            // encoding mode
		TInt iBitRate;         // bitrate (bit/s)
		TInt iSamplingRate;    // sampling frequency (Hz)
		TInt iChannels;        // number of channels
		TInt iFrameSize;       // encoded size (bytes)
		TInt iFrameSamples;    // samples per frame
		TInt iSamplingRateOut; // sampling frequency after conversion (Hz)
		TInt iChannelsOut;     // number of audio channels after conversion (1 or 2)
		TInt iFrameSamplesOut; // decoded size after conversion (samples per frame)
		TInt iPadding;         // padding flag (TRUE or FALSE, TRUE if p slot exists)
		TInt iId;              // id of algorithm (1 MPEG-1, 0 MPEG-2)
    };
    
// Bit rates in bits/sec supported by MPEG2, MPEG1 and MPEG2.5 respectively
const TInt16 cBitRateTable[3][16] =
	{
		{-1,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0},
		{-1,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0},
		{-1,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0},
	};

// Sampling frequencies supported by MPEG2, MPEG1 and MPEG2.5 respectively
const TUint16 cSamplingFrequencyTable[3][4] =
	{
		{22050,24000,16000,0},
		{44100,48000,32000,0},
		{11025,12000,8000,0},
	};

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CMP3AudioPlayControllerDecoder::CMP3AudioPlayControllerDecoder()
    :   CAdvancedAudioDecoder(CActive::EPriorityStandard)
    {
    DP0(_L("CMP3AudioPlayControllerDecoder::CMP3AudioPlayControllerDecoder - Hardware Accelerated"));
    iMaxFrameSize = KRawMp3MaxFrameSize;
   	iSizeOfInBuffer = KSizeOfInBuffer;
    }

// -----------------------------------------------------------------------------
// CMP3AudioPlayControllerDecoder::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CMP3AudioPlayControllerDecoder::ConstructL()
    {
    DP1(_L("CMP3AudioPlayControllerDecoder::ConstructL"), this);

    iFrameTable = CFrameTable::NewL();
    
	RenderEnable();
	UnMarkPlayEnd();
	Enable();
    
    CActiveScheduler::Add(this);
    }

// -----------------------------------------------------------------------------
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CMP3AudioPlayControllerDecoder* CMP3AudioPlayControllerDecoder::NewL()
    {
    DP0(_L("CMP3AudioPlayControllerDecoder::NewL"));

    CMP3AudioPlayControllerDecoder* self = new(ELeave) CMP3AudioPlayControllerDecoder();
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

// Destructor
CMP3AudioPlayControllerDecoder::~CMP3AudioPlayControllerDecoder()
	{
    DP0(_L("CMP3AudioPlayControllerDecoder::~CMP3AudioPlayControllerDecoder"));
    delete iFrameTable;
	}

TInt CMP3AudioPlayControllerDecoder::CodecConfig(RArray<TInt>& aConfigData)
	{
	TInt status = KErrNone;
	TInt sampFreq = ((RArray<TInt>&) aConfigData)[5];
	TInt samplesPerFrame = ((RArray<TInt>&) aConfigData)[8];
	iFrameTable->InitFrameTable(sampFreq, samplesPerFrame);
	return status;
	}

// -----------------------------------------------------------------------------
// CMP3AudioPlayControllerDecoder::ResetL
// Resets configuration data and configures the codec.
// -----------------------------------------------------------------------------
//
void CMP3AudioPlayControllerDecoder::ResetL()
	{
    DP0(_L ("CMP3AudioPlayControllerDecoder::Reset - Enter"));

	delete[] iOutBuffer;
   	delete[] iInBuffer;
   	
   	iInBuffer = NULL;
   	iOutBuffer = NULL;
   	iInBufferCount = 0;
   	iOutBufferCount = 0;
   	iOutBufferPtr = NULL;
   	iOutFrameLength = KRawMp3MaxFrameSize; // since data is not decoded
   	iInBuffer = new (ELeave) TUint8[KSizeOfInBuffer];

    iOutBuffer = new (ELeave) TUint8[iOutFrameLength];
    iOutBufferPtr = iOutBuffer;

    iAccLen = 0;

    DP0(_L ("CMP3AudioPlayControllerDecoder::Reset - Exit"));
	}

TCodecProcessResult CMP3AudioPlayControllerDecoder::ProcessL(CMMFBuffer& aSrc, CMMFBuffer& aDst)
	{
    DP0(_L ("CMP3AudioPlayControllerDecoder::ProcessL"));
	return CAdvancedAudioDecoder::ProcessHwL(aSrc, aDst);
	}

TInt CMP3AudioPlayControllerDecoder::FrameLength(const TUint8* aBuf, TInt aBufLen, TInt& aFrameLength)
	{
	TInt stat = KErrNone;
	TAudioFrameInfo info;
	TInt len = FrameInfo(aBuf, aBufLen, info);
	if (len > 0)
		{
		aFrameLength = len;
		}
	else
		{
		stat = KErrUnknown;
		}
	return stat;
	}
	
// -----------------------------------------------------------------------------
// CMP3AudioPlayControllerDecoder::FrameInfo
// -----------------------------------------------------------------------------
//
TInt CMP3AudioPlayControllerDecoder::FrameInfo(
	const TUint8* aBuf,
	TInt aBufLen,
	TAudioFrameInfo& aInfo)
    {
    TInt length = 0;
    TUint temp;
    TUint lTempVal;

	if (aBufLen >= KRawMp3FrameHeaderSize)
	    {
		// Extract header fields to aInfo and check their bit syntax
		// (including the sync word!). If the syntax is not OK the length
		// is set to zero.

		temp = 0;
		temp = aBuf[0] << 24;
		temp |= (aBuf[1] << 16);
		temp |= (aBuf[2] << 8);
		temp |= aBuf[3];
		if (((temp >> 21) & 0x7FF) != 0x7FF)
			{
			return length;
			}

		lTempVal = (temp >> 19) & 0x03;
		switch (lTempVal)
			{
			case 0:
				aInfo.iId = 2;  // MPEG2.5
				aInfo.iFrameSamples = 576;
				break;
			case 1:
				return length;
			case 2:
				aInfo.iId = 0;  // MPEG 2
				aInfo.iFrameSamples = 576;
				break;
			case 3:
				aInfo.iId = 1;  // MPEG 1
				aInfo.iFrameSamples = 1152;
				break;
			}

		lTempVal = (temp >> 17) & 0x03;
		if (lTempVal != 1)
			{
			return length;
			}

		lTempVal = (temp >> 12) & 0x0F;
		aInfo.iBitRate = cBitRateTable[aInfo.iId][lTempVal]*1000;

		if (aInfo.iBitRate == 0)
			{
			return length;
			}

		lTempVal = (temp >> 10) & 0x03;
		if (lTempVal == 3)
			{
			return length;
			}
		else
			{
			aInfo.iSamplingRate = cSamplingFrequencyTable[aInfo.iId][lTempVal];
			}

		aInfo.iPadding = (temp >> 9) & 0x01;

		lTempVal = (temp >> 6) & 0x03;
		aInfo.iMode = lTempVal;

		if (lTempVal == 3)
			{
			aInfo.iChannels = 1;
			}
		else
            {
            aInfo.iChannels = 2;
            }

        aInfo.iSamplingRateOut = aInfo.iSamplingRate;
        aInfo.iChannelsOut = 2; /* always set to stereo output */
        aInfo.iFrameSamplesOut = aInfo.iFrameSamples;

		if (aInfo.iBitRate == -1)
			{
			// For free mode operation
			length = KRawMp3MaxFrameSize;
			}

		if (aInfo.iSamplingRate > 0  &&  aInfo.iBitRate > 0)
			{
			length = (144*aInfo.iBitRate)/aInfo.iSamplingRate;

			if (aInfo.iId != 1)
				{
				length >>= 1; /*for MPEG2 and MPEG2.5 */
				}

			if (aInfo.iPadding)
				{
				length++;
				}
			}

		aInfo.iFrameSize = length;
//        iChannelsOut = aInfo.iChannelsOut;
    	}
    return length;
    }

// -----------------------------------------------------------------------------
// CMP3AudioPlayControllerDecoder::SeekSync
// -----------------------------------------------------------------------------
TInt CMP3AudioPlayControllerDecoder::SeekSync(TUint8* aBuf, TInt aBufLen)
    {
    const TInt KMaxFrames = 3;          // number of frames to check
    const TInt KNotFound = aBufLen;     // sync not found position
    TAudioFrameInfo frameInfo;           // frame parameters
    TInt i = 0;
    TInt syncPos = KNotFound;
    TInt maxSeek = KMaxFrames;
    const TUint8* endPtr = aBuf+aBufLen;

    // Seek a valid frame candidate byte by byte until a valid frame
    // is found or all bytes have been checked.
    while (aBuf < endPtr  &&  syncPos == KNotFound)
	    {
        TInt seekCount = 0;
        const TUint8* framePtr = aBuf;
        TInt frameBufLen = aBufLen;
        syncPos = i;

		// Check the validity of this frame candidate and the nearest next
        // frames. If they are not OK, syncPos will be set to KNotFound.
        while (framePtr < endPtr  &&  syncPos != KNotFound  && seekCount < maxSeek)
	        {
            TInt length = FrameInfo(framePtr, frameBufLen, frameInfo);

			if (length == 0)
            	{
                syncPos = KNotFound;
				}
            if ((length > 0) && (frameInfo.iBitRate < 0))
            	{
                maxSeek = 1; // free formatcase
				}
            framePtr += length;
            frameBufLen -= length;
            seekCount++;

			// consider SYNC not found if we reach end of buffer before finding 3 SYNC frames
			if ((framePtr >= endPtr) && (seekCount < maxSeek))
				{
				syncPos = KNotFound;
				aBuf += (aBufLen-1);      // force an exit from while loop
				}
        	}
        aBuf++; aBufLen--; i++;
    	}
    return syncPos;
    }


// -----------------------------------------------------------------------------
// CMP3AudioPlayControllerDecoder::IsHwAccelerated
// Always return true when no soft codec is used.
// -----------------------------------------------------------------------------
//
TBool CMP3AudioPlayControllerDecoder::IsHwAccelerated()
	{
	return ETrue;
	}

TInt CMP3AudioPlayControllerDecoder::CodecCmd(TCodecCmd /*aCmd*/)
	{
	return KErrNotSupported;
	}


// End of file
