|
1 // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // include\mmf\utils\rateconvert.cpp |
|
15 // Note this code used to be in mmfutilities.cpp but split off here to allow |
|
16 // scaling of descriptors instead of just CMMFBuffers. |
|
17 // |
|
18 // |
|
19 |
|
20 #include "rateconvertimpl.h" // includes rateconvert.h itself |
|
21 #include <e32const.h> |
|
22 |
|
23 const TInt KMaxInt16Bit = 65536 ; |
|
24 |
|
25 /* |
|
26 The rate conversion algorithms used here are extremely basic, using nearest neighbour, not |
|
27 even interpolation. An increment is created - initially a real, but converted to 16.16 fixed |
|
28 point notation for efficiency purposes. For example, from 8000 to 11025 this increment is set |
|
29 at 8000/11025 (~ 0.73). Each increment to the destination buffer conceptually increments the |
|
30 src pointer by this value (0.73). On each iteration the nearest src sample is used. |
|
31 |
|
32 The idea is that successive buffers run on from each other. The index is adjusted so at the end |
|
33 of the run it is correct for the next buffer, and this is saved from one iteration to the next. |
|
34 If the client wants to convert separate buffers, it should call Reset(), where the index is reset |
|
35 to 0. |
|
36 |
|
37 Note the algorithm is even then not ideal, as it effectively truncates and not rounds the |
|
38 fixed-point index. However, a feature of this is that the nearest src sample is always behind the |
|
39 conceptual fixed-point index. This makes it easy to ensure that processing of the next buffer |
|
40 never needs data from the previous cycle - except this index value. |
|
41 |
|
42 */ |
|
43 |
|
44 enum TPanicCodes |
|
45 { |
|
46 EPanicNoDestinationBuffer=1, |
|
47 EPanicNoSourceConsumed |
|
48 }; |
|
49 |
|
50 #ifdef _DEBUG |
|
51 |
|
52 static void Panic(TInt aPanicCode) |
|
53 { |
|
54 _LIT(KRateConvert,"RateConvert"); |
|
55 User::Panic(KRateConvert, aPanicCode); |
|
56 } |
|
57 |
|
58 #endif // _DEBUG |
|
59 |
|
60 // |
|
61 // CChannelAndSampleRateConverter |
|
62 // |
|
63 |
|
64 CChannelAndSampleRateConverter::CChannelAndSampleRateConverter() |
|
65 { |
|
66 // constructor does nothing but ensures can't derive from outside dll |
|
67 } |
|
68 |
|
69 // Factory function |
|
70 EXPORT_C CChannelAndSampleRateConverter* CChannelAndSampleRateConverter::CreateL(TInt aFromRate, |
|
71 TInt aFromChannels, |
|
72 TInt aToRate, |
|
73 TInt aToChannels) |
|
74 { |
|
75 // check that the params in range so we can assume OK later on |
|
76 if (aFromChannels <= 0 || aFromChannels > 2 || |
|
77 aToChannels <= 0 || aToChannels > 2 || |
|
78 aFromRate <= 0 || aToRate <= 0) |
|
79 { |
|
80 User::Leave(KErrArgument); |
|
81 } |
|
82 |
|
83 CChannelAndSampleRateConverterCommon* converter = NULL; |
|
84 |
|
85 if (aFromChannels==aToChannels) |
|
86 { |
|
87 if (aFromChannels==1) |
|
88 { |
|
89 converter = new (ELeave) CMonoToMonoRateConverter; |
|
90 } |
|
91 else |
|
92 { |
|
93 converter = new (ELeave) CStereoToStereoRateConverter; |
|
94 } |
|
95 } |
|
96 else |
|
97 { |
|
98 if (aFromChannels==1) |
|
99 { |
|
100 if (aFromRate!=aToRate) |
|
101 { |
|
102 converter = new (ELeave) CMonoToStereoRateConverter; |
|
103 } |
|
104 else |
|
105 { |
|
106 converter = new (ELeave) CMonoToStereoConverter; |
|
107 } |
|
108 } |
|
109 else |
|
110 { |
|
111 ASSERT(aFromChannels>1 && aToChannels==1); |
|
112 if (aFromRate!=aToRate) |
|
113 { |
|
114 converter = new (ELeave) CStereoToMonoRateConverter; |
|
115 } |
|
116 else |
|
117 { |
|
118 converter = new (ELeave) CStereoToMonoConverter; |
|
119 } |
|
120 } |
|
121 } |
|
122 converter->SetRates(aFromRate,aToRate); |
|
123 return converter; |
|
124 } |
|
125 |
|
126 // |
|
127 // CChannelAndSampleRateConverterImpl |
|
128 // |
|
129 |
|
130 CChannelAndSampleRateConverterCommon::CChannelAndSampleRateConverterCommon() |
|
131 { |
|
132 } |
|
133 |
|
134 void CChannelAndSampleRateConverterCommon::Reset() |
|
135 { |
|
136 // default does nothing |
|
137 } |
|
138 |
|
139 void CChannelAndSampleRateConverterCommon::SetRates(TInt /*aFromRate*/, TInt /*aToRate*/) |
|
140 { |
|
141 // in default no need to know so don't cache |
|
142 } |
|
143 |
|
144 TInt CChannelAndSampleRateConverterCommon::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower) |
|
145 { |
|
146 // assume aSrcBuffer takes channel change into account |
|
147 TInt rawValue = aSrcBufferSize; |
|
148 if (aRoundUpToPower) |
|
149 { |
|
150 return NextPowerUp(rawValue); |
|
151 } |
|
152 else |
|
153 { |
|
154 return rawValue; |
|
155 } |
|
156 } |
|
157 |
|
158 TInt CChannelAndSampleRateConverterCommon::NextPowerUp(TInt aValue) |
|
159 { |
|
160 TInt power = 128; // no need to start lower |
|
161 while (power<aValue) |
|
162 { |
|
163 power *= 2; |
|
164 } |
|
165 return power; |
|
166 } |
|
167 |
|
168 // |
|
169 // CChannelAndSampleRateConvert |
|
170 // |
|
171 |
|
172 CChannelAndSampleRateConvert::CChannelAndSampleRateConvert() |
|
173 { |
|
174 } |
|
175 |
|
176 |
|
177 TInt CChannelAndSampleRateConvert::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower) |
|
178 { |
|
179 // take rate conversion into account. Assumed channel mismatch handled by the child class. |
|
180 TInt rawValue = aSrcBufferSize; |
|
181 if (iFromRate < iToRate) |
|
182 { |
|
183 TInt result = SizeOfUpsampleBuffer(rawValue); |
|
184 return result; // always rounded up to next size |
|
185 } |
|
186 else |
|
187 { |
|
188 if (aRoundUpToPower) |
|
189 { |
|
190 return NextPowerUp(rawValue); |
|
191 } |
|
192 else |
|
193 { |
|
194 return rawValue; |
|
195 } |
|
196 } |
|
197 } |
|
198 |
|
199 void CChannelAndSampleRateConvert::SetRates(TInt aFromRate, TInt aToRate) |
|
200 { |
|
201 iFromRate=aFromRate; |
|
202 iToRate=aToRate; |
|
203 |
|
204 TReal ratio = TReal(aFromRate) / TReal(aToRate); |
|
205 TInt quotient = TInt(ratio); |
|
206 TReal remainder = ratio - TReal(quotient); |
|
207 iFraction = (quotient << 16) + TInt32(remainder * KMaxInt16Bit); |
|
208 |
|
209 Reset(); |
|
210 } |
|
211 |
|
212 void CChannelAndSampleRateConvert::Reset() |
|
213 { |
|
214 iIndex = 0; |
|
215 } |
|
216 |
|
217 TInt CChannelAndSampleRateConvert::SizeOfUpsampleBuffer(TInt aBufferLength) |
|
218 { |
|
219 TInt rawValue = aBufferLength; |
|
220 ASSERT(iFromRate < iToRate); // should not be called otherwise |
|
221 // upsample - will generate more data. use floats to avoid extra round error |
|
222 rawValue = TInt(rawValue * TReal(iToRate) / TReal(iFromRate) + 0.5) + 4*sizeof(TInt16); // add some fudge factor just in case |
|
223 rawValue = NextPowerUp(rawValue); // when upscaling always give nice power |
|
224 return rawValue; |
|
225 } |
|
226 |
|
227 // |
|
228 // Specific converter code |
|
229 // |
|
230 |
|
231 CStereoToStereoRateConverter::CStereoToStereoRateConverter() |
|
232 { |
|
233 } |
|
234 |
|
235 TInt CStereoToStereoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) |
|
236 { |
|
237 const TInt32* srcPtr = reinterpret_cast<const TInt32*>(aSrcBuffer.Ptr()); |
|
238 TInt32* dstPtr = const_cast<TInt32*>(reinterpret_cast<const TInt32*>(aDstBuffer.Ptr())); |
|
239 |
|
240 const TInt32* srcLimit=srcPtr+LengthBytesTo32Bit(aSrcBuffer.Length()); // ptr+n does *4 for TInt32* ptr |
|
241 TInt32* dstLimit=dstPtr+LengthBytesTo32Bit(aDstBuffer.MaxLength()); // ditto |
|
242 |
|
243 // add left over from last buffer |
|
244 TUint index = iIndex; |
|
245 const TInt32* src = srcPtr + (index>>16); |
|
246 TInt32* dst = dstPtr; |
|
247 |
|
248 if (dst>=dstLimit) |
|
249 { |
|
250 __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer)); |
|
251 return 0; |
|
252 } |
|
253 |
|
254 while (src<srcLimit && dst<dstLimit) |
|
255 { |
|
256 *dst++ = *src; |
|
257 index += iFraction; |
|
258 src = srcPtr + (index>>16); // truncate fix-point index |
|
259 } |
|
260 |
|
261 // get amount by which index exceeded end of buffer |
|
262 // so that we can add it back to start of next buffer |
|
263 const TInt32* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration |
|
264 TInt srcSamplesCopied = conceptualLastSrc - srcPtr; |
|
265 __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space |
|
266 iIndex = index - (srcSamplesCopied << 16); |
|
267 |
|
268 // return sample byte count and setup output buffer |
|
269 TInt dstLength = Length32BitToBytes(dst-dstPtr); |
|
270 aDstBuffer.SetLength(dstLength); //adjust length of destination buffer |
|
271 return Length32BitToBytes(srcSamplesCopied); |
|
272 } |
|
273 |
|
274 CMonoToStereoRateConverter::CMonoToStereoRateConverter() |
|
275 { |
|
276 } |
|
277 |
|
278 TInt CMonoToStereoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) |
|
279 { |
|
280 const TInt16* srcPtr = reinterpret_cast<const TInt16*>(aSrcBuffer.Ptr()); |
|
281 TInt32* dstPtr = const_cast<TInt32*>(reinterpret_cast<const TInt32*>(aDstBuffer.Ptr())); |
|
282 |
|
283 const TInt16* srcLimit=srcPtr+LengthBytesTo16Bit(aSrcBuffer.Length()); // as ptr+n does *2 for TInt16* ptr |
|
284 TInt32* dstLimit=dstPtr+LengthBytesTo32Bit(aDstBuffer.MaxLength()); // ditto but does *4 for TInt32* |
|
285 |
|
286 // add left over from last buffer |
|
287 TUint index = iIndex; |
|
288 const TInt16* src = srcPtr + (index>>16); |
|
289 TInt32* dst = dstPtr; |
|
290 |
|
291 if (dst>=dstLimit) |
|
292 { |
|
293 __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer)); |
|
294 return 0; |
|
295 } |
|
296 |
|
297 while (src<srcLimit && dst<dstLimit-1) |
|
298 { |
|
299 TInt16 sample = *src; |
|
300 TInt32 stereoSample = MonoToStereo(sample); |
|
301 *dst++ = stereoSample; |
|
302 index += iFraction; |
|
303 src = srcPtr + (index>>16); // truncate fix-point index |
|
304 } |
|
305 |
|
306 // get amount by which index exceeded end of buffer |
|
307 // so that we can add it back to start of next buffer |
|
308 const TInt16* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration |
|
309 TInt srcSamplesCopied = conceptualLastSrc - srcPtr; |
|
310 __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space |
|
311 iIndex = index - (srcSamplesCopied << 16); |
|
312 |
|
313 // return sample byte count and setup output buffer |
|
314 TInt dstLength = Length32BitToBytes(dst-dstPtr); // size in bytes |
|
315 aDstBuffer.SetLength(dstLength); //adjust length of destination buffer |
|
316 return Length16BitToBytes(srcSamplesCopied); |
|
317 } |
|
318 |
|
319 |
|
320 TInt CMonoToStereoRateConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower) |
|
321 { |
|
322 return CChannelAndSampleRateConvert::MaxConvertBufferSize(aSrcBufferSize*2, aRoundUpToPower); |
|
323 } |
|
324 |
|
325 CMonoToMonoRateConverter::CMonoToMonoRateConverter() |
|
326 { |
|
327 } |
|
328 |
|
329 TInt CMonoToMonoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) |
|
330 { |
|
331 const TInt16* srcPtr = reinterpret_cast<const TInt16*>(aSrcBuffer.Ptr()); |
|
332 TInt16* dstPtr = const_cast<TInt16*>(reinterpret_cast<const TInt16*>(aDstBuffer.Ptr())); |
|
333 |
|
334 const TInt16* srcLimit=srcPtr+LengthBytesTo16Bit(aSrcBuffer.Length()); // ptr+n does *2 for TInt16* ptr |
|
335 TInt16* dstLimit=dstPtr+LengthBytesTo16Bit(aDstBuffer.MaxLength()); // ditto |
|
336 |
|
337 // add left over from last buffer |
|
338 TUint index = iIndex; |
|
339 const TInt16* src = srcPtr + (index>>16); |
|
340 TInt16* dst = dstPtr; |
|
341 |
|
342 if (dst>=dstLimit) |
|
343 { |
|
344 __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer)); |
|
345 return 0; |
|
346 } |
|
347 |
|
348 while (src<srcLimit && dst<dstLimit) |
|
349 { |
|
350 *dst++ = *src; |
|
351 index += iFraction; |
|
352 src = srcPtr + (index>>16); // truncate fix-point index |
|
353 } |
|
354 |
|
355 // get amount by which index exceeded end of buffer |
|
356 // so that we can add it back to start of next buffer |
|
357 const TInt16* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration |
|
358 TInt srcSamplesCopied = conceptualLastSrc - srcPtr; |
|
359 __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space |
|
360 iIndex = index - (srcSamplesCopied << 16); |
|
361 |
|
362 // return sample byte count and setup output buffer |
|
363 TInt dstLength = Length16BitToBytes(dst-dstPtr); // size in bytes |
|
364 aDstBuffer.SetLength(dstLength); //adjust length of destination buffer |
|
365 return Length16BitToBytes(srcSamplesCopied); |
|
366 } |
|
367 |
|
368 CStereoToMonoRateConverter::CStereoToMonoRateConverter() |
|
369 { |
|
370 } |
|
371 |
|
372 //This method takes the left and right sample of interleaved PCM and sums it, then divides by 2 |
|
373 TInt CStereoToMonoRateConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) |
|
374 { |
|
375 const TInt32* srcPtr = reinterpret_cast<const TInt32*>(aSrcBuffer.Ptr()); |
|
376 TInt16* dstPtr = const_cast<TInt16*>(reinterpret_cast<const TInt16*>(aDstBuffer.Ptr())); |
|
377 |
|
378 const TInt32* srcLimit=srcPtr+LengthBytesTo32Bit(aSrcBuffer.Length()); // ptr+n does *4 for TInt32* ptr |
|
379 TInt16* dstLimit=dstPtr+LengthBytesTo16Bit(aDstBuffer.MaxLength()); // ditto but *2 for TInt16* |
|
380 |
|
381 // add left over from last buffer |
|
382 TUint index = iIndex; |
|
383 const TInt32* src = srcPtr + (index>>16); |
|
384 TInt16* dst = dstPtr; |
|
385 |
|
386 if (dst>=dstLimit) |
|
387 { |
|
388 __ASSERT_DEBUG(EFalse,Panic(EPanicNoDestinationBuffer)); |
|
389 return 0; |
|
390 } |
|
391 |
|
392 while (src<srcLimit && dst<dstLimit) |
|
393 { |
|
394 TInt32 sample = *src; |
|
395 TInt16 monoSample = StereoToMono(sample); |
|
396 *dst++ = monoSample; |
|
397 index += iFraction; |
|
398 src = srcPtr + (index>>16); // truncate fix-point index |
|
399 } |
|
400 |
|
401 // get amount by which index exceeded end of buffer |
|
402 // so that we can add it back to start of next buffer |
|
403 const TInt32* conceptualLastSrc = Min(src, srcLimit); // ptr to last src we have we'd _not_ used next iteration |
|
404 TInt srcSamplesCopied = conceptualLastSrc - srcPtr; |
|
405 __ASSERT_DEBUG(srcSamplesCopied>0, Panic(EPanicNoSourceConsumed)); // should always be ok if we have some destination space |
|
406 iIndex = index - (srcSamplesCopied << 16); |
|
407 |
|
408 // return sample byte count and setup output buffer |
|
409 TInt dstLength = Length16BitToBytes(dst-dstPtr); // size in bytes |
|
410 aDstBuffer.SetLength(dstLength); //adjust length of destination buffer |
|
411 return Length32BitToBytes(srcSamplesCopied); |
|
412 } |
|
413 |
|
414 |
|
415 TInt CStereoToMonoRateConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower) |
|
416 { |
|
417 TUint size = aSrcBufferSize/2; |
|
418 size += aSrcBufferSize & 1; //avoid round down error |
|
419 return CChannelAndSampleRateConvert::MaxConvertBufferSize(size, aRoundUpToPower); |
|
420 } |
|
421 |
|
422 CStereoToMonoConverter::CStereoToMonoConverter() |
|
423 { |
|
424 } |
|
425 |
|
426 //This method takes the left and right sample of interleaved PCM and sums it, then divides by 2 |
|
427 TInt CStereoToMonoConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) |
|
428 { |
|
429 const TInt32* src = reinterpret_cast<const TInt32*>(aSrcBuffer.Ptr()); |
|
430 TInt16* dst = const_cast<TInt16*>(reinterpret_cast<const TInt16*>(aDstBuffer.Ptr())); |
|
431 |
|
432 TInt srcCount = LengthBytesTo32Bit(aSrcBuffer.Length()); |
|
433 TInt dstCount = LengthBytesTo16Bit(aDstBuffer.MaxLength()); |
|
434 TInt count = Min(srcCount, dstCount); // if aDstBuffer is short, just copy that much |
|
435 |
|
436 for (TUint i=0;i<count;++i) |
|
437 { |
|
438 TInt32 sample = *src++; |
|
439 TInt16 monoSample = StereoToMono(sample); |
|
440 *dst++ = monoSample; |
|
441 } |
|
442 |
|
443 aDstBuffer.SetLength(Length16BitToBytes(count)); // *2 because is mono |
|
444 return Length32BitToBytes(count); // *4 as is stereo |
|
445 } |
|
446 |
|
447 TInt CStereoToMonoConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower) |
|
448 { |
|
449 TUint size = aSrcBufferSize/2; |
|
450 size += aSrcBufferSize & 1; //avoid round down error |
|
451 return CChannelAndSampleRateConverterCommon::MaxConvertBufferSize(size, aRoundUpToPower); |
|
452 } |
|
453 |
|
454 CMonoToStereoConverter::CMonoToStereoConverter() |
|
455 { |
|
456 } |
|
457 |
|
458 TInt CMonoToStereoConverter::Convert(const TDesC8& aSrcBuffer, TDes8& aDstBuffer) |
|
459 { |
|
460 const TInt16* src = reinterpret_cast<const TInt16*>(aSrcBuffer.Ptr()); |
|
461 TInt32* dst = const_cast<TInt32*>(reinterpret_cast<const TInt32*>(aDstBuffer.Ptr())); |
|
462 |
|
463 TInt srcCount = LengthBytesTo16Bit(aSrcBuffer.Length()); |
|
464 TInt dstCount = LengthBytesTo32Bit(aDstBuffer.MaxLength()); |
|
465 TInt count = Min(srcCount, dstCount); // if aDstBuffer is short, just copy that much |
|
466 |
|
467 for (TUint i=0;i<count;i++) |
|
468 { |
|
469 TInt16 sample = *src++; |
|
470 TInt32 stereoSample = MonoToStereo(sample); |
|
471 *dst++ = stereoSample; |
|
472 } |
|
473 |
|
474 aDstBuffer.SetLength(Length32BitToBytes(count)); // *4 because is stereo |
|
475 return Length16BitToBytes(count); // *2 as is mono |
|
476 } |
|
477 |
|
478 TInt CMonoToStereoConverter::MaxConvertBufferSize(TInt aSrcBufferSize, TBool aRoundUpToPower) |
|
479 { |
|
480 return CChannelAndSampleRateConverterCommon::MaxConvertBufferSize(aSrcBufferSize*2, aRoundUpToPower); |
|
481 } |
|
482 |
|
483 |
|
484 |
|
485 |
|
486 |