sl@0: // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // include\mmf\utils\rateconvert.cpp sl@0: // Note this code used to be in mmfutilities.cpp but split off here to allow sl@0: // scaling of descriptors instead of just CMMFBuffers. sl@0: // sl@0: // sl@0: sl@0: #include "rateconvertimpl.h" // includes rateconvert.h itself sl@0: #include sl@0: sl@0: const TInt KMaxInt16Bit = 65536 ; sl@0: sl@0: /* sl@0: The rate conversion algorithms used here are extremely basic, using nearest neighbour, not sl@0: even interpolation. An increment is created - initially a real, but converted to 16.16 fixed sl@0: point notation for efficiency purposes. For example, from 8000 to 11025 this increment is set sl@0: at 8000/11025 (~ 0.73). Each increment to the destination buffer conceptually increments the sl@0: src pointer by this value (0.73). On each iteration the nearest src sample is used. sl@0: sl@0: The idea is that successive buffers run on from each other. The index is adjusted so at the end sl@0: of the run it is correct for the next buffer, and this is saved from one iteration to the next. sl@0: If the client wants to convert separate buffers, it should call Reset(), where the index is reset sl@0: to 0. sl@0: sl@0: Note the algorithm is even then not ideal, as it effectively truncates and not rounds the sl@0: fixed-point index. However, a feature of this is that the nearest src sample is always behind the sl@0: conceptual fixed-point index. This makes it easy to ensure that processing of the next buffer sl@0: never needs data from the previous cycle - except this index value. sl@0: sl@0: */ sl@0: sl@0: enum TPanicCodes sl@0: { sl@0: EPanicNoDestinationBuffer=1, sl@0: EPanicNoSourceConsumed sl@0: }; sl@0: sl@0: #ifdef _DEBUG sl@0: sl@0: static void Panic(TInt aPanicCode) sl@0: { sl@0: _LIT(KRateConvert,"RateConvert"); sl@0: User::Panic(KRateConvert, aPanicCode); sl@0: } sl@0: sl@0: #endif // _DEBUG sl@0: sl@0: // sl@0: // CChannelAndSampleRateConverter sl@0: // sl@0: sl@0: CChannelAndSampleRateConverter::CChannelAndSampleRateConverter() sl@0: { sl@0: // constructor does nothing but ensures can't derive from outside dll sl@0: } sl@0: sl@0: // Factory function sl@0: EXPORT_C CChannelAndSampleRateConverter* CChannelAndSampleRateConverter::CreateL(TInt aFromRate, sl@0: TInt aFromChannels, sl@0: TInt aToRate, sl@0: TInt aToChannels) sl@0: { sl@0: // check that the params in range so we can assume OK later on sl@0: if (aFromChannels <= 0 || aFromChannels > 2 || sl@0: aToChannels <= 0 || aToChannels > 2 || sl@0: aFromRate <= 0 || aToRate <= 0) sl@0: { sl@0: User::Leave(KErrArgument); sl@0: } sl@0: sl@0: CChannelAndSampleRateConverterCommon* converter = NULL; sl@0: sl@0: if (aFromChannels==aToChannels) sl@0: { sl@0: if (aFromChannels==1) sl@0: { sl@0: converter = new (ELeave) CMonoToMonoRateConverter; sl@0: } sl@0: else sl@0: { sl@0: converter = new (ELeave) CStereoToStereoRateConverter; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: if (aFromChannels==1) sl@0: { sl@0: if (aFromRate!=aToRate) sl@0: { sl@0: converter = new (ELeave) CMonoToStereoRateConverter; sl@0: } sl@0: else sl@0: { sl@0: converter = new (ELeave) CMonoToStereoConverter; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: ASSERT(aFromChannels>1 && aToChannels==1); sl@0: if (aFromRate!=aToRate) sl@0: { sl@0: converter = new (ELeave) CStereoToMonoRateConverter; sl@0: } sl@0: else sl@0: { sl@0: converter = new (ELeave) CStereoToMonoConverter; sl@0: } sl@0: } sl@0: } sl@0: converter->SetRates(aFromRate,aToRate); sl@0: return converter; sl@0: } sl@0: sl@0: // sl@0: // CChannelAndSampleRateConverterImpl sl@0: // sl@0: sl@0: CChannelAndSampleRateConverterCommon::CChannelAndSampleRateConverterCommon() sl@0: { sl@0: } sl@0: sl@0: void CChannelAndSampleRateConverterCommon::Reset() sl@0: { sl@0: // default does nothing sl@0: } sl@0: sl@0: void CChannelAndSampleRateConverterCommon::SetRates(TInt /*aFromRate*/, TInt /*aToRate*/) sl@0: { sl@0: // in default no need to know so don't cache sl@0: } sl@0: sl@0: TInt CChannelAndSampleRateConverterCommon::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower) sl@0: { sl@0: // assume aSrcBuffer takes channel change into account sl@0: TInt rawValue = aSrcBufferSize; sl@0: if (aRoundUpToPower) sl@0: { sl@0: return NextPowerUp(rawValue); sl@0: } sl@0: else sl@0: { sl@0: return rawValue; sl@0: } sl@0: } sl@0: sl@0: TInt CChannelAndSampleRateConverterCommon::NextPowerUp(TInt aValue) sl@0: { sl@0: TInt power = 128; // no need to start lower sl@0: while (power(aSrcBuffer.Ptr()); sl@0: TInt32* dstPtr = const_cast(reinterpret_cast(aDstBuffer.Ptr())); sl@0: sl@0: const TInt32* srcLimit=srcPtr+LengthBytesTo32Bit(aSrcBuffer.Length()); // ptr+n does *4 for TInt32* ptr sl@0: TInt32* dstLimit=dstPtr+LengthBytesTo32Bit(aDstBuffer.MaxLength()); // ditto sl@0: sl@0: // add left over from last buffer sl@0: TUint index = iIndex; sl@0: const TInt32* src = srcPtr + (index>>16); sl@0: TInt32* dst = dstPtr; sl@0: sl@0: if (dst>=dstLimit) sl@0: { sl@0: __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer)); sl@0: return 0; sl@0: } sl@0: sl@0: while (src>16); // truncate fix-point index sl@0: } sl@0: sl@0: // get amount by which index exceeded end of buffer sl@0: // so that we can add it back to start of next buffer sl@0: const TInt32* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration sl@0: TInt srcSamplesCopied = conceptualLastSrc - srcPtr; sl@0: __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space sl@0: iIndex = index - (srcSamplesCopied << 16); sl@0: sl@0: // return sample byte count and setup output buffer sl@0: TInt dstLength = Length32BitToBytes(dst-dstPtr); sl@0: aDstBuffer.SetLength(dstLength); //adjust length of destination buffer sl@0: return Length32BitToBytes(srcSamplesCopied); sl@0: } sl@0: sl@0: CMonoToStereoRateConverter::CMonoToStereoRateConverter() sl@0: { sl@0: } sl@0: sl@0: TInt CMonoToStereoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) sl@0: { sl@0: const TInt16* srcPtr = reinterpret_cast(aSrcBuffer.Ptr()); sl@0: TInt32* dstPtr = const_cast(reinterpret_cast(aDstBuffer.Ptr())); sl@0: sl@0: const TInt16* srcLimit=srcPtr+LengthBytesTo16Bit(aSrcBuffer.Length()); // as ptr+n does *2 for TInt16* ptr sl@0: TInt32* dstLimit=dstPtr+LengthBytesTo32Bit(aDstBuffer.MaxLength()); // ditto but does *4 for TInt32* sl@0: sl@0: // add left over from last buffer sl@0: TUint index = iIndex; sl@0: const TInt16* src = srcPtr + (index>>16); sl@0: TInt32* dst = dstPtr; sl@0: sl@0: if (dst>=dstLimit) sl@0: { sl@0: __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer)); sl@0: return 0; sl@0: } sl@0: sl@0: while (src>16); // truncate fix-point index sl@0: } sl@0: sl@0: // get amount by which index exceeded end of buffer sl@0: // so that we can add it back to start of next buffer sl@0: const TInt16* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration sl@0: TInt srcSamplesCopied = conceptualLastSrc - srcPtr; sl@0: __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space sl@0: iIndex = index - (srcSamplesCopied << 16); sl@0: sl@0: // return sample byte count and setup output buffer sl@0: TInt dstLength = Length32BitToBytes(dst-dstPtr); // size in bytes sl@0: aDstBuffer.SetLength(dstLength); //adjust length of destination buffer sl@0: return Length16BitToBytes(srcSamplesCopied); sl@0: } sl@0: sl@0: sl@0: TInt CMonoToStereoRateConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower) sl@0: { sl@0: return CChannelAndSampleRateConvert::MaxConvertBufferSize(aSrcBufferSize*2, aRoundUpToPower); sl@0: } sl@0: sl@0: CMonoToMonoRateConverter::CMonoToMonoRateConverter() sl@0: { sl@0: } sl@0: sl@0: TInt CMonoToMonoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) sl@0: { sl@0: const TInt16* srcPtr = reinterpret_cast(aSrcBuffer.Ptr()); sl@0: TInt16* dstPtr = const_cast(reinterpret_cast(aDstBuffer.Ptr())); sl@0: sl@0: const TInt16* srcLimit=srcPtr+LengthBytesTo16Bit(aSrcBuffer.Length()); // ptr+n does *2 for TInt16* ptr sl@0: TInt16* dstLimit=dstPtr+LengthBytesTo16Bit(aDstBuffer.MaxLength()); // ditto sl@0: sl@0: // add left over from last buffer sl@0: TUint index = iIndex; sl@0: const TInt16* src = srcPtr + (index>>16); sl@0: TInt16* dst = dstPtr; sl@0: sl@0: if (dst>=dstLimit) sl@0: { sl@0: __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer)); sl@0: return 0; sl@0: } sl@0: sl@0: while (src>16); // truncate fix-point index sl@0: } sl@0: sl@0: // get amount by which index exceeded end of buffer sl@0: // so that we can add it back to start of next buffer sl@0: const TInt16* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration sl@0: TInt srcSamplesCopied = conceptualLastSrc - srcPtr; sl@0: __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space sl@0: iIndex = index - (srcSamplesCopied << 16); sl@0: sl@0: // return sample byte count and setup output buffer sl@0: TInt dstLength = Length16BitToBytes(dst-dstPtr); // size in bytes sl@0: aDstBuffer.SetLength(dstLength); //adjust length of destination buffer sl@0: return Length16BitToBytes(srcSamplesCopied); sl@0: } sl@0: sl@0: CStereoToMonoRateConverter::CStereoToMonoRateConverter() sl@0: { sl@0: } sl@0: sl@0: //This method takes the left and right sample of interleaved PCM and sums it, then divides by 2 sl@0: TInt CStereoToMonoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) sl@0: { sl@0: const TInt32* srcPtr = reinterpret_cast(aSrcBuffer.Ptr()); sl@0: TInt16* dstPtr = const_cast(reinterpret_cast(aDstBuffer.Ptr())); sl@0: sl@0: const TInt32* srcLimit=srcPtr+LengthBytesTo32Bit(aSrcBuffer.Length()); // ptr+n does *4 for TInt32* ptr sl@0: TInt16* dstLimit=dstPtr+LengthBytesTo16Bit(aDstBuffer.MaxLength()); // ditto but *2 for TInt16* sl@0: sl@0: // add left over from last buffer sl@0: TUint index = iIndex; sl@0: const TInt32* src = srcPtr + (index>>16); sl@0: TInt16* dst = dstPtr; sl@0: sl@0: if (dst>=dstLimit) sl@0: { sl@0: __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer)); sl@0: return 0; sl@0: } sl@0: sl@0: while (src>16); // truncate fix-point index sl@0: } sl@0: sl@0: // get amount by which index exceeded end of buffer sl@0: // so that we can add it back to start of next buffer sl@0: const TInt32* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration sl@0: TInt srcSamplesCopied = conceptualLastSrc - srcPtr; sl@0: __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space sl@0: iIndex = index - (srcSamplesCopied << 16); sl@0: sl@0: // return sample byte count and setup output buffer sl@0: TInt dstLength = Length16BitToBytes(dst-dstPtr); // size in bytes sl@0: aDstBuffer.SetLength(dstLength); //adjust length of destination buffer sl@0: return Length32BitToBytes(srcSamplesCopied); sl@0: } sl@0: sl@0: sl@0: TInt CStereoToMonoRateConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower) sl@0: { sl@0: TUint size = aSrcBufferSize/2; sl@0: size += aSrcBufferSize & 1; //avoid round down error sl@0: return CChannelAndSampleRateConvert::MaxConvertBufferSize(size, aRoundUpToPower); sl@0: } sl@0: sl@0: CStereoToMonoConverter::CStereoToMonoConverter() sl@0: { sl@0: } sl@0: sl@0: //This method takes the left and right sample of interleaved PCM and sums it, then divides by 2 sl@0: TInt CStereoToMonoConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) sl@0: { sl@0: const TInt32* src = reinterpret_cast(aSrcBuffer.Ptr()); sl@0: TInt16* dst = const_cast(reinterpret_cast(aDstBuffer.Ptr())); sl@0: sl@0: TInt srcCount = LengthBytesTo32Bit(aSrcBuffer.Length()); sl@0: TInt dstCount = LengthBytesTo16Bit(aDstBuffer.MaxLength()); sl@0: TInt count = Min(srcCount, dstCount); // if aDstBuffer is short, just copy that much sl@0: sl@0: for (TUint i=0;i(aSrcBuffer.Ptr()); sl@0: TInt32* dst = const_cast(reinterpret_cast(aDstBuffer.Ptr())); sl@0: sl@0: TInt srcCount = LengthBytesTo16Bit(aSrcBuffer.Length()); sl@0: TInt dstCount = LengthBytesTo32Bit(aDstBuffer.MaxLength()); sl@0: TInt count = Min(srcCount, dstCount); // if aDstBuffer is short, just copy that much sl@0: sl@0: for (TUint i=0;i