|
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 "mdasoundadapterbody.h" |
|
16 #include <e32debug.h> |
|
17 |
|
18 #include "rateconvert.h" // if we need to resample |
|
19 |
|
20 #include <hal.h> |
|
21 |
|
22 _LIT(KPddFileName,"SOUNDSC.PDD"); |
|
23 _LIT(KLddFileName,"ESOUNDSC.LDD"); |
|
24 |
|
25 |
|
26 const TInt KBytesPerSample = 2; |
|
27 const TInt KMinBufferSize = 2; |
|
28 |
|
29 /** |
|
30 This function raises a panic |
|
31 EDeviceNotOpened is raised when any of the RMdaDevSound APIs are called before opening the device. |
|
32 */ |
|
33 GLDEF_C void Panic(TSoundAdapterPanicCodes aPanicCode) |
|
34 { |
|
35 User::Panic(KSoundAdapterPanicCategory, aPanicCode); |
|
36 } |
|
37 |
|
38 |
|
39 const TText8 *RMdaDevSound::CBody::TState::Name() const |
|
40 { |
|
41 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
42 switch(iState) |
|
43 { |
|
44 case ENotReady: return _S8("ENotReady"); |
|
45 case EStopped: return _S8("EStopped"); |
|
46 case ERecording: return _S8("ERecording"); |
|
47 case ERecordingPausedInHw: return _S8("ERecordingPausedInHw"); |
|
48 case ERecordingPausedInSw: return _S8("ERecordingPausedInSw"); |
|
49 case EPlaying: return _S8("EPlaying"); |
|
50 case EPlayingPausedInHw: return _S8("EPlayingPausedInHw"); |
|
51 case EPlayingPausedInSw: return _S8("EPlayingPausedInSw"); |
|
52 case EPlayingUnderrun: return _S8("EPlayingUnderrun"); |
|
53 } |
|
54 return _S8("CorruptState"); |
|
55 #else |
|
56 return _S8(""); |
|
57 #endif |
|
58 } |
|
59 |
|
60 |
|
61 |
|
62 RMdaDevSound::CBody::TState &RMdaDevSound::CBody::TState::operator=(TStateEnum aNewState) |
|
63 { |
|
64 if(iState != aNewState) |
|
65 { |
|
66 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
67 RDebug::Printf("RMdaDevSound state %s -> %s", Name(), TState(aNewState).Name()); |
|
68 #endif |
|
69 iState = aNewState; |
|
70 } |
|
71 return *this; |
|
72 } |
|
73 |
|
74 RMdaDevSound::CBody* RMdaDevSound::CBody::NewL() |
|
75 { |
|
76 CBody* self = new(ELeave) CBody(); |
|
77 CleanupStack::PushL(self); |
|
78 self->ConstructL(); |
|
79 CleanupStack::Pop(); |
|
80 return self; |
|
81 } |
|
82 |
|
83 RMdaDevSound::CBody::~CBody() |
|
84 { |
|
85 for(TInt i = 0; i < KPlaySharedChunkBuffers; i++) |
|
86 { |
|
87 delete iPlayers[i]; |
|
88 iPlayers[i] = NULL; |
|
89 } |
|
90 delete iRecorder; |
|
91 iRecorder = NULL; |
|
92 delete iPlayFormatData.iConverter; |
|
93 delete iRecordFormatData.iConverter; |
|
94 iPlayChunk.Close(); |
|
95 iPlaySoundDevice.Close(); |
|
96 iRecordChunk.Close(); |
|
97 iRecordSoundDevice.Close(); |
|
98 iConvertedPlayData.Close(); |
|
99 iSavedTrailingData.Close(); |
|
100 iBufferedRecordData.Close(); |
|
101 } |
|
102 |
|
103 RMdaDevSound::CBody::CBody() |
|
104 :iState(ENotReady), iBufferOffset(-1) |
|
105 { |
|
106 |
|
107 } |
|
108 |
|
109 TVersion RMdaDevSound::CBody::VersionRequired() const |
|
110 { |
|
111 if(iPlaySoundDevice.Handle()) |
|
112 { |
|
113 return iPlaySoundDevice.VersionRequired(); |
|
114 } |
|
115 else |
|
116 { |
|
117 return TVersion(); |
|
118 } |
|
119 } |
|
120 |
|
121 TInt RMdaDevSound::CBody::IsMdaSound() |
|
122 { |
|
123 return ETrue; |
|
124 } |
|
125 |
|
126 void RMdaDevSound::CBody::ConstructL() |
|
127 { |
|
128 // Try to load the audio physical driver |
|
129 TInt err = User::LoadPhysicalDevice(KPddFileName); |
|
130 if ((err!=KErrNone) && (err!=KErrAlreadyExists)) |
|
131 { |
|
132 User::Leave(err); |
|
133 } |
|
134 // Try to load the audio logical driver |
|
135 err = User::LoadLogicalDevice(KLddFileName); |
|
136 if ((err!=KErrNone) && (err!=KErrAlreadyExists)) |
|
137 { |
|
138 User::Leave(err); |
|
139 } |
|
140 for(TInt i=0; i<KPlaySharedChunkBuffers; i++) |
|
141 { |
|
142 iPlayers[i] = new(ELeave) CPlayer(CActive::EPriorityUserInput, *this, i); |
|
143 iFreePlayers.Push(iPlayers[i]); |
|
144 } |
|
145 |
|
146 iRecorder = new(ELeave) CRecorder(CActive::EPriorityUserInput, *this); |
|
147 |
|
148 TInt tmp; |
|
149 User::LeaveIfError(HAL::Get(HAL::ENanoTickPeriod, tmp)); |
|
150 iNTickPeriodInUsec = tmp; |
|
151 } |
|
152 |
|
153 TInt RMdaDevSound::CBody::Open(TInt /*aUnit*/) |
|
154 { |
|
155 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
156 RDebug::Print(_L("RMdaDevSound::CBody::Open ")); |
|
157 #endif |
|
158 TInt err = KErrNone; |
|
159 //Default behavior of this method is to open both the play and record audio devices. |
|
160 if(!iPlaySoundDevice.Handle() && !iRecordSoundDevice.Handle()) |
|
161 { |
|
162 err = iPlaySoundDevice.Open(KSoundScTxUnit0); |
|
163 if(err == KErrNone) |
|
164 { |
|
165 err = iRecordSoundDevice.Open(KSoundScRxUnit0); |
|
166 } |
|
167 } |
|
168 if(err != KErrNone) |
|
169 { |
|
170 Close(); |
|
171 } |
|
172 else |
|
173 { |
|
174 TSoundFormatsSupportedV02Buf capsBuf; |
|
175 iPlaySoundDevice.Caps(capsBuf); |
|
176 TInt minBufferSize = KMinBufferSize; |
|
177 #ifdef SYMBIAN_FORCE_32BIT_LENGTHS |
|
178 minBufferSize = Max(minBufferSize, 4); // force to 32-bit buffer align |
|
179 #endif |
|
180 iRequestMinSize = Max(capsBuf().iRequestMinSize, minBufferSize); |
|
181 // work out mask so that x&iRequestMinMask is equiv to x/iRequestMinSize*iRequestMinSize |
|
182 iRequestMinMask = ~(iRequestMinSize-1); // assume iRequestMinSize is power of 2 |
|
183 iSavedTrailingData.Close(); |
|
184 iSavedTrailingData.Create(iRequestMinSize); |
|
185 |
|
186 iState = EStopped; |
|
187 iBytesPlayed = 0; |
|
188 } |
|
189 |
|
190 return err; |
|
191 } |
|
192 |
|
193 TInt RMdaDevSound::CBody::PlayVolume() |
|
194 { |
|
195 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
196 return iPlaySoundDevice.Volume(); |
|
197 } |
|
198 |
|
199 void RMdaDevSound::CBody::SetPlayVolume(TInt aVolume) |
|
200 { |
|
201 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
202 if(aVolume >=0 && aVolume<=KSoundMaxVolume) |
|
203 { |
|
204 iPlaySoundDevice.SetVolume(KLinerToDbConstantLookup[aVolume].iDBValue); |
|
205 } |
|
206 } |
|
207 void RMdaDevSound::CBody::SetVolume(TInt aLogarithmicVolume) |
|
208 { |
|
209 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
210 if(aLogarithmicVolume >= 0 && aLogarithmicVolume <= KSoundMaxVolume) |
|
211 { |
|
212 iPlaySoundDevice.SetVolume(aLogarithmicVolume); |
|
213 } |
|
214 } |
|
215 |
|
216 void RMdaDevSound::CBody::CancelPlayData() |
|
217 { |
|
218 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
219 RDebug::Printf("RMdaDevSound::CBody::CancelPlayData: state %s", iState.Name()); |
|
220 #endif |
|
221 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
222 |
|
223 // If there is a client request, cancel it |
|
224 // Must do this before canceling players because otherwise they may just restart! |
|
225 if(iClientPlayStatus) |
|
226 { |
|
227 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
228 RDebug::Printf("msp PlayCancelled complete iClientPlayStatus"); |
|
229 #endif |
|
230 User::RequestComplete(iClientPlayStatus, KErrCancel); // Call also sets iClientPlayStatus to NULL |
|
231 } |
|
232 |
|
233 // Discard any buffered data |
|
234 iClientPlayData.Set(0,0); |
|
235 // Discard any saved trailing data (ie. data saved due driver requiring all requests to be a multiple of iRequestMinSize). |
|
236 iSavedTrailingData.SetLength(0); |
|
237 |
|
238 // Emulator RSoundSc PDD when running without a soundcard has a major |
|
239 // issue with cancelling whilst paused. It will not clear the pending |
|
240 // list (because the timer is not active) and therefore this list will |
|
241 // later overflow causing hep corruption. |
|
242 // This means that, for now, we MUST Resume before calling CancelPlayData |
|
243 // to avoid kernel panics... |
|
244 |
|
245 // The device driver will not cancel a request which is in progress... |
|
246 // So, if we are paused in hw, we must resume before cancelling the |
|
247 // player otherwise it will hang in CActive::Cancel |
|
248 if(iState == EPlayingPausedInHw) |
|
249 { |
|
250 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
251 RDebug::Printf("msp Resume to avoid hang"); |
|
252 #endif |
|
253 (void) iPlaySoundDevice.Resume(); |
|
254 } |
|
255 |
|
256 // Update state |
|
257 iState = EStopped; |
|
258 |
|
259 |
|
260 // The RSoundSc driver will not cancel a request which is in progress (or paused). |
|
261 // If we just loop across the players, cancelling each individual request and waiting for it to complete, |
|
262 // several of them will actually play, which is both wrong and time consuming.... |
|
263 // Issue a block cancel upfront to avoid this |
|
264 iPlaySoundDevice.CancelPlayData(); |
|
265 |
|
266 // Cancel all players |
|
267 for (TUint playerIndex=0; playerIndex<KPlaySharedChunkBuffers; ++playerIndex) |
|
268 { |
|
269 // If the player is active it will call PlayRequestCompleted with aDueToCancelCommand true |
|
270 // to update the iFreePlayers and iActivePlayRequestSizes FIFOs. |
|
271 iPlayers[playerIndex]->Cancel(); |
|
272 } |
|
273 |
|
274 iBufferOffset = -1; |
|
275 iBufferLength = 0; |
|
276 |
|
277 return; |
|
278 } |
|
279 |
|
280 TInt RMdaDevSound::CBody::RecordLevel() |
|
281 { |
|
282 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
283 return iRecordSoundDevice.Volume(); |
|
284 } |
|
285 |
|
286 void RMdaDevSound::CBody::SetRecordLevel(TInt aLevel) |
|
287 { |
|
288 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
289 iRecordSoundDevice.SetVolume(aLevel); |
|
290 } |
|
291 |
|
292 void RMdaDevSound::CBody::CancelRecordData() |
|
293 { |
|
294 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
295 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
296 RDebug::Printf("RMdaDevSound::CBody::CancelRecordData: state %s", iState.Name()); |
|
297 #endif |
|
298 |
|
299 // Stop recorder object (and its request) |
|
300 iRecorder->Cancel(); |
|
301 |
|
302 // Stop driver from recording |
|
303 iRecordSoundDevice.CancelRecordData(); |
|
304 |
|
305 // If there is a client request, cancel it |
|
306 if(iClientRecordStatus) |
|
307 { |
|
308 User::RequestComplete(iClientRecordStatus, KErrNone); // Call also sets iClientPlayStatus to NULL |
|
309 } |
|
310 |
|
311 iState = EStopped; |
|
312 return; |
|
313 } |
|
314 |
|
315 void RMdaDevSound::CBody::FlushRecordBuffer() |
|
316 { |
|
317 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
318 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
319 RDebug::Print(_L("RMdaDevSound::CBody::FlushRecordBuffer - implemented by calling PauseRecordBuffer")); |
|
320 #endif |
|
321 |
|
322 PauseRecordBuffer(); |
|
323 } |
|
324 |
|
325 TInt RMdaDevSound::CBody::BytesPlayed() |
|
326 { |
|
327 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
328 RDebug::Printf("RMdaDevSound::BytesPlayed %s", iState.Name()); |
|
329 #endif |
|
330 |
|
331 return I64LOW(BytesPlayed64()); |
|
332 } |
|
333 |
|
334 |
|
335 TUint64 RMdaDevSound::CBody::BytesPlayed64() |
|
336 { |
|
337 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
338 |
|
339 TUint64 currentBytesPlayed = KMaxTUint64; |
|
340 |
|
341 switch(iState) |
|
342 { |
|
343 case ENotReady: |
|
344 Panic(EDeviceNotOpened); |
|
345 break; |
|
346 |
|
347 case EStopped: |
|
348 currentBytesPlayed = iBytesPlayed; |
|
349 break; |
|
350 |
|
351 case ERecording: |
|
352 case ERecordingPausedInHw: |
|
353 case ERecordingPausedInSw: |
|
354 Panic(EBadState); |
|
355 break; |
|
356 |
|
357 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused |
|
358 // Paused, so use pause time |
|
359 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
360 RDebug::Printf("EPlayingPausedInHw: iPausedBytes %x %x", I64HIGH(iPausedBytesPlayed), I64LOW(iPausedBytesPlayed)); |
|
361 #endif |
|
362 currentBytesPlayed = iPausedBytesPlayed; |
|
363 break; |
|
364 |
|
365 case EPlayingPausedInSw: // ie. Driver not playing or paused |
|
366 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
367 RDebug::Printf("EPlayingPausedInSw: iPausedBytesPlayed %x %x", I64HIGH(iPausedBytesPlayed), I64LOW(iPausedBytesPlayed)); |
|
368 #endif |
|
369 currentBytesPlayed = iPausedBytesPlayed; |
|
370 break; |
|
371 |
|
372 case EPlayingUnderrun: |
|
373 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
374 RDebug::Printf("EPlayingUnderrun: iBytesPlayed %x %x", I64HIGH(iBytesPlayed), I64LOW(iBytesPlayed)); |
|
375 #endif |
|
376 currentBytesPlayed = iBytesPlayed; |
|
377 break; |
|
378 |
|
379 case EPlaying: |
|
380 { |
|
381 // Playing so calculate time since last update to iBytesPlayed |
|
382 TUint32 curTime = CurrentTimeInMsec(); |
|
383 TUint32 curRequestSize = iActivePlayRequestSizes.Peek(); |
|
384 |
|
385 TUint32 extraPlayTime = (curTime >= iStartTime) ? (curTime-iStartTime) : (KMaxTUint32 - (iStartTime-curTime)); |
|
386 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
387 RDebug::Printf("iStartTime %d curTime %d extraPlayTime %d", iStartTime, curTime, extraPlayTime); |
|
388 |
|
389 RDebug::Printf("iPlayFormatData.iSampleRate %d KBytesPerSample %d iNTickPeriodInUsec %d", |
|
390 iPlayFormatData.iSampleRate, KBytesPerSample, iNTickPeriodInUsec); |
|
391 #endif |
|
392 TUint32 extraBytesPlayed = TUint32((TUint64(extraPlayTime) * iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample)/1000); |
|
393 if(extraBytesPlayed > curRequestSize) |
|
394 { |
|
395 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
396 RDebug::Printf("caping extraBytes played from %d to %d", extraBytesPlayed, curRequestSize); |
|
397 #endif |
|
398 extraBytesPlayed = curRequestSize; |
|
399 } |
|
400 |
|
401 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
402 RDebug::Printf("iBytesPlayed %d extraBytesPlayed %d (curRequestSize %d) -> currentBytesPlayed %x %x", |
|
403 iBytesPlayed, extraBytesPlayed, curRequestSize, I64HIGH(currentBytesPlayed), I64LOW(currentBytesPlayed)); |
|
404 #endif |
|
405 |
|
406 currentBytesPlayed = iBytesPlayed + extraBytesPlayed; |
|
407 break; |
|
408 } |
|
409 |
|
410 default: |
|
411 Panic(EBadState); |
|
412 break; |
|
413 } |
|
414 |
|
415 |
|
416 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
417 RDebug::Printf("iPlayFormatData.iConverter %x", iPlayFormatData.iConverter); |
|
418 #endif |
|
419 |
|
420 if (iPlayFormatData.iConverter) |
|
421 { |
|
422 // need to scale bytes played to fit with requested rate and channels, not actual |
|
423 if (iPlayFormatData.iActualChannels != iPlayFormatData.iRequestedChannels) |
|
424 { |
|
425 if (iPlayFormatData.iActualChannels == 2) |
|
426 { |
|
427 // requested was mono, we have stereo |
|
428 currentBytesPlayed /= 2; |
|
429 } |
|
430 else |
|
431 { |
|
432 // requested was stereo, we have mono |
|
433 currentBytesPlayed *= 2; |
|
434 } |
|
435 } |
|
436 if (iPlayFormatData.iSampleRate != iPlayFormatData.iActualRate) |
|
437 { |
|
438 currentBytesPlayed = TUint64(currentBytesPlayed* |
|
439 TReal(iPlayFormatData.iSampleRate)/TReal(iPlayFormatData.iActualRate)); // don't round |
|
440 } |
|
441 } |
|
442 |
|
443 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
444 RDebug::Printf("currentBytesPlayed %x %x", I64HIGH(currentBytesPlayed), I64LOW(currentBytesPlayed)); |
|
445 #endif |
|
446 return currentBytesPlayed; |
|
447 } |
|
448 |
|
449 void RMdaDevSound::CBody::ResetBytesPlayed() |
|
450 { |
|
451 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
452 RDebug::Printf("RMdaDevSound::CBody::ResetBytesPlayed %s", iState.Name()); |
|
453 #endif |
|
454 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
455 iBytesPlayed = 0; |
|
456 iPlaySoundDevice.ResetBytesTransferred(); |
|
457 return; |
|
458 } |
|
459 |
|
460 void RMdaDevSound::CBody::PausePlayBuffer() |
|
461 { |
|
462 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
463 RDebug::Printf("RMdaDevSound::CBody::PausePlayBuffer %s", iState.Name()); |
|
464 #endif |
|
465 switch(iState) |
|
466 { |
|
467 case ENotReady: |
|
468 Panic(EDeviceNotOpened); |
|
469 break; |
|
470 |
|
471 case EStopped: |
|
472 // Can not move to EPlayingPausedInSw because it provokes a datapath panic.... |
|
473 break; |
|
474 |
|
475 case ERecording: |
|
476 case ERecordingPausedInHw: |
|
477 case ERecordingPausedInSw: |
|
478 Panic(EBadState); |
|
479 break; |
|
480 |
|
481 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused |
|
482 case EPlayingPausedInSw: // ie. Driver not playing or paused |
|
483 // Already paused so nothing to do. |
|
484 break; |
|
485 |
|
486 case EPlayingUnderrun: |
|
487 iState = EPlayingPausedInSw; |
|
488 break; |
|
489 |
|
490 case EPlaying: |
|
491 { |
|
492 iPauseTime = CurrentTimeInMsec(); |
|
493 iPausedBytesPlayed = BytesPlayed64(); |
|
494 TInt res = iPlaySoundDevice.Pause(); |
|
495 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
496 RDebug::Printf("iPlaySoundDevice.Pause res = %d", res); |
|
497 #endif |
|
498 if(res == KErrNone) |
|
499 { |
|
500 iState = EPlayingPausedInHw; |
|
501 } |
|
502 else |
|
503 { |
|
504 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
505 RDebug::Printf("msp PausePlayBuffer hw pause unexpectedly failed, doing sw pause"); |
|
506 #endif |
|
507 iState = EPlayingPausedInSw; |
|
508 } |
|
509 break; |
|
510 } |
|
511 |
|
512 default: |
|
513 Panic(EBadState); |
|
514 break; |
|
515 } |
|
516 |
|
517 return; |
|
518 } |
|
519 |
|
520 void RMdaDevSound::CBody::ResumePlaying() |
|
521 { |
|
522 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
523 RDebug::Printf("RMdaDevSound::CBody::ResumePlaying %s", iState.Name()); |
|
524 #endif |
|
525 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
526 |
|
527 switch(iState) |
|
528 { |
|
529 case ENotReady: |
|
530 Panic(EDeviceNotOpened); |
|
531 break; |
|
532 |
|
533 case EStopped: |
|
534 // No change |
|
535 break; |
|
536 |
|
537 case ERecording: |
|
538 case ERecordingPausedInHw: |
|
539 case ERecordingPausedInSw: |
|
540 Panic(EBadState); |
|
541 break; |
|
542 |
|
543 case EPlaying: |
|
544 // No change |
|
545 break; |
|
546 |
|
547 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused |
|
548 { |
|
549 // Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing). |
|
550 iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; |
|
551 |
|
552 TInt res = iPlaySoundDevice.Resume(); |
|
553 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
554 RDebug::Printf("ResumePlayBuffer EPlayingPausedInHw res = %d", res); |
|
555 #endif |
|
556 if(res == KErrNone) |
|
557 { |
|
558 // Resume ok so a pending request will complete |
|
559 iState = EPlaying; |
|
560 // Update iStartTime to allow for time spent paused |
|
561 TUint32 curTime = CurrentTimeInMsec(); |
|
562 TUint32 timePaused = (curTime >= iPauseTime) ? (curTime-iPauseTime) : (KMaxTUint32 - (iPauseTime-curTime)); |
|
563 iStartTime += timePaused; // nb. It is harmless if this wraps. |
|
564 } |
|
565 else |
|
566 { |
|
567 // Resume failed, therefore driver is not playing |
|
568 // No need to update iStartTime/iPauseTime because these are only used within a driver request |
|
569 // Change state to Stopped |
|
570 iState = EStopped; |
|
571 // Attempt to start a new (pending) request. |
|
572 StartPlayersAndUpdateState(); |
|
573 } |
|
574 break; |
|
575 } |
|
576 |
|
577 case EPlayingPausedInSw: // ie. Driver not playing/paused |
|
578 { |
|
579 // Driver not playing |
|
580 // Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing). |
|
581 iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; |
|
582 // No need to update iStartTime/iPauseTime because these are only used within a driver request |
|
583 // Change state to Stopped |
|
584 iState = EStopped; |
|
585 // Attempt to start a new (pending) request. |
|
586 StartPlayersAndUpdateState(); |
|
587 break; |
|
588 } |
|
589 |
|
590 case EPlayingUnderrun: |
|
591 // Not paused, therefore no change |
|
592 break; |
|
593 |
|
594 default: |
|
595 Panic(EBadState); |
|
596 break; |
|
597 } |
|
598 |
|
599 return; |
|
600 } |
|
601 |
|
602 void RMdaDevSound::CBody::PauseRecordBuffer() |
|
603 { |
|
604 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
605 RDebug::Printf("RMdaDevSound::CBody::PauseRecordBuffer %s", iState.Name()); |
|
606 #endif |
|
607 |
|
608 switch(iState) |
|
609 { |
|
610 case ENotReady: |
|
611 Panic(EDeviceNotOpened); |
|
612 break; |
|
613 |
|
614 case EStopped: |
|
615 // Driver is not recording |
|
616 // Do not pause because that will cause problems when CAudioDevice::Pause calls |
|
617 // PausePlayBuffer and PauseRecordBuffer in state EStopped... |
|
618 break; |
|
619 |
|
620 case ERecording: |
|
621 { |
|
622 TInt res = iRecordSoundDevice.Pause(); |
|
623 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
624 RDebug::Printf("PauseRecordBuffer EPlaying res = %d", res); |
|
625 #endif |
|
626 if(res == KErrNone) |
|
627 { |
|
628 iState = ERecordingPausedInHw; |
|
629 } |
|
630 else |
|
631 { |
|
632 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
633 RDebug::Printf("PauseRecordBuffer hw pause unexpectedly failed, doing sw pause"); |
|
634 #endif |
|
635 iState = ERecordingPausedInSw; |
|
636 } |
|
637 break; |
|
638 } |
|
639 |
|
640 case ERecordingPausedInHw: |
|
641 case ERecordingPausedInSw: |
|
642 // Already paused so nothing to do. |
|
643 break; |
|
644 |
|
645 case EPlaying: |
|
646 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused |
|
647 Panic(EBadState); |
|
648 break; |
|
649 |
|
650 case EPlayingPausedInSw: |
|
651 // This is an ugly hack to maintain compatibility with CAudioDevice::Pause which |
|
652 // calls both PausePlayBuffer and PauseRecordBuffer whilst in stopped, then later calls ResumePlaying |
|
653 break; |
|
654 |
|
655 case EPlayingUnderrun: // ie. Play request pending on h/w and paused |
|
656 Panic(EBadState); |
|
657 break; |
|
658 |
|
659 default: |
|
660 Panic(EBadState); |
|
661 break; |
|
662 } |
|
663 |
|
664 return; |
|
665 } |
|
666 |
|
667 void RMdaDevSound::CBody::ResumeRecording() |
|
668 { |
|
669 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
670 RDebug::Printf("RMdaDevSound::CBody::ResumeRecording %s", iState.Name()); |
|
671 #endif |
|
672 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
673 |
|
674 switch(iState) |
|
675 { |
|
676 case ENotReady: |
|
677 Panic(EDeviceNotOpened); |
|
678 break; |
|
679 |
|
680 case EStopped: |
|
681 // No change |
|
682 break; |
|
683 |
|
684 case ERecording: |
|
685 // No change |
|
686 break; |
|
687 |
|
688 case ERecordingPausedInHw: |
|
689 { |
|
690 TInt res = iRecordSoundDevice.Resume(); |
|
691 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
692 RDebug::Printf("ResumeRecordBuffer ERecordingPausedInHw res = %d", res); |
|
693 #endif |
|
694 if(res == KErrNone) |
|
695 { |
|
696 // Resume ok so a pending request will complete |
|
697 iState = ERecording; |
|
698 } |
|
699 else |
|
700 { |
|
701 iState = EStopped; |
|
702 // Resume failed, so attempt to start a new (pending) request. |
|
703 // If this works, it will update the state to ERecording. |
|
704 StartRecordRequest(); |
|
705 } |
|
706 break; |
|
707 } |
|
708 case ERecordingPausedInSw: |
|
709 { |
|
710 // Update state to stopped and attempt to start any pending request |
|
711 iState = EStopped; |
|
712 // If this works, it will update the state to ERecording. |
|
713 StartRecordRequest(); |
|
714 break; |
|
715 } |
|
716 |
|
717 case EPlaying: |
|
718 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused |
|
719 case EPlayingPausedInSw: // ie. Driver not playing/paused |
|
720 case EPlayingUnderrun: |
|
721 default: |
|
722 Panic(EBadState); |
|
723 break; |
|
724 } |
|
725 |
|
726 return; |
|
727 |
|
728 |
|
729 } |
|
730 |
|
731 TInt RMdaDevSound::CBody::GetTimePlayed(TTimeIntervalMicroSeconds& aTimePlayed) |
|
732 { |
|
733 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
734 |
|
735 |
|
736 TUint64 bytesPlayed = BytesPlayed64(); |
|
737 |
|
738 TUint64 timePlayed = 1000 * 1000 * bytesPlayed / (iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample); |
|
739 |
|
740 aTimePlayed = TTimeIntervalMicroSeconds(timePlayed); |
|
741 |
|
742 return KErrNone; |
|
743 } |
|
744 |
|
745 |
|
746 void RMdaDevSound::CBody::FormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported, RSoundSc& aSoundDevice) |
|
747 { |
|
748 TSoundFormatsSupportedV02Buf supportedFormat; |
|
749 aSoundDevice.Caps(supportedFormat); |
|
750 TUint32 rates = supportedFormat().iRates; |
|
751 |
|
752 for(TInt i = KNumSampleRates-1; i > 0 ;i--)//min to max |
|
753 { |
|
754 if(rates & KRateEnumLookup[i].iRateConstant) |
|
755 { |
|
756 aFormatsSupported().iMinRate = KRateEnumLookup[i].iRate; |
|
757 break; |
|
758 } |
|
759 } |
|
760 for(TInt i = 0; i < KNumSampleRates; i++)//max to min |
|
761 { |
|
762 if(rates & KRateEnumLookup[i].iRateConstant) |
|
763 { |
|
764 aFormatsSupported().iMaxRate = KRateEnumLookup[i].iRate; |
|
765 break; |
|
766 } |
|
767 } |
|
768 TUint32 enc = supportedFormat().iEncodings; |
|
769 |
|
770 if (enc & KSoundEncoding16BitPCM) |
|
771 { |
|
772 aFormatsSupported().iEncodings = EMdaSoundEncoding16BitPCM;// Always defaults to this |
|
773 } |
|
774 if (enc & KSoundEncoding8BitPCM) |
|
775 { |
|
776 aFormatsSupported().iEncodings |= EMdaSoundEncoding8BitPCM; |
|
777 } |
|
778 TUint32 channels = supportedFormat().iChannels; |
|
779 |
|
780 if (channels & KSoundStereoChannel) |
|
781 { |
|
782 aFormatsSupported().iChannels = 2; |
|
783 } |
|
784 else |
|
785 { |
|
786 aFormatsSupported().iChannels = 1; |
|
787 } |
|
788 aFormatsSupported().iMinBufferSize = supportedFormat().iRequestMinSize; |
|
789 aFormatsSupported().iMaxBufferSize = KMaxBufferSize; |
|
790 aFormatsSupported().iMinVolume = 0; |
|
791 aFormatsSupported().iMaxVolume = KSoundMaxVolume; |
|
792 } |
|
793 |
|
794 void RMdaDevSound::CBody::GetFormat(TCurrentSoundFormatBuf& aFormat, |
|
795 RSoundSc& /*aSoundDevice*/, |
|
796 const TFormatData &aFormatData) |
|
797 { |
|
798 // always return the requested, or the initial, not current device setting |
|
799 aFormat().iChannels = aFormatData.iRequestedChannels; // never clear if this is bitmap or value, but effectively the same |
|
800 aFormat().iRate = aFormatData.iSampleRate; |
|
801 } |
|
802 |
|
803 void RMdaDevSound::CBody::StartPlayersAndUpdateState() |
|
804 { |
|
805 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
806 |
|
807 switch(iState) |
|
808 { |
|
809 case ENotReady: |
|
810 Panic(EDeviceNotOpened); |
|
811 break; |
|
812 |
|
813 case EStopped: |
|
814 // Allow following code to queue more driver play requests and check for stopped |
|
815 break; |
|
816 |
|
817 case ERecording: |
|
818 case ERecordingPausedInHw: |
|
819 case ERecordingPausedInSw: |
|
820 Panic(EBadState); |
|
821 break; |
|
822 |
|
823 case EPlaying: |
|
824 // Allow following code to queue more driver play requests and check for underrun |
|
825 break; |
|
826 |
|
827 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused |
|
828 // Allow following code to queue more driver play requests |
|
829 break; |
|
830 |
|
831 case EPlayingPausedInSw: |
|
832 // Paused but driver not playing+paused, therefore do not queue new requests until ResumePlaying |
|
833 return; |
|
834 |
|
835 case EPlayingUnderrun: |
|
836 // Allow following code to queue more driver play requests |
|
837 break; |
|
838 |
|
839 default: |
|
840 Panic(EBadState); |
|
841 break; |
|
842 } |
|
843 |
|
844 // iState is now either EStopped, EPlaying, EPlayingPausedInHw or EPlayingUnderrun |
|
845 __ASSERT_DEBUG(((iState == EStopped) || (iState == EPlaying) || (iState == EPlayingPausedInHw) || (iState == EPlayingUnderrun)), Panic(EBadState)); |
|
846 |
|
847 while( (iClientPlayData.Length() != 0) && (! iFreePlayers.IsEmpty())) |
|
848 { |
|
849 // More data to play and more players, so issue another request |
|
850 |
|
851 bool wasIdle = iFreePlayers.IsFull(); |
|
852 // Get a free player |
|
853 CPlayer *player = iFreePlayers.Pop(); |
|
854 // Calculate length of request |
|
855 TUint32 lengthToPlay = iClientPlayData.Length(); |
|
856 if(lengthToPlay > iDeviceBufferLength) |
|
857 { |
|
858 lengthToPlay = iDeviceBufferLength; |
|
859 } |
|
860 |
|
861 // Remember request length, so we can update bytes played when it finishes |
|
862 iActivePlayRequestSizes.Push(lengthToPlay); |
|
863 |
|
864 // Find offset to copy data to |
|
865 TUint playerIndex = player->GetPlayerIndex(); |
|
866 ASSERT(playerIndex < KPlaySharedChunkBuffers); |
|
867 TUint chunkOffset = iPlayBufferConfig.iBufferOffsetList[playerIndex]; |
|
868 |
|
869 // Copy data |
|
870 TPtr8 destPtr(iPlayChunk.Base()+ chunkOffset, 0, iDeviceBufferLength); |
|
871 destPtr.Copy(iClientPlayData.Mid(0, lengthToPlay)); |
|
872 |
|
873 // Update iClientPlayData to remove the data just queued |
|
874 iClientPlayData.Set(iClientPlayData.Right(iClientPlayData.Length()-lengthToPlay)); |
|
875 |
|
876 // Start the CPlayer |
|
877 player->PlayData(chunkOffset, lengthToPlay); |
|
878 if(wasIdle) |
|
879 { |
|
880 // HW was not active so update state and start time |
|
881 iState = EPlaying; |
|
882 iStartTime = CurrentTimeInMsec(); |
|
883 } |
|
884 } |
|
885 |
|
886 // Check if the client request is now complete |
|
887 if(iClientPlayData.Length() == 0 && iClientPlayStatus) |
|
888 { |
|
889 // We have queued all the client play data to the driver so we can now complete the client request. |
|
890 // If actual playback fails, we will notify the client via the Play Error notification mechanism. |
|
891 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
892 RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState completing client request"); |
|
893 #endif |
|
894 User::RequestComplete(iClientPlayStatus, KErrNone); // This call also sets iClientPlayStatus to NULL |
|
895 } |
|
896 |
|
897 //nb. iState is now either EStopped, EPlaying, EPlayingPausedInHw or EPlayingUnderrun (see previous switch and assert) |
|
898 if(iState != EPlayingPausedInHw) |
|
899 { |
|
900 if(iFreePlayers.IsFull()) |
|
901 { |
|
902 // Free fifo is full, therefore there are no active players |
|
903 iState = EPlayingUnderrun; |
|
904 if(! iUnderFlowReportedSinceLastPlayOrRecordRequest) |
|
905 { |
|
906 // We report KErrUnderflow if we have not already reported it since the last PlayData call. |
|
907 // Note that |
|
908 // i) We do NOT report driver underflows. |
|
909 // ii) We report underflow when we run out of data to pass to the driver. |
|
910 // iii) We throttle this reporting |
|
911 // iv) We WILL report KErrUnderflow if already stopped and asked to play a zero length buffer |
|
912 // The last point is required because the client maps a manual stop command into a devsound play with a |
|
913 // zero length buffer and the last buffer flag set, this in turn is mapped to a Playdata calll with an empty buffer |
|
914 // which is expected to complete ok and cause a KErrUnderflow error to be reported... |
|
915 iUnderFlowReportedSinceLastPlayOrRecordRequest = ETrue; |
|
916 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
917 RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState stopped and iUnderFlowReportedSinceLastPlayOrRecordRequest false so raising KErrUnderflow"); |
|
918 #endif |
|
919 |
|
920 // Discard any saved trailing data (ie. data saved due driver requiring all requests to be a multiple of iRequestMinSize). |
|
921 // This maybe because client is delibrately letting us underflow to play silence. In that case we do not want to |
|
922 // play the trailing data at the beginning of the new data issued after the silence... |
|
923 iSavedTrailingData.SetLength(0); |
|
924 |
|
925 SoundDeviceError(KErrUnderflow); |
|
926 } |
|
927 } |
|
928 else |
|
929 { |
|
930 // Free fifo not full, therefore there are active players |
|
931 iState = EPlaying; |
|
932 } |
|
933 } |
|
934 return; |
|
935 } |
|
936 |
|
937 TInt RMdaDevSound::CBody::SetFormat(const TCurrentSoundFormatBuf& aFormat, |
|
938 RSoundSc& aSoundDevice, |
|
939 TFormatData &aFormatData) |
|
940 { |
|
941 TInt err = KErrNotFound; |
|
942 TCurrentSoundFormatV02Buf formatBuf; |
|
943 |
|
944 delete aFormatData.iConverter; |
|
945 aFormatData.iConverter = NULL; // setting this to NULL indicates we are not using converter. No other flag |
|
946 iConvertedPlayData.Close(); |
|
947 |
|
948 TInt wantedRate = aFormat().iRate; |
|
949 for(TInt index = 0; index < KNumSampleRates; index++ ) |
|
950 { |
|
951 if(wantedRate == KRateEnumLookup[index].iRate) |
|
952 { |
|
953 formatBuf().iRate = KRateEnumLookup[index].iRateEnum; |
|
954 aFormatData.iSampleRate = wantedRate; |
|
955 err = KErrNone; |
|
956 break; |
|
957 } |
|
958 } |
|
959 |
|
960 if(err == KErrNone) |
|
961 { |
|
962 // Assume, for now, we support the requested channels and rate |
|
963 aFormatData.iActualChannels = aFormatData.iRequestedChannels; |
|
964 aFormatData.iActualRate = aFormatData.iSampleRate; |
|
965 |
|
966 // Attempt to configure driver |
|
967 formatBuf().iChannels = aFormat().iChannels; |
|
968 formatBuf().iEncoding = ESoundEncoding16BitPCM; |
|
969 formatBuf().iDataFormat = ESoundDataFormatInterleaved; |
|
970 err = aSoundDevice.SetAudioFormat(formatBuf); |
|
971 #if defined(SYMBIAN_SOUNDADAPTER_FORCECDRATES) || defined (SYMBIAN_SOUNDADAPTER_FORCESTEREO) |
|
972 err = KErrNotSupported; // force Negotiate - for debugging |
|
973 #endif |
|
974 if (err==KErrNotSupported) |
|
975 { |
|
976 // don't support directly. Perhaps can rate convert? |
|
977 err = NegotiateFormat(aFormat, aSoundDevice, aFormatData); |
|
978 } |
|
979 } |
|
980 return err; |
|
981 } |
|
982 |
|
983 TInt RMdaDevSound::CBody::NegotiateFormat(const TCurrentSoundFormatBuf& aFormat, |
|
984 RSoundSc& aSoundDevice, |
|
985 TFormatData &aFormatData) |
|
986 { |
|
987 ASSERT(!aFormatData.iConverter); // we don't clear on fail - so assuming NULL to start with |
|
988 |
|
989 TInt err = KErrNotFound; |
|
990 TCurrentSoundFormatV02Buf formatBuf; |
|
991 |
|
992 // find out first what the driver supports |
|
993 TSoundFormatsSupportedV02Buf supportedFormat; |
|
994 aSoundDevice.Caps(supportedFormat); |
|
995 TUint32 supportedRates = supportedFormat().iRates; |
|
996 #ifdef SYMBIAN_SOUNDADAPTER_FORCECDRATES |
|
997 supportedRates &= KSoundRate11025Hz| KSoundRate22050Hz | KSoundRate44100Hz; // only use CD rates - for debugging |
|
998 #endif |
|
999 |
|
1000 // For PlayCase: |
|
1001 // first try to find the first rate below or equal to the requested that is supported |
|
1002 // initially go down and be fussy, but if we pass the requested rate find the first that |
|
1003 // is supported |
|
1004 // For RecordCase: |
|
1005 // We want the next rate above consistently - we go down from this to the requested rate. |
|
1006 // If there is one, we don't support - we _never_ upsample. |
|
1007 // note that the table is given in descending order, so we start with the highest |
|
1008 TInt wantedRate = aFormat().iRate; |
|
1009 TInt takeTheFirst = EFalse; |
|
1010 TInt nextUpValidIndex = -1; |
|
1011 for(TInt index = 0; index < KNumSampleRates; index++ ) |
|
1012 { |
|
1013 TBool lookingAtRequestedRate = wantedRate == KRateEnumLookup[index].iRate; |
|
1014 TSoundRate wantedEnum = KRateEnumLookup[index].iRateEnum; |
|
1015 TUint32 equivBitmap = KRateEnumLookup[index].iRateConstant; |
|
1016 TBool isSupported = (equivBitmap & supportedRates) != EFalse; |
|
1017 if (lookingAtRequestedRate || takeTheFirst) |
|
1018 { |
|
1019 if (isSupported) |
|
1020 { |
|
1021 // this rate is supported |
|
1022 formatBuf().iRate = wantedEnum; |
|
1023 aFormatData.iActualRate = KRateEnumLookup[index].iRate; |
|
1024 err = KErrNone; |
|
1025 break; |
|
1026 } |
|
1027 } |
|
1028 else if (!takeTheFirst) |
|
1029 { |
|
1030 // while we are still looking for the rate, want to cache any supported index |
|
1031 // at end of loop, this will be the first rate above ours that is supported |
|
1032 // use for fallback if required |
|
1033 if (isSupported) |
|
1034 { |
|
1035 nextUpValidIndex = index; |
|
1036 } |
|
1037 } |
|
1038 if (lookingAtRequestedRate) |
|
1039 { |
|
1040 // if we get this far we've gone passed the wanted rate. For play we enable |
|
1041 // "takeTheFirst". For record we just abort. |
|
1042 if (&aSoundDevice==&iPlaySoundDevice) |
|
1043 { |
|
1044 takeTheFirst = ETrue; |
|
1045 } |
|
1046 else |
|
1047 { |
|
1048 break; |
|
1049 } |
|
1050 } |
|
1051 } |
|
1052 |
|
1053 if (err) |
|
1054 { |
|
1055 // if there is one above the requested rate, use that |
|
1056 if (nextUpValidIndex>=0) |
|
1057 { |
|
1058 TSoundRate wantedEnum = KRateEnumLookup[nextUpValidIndex].iRateEnum; |
|
1059 formatBuf().iRate = wantedEnum; |
|
1060 aFormatData.iActualRate = KRateEnumLookup[nextUpValidIndex].iRate; |
|
1061 err = KErrNone; |
|
1062 } |
|
1063 } |
|
1064 |
|
1065 if (err) |
|
1066 { |
|
1067 // should have something! |
|
1068 return err; |
|
1069 } |
|
1070 |
|
1071 aFormatData.iSampleRate = wantedRate; // iSampleRate is our requested/apparent rate, not the device rate. |
|
1072 |
|
1073 TUint32 channelsSupported = supportedFormat().iChannels; |
|
1074 #ifdef SYMBIAN_SOUNDADAPTER_FORCESTEREO |
|
1075 channelsSupported &= KSoundStereoChannel; // don't use mono - for debugging |
|
1076 #endif |
|
1077 |
|
1078 if (aFormat().iChannels == 1) |
|
1079 { |
|
1080 aFormatData.iRequestedChannels = 1; |
|
1081 // want mono |
|
1082 if (channelsSupported & KSoundMonoChannel) |
|
1083 { |
|
1084 // mono is supported, as usual |
|
1085 aFormatData.iActualChannels = 1; |
|
1086 } |
|
1087 else if (channelsSupported & KSoundStereoChannel) |
|
1088 { |
|
1089 aFormatData.iActualChannels = 2; |
|
1090 } |
|
1091 else |
|
1092 { |
|
1093 return KErrNotSupported; // should not get this far for real |
|
1094 } |
|
1095 } |
|
1096 else if (aFormat().iChannels == 2) |
|
1097 { |
|
1098 aFormatData.iRequestedChannels = 2; |
|
1099 // want stereo |
|
1100 if (channelsSupported & KSoundStereoChannel) |
|
1101 { |
|
1102 // stereo is supported, as usual |
|
1103 aFormatData.iActualChannels = 2; |
|
1104 } |
|
1105 else if (channelsSupported & KSoundMonoChannel) |
|
1106 { |
|
1107 aFormatData.iActualChannels = 1; |
|
1108 } |
|
1109 else |
|
1110 { |
|
1111 return KErrNotSupported; // should not get this far for real |
|
1112 } |
|
1113 } |
|
1114 else |
|
1115 { |
|
1116 return KErrNotSupported; // unknown number of channels requested! |
|
1117 } |
|
1118 |
|
1119 formatBuf().iChannels = aFormatData.iActualChannels; |
|
1120 |
|
1121 formatBuf().iEncoding = ESoundEncoding16BitPCM; |
|
1122 formatBuf().iDataFormat = ESoundDataFormatInterleaved; |
|
1123 err = aSoundDevice.SetAudioFormat(formatBuf); |
|
1124 |
|
1125 if (!err) |
|
1126 { |
|
1127 ASSERT(!aFormatData.iConverter); // pre-condition at top of function anyway |
|
1128 if (&aSoundDevice==&iPlaySoundDevice) |
|
1129 { |
|
1130 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1131 RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), |
|
1132 aFormatData.iSampleRate, aFormatData.iRequestedChannels, |
|
1133 aFormatData.iActualRate, aFormatData.iActualChannels); |
|
1134 #endif |
|
1135 // when playing we convert from requested to actual |
|
1136 TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iSampleRate, |
|
1137 aFormatData.iRequestedChannels, |
|
1138 aFormatData.iActualRate, |
|
1139 aFormatData.iActualChannels)); |
|
1140 } |
|
1141 else |
|
1142 { |
|
1143 // when recording we convert from actual to requested |
|
1144 TInt outputRateToUse = aFormatData.iSampleRate; |
|
1145 #ifdef SYMBIAN_SKIP_RESAMPLE_ON_RECORD |
|
1146 // with this macro just channel convert at most |
|
1147 outputRateToUse = aFormatData.iActualRate; |
|
1148 #endif |
|
1149 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1150 RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), |
|
1151 aFormatData.iActualRate, aFormatData.iActualChannels, |
|
1152 aFormatData.iSampleRate, aFormatData.iRequestedChannels); |
|
1153 #endif |
|
1154 TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iActualRate, |
|
1155 aFormatData.iActualChannels, |
|
1156 outputRateToUse, |
|
1157 aFormatData.iRequestedChannels)); |
|
1158 } |
|
1159 } |
|
1160 if(err != KErrNone) |
|
1161 { |
|
1162 delete aFormatData.iConverter; |
|
1163 aFormatData.iConverter= NULL; |
|
1164 iConvertedPlayData.Close(); |
|
1165 } |
|
1166 |
|
1167 return err; |
|
1168 } |
|
1169 |
|
1170 void RMdaDevSound::CBody::StartRecordRequest() |
|
1171 { |
|
1172 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1173 |
|
1174 iRecorder->RecordData(iBufferLength); |
|
1175 } |
|
1176 |
|
1177 // Note both InRecordMode and InPlayMode return EFalse for ENotReady and EStopped |
|
1178 TBool RMdaDevSound::CBody::InRecordMode()const |
|
1179 { |
|
1180 switch(iState) |
|
1181 { |
|
1182 case ENotReady: |
|
1183 case EStopped: |
|
1184 return EFalse; |
|
1185 |
|
1186 case ERecording: |
|
1187 case ERecordingPausedInHw: |
|
1188 case ERecordingPausedInSw: |
|
1189 return ETrue; |
|
1190 |
|
1191 case EPlaying: |
|
1192 case EPlayingPausedInHw: |
|
1193 case EPlayingPausedInSw: |
|
1194 case EPlayingUnderrun: |
|
1195 return EFalse; |
|
1196 |
|
1197 default: |
|
1198 Panic(EBadState); |
|
1199 break; |
|
1200 } |
|
1201 return EFalse; |
|
1202 } |
|
1203 |
|
1204 TBool RMdaDevSound::CBody::InPlayMode() const |
|
1205 { |
|
1206 switch(iState) |
|
1207 { |
|
1208 case ENotReady: |
|
1209 case EStopped: |
|
1210 return EFalse; |
|
1211 |
|
1212 case ERecording: |
|
1213 case ERecordingPausedInHw: |
|
1214 case ERecordingPausedInSw: |
|
1215 return EFalse; |
|
1216 |
|
1217 case EPlaying: |
|
1218 case EPlayingPausedInHw: |
|
1219 case EPlayingPausedInSw: |
|
1220 case EPlayingUnderrun: |
|
1221 return ETrue; |
|
1222 |
|
1223 default: |
|
1224 Panic(EBadState); |
|
1225 break; |
|
1226 } |
|
1227 |
|
1228 return EFalse; |
|
1229 } |
|
1230 |
|
1231 |
|
1232 TUint32 RMdaDevSound::CBody::CurrentTimeInMsec() const |
|
1233 { |
|
1234 TUint64 tmp = User::NTickCount(); |
|
1235 tmp *= iNTickPeriodInUsec; |
|
1236 tmp /= 1000; |
|
1237 return TUint32(tmp); |
|
1238 } |
|
1239 |
|
1240 void RMdaDevSound::CBody::PlayFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported) |
|
1241 { |
|
1242 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1243 FormatsSupported(aFormatsSupported, iPlaySoundDevice); |
|
1244 } |
|
1245 |
|
1246 void RMdaDevSound::CBody::GetPlayFormat(TCurrentSoundFormatBuf& aFormat) |
|
1247 { |
|
1248 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1249 GetFormat(aFormat, iPlaySoundDevice, iPlayFormatData); |
|
1250 } |
|
1251 |
|
1252 TInt RMdaDevSound::CBody::SetPlayFormat(const TCurrentSoundFormatBuf& aFormat) |
|
1253 { |
|
1254 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1255 return SetFormat(aFormat, iPlaySoundDevice, iPlayFormatData); |
|
1256 } |
|
1257 |
|
1258 void RMdaDevSound::CBody::RecordFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported) |
|
1259 { |
|
1260 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1261 FormatsSupported(aFormatsSupported, iRecordSoundDevice); |
|
1262 } |
|
1263 |
|
1264 void RMdaDevSound::CBody::GetRecordFormat(TCurrentSoundFormatBuf& aFormat) |
|
1265 { |
|
1266 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1267 GetFormat(aFormat, iRecordSoundDevice, iRecordFormatData); |
|
1268 } |
|
1269 |
|
1270 TInt RMdaDevSound::CBody::SetRecordFormat(const TCurrentSoundFormatBuf& aFormat) |
|
1271 { |
|
1272 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1273 return SetFormat(aFormat, iRecordSoundDevice, iRecordFormatData); |
|
1274 } |
|
1275 |
|
1276 void RMdaDevSound::CBody::Close() |
|
1277 { |
|
1278 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1279 RDebug::Printf("void RMdaDevSound::CBody::Close() started"); |
|
1280 #endif |
|
1281 iBufferOffset = -1; |
|
1282 iBufferLength = 0; |
|
1283 |
|
1284 if(iPlaySoundDevice.Handle() != KNullHandle) |
|
1285 { |
|
1286 // Make sure all player objects are idle |
|
1287 CancelPlayData(); |
|
1288 iPlayChunk.Close(); |
|
1289 iPlaySoundDevice.Close(); |
|
1290 } |
|
1291 |
|
1292 if(iRecordSoundDevice.Handle() != KNullHandle) |
|
1293 { |
|
1294 CancelRecordData(); |
|
1295 iRecordChunk.Close(); |
|
1296 iRecordSoundDevice.Close(); |
|
1297 } |
|
1298 |
|
1299 iState = ENotReady; |
|
1300 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1301 RDebug::Printf("void RMdaDevSound::CBody::Close() ended"); |
|
1302 #endif |
|
1303 } |
|
1304 |
|
1305 TInt RMdaDevSound::CBody::Handle() |
|
1306 {//This method is actually used to check whether the device is opened. Below logic should work |
|
1307 if(iPlaySoundDevice.Handle()) |
|
1308 { |
|
1309 return iPlaySoundDevice.Handle(); |
|
1310 } |
|
1311 if(iRecordSoundDevice.Handle()) |
|
1312 { |
|
1313 return iRecordSoundDevice.Handle(); |
|
1314 } |
|
1315 return 0; |
|
1316 } |
|
1317 |
|
1318 |
|
1319 void RMdaDevSound::CBody::PlayData(TRequestStatus& aStatus, const TDesC8& aData) |
|
1320 { |
|
1321 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1322 RDebug::Printf("RMdaDevSound::CBody::PlayData(0x%x,%d) State=%s Handle=%d.",&aStatus, |
|
1323 aData.Length(), iState.Name(), iPlayChunk.Handle()); |
|
1324 #endif |
|
1325 |
|
1326 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1327 aStatus = KRequestPending; |
|
1328 |
|
1329 if((iClientPlayStatus != NULL) || InRecordMode()) |
|
1330 { |
|
1331 // We only support one outstanding request |
|
1332 // No support for simultaneous play and record in RMdaDevSound |
|
1333 TRequestStatus *pRequest = &aStatus; |
|
1334 User::RequestComplete(pRequest, KErrInUse); |
|
1335 return; |
|
1336 } |
|
1337 iClientPlayStatus = &aStatus;//store the status of datapath player |
|
1338 |
|
1339 if(iPlayFormatData.iConverter || iSavedTrailingData.Length() != 0) |
|
1340 { |
|
1341 // Need a conversion buffer |
|
1342 // Needs to hold any trailing data truncated from the previous request (due |
|
1343 // to alignment requirements) and either the new data, or the new rate adapted data |
|
1344 TUint32 spaceRequired = iSavedTrailingData.Length(); |
|
1345 if(iPlayFormatData.iConverter) |
|
1346 { |
|
1347 // Doing rate conversion so also need space for the converted data |
|
1348 spaceRequired += iPlayFormatData.iConverter->MaxConvertBufferSize(aData.Length()); |
|
1349 } |
|
1350 else |
|
1351 { |
|
1352 // Not doing rate adaptation therefore only need to allow for the new incoming data |
|
1353 spaceRequired += aData.Length(); |
|
1354 } |
|
1355 // Check if existing buffer exists and is big enough |
|
1356 if(iConvertedPlayData.MaxLength() < spaceRequired) |
|
1357 { |
|
1358 iConvertedPlayData.Close(); |
|
1359 TInt err = iConvertedPlayData.Create(spaceRequired); |
|
1360 if(err) |
|
1361 { |
|
1362 User::RequestComplete(iClientPlayStatus, err); |
|
1363 return; |
|
1364 } |
|
1365 } |
|
1366 |
|
1367 // Truncate iConvertedPlayData and copy in saved trailing data (if any) |
|
1368 iConvertedPlayData = iSavedTrailingData; |
|
1369 iSavedTrailingData.SetLength(0); |
|
1370 |
|
1371 // Now append rate adapted data or incoming data |
|
1372 if (iPlayFormatData.iConverter) |
|
1373 { |
|
1374 // The convertor will panic if it fails to convert any data, therefore |
|
1375 // we avoid passing it an empty source buffer |
|
1376 if(aData.Length() != 0) |
|
1377 { |
|
1378 TPtr8 destPtr((TUint8 *)iConvertedPlayData.Ptr()+iConvertedPlayData.Length(), 0, iConvertedPlayData.MaxLength()-iConvertedPlayData.Length()); |
|
1379 TInt len = iPlayFormatData.iConverter->Convert(aData, destPtr); |
|
1380 iConvertedPlayData.SetLength(iConvertedPlayData.Length() + destPtr.Length()); |
|
1381 if(len != aData.Length()) |
|
1382 { |
|
1383 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1384 RDebug::Printf("RMdaDevSound::CBody::PlayData converted %d but expected to convert %d", len, aData.Length()); |
|
1385 #endif |
|
1386 } |
|
1387 } |
|
1388 } |
|
1389 else |
|
1390 { |
|
1391 iConvertedPlayData.Append(aData); |
|
1392 } |
|
1393 iClientPlayData.Set(iConvertedPlayData); |
|
1394 } |
|
1395 else |
|
1396 { |
|
1397 // Do not need a conversion buffer so just aim the descriptor at the data |
|
1398 iClientPlayData.Set(aData); |
|
1399 } |
|
1400 iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; |
|
1401 |
|
1402 // All driver requests must be an exact multiple of iRequestMinSize |
|
1403 TUint32 trailingDataLen = iClientPlayData.Length() % iRequestMinSize; |
|
1404 if(trailingDataLen) |
|
1405 { |
|
1406 // Not a multiple of iRequestMinSize, so need to truncate current request, and save trailing bytes for |
|
1407 // inclusion at the beginning of the next request |
|
1408 iSavedTrailingData = iClientPlayData.Right(trailingDataLen); |
|
1409 iClientPlayData.Set(iClientPlayData.Left(iClientPlayData.Length()-trailingDataLen)); |
|
1410 } |
|
1411 |
|
1412 #ifdef SYMBIAN_FORCE_32BIT_LENGTHS |
|
1413 if (iClientPlayData.Length()%4 != 0) |
|
1414 { |
|
1415 // simulate the limitation of some hardware, where -6 is generated if the |
|
1416 // buffer length is not divisible by 4. |
|
1417 TRequestStatus *pRequest = &aStatus; |
|
1418 User::RequestComplete(pRequest, KErrArgument); |
|
1419 } |
|
1420 #endif |
|
1421 |
|
1422 iRecordChunk.Close(); |
|
1423 if(!iPlayChunk.Handle()) |
|
1424 { |
|
1425 //This is where we setup to play. |
|
1426 //Configure the shared chunk for two buffers with iBufferSize each |
|
1427 iPlayBufferConfig.iNumBuffers = KPlaySharedChunkBuffers; |
|
1428 iDeviceBufferLength = KPlaySharedChunkBufferSize; |
|
1429 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1430 RDebug::Printf("iDeviceBufferLength %d", iDeviceBufferLength); |
|
1431 #endif |
|
1432 iPlayBufferConfig.iFlags = 0;//data will be continuous |
|
1433 // If required, use rate converter etc |
|
1434 iPlayBufferConfig.iBufferSizeInBytes = iDeviceBufferLength; |
|
1435 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1436 RDebug::Printf("number of buffers: [%d]",iPlayBufferConfig.iNumBuffers); |
|
1437 RDebug::Printf("BufferSize in Bytes [%d]",iPlayBufferConfig.iBufferSizeInBytes); |
|
1438 #endif |
|
1439 TPckg<TPlaySharedChunkBufConfig> bufferConfigBuf(iPlayBufferConfig); |
|
1440 TInt error = iPlaySoundDevice.SetBufferChunkCreate(bufferConfigBuf,iPlayChunk); |
|
1441 if(error == KErrNone) |
|
1442 { |
|
1443 iPlaySoundDevice.GetBufferConfig(bufferConfigBuf); |
|
1444 } |
|
1445 if (error) |
|
1446 { |
|
1447 SoundDeviceError(error); |
|
1448 return; |
|
1449 } |
|
1450 } |
|
1451 |
|
1452 StartPlayersAndUpdateState(); |
|
1453 |
|
1454 return; |
|
1455 } |
|
1456 |
|
1457 void RMdaDevSound::CBody::RecordData(TRequestStatus& aStatus, TDes8& aData) |
|
1458 { |
|
1459 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); |
|
1460 aStatus = KRequestPending; |
|
1461 if((iClientPlayStatus != NULL) || InPlayMode()) |
|
1462 { |
|
1463 // We only support one outstanding request |
|
1464 // No support for simultaneous play and record in RMdaDevSound |
|
1465 TRequestStatus *pRequest = &aStatus; |
|
1466 User::RequestComplete(pRequest, KErrInUse); |
|
1467 return; |
|
1468 } |
|
1469 iClientRecordStatus = &aStatus; |
|
1470 iClientRecordData = &aData; |
|
1471 iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; |
|
1472 |
|
1473 iPlayChunk.Close(); |
|
1474 if(!iRecordChunk.Handle()) |
|
1475 { |
|
1476 //Configure the shared chunk for two buffers with iBufferSize each |
|
1477 iRecordBufferConfig.iNumBuffers = KRecordMaxSharedChunkBuffers; |
|
1478 iDeviceBufferLength = KRecordSharedChunkBufferSize; // initial size - resize if needs be |
|
1479 if (iRecordFormatData.iConverter) |
|
1480 { |
|
1481 // if number of channels used differs from request, resize buffer |
|
1482 // assume we have nice rounded values for buffer. |
|
1483 if (iRecordFormatData.iActualChannels>iRecordFormatData.iRequestedChannels) |
|
1484 { |
|
1485 iDeviceBufferLength *= 2; // will record at stereo and convert to mono |
|
1486 } |
|
1487 else if (iRecordFormatData.iActualChannels<iRecordFormatData.iRequestedChannels) |
|
1488 { |
|
1489 iDeviceBufferLength /= 2; // will record at mono and convert to stereo |
|
1490 } |
|
1491 } |
|
1492 iRecordBufferConfig.iBufferSizeInBytes = iDeviceBufferLength; |
|
1493 iRecordBufferConfig.iFlags = 0; |
|
1494 TPckg<TRecordSharedChunkBufConfig> bufferConfigBuf(iRecordBufferConfig); |
|
1495 TInt error = iRecordSoundDevice.SetBufferChunkCreate(bufferConfigBuf,iRecordChunk); |
|
1496 if(error == KErrNone) |
|
1497 { |
|
1498 iRecordSoundDevice.GetBufferConfig(bufferConfigBuf); |
|
1499 } |
|
1500 else |
|
1501 { |
|
1502 SoundDeviceError(error); |
|
1503 return; |
|
1504 } |
|
1505 iState = ERecording; |
|
1506 } |
|
1507 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1508 RDebug::Printf("RMdaDevSound::CBody::RecordData,iBufferOffset[%d]",iBufferOffset); |
|
1509 #endif |
|
1510 |
|
1511 switch(iState) |
|
1512 { |
|
1513 case ENotReady: |
|
1514 Panic(EBadState); |
|
1515 break; |
|
1516 |
|
1517 case EStopped: |
|
1518 case ERecording: |
|
1519 // Either idle or recording is in progress, therefore we can issue another request |
|
1520 StartRecordRequest(); |
|
1521 break; |
|
1522 |
|
1523 case ERecordingPausedInHw: |
|
1524 // Driver is paused, therefore we can issue a request which will immediately return buffered data |
|
1525 // or be aborted (in the driver) with KErrCancelled if there is no more data). nb. That KErrCancelled should not be |
|
1526 // returned to the client because the old RMdaDevSound driver would have completed with KErrNone and zero data length. |
|
1527 StartRecordRequest(); |
|
1528 break; |
|
1529 |
|
1530 case ERecordingPausedInSw: |
|
1531 // Paused in s/w but driver is not paused, therefore can not issue a new request to driver because |
|
1532 // it would re-start recording. |
|
1533 // This implies we were paused whilst the h/w was not recording, so there is no buffered data. |
|
1534 |
|
1535 // Complete the request with KErrNone and no data. |
|
1536 iClientRecordData->SetLength(0); |
|
1537 User::RequestComplete(iClientRecordStatus, KErrNone); |
|
1538 break; |
|
1539 |
|
1540 case EPlaying: |
|
1541 case EPlayingPausedInHw: |
|
1542 case EPlayingPausedInSw: |
|
1543 case EPlayingUnderrun: |
|
1544 Panic(EBadState); |
|
1545 break; |
|
1546 |
|
1547 default: |
|
1548 Panic(EBadState); |
|
1549 break; |
|
1550 } |
|
1551 } |
|
1552 |
|
1553 /** |
|
1554 Notify client of error. |
|
1555 |
|
1556 Note that we continue playing/recording if possible. |
|
1557 |
|
1558 We do not maintain information which could map the error back to a particular client play/record request |
|
1559 and therefore we have to notify the client of error every time it happens. |
|
1560 |
|
1561 nb. A client play/record request is completed with KErrNone if it queues ok - All errors are reported via the Notify*Error |
|
1562 mechanism. |
|
1563 */ |
|
1564 void RMdaDevSound::CBody::SoundDeviceError(TInt aError) |
|
1565 { |
|
1566 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1567 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError: Error[%d] state %s", aError, iState.Name()); |
|
1568 #endif |
|
1569 |
|
1570 ASSERT(aError != KErrNone); |
|
1571 |
|
1572 if(iClientPlayErrorStatus) |
|
1573 { |
|
1574 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1575 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iPlayerErrorStatus"); |
|
1576 #endif |
|
1577 |
|
1578 User::RequestComplete(iClientPlayErrorStatus, aError); // nb call also sets iClientPlayErrorStatus to NULL |
|
1579 } |
|
1580 |
|
1581 if(iClientRecordErrorStatus) |
|
1582 { |
|
1583 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1584 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iClientRecordErrorStatus"); |
|
1585 #endif |
|
1586 User::RequestComplete(iClientRecordErrorStatus, aError); // nb call also sets iClientRecordErrorStatus to NULL |
|
1587 } |
|
1588 |
|
1589 return; |
|
1590 } |
|
1591 |
|
1592 void RMdaDevSound::CBody::NotifyRecordError(TRequestStatus& aStatus) |
|
1593 { |
|
1594 aStatus = KRequestPending; |
|
1595 iClientRecordErrorStatus = &aStatus; |
|
1596 } |
|
1597 |
|
1598 void RMdaDevSound::CBody::NotifyPlayError(TRequestStatus& aStatus) |
|
1599 { |
|
1600 aStatus = KRequestPending; |
|
1601 iClientPlayErrorStatus = &aStatus; |
|
1602 } |
|
1603 |
|
1604 void RMdaDevSound::CBody::CancelNotifyPlayError() |
|
1605 { |
|
1606 if(iClientPlayErrorStatus) |
|
1607 { |
|
1608 User::RequestComplete(iClientPlayErrorStatus, KErrCancel); |
|
1609 } |
|
1610 } |
|
1611 |
|
1612 void RMdaDevSound::CBody::CancelNotifyRecordError() |
|
1613 { |
|
1614 if(iClientRecordErrorStatus) |
|
1615 { |
|
1616 User::RequestComplete(iClientRecordErrorStatus, KErrCancel); |
|
1617 } |
|
1618 else |
|
1619 { |
|
1620 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1621 RDebug::Printf("msp BufferEmptied but iClientPlayStatus==NULL"); |
|
1622 #endif |
|
1623 } |
|
1624 } |
|
1625 |
|
1626 void RMdaDevSound::CBody::FlushPlayBuffer() |
|
1627 { |
|
1628 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1629 RDebug::Printf("RMdaDevSound::CBody::FlushPlayBuffer calling CancelPlayData"); |
|
1630 #endif |
|
1631 CancelPlayData(); |
|
1632 } |
|
1633 |
|
1634 RSoundSc& RMdaDevSound::CBody::PlaySoundDevice() |
|
1635 { |
|
1636 return iPlaySoundDevice; |
|
1637 } |
|
1638 |
|
1639 RSoundSc& RMdaDevSound::CBody::RecordSoundDevice() |
|
1640 { |
|
1641 return iRecordSoundDevice; |
|
1642 } |
|
1643 |
|
1644 const RMdaDevSound::CBody::TState &RMdaDevSound::CBody::State() const |
|
1645 { |
|
1646 return iState; |
|
1647 } |
|
1648 |
|
1649 |
|
1650 void RMdaDevSound::CBody::BufferFilled(TInt aBufferOffset) |
|
1651 { |
|
1652 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1653 RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled:")); |
|
1654 #endif |
|
1655 |
|
1656 ASSERT(aBufferOffset>=0 || aBufferOffset==KErrCancel); |
|
1657 ASSERT(iClientRecordData); // request should not get this without |
|
1658 |
|
1659 if(aBufferOffset==KErrCancel) |
|
1660 { |
|
1661 //we can get KErrCancel when we call pause and there is no more data left with the driver |
|
1662 //we send the empty buffer to the HwDevice, where this should trigger the shutdown mechanism |
|
1663 iClientRecordData->SetLength(0); |
|
1664 User::RequestComplete(iClientRecordStatus, KErrNone); |
|
1665 iClientRecordStatus = NULL; |
|
1666 return; |
|
1667 } |
|
1668 |
|
1669 iBufferOffset = aBufferOffset; |
|
1670 //when last buffer is flushed, new driver sometimes gives buffer size of odd number. One of our codecs |
|
1671 //expects that the buffer size should always be even. Base suggested that we fix in multimedia |
|
1672 //as it is quite complicated to fix in overthere. |
|
1673 iBufferLength = iBufferLength & 0xfffffffe; |
|
1674 TPtr8 dataPtr(iRecordChunk.Base()+ iBufferOffset, iBufferLength, iClientRecordData->MaxLength()); |
|
1675 if (iRecordFormatData.iConverter) |
|
1676 { |
|
1677 iRecordFormatData.iConverter->Convert(dataPtr, *iClientRecordData); |
|
1678 } |
|
1679 else |
|
1680 { |
|
1681 iClientRecordData->Copy(dataPtr); |
|
1682 } |
|
1683 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1684 RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled: BufferOffset[%d] BufferLen[%d]"), iBufferOffset, iBufferLength); |
|
1685 #endif |
|
1686 if(iBufferOffset >= 0) |
|
1687 { |
|
1688 iRecordSoundDevice.ReleaseBuffer(iBufferOffset); |
|
1689 } |
|
1690 if(iClientRecordStatus) |
|
1691 { |
|
1692 User::RequestComplete(iClientRecordStatus, KErrNone); |
|
1693 iClientRecordStatus = NULL; |
|
1694 } |
|
1695 else |
|
1696 { |
|
1697 RDebug::Printf("msp PlayCancelled but iClientPlayStatus==NULL"); |
|
1698 } |
|
1699 } |
|
1700 |
|
1701 /* |
|
1702 This function is called to notify us that a CPlayer's request has completed and what its status was. |
|
1703 |
|
1704 It is important to note that:- |
|
1705 1) RSoundSc driver PlayData requests are guaranteed to complete in order, oldest first |
|
1706 2) If we are overloaded, it is possible for more than one request to complete before any CPlayer::RunL is ran. In |
|
1707 this situation the CPlayer::RunL functions, and hence this callback, maybe invoked in non-oldest first order |
|
1708 |
|
1709 but |
|
1710 |
|
1711 a) It is impossible for callback for the second oldest CPlayer to occur before the driver request for the oldest has |
|
1712 been complete (because of 1) |
|
1713 b) We will always get exactly one callback for every complete request. |
|
1714 |
|
1715 Therefore this callback notifies us of two subtly separate things:- |
|
1716 |
|
1717 i) The oldest request has been completed (so we can reduce can increase the bytes played counter by its length |
|
1718 ii) CPlayer aPlayerIndex is free for re-use |
|
1719 |
|
1720 but we can not assume that aPlayerIndex is the oldest request, therefore we save the play request lengths outside of |
|
1721 the CPlayer object. |
|
1722 */ |
|
1723 void RMdaDevSound::CBody::PlayRequestHasCompleted(CPlayer *aPlayer, TInt aStatus, TBool aDueToCancelCommand) |
|
1724 { |
|
1725 // CPlayer is done so put it on the free queue |
|
1726 iFreePlayers.Push(aPlayer); |
|
1727 |
|
1728 TUint32 bytesPlayed = iActivePlayRequestSizes.Pop(); |
|
1729 // Request has finished therefore now timing the following request to simulate bytes played |
|
1730 iStartTime = CurrentTimeInMsec(); |
|
1731 if(aDueToCancelCommand) |
|
1732 { |
|
1733 // Callback due to CPlayer::Cancel/DoCancel being called, therefore we |
|
1734 // do not want to update bytes played, process state, report a error or start new players |
|
1735 return; |
|
1736 } |
|
1737 |
|
1738 // Update iBytesPlayed by the length of the oldest request (which might not be the one that CPlayer was |
|
1739 // handling). |
|
1740 iBytesPlayed += bytesPlayed; |
|
1741 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1742 RDebug::Printf("PlayRequestHasCompleted increasing iBytesPlayed by %d to %d", bytesPlayed, iBytesPlayed); |
|
1743 #endif |
|
1744 |
|
1745 // Process state |
|
1746 switch(iState) |
|
1747 { |
|
1748 case ENotReady: |
|
1749 Panic(EDeviceNotOpened); |
|
1750 break; |
|
1751 |
|
1752 case EStopped: |
|
1753 // Will happen if we are doing CancelPlayData processing with active players |
|
1754 break; |
|
1755 |
|
1756 case ERecording: |
|
1757 case ERecordingPausedInHw: |
|
1758 case ERecordingPausedInSw: |
|
1759 Panic(EBadState); |
|
1760 break; |
|
1761 |
|
1762 case EPlaying: |
|
1763 // Normal situation |
|
1764 break; |
|
1765 |
|
1766 case EPlayingPausedInHw: |
|
1767 // H/W was/is paused, but there must have been an already complete request that we had not |
|
1768 // processed yet. |
|
1769 // There must be at least one more pending request on h/w, otherwise the h/w would have refused to pause |
|
1770 // I would expect this be rare, but it happens quite often... |
|
1771 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1772 ASSERT(iActivePlayRequestSizes.Length() != 0); |
|
1773 #endif |
|
1774 // Need to update the start and pause time to now because we have just updated the actual iBytesPlayed |
|
1775 // and logically the h/w is paused at the beginning of the next request |
|
1776 iStartTime = CurrentTimeInMsec(); |
|
1777 iPauseTime = iStartTime; |
|
1778 break; |
|
1779 |
|
1780 case EPlayingPausedInSw: |
|
1781 // This will happen if there is only a single hw request outstanding, and the hardware has finished it, but the |
|
1782 // corresponding RunL has not run yet (in which case PausePlayBuffer will have attempted to use h/w pause, |
|
1783 // but the driver call would have failed, and the state changed to EPlayingPausedInSw). |
|
1784 iStartTime = CurrentTimeInMsec(); |
|
1785 iPauseTime = iStartTime; |
|
1786 return; |
|
1787 |
|
1788 case EPlayingUnderrun: |
|
1789 // Probably can not happen... |
|
1790 break; |
|
1791 |
|
1792 default: |
|
1793 Panic(EBadState); |
|
1794 break; |
|
1795 } |
|
1796 |
|
1797 |
|
1798 // If we have an error, report it to the client |
|
1799 // We NEVER report driver underflow, instead we report KErrUnderflow if we run out of data to pass to driver. |
|
1800 if( (aStatus != KErrNone) && (aStatus != KErrUnderflow) ) |
|
1801 { |
|
1802 SoundDeviceError(aStatus); |
|
1803 } |
|
1804 |
|
1805 // If appropriate start more players |
|
1806 StartPlayersAndUpdateState(); |
|
1807 return; |
|
1808 } |
|
1809 |
|
1810 RMdaDevSound::CBody::CPlayer::CPlayer(TInt aPriority, RMdaDevSound::CBody& aParent, TInt aIndex): |
|
1811 CActive(aPriority), iParent(aParent), iIndex(aIndex), iBufferOffset(-1), iBufferLength(0) |
|
1812 { |
|
1813 CActiveScheduler::Add(this); |
|
1814 } |
|
1815 |
|
1816 RMdaDevSound::CBody::CPlayer::~CPlayer() |
|
1817 { |
|
1818 Cancel(); |
|
1819 } |
|
1820 |
|
1821 |
|
1822 void RMdaDevSound::CBody::CPlayer::RunL() |
|
1823 { |
|
1824 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1825 RDebug::Printf("****RMdaDevSound::CBody::CPlayer(%d)::RunL: Error[%d] ParentState[%s]", |
|
1826 iIndex, iStatus.Int(), iParent.State().Name()); |
|
1827 RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (including this one as active)", |
|
1828 iParent.iActivePlayRequestSizes.Length(), |
|
1829 iParent.iFreePlayers.Length()); |
|
1830 #endif |
|
1831 iParent.PlayRequestHasCompleted(this, iStatus.Int(), EFalse); |
|
1832 return; |
|
1833 } |
|
1834 |
|
1835 TInt RMdaDevSound::CBody::CPlayer::RunError(TInt aError) |
|
1836 { |
|
1837 iParent.PlayRequestHasCompleted(this, aError, EFalse); |
|
1838 return KErrNone; |
|
1839 } |
|
1840 |
|
1841 void RMdaDevSound::CBody::CPlayer::DoCancel() |
|
1842 { |
|
1843 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1844 RDebug::Printf("RMdaDevSound::CBody::CPlayer(%d)::DoCancel", iIndex); |
|
1845 #endif |
|
1846 if(iStatus == KRequestPending) |
|
1847 { |
|
1848 // Avoid cancelling requests which have already completed. |
|
1849 // It wastes time, and might provoke a sound driver problem |
|
1850 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1851 RDebug::Printf("RMdaDevSound::CBody::CPlayer::DoCancel - would have cancelled driver request"); |
|
1852 #endif |
|
1853 iParent.PlaySoundDevice().Cancel(iStatus); |
|
1854 } |
|
1855 iParent.PlayRequestHasCompleted(this, KErrCancel, ETrue); |
|
1856 } |
|
1857 |
|
1858 void RMdaDevSound::CBody::CPlayer::PlayData(TUint aChunkOffset, TInt aLength) |
|
1859 { |
|
1860 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1861 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::PlayData : IsActive[%d]"), |
|
1862 iIndex, IsActive()); |
|
1863 RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (inc this player)", |
|
1864 iParent.iActivePlayRequestSizes.Length(), |
|
1865 iParent.iFreePlayers.Length()); |
|
1866 #endif |
|
1867 |
|
1868 iBufferOffset = aChunkOffset; |
|
1869 iBufferLength = aLength; |
|
1870 |
|
1871 //Make sure the length is a multiple of 4 to work around an h6 limitation. |
|
1872 iBufferLength = iBufferLength & 0xfffffffc; |
|
1873 |
|
1874 // Issue the RSoundSc request |
|
1875 iParent.PlaySoundDevice().PlayData(iStatus, iBufferOffset, iBufferLength, EFalse); |
|
1876 SetActive(); |
|
1877 return; |
|
1878 } |
|
1879 |
|
1880 TUint RMdaDevSound::CBody::CPlayer::GetPlayerIndex() const |
|
1881 { |
|
1882 return iIndex; |
|
1883 } |
|
1884 |
|
1885 RMdaDevSound::CBody::CRecorder::CRecorder(TInt aPriority, RMdaDevSound::CBody& aParent): |
|
1886 CActive(aPriority), iParent(aParent), iBufferOffset(-1), iBufferLength(0) |
|
1887 { |
|
1888 CActiveScheduler::Add(this); |
|
1889 } |
|
1890 |
|
1891 RMdaDevSound::CBody::CRecorder::~CRecorder() |
|
1892 { |
|
1893 Cancel(); |
|
1894 } |
|
1895 |
|
1896 void RMdaDevSound::CBody::CRecorder::RecordData(TInt& aBufferLength) |
|
1897 { |
|
1898 if (!IsActive()) |
|
1899 { |
|
1900 iStatus = KRequestPending; |
|
1901 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1902 RDebug::Printf("Recording request: BufferLength[%d]", aBufferLength); |
|
1903 #endif |
|
1904 iParent.RecordSoundDevice().RecordData(iStatus, aBufferLength); |
|
1905 SetActive(); |
|
1906 } |
|
1907 } |
|
1908 |
|
1909 void RMdaDevSound::CBody::CRecorder::RunL() |
|
1910 { |
|
1911 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1912 RDebug::Printf("****RMdaDevSound::CBody::CRecorder()::RunL: Error[%d] ParentState[%s]", |
|
1913 iStatus.Int(), iParent.State().Name()); |
|
1914 #endif |
|
1915 |
|
1916 |
|
1917 TInt error = iStatus.Int(); |
|
1918 |
|
1919 if((error >= 0) || (error == KErrCancel)) |
|
1920 {//we can get KErrCancel when we call pause and there is no more data left with the driver |
|
1921 iParent.BufferFilled(error); |
|
1922 } |
|
1923 else |
|
1924 { |
|
1925 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1926 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer()::RunL: Error[%d]"), error); |
|
1927 #endif |
|
1928 iParent.SoundDeviceError(error); |
|
1929 } |
|
1930 } |
|
1931 |
|
1932 |
|
1933 TInt RMdaDevSound::CBody::CRecorder::RunError(TInt aError) |
|
1934 { |
|
1935 iParent.SoundDeviceError(aError); |
|
1936 return KErrNone; |
|
1937 } |
|
1938 |
|
1939 void RMdaDevSound::CBody::CRecorder::DoCancel() |
|
1940 { |
|
1941 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1942 RDebug::Printf("RMdaDevSound::CBody::CRecorder()::DoCancel"); |
|
1943 #endif |
|
1944 iParent.RecordSoundDevice().Cancel(iStatus); |
|
1945 } |
|
1946 |
|
1947 |
|
1948 // End of file |