// Copyright (c) 2002-2009 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:
//

#include "MmfUtilities.h"
#include "mmfutilitiespriv.h"
#include <mmf/utils/rateconvert.h>

const TInt KMaxInt16Bit = 65536 ; //Maximum for a 16bit int

EXPORT_C CMMFChannelAndSampleRateConverterFactory::~CMMFChannelAndSampleRateConverterFactory()
	{
	delete iConverter;
	}

	
EXPORT_C CMMFChannelAndSampleRateConverter* CMMFChannelAndSampleRateConverterFactory::CreateConverterL(TInt aFromRate,TInt aFromChannels,
													   TInt aToRate,TInt aToChannels)
	{
	if(aFromRate<0 || aFromChannels<0 || aToRate<0 || aToChannels<0)
		{
		User::Leave(KErrArgument);
		}

	iFromRate=aFromRate;
	iToRate=aToRate;
	iFromChannels=aFromChannels;
	iToChannels=aToChannels;
	return CreateConverterL();
	}

EXPORT_C CMMFChannelAndSampleRateConverter* CMMFChannelAndSampleRateConverterFactory::CreateConverterL()
	{
	if (iConverter
		&&(iFromRate==iConverter->iFromRate)
		&&(iToRate==iConverter->iToRate)
		&&(iFromChannels==iConverter->iFromChannels)
		&&(iToChannels==iConverter->iToChannels))
		return iConverter;

	delete iConverter;
	iConverter=NULL;
	
	iConverter = CMMFForwardingChannelAndSampleRateConverter::NewL(iFromRate, iFromChannels, iToRate, iToChannels);

	return iConverter;
	}
	
// SetRates kept for BC reasons - populates publically visible properties
	
void CMMFChannelAndSampleRateConverter::SetRates(TInt aFromRate,TInt aFromChannels,
											 TInt aToRate,TInt aToChannels)
	{
	iFromRate=aFromRate;
	iToRate=aToRate;
	iFromChannels=aFromChannels;
	iToChannels=aToChannels;

	iRatio = (TReal)aFromRate / (TReal)aToRate;

	TInt quotient = (TInt)iRatio;
	TReal remainder = iRatio - (TReal)quotient;
	iFraction = quotient * KMaxInt16Bit + (TInt32)(remainder * KMaxInt16Bit);

	Reset();
	}
	
//
// Forwarding converter - adapt to the implementation in audioutils.
//
	
CMMFForwardingChannelAndSampleRateConverter* CMMFForwardingChannelAndSampleRateConverter::NewL(TInt aFromRate,
																							   TInt aFromChannels,
										    			      								   TInt aToRate,
										    			      								   TInt aToChannels)
	{
	CMMFForwardingChannelAndSampleRateConverter* self = new (ELeave) CMMFForwardingChannelAndSampleRateConverter;
	CleanupStack::PushL(self);
	self->ConstructL(aFromRate,aFromChannels,aToRate,aToChannels);
	CleanupStack::Pop(self);
	return self;
	}
	
CMMFForwardingChannelAndSampleRateConverter::CMMFForwardingChannelAndSampleRateConverter()
	{
	}

void CMMFForwardingChannelAndSampleRateConverter::ConstructL(TInt aFromRate,
															 TInt aFromChannels,
										    			     TInt aToRate,
										    			     TInt aToChannels)
	{
	SetRates(aFromRate, aFromChannels, aToRate, aToChannels); 
		// delib called before iRealConverter created. Reset() will be no-op.
		// this is preserved solely to keep BC with original API that exposes too much
	iRealConverter = CChannelAndSampleRateConverter::CreateL(aFromRate,aFromChannels,aToRate,aToChannels);
	}
	
CMMFForwardingChannelAndSampleRateConverter::~CMMFForwardingChannelAndSampleRateConverter()
	{
	delete iRealConverter;
	}
	
TInt CMMFForwardingChannelAndSampleRateConverter::Convert(const CMMFDataBuffer& aSrcBuffer, CMMFDataBuffer& aDstBuffer)
	{
	TInt ignore = iRealConverter->Convert(aSrcBuffer.Data(), aDstBuffer.Data());
	// CChannelAndSampleRateConverter returns the source length converted
	// while the old API wants the destination generated, so change. 
	TInt result = aDstBuffer.Data().Length(); 
	return result;
	}
	
void CMMFForwardingChannelAndSampleRateConverter::Reset()
	{
	// need to check iRealConverter created. Won't be true during construction
	if (iRealConverter)
		{
		iRealConverter->Reset();
		}
	}
	
TUint CMMFForwardingChannelAndSampleRateConverter::MaxConvertBufferSize(TUint aSrcBufferSize)
	{
	return TUint(iRealConverter->MaxConvertBufferSize(aSrcBufferSize));
	}
	
//
// Old derivatives of CMMFChannelAndSampleRateConverter. These were previously returned by
// CreateConverterL(). Kept for BC, but no longer used by main code.
//

TInt CMMFStereoToStereoRateConverter::Convert(const CMMFDataBuffer& aSrcBuffer, CMMFDataBuffer& aDstBuffer)
	{
	TInt32* aSrc = (TInt32*)aSrcBuffer.Data().Ptr();
	TInt32* aDst = (TInt32*)aDstBuffer.Data().Ptr();
	TUint aSamples = aSrcBuffer.Data().Length()/4;

	TInt32* src = aSrc;
	TInt32* dst = aDst;
	TInt32* limit=src+aSamples;
	
	// add left over from last buffer
	TUint index = iIndex;
	src = aSrc + (index>>16);

	while(src<limit)
		{
		*dst++ = *src;
		index += iFraction;
		src = aSrc + (index>>16);
		}
	
	// get amount by which index exceeded end of buffer
	// so that we can add it back to start of next buffer
	iIndex = index - (aSamples << 16);

	// return sample byte count and setup output buffer
	TInt length = (dst-(TInt32*)aDst)*4;	//dealing with 32bit values so multiply by 4 for bytes
	aDstBuffer.Data().SetLength(length); //adjust length of destination buffer
	return (length);
	}	

TInt CMMFMonoToStereoRateConverter::Convert(const CMMFDataBuffer& aSrcBuffer, CMMFDataBuffer& aDstBuffer)
	{
	TInt16* aSrc = (TInt16*)aSrcBuffer.Data().Ptr();
	TInt16* aDst = (TInt16*)aDstBuffer.Data().Ptr();
	TUint aSamples = aSrcBuffer.Data().Length()/2;

	TInt16* src = aSrc;
	TInt16* dst = aDst;
	TInt16* limit=aSrc+aSamples;

	// add left over from last buffer
	TUint index = iIndex;
	src = aSrc + (index>>16);

	while(src<limit)
		{
		*dst++ = *src;
		*dst++ = *src;
		index += iFraction;
		src = aSrc + (index>>16);
		}

	// get amount by which index exceeded end of buffer
	// so that we can add it back to start of next buffer
	iIndex = index - (aSamples << 16);

	// return sample byte count and setup output buffer
	TInt length = (dst-aDst) * 2;		// size in bytes
	aDstBuffer.Data().SetLength(length); //adjust length of destination buffer
	return (length);
	}	
	

TUint CMMFMonoToStereoRateConverter::MaxConvertBufferSize(TUint aSrcBufferSize)
	{
	return aSrcBufferSize*2;
	}



TInt CMMFMonoToMonoRateConverter::Convert(const CMMFDataBuffer& aSrcBuffer, CMMFDataBuffer& aDstBuffer)
	{
	TInt16* aSrc = (TInt16*)aSrcBuffer.Data().Ptr();
	TInt16* aDst = (TInt16*)aDstBuffer.Data().Ptr();
	TUint aSamples = aSrcBuffer.Data().Length()/2;

	TInt16* src = aSrc;
	TInt16* dst = aDst;
	TInt16* limit=aSrc+aSamples; //*2 ???

	// add left over from last buffer
	TUint index = iIndex;
	src = aSrc + (index>>16);

	while(src<limit)
		{
  		*dst++ = *src;
		index += iFraction;
		src = aSrc + (index>>16);
		}

	// get amount by which index exceeded end of buffer
	// so that we can add it back to start of next buffer
	iIndex = index - (aSamples << 16);

	// return sample byte count and setup output buffer
	TInt length = (dst-aDst)*2;			// size in bytes
	aDstBuffer.Data().SetLength(length); //adjust length of destination buffer
	return (length);
	}	

//This method takes the left and right sample of interleaved PCM and sums it, then divides by 2
TInt CMMFStereoToMonoRateConverter::Convert(const CMMFDataBuffer& aSrcBuffer, CMMFDataBuffer& aDstBuffer)
	{
	TInt16* aSrc = (TInt16*)aSrcBuffer.Data().Ptr();
	TInt16* aDst = (TInt16*)aDstBuffer.Data().Ptr();
	TUint aSamples = aSrcBuffer.Data().Length()/4;

	TInt16* src = aSrc;
	TInt16* limit=aSrc+aSamples*2;	//because 1 sample = two TInt16s

	// add left over from last buffer
	TUint index = iIndex;
	src = aSrc + (index>>16)*2;

	TInt length = 0;

	while(src<limit)
		{
		*aDst++ = (TInt16)((src[0] + (src[1])) / 2);
		index += iFraction;
		src = aSrc + (index>>16)*2;
		length++;
		}
	// get amount by which index exceeded end of buffer
	// so that we can add it back to start of next buffer
	iIndex = index - (aSamples << 16);

	// return sample byte count and setup output buffer
	aDstBuffer.Data().SetLength(length * 2); //adjust length of destination buffer
	return (length);
	}


TUint CMMFStereoToMonoRateConverter::MaxConvertBufferSize(TUint aSrcBufferSize)
	{
	TUint size = aSrcBufferSize/2;
	size += aSrcBufferSize & 1; //avoid round down error
	return size;
	}

//This method takes the left and right sample of interleaved PCM and sums it, then divides by 2
TInt CMMFStereoToMonoConverter::Convert(const CMMFDataBuffer& aSrcBuffer, CMMFDataBuffer& aDstBuffer)
	{
	TInt16* aSrc = (TInt16*)aSrcBuffer.Data().Ptr();
	TInt16* aDst = (TInt16*)aDstBuffer.Data().Ptr();
	TUint aSamples = aSrcBuffer.Data().Length()/4;
	for (TUint i=0;i<aSamples;i++)
		{
		*aDst++ = (TInt16)((aSrc[0] + (aSrc[1])) / 2);
		aSrc+=2;
		}
	aDstBuffer.Data().SetLength(aSamples*2); //adjust length of destination buffer
	return aSamples*2;
	}	

TUint CMMFStereoToMonoConverter::MaxConvertBufferSize(TUint aSrcBufferSize)
	{
	TUint size = aSrcBufferSize/2;
	size += aSrcBufferSize & 1; //avoid round down error
	return size;
	}



TInt CMMFMonoToStereoConverter::Convert(const CMMFDataBuffer& aSrcBuffer, CMMFDataBuffer& aDstBuffer)
	{
	TInt16* aSrc = (TInt16*)aSrcBuffer.Data().Ptr();
	TInt16* aDst = (TInt16*)aDstBuffer.Data().Ptr();
	TUint aSamples = aSrcBuffer.Data().Length()/2;
	for (TUint i=0;i<aSamples;i++)
		{
		*aDst++ = *aSrc;
		*aDst++ = *aSrc++;
		}
	aDstBuffer.Data().SetLength(aSamples*4); //adjust length of destination buffer
	return aSamples*4;
	}	

TUint CMMFMonoToStereoConverter::MaxConvertBufferSize(TUint aSrcBufferSize)
	{
	return aSrcBufferSize*2;
	}





