1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/mm/mmlibs/mmfw/src/utils/audioutils/rateconvert.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,486 @@
1.4 +// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// include\mmf\utils\rateconvert.cpp
1.18 +// Note this code used to be in mmfutilities.cpp but split off here to allow
1.19 +// scaling of descriptors instead of just CMMFBuffers.
1.20 +//
1.21 +//
1.22 +
1.23 +#include "rateconvertimpl.h" // includes rateconvert.h itself
1.24 +#include <e32const.h>
1.25 +
1.26 +const TInt KMaxInt16Bit = 65536 ;
1.27 +
1.28 +/*
1.29 +The rate conversion algorithms used here are extremely basic, using nearest neighbour, not
1.30 +even interpolation. An increment is created - initially a real, but converted to 16.16 fixed
1.31 +point notation for efficiency purposes. For example, from 8000 to 11025 this increment is set
1.32 +at 8000/11025 (~ 0.73). Each increment to the destination buffer conceptually increments the
1.33 +src pointer by this value (0.73). On each iteration the nearest src sample is used.
1.34 +
1.35 +The idea is that successive buffers run on from each other. The index is adjusted so at the end
1.36 +of the run it is correct for the next buffer, and this is saved from one iteration to the next.
1.37 +If the client wants to convert separate buffers, it should call Reset(), where the index is reset
1.38 +to 0.
1.39 +
1.40 +Note the algorithm is even then not ideal, as it effectively truncates and not rounds the
1.41 +fixed-point index. However, a feature of this is that the nearest src sample is always behind the
1.42 +conceptual fixed-point index. This makes it easy to ensure that processing of the next buffer
1.43 +never needs data from the previous cycle - except this index value.
1.44 +
1.45 +*/
1.46 +
1.47 +enum TPanicCodes
1.48 + {
1.49 + EPanicNoDestinationBuffer=1,
1.50 + EPanicNoSourceConsumed
1.51 + };
1.52 +
1.53 +#ifdef _DEBUG
1.54 +
1.55 +static void Panic(TInt aPanicCode)
1.56 + {
1.57 + _LIT(KRateConvert,"RateConvert");
1.58 + User::Panic(KRateConvert, aPanicCode);
1.59 + }
1.60 +
1.61 +#endif // _DEBUG
1.62 +
1.63 +//
1.64 +// CChannelAndSampleRateConverter
1.65 +//
1.66 +
1.67 +CChannelAndSampleRateConverter::CChannelAndSampleRateConverter()
1.68 + {
1.69 + // constructor does nothing but ensures can't derive from outside dll
1.70 + }
1.71 +
1.72 +// Factory function
1.73 +EXPORT_C CChannelAndSampleRateConverter* CChannelAndSampleRateConverter::CreateL(TInt aFromRate,
1.74 + TInt aFromChannels,
1.75 + TInt aToRate,
1.76 + TInt aToChannels)
1.77 + {
1.78 + // check that the params in range so we can assume OK later on
1.79 + if (aFromChannels <= 0 || aFromChannels > 2 ||
1.80 + aToChannels <= 0 || aToChannels > 2 ||
1.81 + aFromRate <= 0 || aToRate <= 0)
1.82 + {
1.83 + User::Leave(KErrArgument);
1.84 + }
1.85 +
1.86 + CChannelAndSampleRateConverterCommon* converter = NULL;
1.87 +
1.88 + if (aFromChannels==aToChannels)
1.89 + {
1.90 + if (aFromChannels==1)
1.91 + {
1.92 + converter = new (ELeave) CMonoToMonoRateConverter;
1.93 + }
1.94 + else
1.95 + {
1.96 + converter = new (ELeave) CStereoToStereoRateConverter;
1.97 + }
1.98 + }
1.99 + else
1.100 + {
1.101 + if (aFromChannels==1)
1.102 + {
1.103 + if (aFromRate!=aToRate)
1.104 + {
1.105 + converter = new (ELeave) CMonoToStereoRateConverter;
1.106 + }
1.107 + else
1.108 + {
1.109 + converter = new (ELeave) CMonoToStereoConverter;
1.110 + }
1.111 + }
1.112 + else
1.113 + {
1.114 + ASSERT(aFromChannels>1 && aToChannels==1);
1.115 + if (aFromRate!=aToRate)
1.116 + {
1.117 + converter = new (ELeave) CStereoToMonoRateConverter;
1.118 + }
1.119 + else
1.120 + {
1.121 + converter = new (ELeave) CStereoToMonoConverter;
1.122 + }
1.123 + }
1.124 + }
1.125 + converter->SetRates(aFromRate,aToRate);
1.126 + return converter;
1.127 + }
1.128 +
1.129 +//
1.130 +// CChannelAndSampleRateConverterImpl
1.131 +//
1.132 +
1.133 +CChannelAndSampleRateConverterCommon::CChannelAndSampleRateConverterCommon()
1.134 + {
1.135 + }
1.136 +
1.137 +void CChannelAndSampleRateConverterCommon::Reset()
1.138 + {
1.139 + // default does nothing
1.140 + }
1.141 +
1.142 +void CChannelAndSampleRateConverterCommon::SetRates(TInt /*aFromRate*/, TInt /*aToRate*/)
1.143 + {
1.144 + // in default no need to know so don't cache
1.145 + }
1.146 +
1.147 +TInt CChannelAndSampleRateConverterCommon::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower)
1.148 + {
1.149 + // assume aSrcBuffer takes channel change into account
1.150 + TInt rawValue = aSrcBufferSize;
1.151 + if (aRoundUpToPower)
1.152 + {
1.153 + return NextPowerUp(rawValue);
1.154 + }
1.155 + else
1.156 + {
1.157 + return rawValue;
1.158 + }
1.159 + }
1.160 +
1.161 +TInt CChannelAndSampleRateConverterCommon::NextPowerUp(TInt aValue)
1.162 + {
1.163 + TInt power = 128; // no need to start lower
1.164 + while (power<aValue)
1.165 + {
1.166 + power *= 2;
1.167 + }
1.168 + return power;
1.169 + }
1.170 +
1.171 +//
1.172 +// CChannelAndSampleRateConvert
1.173 +//
1.174 +
1.175 +CChannelAndSampleRateConvert::CChannelAndSampleRateConvert()
1.176 + {
1.177 + }
1.178 +
1.179 +
1.180 +TInt CChannelAndSampleRateConvert::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower)
1.181 + {
1.182 + // take rate conversion into account. Assumed channel mismatch handled by the child class.
1.183 + TInt rawValue = aSrcBufferSize;
1.184 + if (iFromRate < iToRate)
1.185 + {
1.186 + TInt result = SizeOfUpsampleBuffer(rawValue);
1.187 + return result; // always rounded up to next size
1.188 + }
1.189 + else
1.190 + {
1.191 + if (aRoundUpToPower)
1.192 + {
1.193 + return NextPowerUp(rawValue);
1.194 + }
1.195 + else
1.196 + {
1.197 + return rawValue;
1.198 + }
1.199 + }
1.200 + }
1.201 +
1.202 +void CChannelAndSampleRateConvert::SetRates(TInt aFromRate, TInt aToRate)
1.203 + {
1.204 + iFromRate=aFromRate;
1.205 + iToRate=aToRate;
1.206 +
1.207 + TReal ratio = TReal(aFromRate) / TReal(aToRate);
1.208 + TInt quotient = TInt(ratio);
1.209 + TReal remainder = ratio - TReal(quotient);
1.210 + iFraction = (quotient << 16) + TInt32(remainder * KMaxInt16Bit);
1.211 +
1.212 + Reset();
1.213 + }
1.214 +
1.215 +void CChannelAndSampleRateConvert::Reset()
1.216 + {
1.217 + iIndex = 0;
1.218 + }
1.219 +
1.220 +TInt CChannelAndSampleRateConvert::SizeOfUpsampleBuffer(TInt aBufferLength)
1.221 + {
1.222 + TInt rawValue = aBufferLength;
1.223 + ASSERT(iFromRate < iToRate); // should not be called otherwise
1.224 + // upsample - will generate more data. use floats to avoid extra round error
1.225 + rawValue = TInt(rawValue * TReal(iToRate) / TReal(iFromRate) + 0.5) + 4*sizeof(TInt16); // add some buffer extra buffer
1.226 + rawValue = NextPowerUp(rawValue); // when upscaling always give nice power
1.227 + return rawValue;
1.228 + }
1.229 +
1.230 +//
1.231 +// Specific converter code
1.232 +//
1.233 +
1.234 +CStereoToStereoRateConverter::CStereoToStereoRateConverter()
1.235 + {
1.236 + }
1.237 +
1.238 +TInt CStereoToStereoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer)
1.239 + {
1.240 + const TInt32* srcPtr = reinterpret_cast<const TInt32*>(aSrcBuffer.Ptr());
1.241 + TInt32* dstPtr = const_cast<TInt32*>(reinterpret_cast<const TInt32*>(aDstBuffer.Ptr()));
1.242 +
1.243 + const TInt32* srcLimit=srcPtr+LengthBytesTo32Bit(aSrcBuffer.Length()); // ptr+n does *4 for TInt32* ptr
1.244 + TInt32* dstLimit=dstPtr+LengthBytesTo32Bit(aDstBuffer.MaxLength()); // ditto
1.245 +
1.246 + // add left over from last buffer
1.247 + TUint index = iIndex;
1.248 + const TInt32* src = srcPtr + (index>>16);
1.249 + TInt32* dst = dstPtr;
1.250 +
1.251 + if (dst>=dstLimit)
1.252 + {
1.253 + __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer));
1.254 + return 0;
1.255 + }
1.256 +
1.257 + while (src<srcLimit && dst<dstLimit)
1.258 + {
1.259 + *dst++ = *src;
1.260 + index += iFraction;
1.261 + src = srcPtr + (index>>16); // truncate fix-point index
1.262 + }
1.263 +
1.264 + // get amount by which index exceeded end of buffer
1.265 + // so that we can add it back to start of next buffer
1.266 + const TInt32* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration
1.267 + TInt srcSamplesCopied = conceptualLastSrc - srcPtr;
1.268 + __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space
1.269 + iIndex = index - (srcSamplesCopied << 16);
1.270 +
1.271 + // return sample byte count and setup output buffer
1.272 + TInt dstLength = Length32BitToBytes(dst-dstPtr);
1.273 + aDstBuffer.SetLength(dstLength); //adjust length of destination buffer
1.274 + return Length32BitToBytes(srcSamplesCopied);
1.275 + }
1.276 +
1.277 +CMonoToStereoRateConverter::CMonoToStereoRateConverter()
1.278 + {
1.279 + }
1.280 +
1.281 +TInt CMonoToStereoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer)
1.282 + {
1.283 + const TInt16* srcPtr = reinterpret_cast<const TInt16*>(aSrcBuffer.Ptr());
1.284 + TInt32* dstPtr = const_cast<TInt32*>(reinterpret_cast<const TInt32*>(aDstBuffer.Ptr()));
1.285 +
1.286 + const TInt16* srcLimit=srcPtr+LengthBytesTo16Bit(aSrcBuffer.Length()); // as ptr+n does *2 for TInt16* ptr
1.287 + TInt32* dstLimit=dstPtr+LengthBytesTo32Bit(aDstBuffer.MaxLength()); // ditto but does *4 for TInt32*
1.288 +
1.289 + // add left over from last buffer
1.290 + TUint index = iIndex;
1.291 + const TInt16* src = srcPtr + (index>>16);
1.292 + TInt32* dst = dstPtr;
1.293 +
1.294 + if (dst>=dstLimit)
1.295 + {
1.296 + __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer));
1.297 + return 0;
1.298 + }
1.299 +
1.300 + while (src<srcLimit && dst<dstLimit-1)
1.301 + {
1.302 + TInt16 sample = *src;
1.303 + TInt32 stereoSample = MonoToStereo(sample);
1.304 + *dst++ = stereoSample;
1.305 + index += iFraction;
1.306 + src = srcPtr + (index>>16); // truncate fix-point index
1.307 + }
1.308 +
1.309 + // get amount by which index exceeded end of buffer
1.310 + // so that we can add it back to start of next buffer
1.311 + const TInt16* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration
1.312 + TInt srcSamplesCopied = conceptualLastSrc - srcPtr;
1.313 + __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space
1.314 + iIndex = index - (srcSamplesCopied << 16);
1.315 +
1.316 + // return sample byte count and setup output buffer
1.317 + TInt dstLength = Length32BitToBytes(dst-dstPtr); // size in bytes
1.318 + aDstBuffer.SetLength(dstLength); //adjust length of destination buffer
1.319 + return Length16BitToBytes(srcSamplesCopied);
1.320 + }
1.321 +
1.322 +
1.323 +TInt CMonoToStereoRateConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower)
1.324 + {
1.325 + return CChannelAndSampleRateConvert::MaxConvertBufferSize(aSrcBufferSize*2, aRoundUpToPower);
1.326 + }
1.327 +
1.328 +CMonoToMonoRateConverter::CMonoToMonoRateConverter()
1.329 + {
1.330 + }
1.331 +
1.332 +TInt CMonoToMonoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer)
1.333 + {
1.334 + const TInt16* srcPtr = reinterpret_cast<const TInt16*>(aSrcBuffer.Ptr());
1.335 + TInt16* dstPtr = const_cast<TInt16*>(reinterpret_cast<const TInt16*>(aDstBuffer.Ptr()));
1.336 +
1.337 + const TInt16* srcLimit=srcPtr+LengthBytesTo16Bit(aSrcBuffer.Length()); // ptr+n does *2 for TInt16* ptr
1.338 + TInt16* dstLimit=dstPtr+LengthBytesTo16Bit(aDstBuffer.MaxLength()); // ditto
1.339 +
1.340 + // add left over from last buffer
1.341 + TUint index = iIndex;
1.342 + const TInt16* src = srcPtr + (index>>16);
1.343 + TInt16* dst = dstPtr;
1.344 +
1.345 + if (dst>=dstLimit)
1.346 + {
1.347 + __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer));
1.348 + return 0;
1.349 + }
1.350 +
1.351 + while (src<srcLimit && dst<dstLimit)
1.352 + {
1.353 + *dst++ = *src;
1.354 + index += iFraction;
1.355 + src = srcPtr + (index>>16); // truncate fix-point index
1.356 + }
1.357 +
1.358 + // get amount by which index exceeded end of buffer
1.359 + // so that we can add it back to start of next buffer
1.360 + const TInt16* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration
1.361 + TInt srcSamplesCopied = conceptualLastSrc - srcPtr;
1.362 + __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space
1.363 + iIndex = index - (srcSamplesCopied << 16);
1.364 +
1.365 + // return sample byte count and setup output buffer
1.366 + TInt dstLength = Length16BitToBytes(dst-dstPtr); // size in bytes
1.367 + aDstBuffer.SetLength(dstLength); //adjust length of destination buffer
1.368 + return Length16BitToBytes(srcSamplesCopied);
1.369 + }
1.370 +
1.371 +CStereoToMonoRateConverter::CStereoToMonoRateConverter()
1.372 + {
1.373 + }
1.374 +
1.375 +//This method takes the left and right sample of interleaved PCM and sums it, then divides by 2
1.376 +TInt CStereoToMonoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer)
1.377 + {
1.378 + const TInt32* srcPtr = reinterpret_cast<const TInt32*>(aSrcBuffer.Ptr());
1.379 + TInt16* dstPtr = const_cast<TInt16*>(reinterpret_cast<const TInt16*>(aDstBuffer.Ptr()));
1.380 +
1.381 + const TInt32* srcLimit=srcPtr+LengthBytesTo32Bit(aSrcBuffer.Length()); // ptr+n does *4 for TInt32* ptr
1.382 + TInt16* dstLimit=dstPtr+LengthBytesTo16Bit(aDstBuffer.MaxLength()); // ditto but *2 for TInt16*
1.383 +
1.384 + // add left over from last buffer
1.385 + TUint index = iIndex;
1.386 + const TInt32* src = srcPtr + (index>>16);
1.387 + TInt16* dst = dstPtr;
1.388 +
1.389 + if (dst>=dstLimit)
1.390 + {
1.391 + __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer));
1.392 + return 0;
1.393 + }
1.394 +
1.395 + while (src<srcLimit && dst<dstLimit)
1.396 + {
1.397 + TInt32 sample = *src;
1.398 + TInt16 monoSample = StereoToMono(sample);
1.399 + *dst++ = monoSample;
1.400 + index += iFraction;
1.401 + src = srcPtr + (index>>16); // truncate fix-point index
1.402 + }
1.403 +
1.404 + // get amount by which index exceeded end of buffer
1.405 + // so that we can add it back to start of next buffer
1.406 + const TInt32* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration
1.407 + TInt srcSamplesCopied = conceptualLastSrc - srcPtr;
1.408 + __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space
1.409 + iIndex = index - (srcSamplesCopied << 16);
1.410 +
1.411 + // return sample byte count and setup output buffer
1.412 + TInt dstLength = Length16BitToBytes(dst-dstPtr); // size in bytes
1.413 + aDstBuffer.SetLength(dstLength); //adjust length of destination buffer
1.414 + return Length32BitToBytes(srcSamplesCopied);
1.415 + }
1.416 +
1.417 +
1.418 +TInt CStereoToMonoRateConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower)
1.419 + {
1.420 + TUint size = aSrcBufferSize/2;
1.421 + size += aSrcBufferSize & 1; //avoid round down error
1.422 + return CChannelAndSampleRateConvert::MaxConvertBufferSize(size, aRoundUpToPower);
1.423 + }
1.424 +
1.425 +CStereoToMonoConverter::CStereoToMonoConverter()
1.426 + {
1.427 + }
1.428 +
1.429 +//This method takes the left and right sample of interleaved PCM and sums it, then divides by 2
1.430 +TInt CStereoToMonoConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer)
1.431 + {
1.432 + const TInt32* src = reinterpret_cast<const TInt32*>(aSrcBuffer.Ptr());
1.433 + TInt16* dst = const_cast<TInt16*>(reinterpret_cast<const TInt16*>(aDstBuffer.Ptr()));
1.434 +
1.435 + TInt srcCount = LengthBytesTo32Bit(aSrcBuffer.Length());
1.436 + TInt dstCount = LengthBytesTo16Bit(aDstBuffer.MaxLength());
1.437 + TInt count = Min(srcCount, dstCount); // if aDstBuffer is short, just copy that much
1.438 +
1.439 + for (TUint i=0;i<count;++i)
1.440 + {
1.441 + TInt32 sample = *src++;
1.442 + TInt16 monoSample = StereoToMono(sample);
1.443 + *dst++ = monoSample;
1.444 + }
1.445 +
1.446 + aDstBuffer.SetLength(Length16BitToBytes(count)); // *2 because is mono
1.447 + return Length32BitToBytes(count); // *4 as is stereo
1.448 + }
1.449 +
1.450 +TInt CStereoToMonoConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower)
1.451 + {
1.452 + TUint size = aSrcBufferSize/2;
1.453 + size += aSrcBufferSize & 1; //avoid round down error
1.454 + return CChannelAndSampleRateConverterCommon::MaxConvertBufferSize(size, aRoundUpToPower);
1.455 + }
1.456 +
1.457 +CMonoToStereoConverter::CMonoToStereoConverter()
1.458 + {
1.459 + }
1.460 +
1.461 +TInt CMonoToStereoConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer)
1.462 + {
1.463 + const TInt16* src = reinterpret_cast<const TInt16*>(aSrcBuffer.Ptr());
1.464 + TInt32* dst = const_cast<TInt32*>(reinterpret_cast<const TInt32*>(aDstBuffer.Ptr()));
1.465 +
1.466 + TInt srcCount = LengthBytesTo16Bit(aSrcBuffer.Length());
1.467 + TInt dstCount = LengthBytesTo32Bit(aDstBuffer.MaxLength());
1.468 + TInt count = Min(srcCount, dstCount); // if aDstBuffer is short, just copy that much
1.469 +
1.470 + for (TUint i=0;i<count;i++)
1.471 + {
1.472 + TInt16 sample = *src++;
1.473 + TInt32 stereoSample = MonoToStereo(sample);
1.474 + *dst++ = stereoSample;
1.475 + }
1.476 +
1.477 + aDstBuffer.SetLength(Length32BitToBytes(count)); // *4 because is stereo
1.478 + return Length16BitToBytes(count); // *2 as is mono
1.479 + }
1.480 +
1.481 +TInt CMonoToStereoConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower)
1.482 + {
1.483 + return CChannelAndSampleRateConverterCommon::MaxConvertBufferSize(aSrcBufferSize*2, aRoundUpToPower);
1.484 + }
1.485 +
1.486 +
1.487 +
1.488 +
1.489 +