|
1 // Copyright (c) 2008-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 // |
|
15 #include "mdasoundadapterconsts.h" |
|
16 #include "mdasoundadapterbody.h" |
|
17 #include <e32debug.h> |
|
18 |
|
19 #include "mmf/utils/rateconvert.h" // if we need to resample |
|
20 |
|
21 #ifdef SYMBIAN_SOUNDADAPTER_BYTESPLAYED |
|
22 #include <hal.h> |
|
23 #endif |
|
24 |
|
25 _LIT(KPddFileName,"SOUNDSC.PDD"); |
|
26 _LIT(KLddFileName,"ESOUNDSC.LDD"); |
|
27 |
|
28 |
|
29 const TInt KBytesPerSample = 2; |
|
30 const TInt KMinBufferSize = 2; |
|
31 |
|
32 /** |
|
33 This function raises a panic |
|
34 EDeviceNotOpened is raised when any of the RMdaDevSound APIs are called before opening the device. |
|
35 */ |
|
36 GLDEF_C void Panic(TSoundAdapterPanicCodes aPanicCode) |
|
37 { |
|
38 User::Panic(KSoundAdapterPanicCategory, aPanicCode); |
|
39 } |
|
40 |
|
41 RMdaDevSound::CBody* RMdaDevSound::CBody::NewL() |
|
42 { |
|
43 CBody* self = new(ELeave) CBody(); |
|
44 CleanupStack::PushL(self); |
|
45 self->ConstructL(); |
|
46 CleanupStack::Pop(); |
|
47 return self; |
|
48 } |
|
49 |
|
50 RMdaDevSound::CBody::~CBody() |
|
51 { |
|
52 for(TInt i = 0; i < KNumPlayers; i++) |
|
53 { |
|
54 delete iPlayers[i]; |
|
55 iPlayers[i] = NULL; |
|
56 } |
|
57 iBufferRemaining.Close(); |
|
58 delete iPlayData.iConverter; |
|
59 delete iRecordData.iConverter; |
|
60 iChunk.Close(); |
|
61 iPlaySoundDevice.Close(); |
|
62 iRecordSoundDevice.Close(); |
|
63 } |
|
64 |
|
65 RMdaDevSound::CBody::CBody() |
|
66 :iState(ENotReady), iBufferIndex(-1), iBufferOffset(-1), iSecondPhaseData(0,0) |
|
67 { |
|
68 |
|
69 } |
|
70 |
|
71 TVersion RMdaDevSound::CBody::VersionRequired() const |
|
72 { |
|
73 if(iPlaySoundDevice.Handle()) |
|
74 { |
|
75 return iPlaySoundDevice.VersionRequired(); |
|
76 } |
|
77 else |
|
78 { |
|
79 return TVersion(); |
|
80 } |
|
81 } |
|
82 |
|
83 TInt RMdaDevSound::CBody::IsMdaSound() |
|
84 { |
|
85 return ETrue; |
|
86 } |
|
87 |
|
88 void RMdaDevSound::CBody::ConstructL() |
|
89 { |
|
90 // Try to load the audio physical driver |
|
91 TInt err = User::LoadPhysicalDevice(KPddFileName); |
|
92 if ((err!=KErrNone) && (err!=KErrAlreadyExists)) |
|
93 { |
|
94 User::Leave(err); |
|
95 } |
|
96 // Try to load the audio logical driver |
|
97 err = User::LoadLogicalDevice(KLddFileName); |
|
98 if ((err!=KErrNone) && (err!=KErrAlreadyExists)) |
|
99 { |
|
100 User::Leave(err); |
|
101 } |
|
102 for(TInt i=0; i<KNumPlayers; i++) |
|
103 { |
|
104 iPlayers[i] = new(ELeave) CPlayer(CActive::EPriorityUserInput, *this, i); |
|
105 } |
|
106 #ifdef SYMBIAN_SOUNDADAPTER_BYTESPLAYED |
|
107 User::LeaveIfError(HAL::Get(HALData::EFastCounterFrequency,iFCFrequency)); |
|
108 #endif |
|
109 } |
|
110 |
|
111 TInt RMdaDevSound::CBody::Open(TInt /*aUnit*/) |
|
112 { |
|
113 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
114 RDebug::Print(_L("RMdaDevSound::CBody::Open ")); |
|
115 #endif |
|
116 TInt err = KErrNone; |
|
117 //Default behavior of this method is to open both the play and record audio devices. |
|
118 if(!iPlaySoundDevice.Handle() && !iRecordSoundDevice.Handle()) |
|
119 { |
|
120 err = iPlaySoundDevice.Open(KSoundScTxUnit0); |
|
121 if(err == KErrNone) |
|
122 { |
|
123 err = iRecordSoundDevice.Open(KSoundScRxUnit0); |
|
124 } |
|
125 } |
|
126 if(err != KErrNone) |
|
127 { |
|
128 Close(); |
|
129 } |
|
130 else |
|
131 { |
|
132 iState = EOpened; |
|
133 } |
|
134 return err; |
|
135 } |
|
136 |
|
137 TInt RMdaDevSound::CBody::PlayVolume() |
|
138 { |
|
139 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
140 return iPlaySoundDevice.Volume(); |
|
141 } |
|
142 |
|
143 void RMdaDevSound::CBody::SetPlayVolume(TInt aVolume) |
|
144 { |
|
145 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
146 if(aVolume >=0 && aVolume<=KSoundMaxVolume) |
|
147 { |
|
148 iPlaySoundDevice.SetVolume(KLinerToDbConstantLookup[aVolume].iDBValue); |
|
149 } |
|
150 } |
|
151 void RMdaDevSound::CBody::SetVolume(TInt aLogarithmicVolume) |
|
152 { |
|
153 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
154 if(aLogarithmicVolume >= 0 && aLogarithmicVolume <= KSoundMaxVolume) |
|
155 { |
|
156 iPlaySoundDevice.SetVolume(aLogarithmicVolume); |
|
157 } |
|
158 } |
|
159 |
|
160 void RMdaDevSound::CBody::CancelPlayData() |
|
161 { |
|
162 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
163 RDebug::Print(_L("RMdaDevSound::CBody::CancelPlayData:")); |
|
164 #endif |
|
165 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
166 iPlaySoundDevice.CancelPlayData(); |
|
167 iPauseDeviceDriverOnNewData = EFalse; |
|
168 SoundDeviceError(KErrNone);//cancel the players |
|
169 } |
|
170 |
|
171 TInt RMdaDevSound::CBody::RecordLevel() |
|
172 { |
|
173 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
174 return iRecordSoundDevice.Volume(); |
|
175 } |
|
176 |
|
177 void RMdaDevSound::CBody::SetRecordLevel(TInt aLevel) |
|
178 { |
|
179 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
180 iRecordSoundDevice.SetVolume(aLevel); |
|
181 } |
|
182 |
|
183 void RMdaDevSound::CBody::CancelRecordData() |
|
184 { |
|
185 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
186 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
187 RDebug::Print(_L("RMdaDevSound::CBody::CancelRecordData:")); |
|
188 #endif |
|
189 iRecordSoundDevice.CancelRecordData(); |
|
190 SoundDeviceError(KErrNone); |
|
191 } |
|
192 |
|
193 void RMdaDevSound::CBody::FlushRecordBuffer() |
|
194 { |
|
195 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
196 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
197 RDebug::Print(_L("RMdaDevSound::CBody::FlushRecordBuffer")); |
|
198 #endif |
|
199 |
|
200 if(iState == ERecording) |
|
201 { |
|
202 iRecordSoundDevice.Pause();//this is equivalent call in the new sound driver |
|
203 } |
|
204 } |
|
205 |
|
206 TInt RMdaDevSound::CBody::BytesPlayed() |
|
207 { |
|
208 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
209 TInt currentBytesPlayed = 0; |
|
210 #ifdef SYMBIAN_SOUNDADAPTER_BYTESPLAYED |
|
211 if(iTimerActive) |
|
212 { |
|
213 TUint32 endTime = User::FastCounter(); |
|
214 TUint timePlayed = 0; |
|
215 if(endTime<iStartTime) |
|
216 { |
|
217 timePlayed = (KMaxTUint32-iStartTime)+endTime; |
|
218 } |
|
219 else |
|
220 { |
|
221 timePlayed = endTime-iStartTime; |
|
222 } |
|
223 TUint32 bytesPlayed = (timePlayed*iPlayData.iSampleRate*KBytesPerSample)/iFCFrequency; |
|
224 currentBytesPlayed = iBytesPlayed+bytesPlayed; |
|
225 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
226 RDebug::Printf("EstimatedBytesPlayed[%d] Driver's bytesPlayed[%d]", currentBytesPlayed, iBytesPlayed); |
|
227 #endif |
|
228 } |
|
229 else |
|
230 { |
|
231 currentBytesPlayed = iPlaySoundDevice.BytesTransferred(); |
|
232 } |
|
233 |
|
234 #else |
|
235 currentBytesPlayed = iPlaySoundDevice.BytesTransferred(); |
|
236 #endif |
|
237 if (iPlayData.iConverter) |
|
238 { |
|
239 // need to scale bytes played to fit with requested rate and channels, not actual |
|
240 if (iPlayData.iActualChannels != iPlayData.iRequestedChannels) |
|
241 { |
|
242 if (iPlayData.iActualChannels == 2) |
|
243 { |
|
244 // requested was mono, we have stereo |
|
245 currentBytesPlayed /= 2; |
|
246 } |
|
247 else |
|
248 { |
|
249 // requested was stereo, we have mono |
|
250 currentBytesPlayed *= 2; |
|
251 } |
|
252 } |
|
253 if (iPlayData.iSampleRate != iPlayData.iActualRate) |
|
254 { |
|
255 currentBytesPlayed = TInt(currentBytesPlayed* |
|
256 TReal(iPlayData.iSampleRate)/TReal(iPlayData.iActualRate)); // don't round |
|
257 } |
|
258 } |
|
259 return currentBytesPlayed; |
|
260 } |
|
261 |
|
262 void RMdaDevSound::CBody::ResetBytesPlayed() |
|
263 { |
|
264 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
265 return iPlaySoundDevice.ResetBytesTransferred(); |
|
266 } |
|
267 |
|
268 void RMdaDevSound::CBody::PausePlayBuffer() |
|
269 { |
|
270 if (iState == EPlaying) |
|
271 { |
|
272 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
273 RDebug::Print(_L("RMdaDevSound::CBody::PausePlayBuffer() offset = %d length = %d"), iBufferOffset, iBufferLength); |
|
274 #endif |
|
275 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
276 // If iPlayerStatus is NULL, we're not playing currently any data, and the device driver won't pause properly. In this case, |
|
277 // we set a reminder to ourselves to pause the driver once we have data later |
|
278 if (iPlayerStatus == NULL) |
|
279 { |
|
280 iPauseDeviceDriverOnNewData = ETrue; |
|
281 } |
|
282 else |
|
283 { |
|
284 TInt res = iPlaySoundDevice.Pause(); |
|
285 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
286 RDebug::Printf("iPlaySoundDevice.Pause res = %d", res); |
|
287 #endif |
|
288 (void)res; |
|
289 } |
|
290 iState = EPaused; |
|
291 iTimerActive = EFalse; |
|
292 } |
|
293 } |
|
294 |
|
295 void RMdaDevSound::CBody::ResumePlaying() |
|
296 { |
|
297 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
298 iPauseDeviceDriverOnNewData = EFalse; |
|
299 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
300 RDebug::Printf("RMdaDevSound::CBody::ResumePlaying"); |
|
301 #endif |
|
302 if (iFlushCalledDuringPause) |
|
303 { |
|
304 // if we resume having called flush, we can't just keep going as the driver does not work |
|
305 // that way. Instead we cancel so that buffer play completes and a new buffer will be passed |
|
306 iFlushCalledDuringPause = EFalse; |
|
307 PlayCancelled(); |
|
308 } |
|
309 else |
|
310 { |
|
311 iState = EPlaying; |
|
312 iPlaySoundDevice.Resume(); |
|
313 } |
|
314 } |
|
315 |
|
316 void RMdaDevSound::CBody::PauseRecordBuffer() |
|
317 { |
|
318 if(iState == ERecording) |
|
319 { |
|
320 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
321 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
322 RDebug::Printf("RMdaDevSound::CBody::PauseRecordBuffer"); |
|
323 #endif |
|
324 iRecordSoundDevice.Pause(); |
|
325 } |
|
326 } |
|
327 |
|
328 void RMdaDevSound::CBody::ResumeRecording() |
|
329 { |
|
330 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
331 iRecordSoundDevice.Resume(); |
|
332 } |
|
333 |
|
334 TInt RMdaDevSound::CBody::GetTimePlayed(TTimeIntervalMicroSeconds& aTimePlayed) |
|
335 { |
|
336 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
337 TTimeIntervalMicroSecondsBuf aTimePlayedBuf; |
|
338 TInt err; |
|
339 err = iPlaySoundDevice.TimePlayed(aTimePlayedBuf); |
|
340 if (err == KErrNone) |
|
341 { |
|
342 aTimePlayed = aTimePlayedBuf(); |
|
343 } |
|
344 |
|
345 return err; |
|
346 } |
|
347 |
|
348 |
|
349 void RMdaDevSound::CBody::FormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported, RSoundSc& aSoundDevice) |
|
350 { |
|
351 TSoundFormatsSupportedV02Buf supportedFormat; |
|
352 aSoundDevice.Caps(supportedFormat); |
|
353 TUint32 rates = supportedFormat().iRates; |
|
354 |
|
355 for(TInt i = KNumSampleRates-1; i > 0 ;i--)//min to max |
|
356 { |
|
357 if(rates & KRateEnumLookup[i].iRateConstant) |
|
358 { |
|
359 aFormatsSupported().iMinRate = KRateEnumLookup[i].iRate; |
|
360 break; |
|
361 } |
|
362 } |
|
363 for(TInt i = 0; i < KNumSampleRates; i++)//max to min |
|
364 { |
|
365 if(rates & KRateEnumLookup[i].iRateConstant) |
|
366 { |
|
367 aFormatsSupported().iMaxRate = KRateEnumLookup[i].iRate; |
|
368 break; |
|
369 } |
|
370 } |
|
371 TUint32 enc = supportedFormat().iEncodings; |
|
372 |
|
373 if (enc & KSoundEncoding16BitPCM) |
|
374 { |
|
375 aFormatsSupported().iEncodings = EMdaSoundEncoding16BitPCM;// Always defaults to this |
|
376 } |
|
377 if (enc & KSoundEncoding8BitPCM) |
|
378 { |
|
379 aFormatsSupported().iEncodings |= EMdaSoundEncoding8BitPCM; |
|
380 } |
|
381 TUint32 channels = supportedFormat().iChannels; |
|
382 |
|
383 if (channels & KSoundStereoChannel) |
|
384 { |
|
385 aFormatsSupported().iChannels = 2; |
|
386 } |
|
387 else |
|
388 { |
|
389 aFormatsSupported().iChannels = 1; |
|
390 } |
|
391 aFormatsSupported().iMinBufferSize = supportedFormat().iRequestMinSize; |
|
392 aFormatsSupported().iMaxBufferSize = KMaxBufferSize; |
|
393 aFormatsSupported().iMinVolume = 0; |
|
394 aFormatsSupported().iMaxVolume = KSoundMaxVolume; |
|
395 } |
|
396 |
|
397 void RMdaDevSound::CBody::GetFormat(TCurrentSoundFormatBuf& aFormat, |
|
398 RSoundSc& /*aSoundDevice*/, |
|
399 const TFormatData &aFormatData) |
|
400 { |
|
401 // always return the requested, or the initial, not current device setting |
|
402 aFormat().iChannels = aFormatData.iRequestedChannels; // never clear if this is bitmap or value, but effectively the same |
|
403 aFormat().iRate = aFormatData.iSampleRate; |
|
404 } |
|
405 |
|
406 TInt RMdaDevSound::CBody::SetFormat(const TCurrentSoundFormatBuf& aFormat, |
|
407 RSoundSc& aSoundDevice, |
|
408 TFormatData &aFormatData) |
|
409 { |
|
410 TInt err = KErrNotFound; |
|
411 TCurrentSoundFormatV02Buf formatBuf; |
|
412 |
|
413 delete aFormatData.iConverter; |
|
414 aFormatData.iConverter = NULL; // setting this to NULL indicates we are not using converter. No other flag |
|
415 |
|
416 TInt wantedRate = aFormat().iRate; |
|
417 for(TInt index = 0; index < KNumSampleRates; index++ ) |
|
418 { |
|
419 if(wantedRate == KRateEnumLookup[index].iRate) |
|
420 { |
|
421 formatBuf().iRate = KRateEnumLookup[index].iRateEnum; |
|
422 aFormatData.iSampleRate = wantedRate; |
|
423 err = KErrNone; |
|
424 break; |
|
425 } |
|
426 } |
|
427 |
|
428 if(err == KErrNone) |
|
429 { |
|
430 formatBuf().iChannels = aFormat().iChannels; |
|
431 formatBuf().iEncoding = ESoundEncoding16BitPCM; |
|
432 formatBuf().iDataFormat = ESoundDataFormatInterleaved; |
|
433 err = aSoundDevice.SetAudioFormat(formatBuf); |
|
434 #if defined(SYMBIAN_SOUNDADAPTER_FORCECDRATES) || defined (SYMBIAN_SOUNDADAPTER_FORCESTEREO) |
|
435 err = KErrNotSupported; // force Negotiate - for debugging |
|
436 #endif |
|
437 if (err==KErrNotSupported) |
|
438 { |
|
439 // don't support directly. Perhaps can rate convert? |
|
440 err = NegotiateFormat(aFormat, aSoundDevice, aFormatData); |
|
441 } |
|
442 } |
|
443 return err; |
|
444 } |
|
445 |
|
446 TInt RMdaDevSound::CBody::NegotiateFormat(const TCurrentSoundFormatBuf& aFormat, |
|
447 RSoundSc& aSoundDevice, |
|
448 TFormatData &aFormatData) |
|
449 { |
|
450 ASSERT(!aFormatData.iConverter); // we don't clear on fail - so assuming NULL to start with |
|
451 |
|
452 TInt err = KErrNotFound; |
|
453 TCurrentSoundFormatV02Buf formatBuf; |
|
454 |
|
455 // find out first what the driver supports |
|
456 TSoundFormatsSupportedV02Buf supportedFormat; |
|
457 aSoundDevice.Caps(supportedFormat); |
|
458 TUint32 supportedRates = supportedFormat().iRates; |
|
459 #ifdef SYMBIAN_SOUNDADAPTER_FORCECDRATES |
|
460 supportedRates &= KSoundRate11025Hz| KSoundRate22050Hz | KSoundRate44100Hz; // only use CD rates - for debugging |
|
461 #endif |
|
462 |
|
463 // For PlayCase: |
|
464 // first try to find the first rate below or equal to the requested that is supported |
|
465 // initially go down and be fussy, but if we pass the requested rate find the first that |
|
466 // is supported |
|
467 // For RecordCase: |
|
468 // We want the next rate above consistently - we go down from this to the requested rate. |
|
469 // If there is one, we don't support - we _never_ upsample. |
|
470 // note that the table is given in descending order, so we start with the highest |
|
471 TInt wantedRate = aFormat().iRate; |
|
472 TInt takeTheFirst = EFalse; |
|
473 TInt nextUpValidIndex = -1; |
|
474 for(TInt index = 0; index < KNumSampleRates; index++ ) |
|
475 { |
|
476 TBool lookingAtRequestedRate = wantedRate == KRateEnumLookup[index].iRate; |
|
477 TSoundRate wantedEnum = KRateEnumLookup[index].iRateEnum; |
|
478 TUint32 equivBitmap = KRateEnumLookup[index].iRateConstant; |
|
479 TBool isSupported = (equivBitmap & supportedRates) != EFalse; |
|
480 if (lookingAtRequestedRate || takeTheFirst) |
|
481 { |
|
482 if (isSupported) |
|
483 { |
|
484 // this rate is supported |
|
485 formatBuf().iRate = wantedEnum; |
|
486 aFormatData.iActualRate = KRateEnumLookup[index].iRate; |
|
487 err = KErrNone; |
|
488 break; |
|
489 } |
|
490 } |
|
491 else if (!takeTheFirst) |
|
492 { |
|
493 // while we are still looking for the rate, want to cache any supported index |
|
494 // at end of loop, this will be the first rate above ours that is supported |
|
495 // use for fallback if required |
|
496 if (isSupported) |
|
497 { |
|
498 nextUpValidIndex = index; |
|
499 } |
|
500 } |
|
501 if (lookingAtRequestedRate) |
|
502 { |
|
503 // if we get this far we've gone passed the wanted rate. For play we enable |
|
504 // "takeTheFirst". For record we just abort. |
|
505 if (&aSoundDevice==&iPlaySoundDevice) |
|
506 { |
|
507 takeTheFirst = ETrue; |
|
508 } |
|
509 else |
|
510 { |
|
511 break; |
|
512 } |
|
513 } |
|
514 } |
|
515 |
|
516 if (err) |
|
517 { |
|
518 // if there is one above the requested rate, use that |
|
519 if (nextUpValidIndex>=0) |
|
520 { |
|
521 TSoundRate wantedEnum = KRateEnumLookup[nextUpValidIndex].iRateEnum; |
|
522 formatBuf().iRate = wantedEnum; |
|
523 aFormatData.iActualRate = KRateEnumLookup[nextUpValidIndex].iRate; |
|
524 err = KErrNone; |
|
525 } |
|
526 } |
|
527 |
|
528 if (err) |
|
529 { |
|
530 // should have something! |
|
531 return err; |
|
532 } |
|
533 |
|
534 aFormatData.iSampleRate = wantedRate; // iSampleRate is our requested/apparent rate, not the device rate. |
|
535 |
|
536 TUint32 channelsSupported = supportedFormat().iChannels; |
|
537 #ifdef SYMBIAN_SOUNDADAPTER_FORCESTEREO |
|
538 channelsSupported &= KSoundStereoChannel; // don't use mono - for debugging |
|
539 #endif |
|
540 if(KSoundAdapterForceStereo==1) |
|
541 { |
|
542 channelsSupported &= KSoundStereoChannel; |
|
543 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
544 RDebug::Print(_L("Added stereo support.")); |
|
545 #endif |
|
546 } |
|
547 if (aFormat().iChannels == 1) |
|
548 { |
|
549 aFormatData.iRequestedChannels = 1; |
|
550 // want mono |
|
551 if (channelsSupported & KSoundMonoChannel) |
|
552 { |
|
553 // mono is supported, as usual |
|
554 aFormatData.iActualChannels = 1; |
|
555 } |
|
556 else if (channelsSupported & KSoundStereoChannel) |
|
557 { |
|
558 aFormatData.iActualChannels = 2; |
|
559 } |
|
560 else |
|
561 { |
|
562 return KErrNotSupported; // should not get this far for real |
|
563 } |
|
564 } |
|
565 else if (aFormat().iChannels == 2) |
|
566 { |
|
567 aFormatData.iRequestedChannels = 2; |
|
568 // want stereo |
|
569 if (channelsSupported & KSoundStereoChannel) |
|
570 { |
|
571 // stereo is supported, as usual |
|
572 aFormatData.iActualChannels = 2; |
|
573 } |
|
574 else if (channelsSupported & KSoundMonoChannel) |
|
575 { |
|
576 aFormatData.iActualChannels = 1; |
|
577 } |
|
578 else |
|
579 { |
|
580 return KErrNotSupported; // should not get this far for real |
|
581 } |
|
582 } |
|
583 else |
|
584 { |
|
585 return KErrNotSupported; // unknown number of channels requested! |
|
586 } |
|
587 |
|
588 formatBuf().iChannels = aFormatData.iActualChannels; |
|
589 |
|
590 formatBuf().iEncoding = ESoundEncoding16BitPCM; |
|
591 formatBuf().iDataFormat = ESoundDataFormatInterleaved; |
|
592 err = aSoundDevice.SetAudioFormat(formatBuf); |
|
593 |
|
594 if (!err) |
|
595 { |
|
596 ASSERT(!aFormatData.iConverter); // pre-condition at top of function anyway |
|
597 if (&aSoundDevice==&iPlaySoundDevice) |
|
598 { |
|
599 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
600 RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), |
|
601 aFormatData.iSampleRate, aFormatData.iRequestedChannels, |
|
602 aFormatData.iActualRate, aFormatData.iActualChannels); |
|
603 #endif |
|
604 // when playing we convert from requested to actual |
|
605 TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iSampleRate, |
|
606 aFormatData.iRequestedChannels, |
|
607 aFormatData.iActualRate, |
|
608 aFormatData.iActualChannels)); |
|
609 } |
|
610 else |
|
611 { |
|
612 // when recording we convert from actual to requested |
|
613 TInt outputRateToUse = aFormatData.iSampleRate; |
|
614 #ifdef SYMBIAN_SKIP_RESAMPLE_ON_RECORD |
|
615 // with this macro just channel convert at most |
|
616 outputRateToUse = aFormatData.iActualRate; |
|
617 #endif |
|
618 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
619 RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), |
|
620 aFormatData.iActualRate, aFormatData.iActualChannels, |
|
621 aFormatData.iSampleRate, aFormatData.iRequestedChannels); |
|
622 #endif |
|
623 TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iActualRate, |
|
624 aFormatData.iActualChannels, |
|
625 outputRateToUse, |
|
626 aFormatData.iRequestedChannels)); |
|
627 } |
|
628 } |
|
629 |
|
630 return err; |
|
631 } |
|
632 |
|
633 |
|
634 void RMdaDevSound::CBody::PlayFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported) |
|
635 { |
|
636 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
637 FormatsSupported(aFormatsSupported, iPlaySoundDevice); |
|
638 } |
|
639 |
|
640 void RMdaDevSound::CBody::GetPlayFormat(TCurrentSoundFormatBuf& aFormat) |
|
641 { |
|
642 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
643 GetFormat(aFormat, iPlaySoundDevice, iPlayData); |
|
644 } |
|
645 |
|
646 TInt RMdaDevSound::CBody::SetPlayFormat(const TCurrentSoundFormatBuf& aFormat) |
|
647 { |
|
648 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
649 return SetFormat(aFormat, iPlaySoundDevice, iPlayData); |
|
650 } |
|
651 |
|
652 void RMdaDevSound::CBody::RecordFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported) |
|
653 { |
|
654 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
655 FormatsSupported(aFormatsSupported, iRecordSoundDevice); |
|
656 } |
|
657 |
|
658 void RMdaDevSound::CBody::GetRecordFormat(TCurrentSoundFormatBuf& aFormat) |
|
659 { |
|
660 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
661 GetFormat(aFormat, iRecordSoundDevice, iRecordData); |
|
662 } |
|
663 |
|
664 TInt RMdaDevSound::CBody::SetRecordFormat(const TCurrentSoundFormatBuf& aFormat) |
|
665 { |
|
666 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
667 return SetFormat(aFormat, iRecordSoundDevice, iRecordData); |
|
668 } |
|
669 |
|
670 void RMdaDevSound::CBody::Close() |
|
671 { |
|
672 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
673 RDebug::Printf("void RMdaDevSound::CBody::Close() started"); |
|
674 #endif |
|
675 iBufferIndex = -1; |
|
676 iBufferOffset = -1; |
|
677 iBufferLength = 0; |
|
678 iCurrentPlayer = 0; |
|
679 iTimerActive = EFalse; |
|
680 iChunk.Close(); |
|
681 iPlaySoundDevice.Close(); |
|
682 iRecordSoundDevice.Close(); |
|
683 iState = ENotReady; |
|
684 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
685 RDebug::Printf("void RMdaDevSound::CBody::Close() ended"); |
|
686 #endif |
|
687 } |
|
688 |
|
689 TInt RMdaDevSound::CBody::Handle() |
|
690 {//This method is actually used to check whether the device is opened. Below logic should work |
|
691 if(iPlaySoundDevice.Handle()) |
|
692 { |
|
693 return iPlaySoundDevice.Handle(); |
|
694 } |
|
695 return 0; |
|
696 } |
|
697 |
|
698 void RMdaDevSound::CBody::PlayData(TRequestStatus& aStatus, const TDesC8& aData) |
|
699 { |
|
700 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
701 RDebug::Printf("RMdaDevSound::CBody::PlayData(0x%x,%d) State=%d Current=%d. Handle=%d.",&aStatus, |
|
702 aData.Length(), iState, iCurrentPlayer, iChunk.Handle()); |
|
703 RDebug::Printf("KPlayMaxSharedChunkBuffersMask=0x%x KNumPlayersMask=0x%x", |
|
704 KPlayMaxSharedChunkBuffersMask, KNumPlayersMask); |
|
705 #endif |
|
706 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
707 aStatus = KRequestPending; |
|
708 iPlayerStatus = &aStatus;//store the status of datapath player |
|
709 //No support for simultaneous play and record in RMdaDevSound |
|
710 if(iState == ERecording) |
|
711 { |
|
712 SoundDeviceError(KErrInUse); |
|
713 return; |
|
714 } |
|
715 const TDes8* data = static_cast<const TDes8*>(&aData); |
|
716 |
|
717 if(!iChunk.Handle()) |
|
718 { |
|
719 //This is where we setup to play. |
|
720 //Configure the shared chunk for two buffers with iBufferSize each |
|
721 iBufferConfig.iNumBuffers = KPlayMaxSharedChunkBuffers; |
|
722 iDeviceBufferLength = data->MaxLength(); |
|
723 iBufferConfig.iFlags = 0;//data will be continuous |
|
724 // If required, use rate converter etc |
|
725 if (iPlayData.iConverter) |
|
726 { |
|
727 iDeviceBufferLength = iPlayData.iConverter->MaxConvertBufferSize(iDeviceBufferLength, ETrue); |
|
728 } |
|
729 iBufferConfig.iBufferSizeInBytes = iDeviceBufferLength; |
|
730 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
731 RDebug::Printf("number of buffers: [%d]",iBufferConfig.iNumBuffers); |
|
732 RDebug::Printf("BufferSize in Bytes [%d]",iBufferConfig.iBufferSizeInBytes); |
|
733 #endif |
|
734 TPckg<TPlaySharedChunkBufConfig> bufferConfigBuf(iBufferConfig); |
|
735 TInt error = iPlaySoundDevice.SetBufferChunkCreate(bufferConfigBuf,iChunk); |
|
736 if(error == KErrNone) |
|
737 { |
|
738 iPlaySoundDevice.GetBufferConfig(bufferConfigBuf); |
|
739 TSoundFormatsSupportedV02Buf modnumber; |
|
740 iPlaySoundDevice.Caps(modnumber); |
|
741 TInt minBufferSize = KMinBufferSize; |
|
742 #ifdef SYMBIAN_FORCE_32BIT_LENGTHS |
|
743 minBufferSize = Max(minBufferSize, 4); // force to 32-bit buffer align |
|
744 #endif |
|
745 iRequestMinSize = Max(modnumber().iRequestMinSize, minBufferSize); |
|
746 error = iBufferRemaining.Create(iRequestMinSize); |
|
747 // work out mask so that x&iRequestMinMask is equiv to x/iRequestMinSize*iRequestMinSize |
|
748 iRequestMinMask = ~(iRequestMinSize-1); // assume iRequestMinSize is power of 2 |
|
749 } |
|
750 if (error) |
|
751 { |
|
752 SoundDeviceError(error); |
|
753 return; |
|
754 } |
|
755 } |
|
756 |
|
757 iBufferIndex = (iBufferIndex+1) & KPlayMaxSharedChunkBuffersMask; |
|
758 |
|
759 TPtr8 dataPtr(iChunk.Base()+ iBufferConfig.iBufferOffsetList[iBufferIndex], 0, iDeviceBufferLength); |
|
760 |
|
761 __ASSERT_DEBUG(!(iBufferRemaining.Length()>0 && iPlayData.iConverter), |
|
762 Panic(EPanicPartialBufferConverterNotSupported)); // can't deal with conversion with restrictions on buffer size |
|
763 |
|
764 if (iBufferRemaining.Length() != 0) |
|
765 { |
|
766 // This checks if any data was left over from last times rounding and adds it to the dataPtr |
|
767 dataPtr.Copy(iBufferRemaining); |
|
768 iBufferRemaining.SetLength(0); |
|
769 } |
|
770 |
|
771 if (iPlayData.iConverter) |
|
772 { |
|
773 iPlayData.iConverter->Convert(aData, dataPtr); |
|
774 ASSERT(iSecondPhaseData.Length()==0); // assume this below, so check |
|
775 ASSERT(iBufferRemaining.Length()==0); |
|
776 } |
|
777 else |
|
778 { |
|
779 TInt dataLength = aData.Length(); |
|
780 |
|
781 TInt lengthAlreadyInDeviceBuffer = dataPtr.Length(); |
|
782 TInt desiredDeviceBufferLength = (lengthAlreadyInDeviceBuffer + dataLength) & iRequestMinMask; |
|
783 if (desiredDeviceBufferLength > dataPtr.MaxLength()) |
|
784 { |
|
785 // the buffer would be two long to do in one go, so do as two phases |
|
786 desiredDeviceBufferLength = (lengthAlreadyInDeviceBuffer + (dataLength/2)) & iRequestMinMask; |
|
787 } |
|
788 |
|
789 TInt lengthToCopy = desiredDeviceBufferLength - lengthAlreadyInDeviceBuffer; |
|
790 lengthToCopy = Max(lengthToCopy, 0); // ensure lengthToCopy is not negative |
|
791 |
|
792 TInt remainingToBeCopied = dataLength - lengthToCopy; |
|
793 TInt secondPhaseLength = remainingToBeCopied & iRequestMinMask; |
|
794 TInt remainingForNextRun = remainingToBeCopied - secondPhaseLength; |
|
795 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
796 RDebug::Printf("dataLength: [%d]",dataLength); |
|
797 RDebug::Printf("lengthAlreadyInDeviceBuffer: [%d]",lengthAlreadyInDeviceBuffer); |
|
798 RDebug::Printf("desiredDeviceBufferLength: [%d]",desiredDeviceBufferLength); |
|
799 RDebug::Printf("lengthToCopy: [%d]",lengthToCopy); |
|
800 RDebug::Printf("remainingToBeCopied: [%d]",remainingToBeCopied); |
|
801 RDebug::Printf("secondPhaseLength: [%d]",secondPhaseLength); |
|
802 RDebug::Printf("remainingForNextRun: [%d]",remainingForNextRun); |
|
803 #endif |
|
804 dataPtr.Append(aData.Left(lengthToCopy)); |
|
805 iSecondPhaseData.Set(aData.Mid(lengthToCopy, secondPhaseLength)); |
|
806 iBufferRemaining.Copy(aData.Mid(lengthToCopy + secondPhaseLength, remainingForNextRun)); |
|
807 iHaveSecondPhaseData = secondPhaseLength>0; |
|
808 } |
|
809 |
|
810 if(iState == EOpened || iState == EPlayBuffersFlushed) |
|
811 { |
|
812 if(dataPtr.Length() > 0 && iSecondPhaseData.Length()==0) |
|
813 { |
|
814 // Note: if we have identified second phase, don't call BufferEmptied() here as we can't cope with a new PlayData() call |
|
815 //Make sure that next player do not overtake the current player, especially when recovering from underflow |
|
816 TInt otherPlayer = (iCurrentPlayer+1) & KNumPlayersMask; |
|
817 iPlayers[otherPlayer]->Deque(); |
|
818 CActiveScheduler::Add(iPlayers[otherPlayer]); |
|
819 //Beginning we need to give two play requests for an uninterrupted playback with the new driver |
|
820 BufferEmptied(); |
|
821 } |
|
822 iState = EPlaying; |
|
823 } |
|
824 #ifdef _DEBUG |
|
825 TInt cachePlayer = iCurrentPlayer; // TODO: remove |
|
826 #endif |
|
827 iPlayers[iCurrentPlayer]->PlayData(iBufferConfig.iBufferOffsetList[iBufferIndex], dataPtr.Length()); |
|
828 ASSERT(iCurrentPlayer==cachePlayer); // check have not changed since previous calc |
|
829 iCurrentPlayer = (iCurrentPlayer+1) & KNumPlayersMask; |
|
830 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
831 RDebug::Printf("RMdaDevSound::CBody::PlayData() Exit. Current=%d, Handle=%d.", |
|
832 iCurrentPlayer, iChunk.Handle()); |
|
833 #endif |
|
834 } |
|
835 |
|
836 void RMdaDevSound::CBody::RecordData(TRequestStatus& aStatus, TDes8& aData) |
|
837 { |
|
838 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
839 aStatus = KRequestPending; |
|
840 iRecorderStatus = &aStatus; |
|
841 //No support for simultaneous play and record in RMdaDevSound |
|
842 if(iState == EPlaying) |
|
843 { |
|
844 SoundDeviceError(KErrInUse); |
|
845 return; |
|
846 } |
|
847 |
|
848 iData = &aData; |
|
849 |
|
850 if(!iChunk.Handle()) |
|
851 { |
|
852 //Configure the shared chunk for two buffers with iBufferSize each |
|
853 iRecordBufferConfig.iNumBuffers = KRecordMaxSharedChunkBuffers; |
|
854 iDeviceBufferLength = iData->MaxLength(); // initial size - resize if needs be |
|
855 if (iRecordData.iConverter) |
|
856 { |
|
857 // if number of channels used differs from request, resize buffer |
|
858 // assume we have nice rounded values for buffer. |
|
859 if (iRecordData.iActualChannels>iRecordData.iRequestedChannels) |
|
860 { |
|
861 iDeviceBufferLength *= 2; // will record at stereo and convert to mono |
|
862 } |
|
863 else if (iRecordData.iActualChannels<iRecordData.iRequestedChannels) |
|
864 { |
|
865 iDeviceBufferLength /= 2; // will record at mono and convert to stereo |
|
866 } |
|
867 } |
|
868 iRecordBufferConfig.iBufferSizeInBytes = iDeviceBufferLength; |
|
869 iRecordBufferConfig.iFlags = 0; |
|
870 TPckg<TRecordSharedChunkBufConfig> bufferConfigBuf(iRecordBufferConfig); |
|
871 TInt error = iRecordSoundDevice.SetBufferChunkCreate(bufferConfigBuf,iChunk); |
|
872 if(error == KErrNone) |
|
873 { |
|
874 iRecordSoundDevice.GetBufferConfig(bufferConfigBuf); |
|
875 } |
|
876 else |
|
877 { |
|
878 SoundDeviceError(error); |
|
879 return; |
|
880 } |
|
881 iState = ERecording; |
|
882 } |
|
883 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
884 RDebug::Printf("RMdaDevSound::CBody::RecordData,iBufferOffset[%d]",iBufferOffset); |
|
885 #endif |
|
886 iPlayers[iCurrentPlayer]->RecordData(iBufferLength); |
|
887 } |
|
888 |
|
889 void RMdaDevSound::CBody::SoundDeviceError(TInt aError, TInt /*aPlayerIndex*/) |
|
890 // This is called by CPlayer objects when there is an error in RunL |
|
891 { |
|
892 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
893 RDebug::Print(_L("RMdaDevSound::CBody::SoundDeviceError: aError[%d]"), aError); |
|
894 #endif |
|
895 |
|
896 //When we get an underflow from one of the players and the other player is active, we are |
|
897 //bound to get an underflow from the other player too. So we ignore the first and process |
|
898 //the second |
|
899 TInt otherPlayerIndex = (iCurrentPlayer+1) & KNumPlayersMask; |
|
900 if (iPlayers[otherPlayerIndex]->IsActive() && aError==KErrUnderflow) |
|
901 { |
|
902 return; |
|
903 } |
|
904 SoundDeviceError(aError); |
|
905 } |
|
906 /** |
|
907 Note for maintainers: Please note that this method is called from |
|
908 CancelPlayData and CancelRecordData with KErrNone as a parameter in order to |
|
909 cancel the players and reset the internal state. |
|
910 */ |
|
911 void RMdaDevSound::CBody::SoundDeviceError(TInt aError) |
|
912 { |
|
913 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
914 RDebug::Print(_L("RMdaDevSound::CBody::SoundDeviceError: Error[%d] CurrentPlayer[%d]"), aError, iCurrentPlayer); |
|
915 #endif |
|
916 |
|
917 for (TInt i=0; i<KNumPlayers; i++) |
|
918 { |
|
919 if(KErrNone == aError) |
|
920 { |
|
921 // If we are here, SoundDeviceError(KErrNone) has been called from |
|
922 // CancelPlayData or CancelRecordData to maje sure the players are |
|
923 // cancel and their state reset |
|
924 iPlayers[i]->Stop(); |
|
925 } |
|
926 else |
|
927 { |
|
928 if (!iPlayers[i]->IsActive()) |
|
929 { |
|
930 iPlayers[i]->ResetPlayer(); |
|
931 } |
|
932 } |
|
933 } |
|
934 |
|
935 iBufferRemaining.SetLength(0); |
|
936 if(iPlayErrorStatus && aError!=KErrNone)//error receiver is only for errors |
|
937 { |
|
938 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
939 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iPlayerErrorStatus"); |
|
940 #endif |
|
941 User::RequestComplete(iPlayErrorStatus, aError); |
|
942 iPlayErrorStatus = NULL; |
|
943 } |
|
944 if(iPlayerStatus) |
|
945 { |
|
946 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
947 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iPlayerStatus"); |
|
948 #endif |
|
949 User::RequestComplete(iPlayerStatus, KErrNone); // effectively buffer emptied |
|
950 iPlayerStatus = NULL; |
|
951 } |
|
952 if(iRecordErrorStatus && aError!=KErrNone) |
|
953 { |
|
954 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
955 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iRecordErrorStatus"); |
|
956 #endif |
|
957 User::RequestComplete(iRecordErrorStatus, aError); |
|
958 iRecordErrorStatus = NULL; |
|
959 } |
|
960 else if(iRecorderStatus) |
|
961 { |
|
962 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
963 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iRecorderStatus"); |
|
964 #endif |
|
965 User::RequestComplete(iRecorderStatus, aError); |
|
966 iRecorderStatus = NULL; |
|
967 } |
|
968 iBufferIndex = -1; |
|
969 iCurrentPlayer = 0; |
|
970 iBufferOffset = -1; |
|
971 iBufferLength = 0; |
|
972 iTimerActive = EFalse; |
|
973 iState = EOpened; |
|
974 } |
|
975 |
|
976 void RMdaDevSound::CBody::NotifyRecordError(TRequestStatus& aStatus) |
|
977 { |
|
978 aStatus = KRequestPending; |
|
979 iRecordErrorStatus = &aStatus; |
|
980 } |
|
981 |
|
982 void RMdaDevSound::CBody::NotifyPlayError(TRequestStatus& aStatus) |
|
983 { |
|
984 aStatus = KRequestPending; |
|
985 iPlayErrorStatus = &aStatus; |
|
986 } |
|
987 |
|
988 void RMdaDevSound::CBody::CancelNotifyPlayError() |
|
989 { |
|
990 if(iPlayErrorStatus) |
|
991 { |
|
992 User::RequestComplete(iPlayErrorStatus, KErrCancel); |
|
993 } |
|
994 } |
|
995 |
|
996 void RMdaDevSound::CBody::CancelNotifyRecordError() |
|
997 { |
|
998 if(iRecordErrorStatus) |
|
999 { |
|
1000 User::RequestComplete(iRecordErrorStatus, KErrCancel); |
|
1001 } |
|
1002 } |
|
1003 |
|
1004 void RMdaDevSound::CBody::FlushPlayBuffer() |
|
1005 { |
|
1006 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1007 //There is no equivalent of FlushPlaybuffer in the new sound driver. So use CancelPlayData |
|
1008 //to simulate the original behavior |
|
1009 if ((iState == EPlaying) || (iState == EPaused)) |
|
1010 { |
|
1011 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1012 RDebug::Print(_L("RMdaDevSound::CBody::FlushPlayBuffers in Playing or Paused state")); |
|
1013 #endif |
|
1014 |
|
1015 if (iState == EPaused) |
|
1016 { |
|
1017 iFlushCalledDuringPause = ETrue; |
|
1018 } |
|
1019 |
|
1020 |
|
1021 iPlaySoundDevice.CancelPlayData(); |
|
1022 iBufferRemaining.SetLength(0); |
|
1023 iState = EPlayBuffersFlushed; |
|
1024 } |
|
1025 |
|
1026 } |
|
1027 |
|
1028 |
|
1029 |
|
1030 |
|
1031 RSoundSc& RMdaDevSound::CBody::PlaySoundDevice() |
|
1032 { |
|
1033 return iPlaySoundDevice; |
|
1034 } |
|
1035 |
|
1036 RSoundSc& RMdaDevSound::CBody::RecordSoundDevice() |
|
1037 { |
|
1038 return iRecordSoundDevice; |
|
1039 } |
|
1040 |
|
1041 RMdaDevSound::CBody::TState RMdaDevSound::CBody::State() |
|
1042 { |
|
1043 return iState; |
|
1044 } |
|
1045 |
|
1046 void RMdaDevSound::CBody::BufferEmptied() |
|
1047 { |
|
1048 if(iPlayerStatus) |
|
1049 { |
|
1050 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1051 RDebug::Print(_L("***Buffer Emptied***")); |
|
1052 #endif |
|
1053 User::RequestComplete(iPlayerStatus, KErrNone); |
|
1054 iPlayerStatus = NULL; |
|
1055 } |
|
1056 } |
|
1057 |
|
1058 void RMdaDevSound::CBody::BufferFilled(TInt aBufferOffset) |
|
1059 { |
|
1060 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1061 RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled:")); |
|
1062 #endif |
|
1063 |
|
1064 ASSERT(aBufferOffset>=0 || aBufferOffset==KErrCancel); |
|
1065 ASSERT(iData); // request should not get this without |
|
1066 |
|
1067 if(aBufferOffset==KErrCancel) |
|
1068 { |
|
1069 //we can get KErrCancel when we call pause and there is no more data left with the driver |
|
1070 //we send the empty buffer to the HwDevice, where this should trigger the shutdown mechanism |
|
1071 iData->SetLength(0); |
|
1072 User::RequestComplete(iRecorderStatus, KErrNone); |
|
1073 iRecorderStatus = NULL; |
|
1074 return; |
|
1075 } |
|
1076 |
|
1077 iBufferOffset = aBufferOffset; |
|
1078 //when last buffer is flushed, new driver sometimes gives buffer size of odd number. One of our codecs |
|
1079 //expects that the buffer size should always be even. Base suggested that we fix in multimedia |
|
1080 //as it is quite complicated to fix in overthere. |
|
1081 iBufferLength = iBufferLength & 0xfffffffe; |
|
1082 TPtr8 dataPtr(iChunk.Base()+ iBufferOffset, iBufferLength, iData->MaxLength()); |
|
1083 if (iRecordData.iConverter) |
|
1084 { |
|
1085 iRecordData.iConverter->Convert(dataPtr, *iData); |
|
1086 } |
|
1087 else |
|
1088 { |
|
1089 iData->Copy(dataPtr); |
|
1090 } |
|
1091 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1092 RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled: BufferOffset[%d] BufferLen[%d]"), iBufferOffset, iBufferLength); |
|
1093 #endif |
|
1094 if(iBufferOffset >= 0) |
|
1095 { |
|
1096 iRecordSoundDevice.ReleaseBuffer(iBufferOffset); |
|
1097 } |
|
1098 if(iRecorderStatus) |
|
1099 { |
|
1100 User::RequestComplete(iRecorderStatus, KErrNone); |
|
1101 iRecorderStatus = NULL; |
|
1102 } |
|
1103 } |
|
1104 |
|
1105 void RMdaDevSound::CBody::PlayCancelled() |
|
1106 { |
|
1107 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1108 RDebug::Print(_L("RMdaDevSound::CBody::PlayCancelled:")); |
|
1109 #endif |
|
1110 |
|
1111 for (TInt index=0; index<KNumPlayers; index++) |
|
1112 { |
|
1113 iPlayers[index]->Cancel(); |
|
1114 } |
|
1115 iBufferIndex = -1; |
|
1116 iCurrentPlayer = 0; |
|
1117 iBufferOffset = -1; |
|
1118 iBufferLength = 0; |
|
1119 iTimerActive = EFalse; |
|
1120 if(iPlayerStatus) |
|
1121 { |
|
1122 User::RequestComplete(iPlayerStatus, KErrNone); |
|
1123 iPlayerStatus = NULL; |
|
1124 } |
|
1125 } |
|
1126 |
|
1127 void RMdaDevSound::CBody::UpdateTimeAndBytesPlayed() |
|
1128 { |
|
1129 iBytesPlayed = iPlaySoundDevice.BytesTransferred(); |
|
1130 iStartTime = User::FastCounter(); |
|
1131 iTimerActive=ETrue; |
|
1132 } |
|
1133 |
|
1134 TBool RMdaDevSound::CBody::TimerActive() |
|
1135 { |
|
1136 return iTimerActive; |
|
1137 } |
|
1138 |
|
1139 TBool RMdaDevSound::CBody::FlushCalledDuringPause() |
|
1140 { |
|
1141 return iFlushCalledDuringPause; |
|
1142 } |
|
1143 |
|
1144 RMdaDevSound::CBody::CPlayer::CPlayer(TInt aPriority, RMdaDevSound::CBody& aParent, TInt aIndex): |
|
1145 CActive(aPriority), iParent(aParent), iIndex(aIndex), iBufferOffset(-1), iBufferLength(0) |
|
1146 { |
|
1147 CActiveScheduler::Add(this); |
|
1148 } |
|
1149 |
|
1150 RMdaDevSound::CBody::CPlayer::~CPlayer() |
|
1151 { |
|
1152 Cancel(); |
|
1153 } |
|
1154 |
|
1155 void RMdaDevSound::CBody::CPlayer::RunL() |
|
1156 { |
|
1157 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1158 RDebug::Print(_L("****RMdaDevSound::CBody::CPlayer(%d)::RunL: Error[%d] ParentState[%d] Outstanding[%d], pending[%d]"), |
|
1159 iIndex, iStatus.Int(), iParent.State(), iParent.iHaveSecondPhaseData, iRequestPending); |
|
1160 #endif |
|
1161 |
|
1162 //this is required to avoid receiving the request completions in the order diff. from the |
|
1163 //issued order |
|
1164 Deque(); |
|
1165 CActiveScheduler::Add(this); |
|
1166 |
|
1167 TInt error = iStatus.Int(); |
|
1168 |
|
1169 // It's fine to schedule buffers to the driver in the paused state (i.e. iRequestPending == EFalse) |
|
1170 if(!iRequestPending && (iParent.State() == EPlaying || iParent.State() == EPaused) && error == KErrNone) |
|
1171 { |
|
1172 //this is from playdata |
|
1173 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1174 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::RunL: Playing BufferOffset[%d] BufferLength[%d]"), iIndex, iBufferOffset, iBufferLength); |
|
1175 #endif |
|
1176 //Make sure the length is even. We may get odd for the last partial buffer. |
|
1177 iBufferLength = iBufferLength & 0xfffffffe; |
|
1178 |
|
1179 PlaySoundDevice(); |
|
1180 //Need this for the first time only |
|
1181 if(!iParent.TimerActive()) |
|
1182 { |
|
1183 iParent.UpdateTimeAndBytesPlayed(); |
|
1184 } |
|
1185 iRequestPending = ETrue; |
|
1186 } |
|
1187 // TODO: The case below shouldn't be valid under EPaused state, i.e. the driver shouldn't complete playback if it was paused. However due to a current problem in the driver we have to handle this case |
|
1188 else if (iRequestPending && (iParent.State() == EPlaying || iParent.State() == EPaused) && error == KErrNone) //this is from driver |
|
1189 { |
|
1190 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1191 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::RunL: Buffer emptied successfully"), iIndex); |
|
1192 #endif |
|
1193 if (iParent.iHaveSecondPhaseData) |
|
1194 { |
|
1195 TPtr8 dataPtr(iParent.iChunk.Base()+ iParent.iBufferConfig.iBufferOffsetList[iParent.iBufferIndex], 0, iParent.iDeviceBufferLength); |
|
1196 dataPtr.Copy(iParent.iSecondPhaseData); |
|
1197 |
|
1198 PlaySoundDevice(); |
|
1199 iParent.iCurrentPlayer = (iParent.iCurrentPlayer+1) & KNumPlayersMask; |
|
1200 iParent.UpdateTimeAndBytesPlayed(); |
|
1201 iParent.iHaveSecondPhaseData = EFalse; |
|
1202 } |
|
1203 else |
|
1204 { |
|
1205 iRequestPending = EFalse; |
|
1206 iParent.UpdateTimeAndBytesPlayed(); |
|
1207 iParent.BufferEmptied(); |
|
1208 } |
|
1209 } |
|
1210 else if(iParent.State() == EPlayBuffersFlushed && error == KErrCancel) |
|
1211 { |
|
1212 iRequestPending = EFalse; |
|
1213 if (!iParent.FlushCalledDuringPause()) |
|
1214 { |
|
1215 iParent.PlayCancelled(); |
|
1216 } |
|
1217 } |
|
1218 else if(iParent.State() == ERecording && (error >= 0 || error == KErrCancel)) |
|
1219 {//we can get KErrCancel when we call pause and there is no more data left with the driver |
|
1220 iParent.BufferFilled(error); |
|
1221 } |
|
1222 else |
|
1223 { |
|
1224 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1225 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::RunL: Error[%d] Outstanding[%d], pending[%d]"), |
|
1226 iIndex, error, iParent.iHaveSecondPhaseData,iRequestPending); |
|
1227 #endif |
|
1228 iParent.SoundDeviceError(error, iIndex); |
|
1229 } |
|
1230 } |
|
1231 |
|
1232 TInt RMdaDevSound::CBody::CPlayer::RunError(TInt aError) |
|
1233 { |
|
1234 iParent.SoundDeviceError(aError, iIndex); |
|
1235 return KErrNone; |
|
1236 } |
|
1237 |
|
1238 void RMdaDevSound::CBody::CPlayer::DoCancel() |
|
1239 { |
|
1240 //nothing to do |
|
1241 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1242 RDebug::Printf("RMdaDevSound::CBody::CPlayer(%d)::DoCancel", iIndex); |
|
1243 #endif |
|
1244 } |
|
1245 |
|
1246 void RMdaDevSound::CBody::CPlayer::ResetPlayer() |
|
1247 { |
|
1248 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1249 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::ResetPlayer: IsActive[%d] pending[%d] iBufferOffset[%d] iBufferLength[%d]"), iIndex, IsActive(), iRequestPending, iBufferOffset, iBufferLength); |
|
1250 #endif |
|
1251 |
|
1252 iRequestPending = EFalse; |
|
1253 iBufferOffset = -1; |
|
1254 iBufferLength = 0; |
|
1255 } |
|
1256 |
|
1257 void RMdaDevSound::CBody::CPlayer::Stop() |
|
1258 { |
|
1259 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1260 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::Stop: IsActive[%d] pending[%d] iBufferOffset[%d] iBufferLength[%d]"), iIndex, IsActive(), iRequestPending, iBufferOffset, iBufferLength); |
|
1261 #endif |
|
1262 |
|
1263 ResetPlayer(); |
|
1264 Cancel(); |
|
1265 } |
|
1266 |
|
1267 void RMdaDevSound::CBody::CPlayer::PlayData(TInt aBufferOffset, TInt aLength) |
|
1268 { |
|
1269 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1270 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::PlayData : IsActive[%d]"), iIndex, IsActive()); |
|
1271 #endif |
|
1272 |
|
1273 ASSERT(!IsActive()); // TODO: remove or replace redundant test |
|
1274 iBufferOffset = aBufferOffset; |
|
1275 iBufferLength = aLength; |
|
1276 |
|
1277 iStatus = KRequestPending; |
|
1278 SetActive(); |
|
1279 TRequestStatus* status = &iStatus; |
|
1280 User::RequestComplete(status, KErrNone); |
|
1281 } |
|
1282 |
|
1283 void RMdaDevSound::CBody::CPlayer::RecordData(TInt& aBufferLength) |
|
1284 { |
|
1285 if (!IsActive()) |
|
1286 { |
|
1287 iStatus = KRequestPending; |
|
1288 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1289 RDebug::Printf("Recording request: BufferLength[%d]", aBufferLength); |
|
1290 #endif |
|
1291 iParent.RecordSoundDevice().RecordData(iStatus, aBufferLength); |
|
1292 SetActive(); |
|
1293 } |
|
1294 } |
|
1295 |
|
1296 void RMdaDevSound::CBody::CPlayer::PlaySoundDevice() |
|
1297 { |
|
1298 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1299 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::PlaySoundDevice : IsActive[%d]"), iIndex, IsActive()); |
|
1300 #endif |
|
1301 |
|
1302 #ifdef SYMBIAN_FORCE_32BIT_LENGTHS |
|
1303 if (iBufferLength%4 != 0) |
|
1304 { |
|
1305 // simulate the limitation of some hardware, where -6 is generated if the |
|
1306 // buffer length is not divisible by 4. |
|
1307 TRequestStatus*status = &iStatus; |
|
1308 User::RequestComplete(status, KErrArgument); |
|
1309 } |
|
1310 else |
|
1311 #endif |
|
1312 { |
|
1313 iParent.PlaySoundDevice().PlayData(iStatus, iBufferOffset, iBufferLength, EFalse); |
|
1314 // Pause was called when there was no data available. Now that we have data available, we should pause the driver |
|
1315 if (iParent.iPauseDeviceDriverOnNewData) |
|
1316 { |
|
1317 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1318 RDebug::Printf("Pausing the driver after receiving data to play"); |
|
1319 #endif |
|
1320 iParent.PlaySoundDevice().Pause(); |
|
1321 iParent.iPauseDeviceDriverOnNewData = EFalse; |
|
1322 } |
|
1323 } |
|
1324 SetActive(); |
|
1325 |
|
1326 } |