First public contribution.
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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
15 #include "mdasoundadapterconsts.h"
16 #include "mdasoundadapterbody.h"
19 #include "mmf/utils/rateconvert.h" // if we need to resample
23 _LIT(KPddFileName,"SOUNDSC.PDD");
24 _LIT(KLddFileName,"ESOUNDSC.LDD");
27 const TInt KBytesPerSample = 2;
28 const TInt KMinBufferSize = 2;
31 This function raises a panic
32 EDeviceNotOpened is raised when any of the RMdaDevSound APIs are called before opening the device.
34 GLDEF_C void Panic(TSoundAdapterPanicCodes aPanicCode)
36 User::Panic(KSoundAdapterPanicCategory, aPanicCode);
40 const TText8 *RMdaDevSound::CBody::TState::Name() const
42 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
45 case ENotReady: return _S8("ENotReady");
46 case EStopped: return _S8("EStopped");
47 case ERecording: return _S8("ERecording");
48 case ERecordingPausedInHw: return _S8("ERecordingPausedInHw");
49 case ERecordingPausedInSw: return _S8("ERecordingPausedInSw");
50 case EPlaying: return _S8("EPlaying");
51 case EPlayingPausedInHw: return _S8("EPlayingPausedInHw");
52 case EPlayingPausedInSw: return _S8("EPlayingPausedInSw");
53 case EPlayingUnderrun: return _S8("EPlayingUnderrun");
55 return _S8("CorruptState");
63 RMdaDevSound::CBody::TState &RMdaDevSound::CBody::TState::operator=(TStateEnum aNewState)
65 if(iState != aNewState)
67 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
68 RDebug::Printf("RMdaDevSound state %s -> %s", Name(), TState(aNewState).Name());
75 RMdaDevSound::CBody* RMdaDevSound::CBody::NewL()
77 CBody* self = new(ELeave) CBody();
78 CleanupStack::PushL(self);
84 RMdaDevSound::CBody::~CBody()
86 for(TInt i = 0; i < KPlaySharedChunkBuffers; i++)
93 delete iPlayFormatData.iConverter;
94 delete iRecordFormatData.iConverter;
96 iPlaySoundDevice.Close();
98 iRecordSoundDevice.Close();
99 iConvertedPlayData.Close();
100 iSavedTrailingData.Close();
101 iBufferedRecordData.Close();
104 RMdaDevSound::CBody::CBody()
105 :iState(ENotReady), iBufferOffset(-1)
110 TVersion RMdaDevSound::CBody::VersionRequired() const
112 if(iPlaySoundDevice.Handle())
114 return iPlaySoundDevice.VersionRequired();
122 TInt RMdaDevSound::CBody::IsMdaSound()
127 void RMdaDevSound::CBody::ConstructL()
129 // Try to load the audio physical driver
130 TInt err = User::LoadPhysicalDevice(KPddFileName);
131 if ((err!=KErrNone) && (err!=KErrAlreadyExists))
135 // Try to load the audio logical driver
136 err = User::LoadLogicalDevice(KLddFileName);
137 if ((err!=KErrNone) && (err!=KErrAlreadyExists))
141 for(TInt i=0; i<KPlaySharedChunkBuffers; i++)
143 iPlayers[i] = new(ELeave) CPlayer(CActive::EPriorityUserInput, *this, i);
144 iFreePlayers.Push(iPlayers[i]);
147 iRecorder = new(ELeave) CRecorder(CActive::EPriorityUserInput, *this);
150 User::LeaveIfError(HAL::Get(HAL::ENanoTickPeriod, tmp));
151 iNTickPeriodInUsec = tmp;
154 TInt RMdaDevSound::CBody::Open(TInt /*aUnit*/)
156 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
157 RDebug::Print(_L("RMdaDevSound::CBody::Open "));
160 //Default behavior of this method is to open both the play and record audio devices.
161 if(!iPlaySoundDevice.Handle() && !iRecordSoundDevice.Handle())
163 err = iPlaySoundDevice.Open(KSoundScTxUnit0);
166 err = iRecordSoundDevice.Open(KSoundScRxUnit0);
175 TSoundFormatsSupportedV02Buf capsBuf;
176 iPlaySoundDevice.Caps(capsBuf);
177 TInt minBufferSize = KMinBufferSize;
178 #ifdef SYMBIAN_FORCE_32BIT_LENGTHS
179 minBufferSize = Max(minBufferSize, 4); // force to 32-bit buffer align
181 iRequestMinSize = Max(capsBuf().iRequestMinSize, minBufferSize);
182 // work out mask so that x&iRequestMinMask is equiv to x/iRequestMinSize*iRequestMinSize
183 iRequestMinMask = ~(iRequestMinSize-1); // assume iRequestMinSize is power of 2
184 iSavedTrailingData.Close();
185 iSavedTrailingData.Create(iRequestMinSize);
194 TInt RMdaDevSound::CBody::PlayVolume()
196 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
197 return iPlaySoundDevice.Volume();
200 void RMdaDevSound::CBody::SetPlayVolume(TInt aVolume)
202 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
203 if(aVolume >=0 && aVolume<=KSoundMaxVolume)
205 iPlaySoundDevice.SetVolume(KLinerToDbConstantLookup[aVolume].iDBValue);
208 void RMdaDevSound::CBody::SetVolume(TInt aLogarithmicVolume)
210 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
211 if(aLogarithmicVolume >= 0 && aLogarithmicVolume <= KSoundMaxVolume)
213 iPlaySoundDevice.SetVolume(aLogarithmicVolume);
217 void RMdaDevSound::CBody::CancelPlayData()
219 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
220 RDebug::Printf("RMdaDevSound::CBody::CancelPlayData: state %s", iState.Name());
222 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
224 // If there is a client request, cancel it
225 // Must do this before canceling players because otherwise they may just restart!
226 if(iClientPlayStatus)
228 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
229 RDebug::Printf("msp PlayCancelled complete iClientPlayStatus");
231 User::RequestComplete(iClientPlayStatus, KErrCancel); // Call also sets iClientPlayStatus to NULL
234 // Discard any buffered data
235 iClientPlayData.Set(0,0);
236 // Discard any saved trailing data (ie. data saved due driver requiring all requests to be a multiple of iRequestMinSize).
237 iSavedTrailingData.SetLength(0);
239 // Emulator RSoundSc PDD when running without a soundcard has a major
240 // issue with cancelling whilst paused. It will not clear the pending
241 // list (because the timer is not active) and therefore this list will
242 // later overflow causing hep corruption.
243 // This means that, for now, we MUST Resume before calling CancelPlayData
244 // to avoid kernel panics...
246 // The device driver will not cancel a request which is in progress...
247 // So, if we are paused in hw, we must resume before cancelling the
248 // player otherwise it will hang in CActive::Cancel
249 if(iState == EPlayingPausedInHw)
251 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
252 RDebug::Printf("msp Resume to avoid hang");
254 (void) iPlaySoundDevice.Resume();
261 // The RSoundSc driver will not cancel a request which is in progress (or paused).
262 // If we just loop across the players, cancelling each individual request and waiting for it to complete,
263 // several of them will actually play, which is both wrong and time consuming....
264 // Issue a block cancel upfront to avoid this
265 iPlaySoundDevice.CancelPlayData();
267 // Cancel all players
268 for (TUint playerIndex=0; playerIndex<KPlaySharedChunkBuffers; ++playerIndex)
270 // If the player is active it will call PlayRequestCompleted with aDueToCancelCommand true
271 // to update the iFreePlayers and iActivePlayRequestSizes FIFOs.
272 iPlayers[playerIndex]->Cancel();
281 TInt RMdaDevSound::CBody::RecordLevel()
283 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
284 return iRecordSoundDevice.Volume();
287 void RMdaDevSound::CBody::SetRecordLevel(TInt aLevel)
289 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
290 iRecordSoundDevice.SetVolume(aLevel);
293 void RMdaDevSound::CBody::CancelRecordData()
295 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
296 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
297 RDebug::Printf("RMdaDevSound::CBody::CancelRecordData: state %s", iState.Name());
300 // Stop recorder object (and its request)
303 // Stop driver from recording
304 iRecordSoundDevice.CancelRecordData();
306 // If there is a client request, cancel it
307 if(iClientRecordStatus)
309 User::RequestComplete(iClientRecordStatus, KErrNone); // Call also sets iClientPlayStatus to NULL
316 void RMdaDevSound::CBody::FlushRecordBuffer()
318 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
319 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
320 RDebug::Print(_L("RMdaDevSound::CBody::FlushRecordBuffer - implemented by calling PauseRecordBuffer"));
326 TInt RMdaDevSound::CBody::BytesPlayed()
328 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
329 RDebug::Printf("RMdaDevSound::BytesPlayed %s", iState.Name());
332 return I64LOW(BytesPlayed64());
336 TUint64 RMdaDevSound::CBody::BytesPlayed64()
338 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
340 TUint64 currentBytesPlayed = KMaxTUint64;
345 Panic(EDeviceNotOpened);
349 currentBytesPlayed = iBytesPlayed;
353 case ERecordingPausedInHw:
354 case ERecordingPausedInSw:
358 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
359 // Paused, so use pause time
360 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
361 RDebug::Printf("EPlayingPausedInHw: iPausedBytes %x %x", I64HIGH(iPausedBytesPlayed), I64LOW(iPausedBytesPlayed));
363 currentBytesPlayed = iPausedBytesPlayed;
366 case EPlayingPausedInSw: // ie. Driver not playing or paused
367 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
368 RDebug::Printf("EPlayingPausedInSw: iPausedBytesPlayed %x %x", I64HIGH(iPausedBytesPlayed), I64LOW(iPausedBytesPlayed));
370 currentBytesPlayed = iPausedBytesPlayed;
372 case EPlayingUnderrun:
373 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
374 RDebug::Printf("EPlayingUnderrun: iBytesPlayed %x %x", I64HIGH(iBytesPlayed), I64LOW(iBytesPlayed));
376 currentBytesPlayed = iBytesPlayed;
381 // Playing so calculate time since last update to iBytesPlayed
382 TUint32 curTime = CurrentTimeInMsec();
383 TUint32 curRequestSize = iActivePlayRequestSizes.Peek();
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);
389 RDebug::Printf("iPlayFormatData.iSampleRate %d KBytesPerSample %d iNTickPeriodInUsec %d",
390 iPlayFormatData.iSampleRate, KBytesPerSample, iNTickPeriodInUsec);
392 TUint32 extraBytesPlayed = TUint32((TUint64(extraPlayTime) * iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample)/1000);
393 if(extraBytesPlayed > curRequestSize)
395 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
396 RDebug::Printf("caping extraBytes played from %d to %d", extraBytesPlayed, curRequestSize);
398 extraBytesPlayed = curRequestSize;
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));
406 currentBytesPlayed = iBytesPlayed + extraBytesPlayed;
416 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
417 RDebug::Printf("iPlayFormatData.iConverter %x", iPlayFormatData.iConverter);
420 if (iPlayFormatData.iConverter)
422 // need to scale bytes played to fit with requested rate and channels, not actual
423 if (iPlayFormatData.iActualChannels != iPlayFormatData.iRequestedChannels)
425 if (iPlayFormatData.iActualChannels == 2)
427 // requested was mono, we have stereo
428 currentBytesPlayed /= 2;
432 // requested was stereo, we have mono
433 currentBytesPlayed *= 2;
436 if (iPlayFormatData.iSampleRate != iPlayFormatData.iActualRate)
438 currentBytesPlayed = TUint64(currentBytesPlayed*
439 TReal(iPlayFormatData.iSampleRate)/TReal(iPlayFormatData.iActualRate)); // don't round
443 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
444 RDebug::Printf("currentBytesPlayed %x %x", I64HIGH(currentBytesPlayed), I64LOW(currentBytesPlayed));
446 return currentBytesPlayed;
449 void RMdaDevSound::CBody::ResetBytesPlayed()
451 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
452 RDebug::Printf("RMdaDevSound::CBody::ResetBytesPlayed %s", iState.Name());
454 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
456 iPlaySoundDevice.ResetBytesTransferred();
460 void RMdaDevSound::CBody::PausePlayBuffer()
462 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
463 RDebug::Printf("RMdaDevSound::CBody::PausePlayBuffer %s", iState.Name());
468 Panic(EDeviceNotOpened);
472 // Driver is not playing so pause in s/w
476 case ERecordingPausedInHw:
477 case ERecordingPausedInSw:
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.
485 case EPlayingUnderrun:
486 iState = EPlayingPausedInSw;
491 iPauseTime = CurrentTimeInMsec();
492 iPausedBytesPlayed = BytesPlayed64();
493 TInt res = iPlaySoundDevice.Pause();
494 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
495 RDebug::Printf("iPlaySoundDevice.Pause res = %d", res);
499 iState = EPlayingPausedInHw;
503 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
504 RDebug::Printf("msp PausePlayBuffer hw pause unexpectedly failed, doing sw pause");
506 iState = EPlayingPausedInSw;
519 void RMdaDevSound::CBody::ResumePlaying()
521 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
522 RDebug::Printf("RMdaDevSound::CBody::ResumePlaying %s", iState.Name());
524 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
529 Panic(EDeviceNotOpened);
537 case ERecordingPausedInHw:
538 case ERecordingPausedInSw:
546 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
548 // Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing).
549 iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse;
551 TInt res = iPlaySoundDevice.Resume();
552 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
553 RDebug::Printf("ResumePlayBuffer EPlayingPausedInHw res = %d", res);
557 // Resume ok so a pending request will complete
559 // Update iStartTime to allow for time spent paused
560 TUint32 curTime = CurrentTimeInMsec();
561 TUint32 timePaused = (curTime >= iPauseTime) ? (curTime-iPauseTime) : (KMaxTUint32 - (iPauseTime-curTime));
562 iStartTime += timePaused; // nb. It is harmless if this wraps.
566 // Resume failed, therefore driver is not playing
567 // No need to update iStartTime/iPauseTime because these are only used within a driver request
568 // Change state to Stopped
570 // Attempt to start a new (pending) request.
571 StartPlayersAndUpdateState();
575 case EPlayingPausedInSw: // ie. Driver not playing/paused
577 // Driver not playing
578 // Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing).
579 iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse;
580 // No need to update iStartTime/iPauseTime because these are only used within a driver request
581 // Change state to Stopped
583 // Attempt to start a new (pending) request.
584 StartPlayersAndUpdateState();
587 case EPlayingUnderrun:
598 void RMdaDevSound::CBody::PauseRecordBuffer()
600 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
601 RDebug::Printf("RMdaDevSound::CBody::PauseRecordBuffer %s", iState.Name());
607 Panic(EDeviceNotOpened);
611 // Driver is not recording so pause in s/w
612 // Do not pause because that will cause problems when CAudioDevice::Pause calls
617 TInt res = iRecordSoundDevice.Pause();
618 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
619 RDebug::Printf("PauseRecordBuffer EPlaying res = %d", res);
623 iState = ERecordingPausedInHw;
627 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
628 RDebug::Printf("PauseRecordBuffer hw pause unexpectedly failed, doing sw pause");
630 iState = ERecordingPausedInSw;
635 case ERecordingPausedInHw:
636 case ERecordingPausedInSw:
637 // Already paused so nothing to do.
641 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
645 case EPlayingPausedInSw:
646 // This is an ugly hack to maintain compatibility with CAudioDevice::Pause which
647 // calls both PausePlayBuffer and PauseRecordBuffer whilst in stopped, then later calls ResumePlaying
649 case EPlayingUnderrun: // ie. Play request pending on h/w and paused
661 void RMdaDevSound::CBody::ResumeRecording()
663 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
664 RDebug::Printf("RMdaDevSound::CBody::ResumeRecording %s", iState.Name());
666 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
671 Panic(EDeviceNotOpened);
682 case ERecordingPausedInHw:
684 TInt res = iRecordSoundDevice.Resume();
685 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
686 RDebug::Printf("ResumeRecordBuffer ERecordingPausedInHw res = %d", res);
690 // Resume ok so a pending request will complete
696 // Resume failed, so attempt to start a new (pending) request.
697 // If this works, it will update the state to ERecording.
698 StartRecordRequest();
702 case ERecordingPausedInSw:
704 // Update state to stopped and attempt to start any pending request
706 // If this works, it will update the state to ERecording.
707 StartRecordRequest();
712 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
713 case EPlayingPausedInSw: // ie. Driver not playing/paused
714 case EPlayingUnderrun:
725 TInt RMdaDevSound::CBody::GetTimePlayed(TTimeIntervalMicroSeconds& aTimePlayed)
727 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
730 TUint64 bytesPlayed = BytesPlayed64();
732 TUint64 timePlayed = 1000 * 1000 * bytesPlayed / (iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample);
734 aTimePlayed = TTimeIntervalMicroSeconds(timePlayed);
740 void RMdaDevSound::CBody::FormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported, RSoundSc& aSoundDevice)
742 TSoundFormatsSupportedV02Buf supportedFormat;
743 aSoundDevice.Caps(supportedFormat);
744 TUint32 rates = supportedFormat().iRates;
746 for(TInt i = KNumSampleRates-1; i > 0 ;i--)//min to max
748 if(rates & KRateEnumLookup[i].iRateConstant)
750 aFormatsSupported().iMinRate = KRateEnumLookup[i].iRate;
754 for(TInt i = 0; i < KNumSampleRates; i++)//max to min
756 if(rates & KRateEnumLookup[i].iRateConstant)
758 aFormatsSupported().iMaxRate = KRateEnumLookup[i].iRate;
762 TUint32 enc = supportedFormat().iEncodings;
764 if (enc & KSoundEncoding16BitPCM)
766 aFormatsSupported().iEncodings = EMdaSoundEncoding16BitPCM;// Always defaults to this
768 if (enc & KSoundEncoding8BitPCM)
770 aFormatsSupported().iEncodings |= EMdaSoundEncoding8BitPCM;
772 TUint32 channels = supportedFormat().iChannels;
774 if (channels & KSoundStereoChannel)
776 aFormatsSupported().iChannels = 2;
780 aFormatsSupported().iChannels = 1;
782 aFormatsSupported().iMinBufferSize = supportedFormat().iRequestMinSize;
783 aFormatsSupported().iMaxBufferSize = KMaxBufferSize;
784 aFormatsSupported().iMinVolume = 0;
785 aFormatsSupported().iMaxVolume = KSoundMaxVolume;
788 void RMdaDevSound::CBody::GetFormat(TCurrentSoundFormatBuf& aFormat,
789 RSoundSc& /*aSoundDevice*/,
790 const TFormatData &aFormatData)
792 // always return the requested, or the initial, not current device setting
793 aFormat().iChannels = aFormatData.iRequestedChannels; // never clear if this is bitmap or value, but effectively the same
794 aFormat().iRate = aFormatData.iSampleRate;
797 void RMdaDevSound::CBody::StartPlayersAndUpdateState()
799 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
804 Panic(EDeviceNotOpened);
808 // Allow following code to queue more driver play requests and check for stopped
812 case ERecordingPausedInHw:
813 case ERecordingPausedInSw:
818 // Allow following code to queue more driver play requests and check for stopped
820 case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
821 // Allow following code to queue more driver play requests
824 case EPlayingPausedInSw:
825 // Paused but driver not playing+paused, therefore do not queue new requests until ResumePlaying
827 case EPlayingUnderrun:
835 // iState is now either EStopped, EPlaying or EPlayingPausedInHw
836 __ASSERT_DEBUG(((iState == EStopped) || (iState == EPlaying) || (iState == EPlayingPausedInHw) || (iState == EPlayingUnderrun)), Panic(EBadState));
838 while( (iClientPlayData.Length() != 0) && (! iFreePlayers.IsEmpty()))
840 // More data to play and more players, so issue another request
842 bool wasIdle = iFreePlayers.IsFull();
844 CPlayer *player = iFreePlayers.Pop();
845 // Calculate length of request
846 TUint32 lengthToPlay = iClientPlayData.Length();
847 if(lengthToPlay > iDeviceBufferLength)
849 lengthToPlay = iDeviceBufferLength;
852 // Remember request length, so we can update bytes played when it finishes
853 iActivePlayRequestSizes.Push(lengthToPlay);
855 // Find offset to copy data to
856 TUint playerIndex = player->GetPlayerIndex();
857 ASSERT(playerIndex < KPlaySharedChunkBuffers);
858 TUint chunkOffset = iPlayBufferConfig.iBufferOffsetList[playerIndex];
861 TPtr8 destPtr(iPlayChunk.Base()+ chunkOffset, 0, iDeviceBufferLength);
862 destPtr.Copy(iClientPlayData.Mid(0, lengthToPlay));
864 // Update iClientPlayData to remove the data just queued
865 iClientPlayData.Set(iClientPlayData.Right(iClientPlayData.Length()-lengthToPlay));
868 player->PlayData(chunkOffset, lengthToPlay);
872 iStartTime = CurrentTimeInMsec();
878 // Check if the client request is now complete
879 if(iClientPlayData.Length() == 0 && iClientPlayStatus)
881 // We have queued all the client play data to the driver so we can now complete the client request.
882 // If actual playback fails, we will notify the client via the Play Error notification mechanism.
883 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
884 RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState completing client request");
886 User::RequestComplete(iClientPlayStatus, KErrNone); // This call also sets iClientPlayStatus to NULL
889 //nb. iState is now either EStopped, EPlaying or EPlayingPausedInHw (see previous switch and assert)
890 if(iState != EPlayingPausedInHw)
892 if(iFreePlayers.IsFull())
894 // Free fifo is full, therefore there are no active players
895 iState = EPlayingUnderrun;
896 if(! iUnderFlowReportedSinceLastPlayOrRecordRequest)
898 // We report KErrUnderflow if we have not already reported it since the last PlayData call.
900 // i) We do NOT report driver underflows.
901 // ii) We report underflow when we run out of data to pass to the driver.
902 // iii) We throttle this reporting
903 // iv) We WILL report KErrUnderflow if already stopped and asked to play a zero length buffer
904 // The last point is required because the client maps a manual stop command into a devsound play with a
905 // zero length buffer and the last buffer flag set, this in turn is mapped to a Playdata calll with an empty buffer
906 // which is expected to complete ok and cause a KErrUnderflow error to be reported...
907 iUnderFlowReportedSinceLastPlayOrRecordRequest = ETrue;
908 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
909 RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState stopped and iUnderFlowReportedSinceLastPlayOrRecordRequest false so raising KErrUnderflow");
912 // Discard any saved trailing data (ie. data saved due driver requiring all requests to be a multiple of iRequestMinSize).
913 // This maybe because client is delibrately letting us underflow to play silence. In that case we do not want to
914 // play the trailing data at the beginning of the new data issued after the silence...
915 iSavedTrailingData.SetLength(0);
917 SoundDeviceError(KErrUnderflow);
922 // Free fifo not full, therefore there are active players
929 TInt RMdaDevSound::CBody::SetFormat(const TCurrentSoundFormatBuf& aFormat,
930 RSoundSc& aSoundDevice,
931 TFormatData &aFormatData)
933 TInt err = KErrNotFound;
934 TCurrentSoundFormatV02Buf formatBuf;
936 delete aFormatData.iConverter;
937 aFormatData.iConverter = NULL; // setting this to NULL indicates we are not using converter. No other flag
938 iConvertedPlayData.Close();
940 TInt wantedRate = aFormat().iRate;
941 for(TInt index = 0; index < KNumSampleRates; index++ )
943 if(wantedRate == KRateEnumLookup[index].iRate)
945 formatBuf().iRate = KRateEnumLookup[index].iRateEnum;
946 aFormatData.iSampleRate = wantedRate;
954 // Assume, for now, we support the requested channels and rate
955 aFormatData.iActualChannels = aFormatData.iRequestedChannels;
956 aFormatData.iActualRate = aFormatData.iSampleRate;
958 // Attempt to configure driver
959 formatBuf().iChannels = aFormat().iChannels;
960 formatBuf().iEncoding = ESoundEncoding16BitPCM;
961 formatBuf().iDataFormat = ESoundDataFormatInterleaved;
962 err = aSoundDevice.SetAudioFormat(formatBuf);
963 #if defined(SYMBIAN_SOUNDADAPTER_FORCECDRATES) || defined (SYMBIAN_SOUNDADAPTER_FORCESTEREO)
964 err = KErrNotSupported; // force Negotiate - for debugging
966 if (err==KErrNotSupported)
968 // don't support directly. Perhaps can rate convert?
969 err = NegotiateFormat(aFormat, aSoundDevice, aFormatData);
975 TInt RMdaDevSound::CBody::NegotiateFormat(const TCurrentSoundFormatBuf& aFormat,
976 RSoundSc& aSoundDevice,
977 TFormatData &aFormatData)
979 ASSERT(!aFormatData.iConverter); // we don't clear on fail - so assuming NULL to start with
981 TInt err = KErrNotFound;
982 TCurrentSoundFormatV02Buf formatBuf;
984 // find out first what the driver supports
985 TSoundFormatsSupportedV02Buf supportedFormat;
986 aSoundDevice.Caps(supportedFormat);
987 TUint32 supportedRates = supportedFormat().iRates;
988 #ifdef SYMBIAN_SOUNDADAPTER_FORCECDRATES
989 supportedRates &= KSoundRate11025Hz| KSoundRate22050Hz | KSoundRate44100Hz; // only use CD rates - for debugging
993 // first try to find the first rate below or equal to the requested that is supported
994 // initially go down and be fussy, but if we pass the requested rate find the first that
997 // We want the next rate above consistently - we go down from this to the requested rate.
998 // If there is one, we don't support - we _never_ upsample.
999 // note that the table is given in descending order, so we start with the highest
1000 TInt wantedRate = aFormat().iRate;
1001 TInt takeTheFirst = EFalse;
1002 TInt nextUpValidIndex = -1;
1003 for(TInt index = 0; index < KNumSampleRates; index++ )
1005 TBool lookingAtRequestedRate = wantedRate == KRateEnumLookup[index].iRate;
1006 TSoundRate wantedEnum = KRateEnumLookup[index].iRateEnum;
1007 TUint32 equivBitmap = KRateEnumLookup[index].iRateConstant;
1008 TBool isSupported = (equivBitmap & supportedRates) != EFalse;
1009 if (lookingAtRequestedRate || takeTheFirst)
1013 // this rate is supported
1014 formatBuf().iRate = wantedEnum;
1015 aFormatData.iActualRate = KRateEnumLookup[index].iRate;
1020 else if (!takeTheFirst)
1022 // while we are still looking for the rate, want to cache any supported index
1023 // at end of loop, this will be the first rate above ours that is supported
1024 // use for fallback if required
1027 nextUpValidIndex = index;
1030 if (lookingAtRequestedRate)
1032 // if we get this far we've gone passed the wanted rate. For play we enable
1033 // "takeTheFirst". For record we just abort.
1034 if (&aSoundDevice==&iPlaySoundDevice)
1036 takeTheFirst = ETrue;
1047 // if there is one above the requested rate, use that
1048 if (nextUpValidIndex>=0)
1050 TSoundRate wantedEnum = KRateEnumLookup[nextUpValidIndex].iRateEnum;
1051 formatBuf().iRate = wantedEnum;
1052 aFormatData.iActualRate = KRateEnumLookup[nextUpValidIndex].iRate;
1059 // should have something!
1063 aFormatData.iSampleRate = wantedRate; // iSampleRate is our requested/apparent rate, not the device rate.
1065 TUint32 channelsSupported = supportedFormat().iChannels;
1066 #ifdef SYMBIAN_SOUNDADAPTER_FORCESTEREO
1067 channelsSupported &= KSoundStereoChannel; // don't use mono - for debugging
1069 if(KSoundAdapterForceStereo==1)
1071 channelsSupported &= KSoundStereoChannel;
1072 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1073 RDebug::Print(_L("Added stereo support."));
1076 if (aFormat().iChannels == 1)
1078 aFormatData.iRequestedChannels = 1;
1080 if (channelsSupported & KSoundMonoChannel)
1082 // mono is supported, as usual
1083 aFormatData.iActualChannels = 1;
1085 else if (channelsSupported & KSoundStereoChannel)
1087 aFormatData.iActualChannels = 2;
1091 return KErrNotSupported; // should not get this far for real
1094 else if (aFormat().iChannels == 2)
1096 aFormatData.iRequestedChannels = 2;
1098 if (channelsSupported & KSoundStereoChannel)
1100 // stereo is supported, as usual
1101 aFormatData.iActualChannels = 2;
1103 else if (channelsSupported & KSoundMonoChannel)
1105 aFormatData.iActualChannels = 1;
1109 return KErrNotSupported; // should not get this far for real
1114 return KErrNotSupported; // unknown number of channels requested!
1117 formatBuf().iChannels = aFormatData.iActualChannels;
1119 formatBuf().iEncoding = ESoundEncoding16BitPCM;
1120 formatBuf().iDataFormat = ESoundDataFormatInterleaved;
1121 err = aSoundDevice.SetAudioFormat(formatBuf);
1125 ASSERT(!aFormatData.iConverter); // pre-condition at top of function anyway
1126 if (&aSoundDevice==&iPlaySoundDevice)
1128 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1129 RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"),
1130 aFormatData.iSampleRate, aFormatData.iRequestedChannels,
1131 aFormatData.iActualRate, aFormatData.iActualChannels);
1133 // when playing we convert from requested to actual
1134 TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iSampleRate,
1135 aFormatData.iRequestedChannels,
1136 aFormatData.iActualRate,
1137 aFormatData.iActualChannels));
1141 // when recording we convert from actual to requested
1142 TInt outputRateToUse = aFormatData.iSampleRate;
1143 #ifdef SYMBIAN_SKIP_RESAMPLE_ON_RECORD
1144 // with this macro just channel convert at most
1145 outputRateToUse = aFormatData.iActualRate;
1147 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1148 RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"),
1149 aFormatData.iActualRate, aFormatData.iActualChannels,
1150 aFormatData.iSampleRate, aFormatData.iRequestedChannels);
1152 TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iActualRate,
1153 aFormatData.iActualChannels,
1155 aFormatData.iRequestedChannels));
1160 delete aFormatData.iConverter;
1161 aFormatData.iConverter= NULL;
1162 iConvertedPlayData.Close();
1168 void RMdaDevSound::CBody::StartRecordRequest()
1170 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
1172 iRecorder->RecordData(iBufferLength);
1175 // Note both InRecordMode and InPlayMode return EFalse for ENotReady and EStopped
1176 TBool RMdaDevSound::CBody::InRecordMode()const
1185 case ERecordingPausedInHw:
1186 case ERecordingPausedInSw:
1190 case EPlayingPausedInHw:
1191 case EPlayingPausedInSw:
1192 case EPlayingUnderrun:
1202 TBool RMdaDevSound::CBody::InPlayMode() const
1211 case ERecordingPausedInHw:
1212 case ERecordingPausedInSw:
1216 case EPlayingPausedInHw:
1217 case EPlayingPausedInSw:
1218 case EPlayingUnderrun:
1230 TUint32 RMdaDevSound::CBody::CurrentTimeInMsec() const
1232 TUint64 tmp = User::NTickCount();
1233 tmp *= iNTickPeriodInUsec;
1235 return TUint32(tmp);
1238 void RMdaDevSound::CBody::PlayFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported)
1240 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
1241 FormatsSupported(aFormatsSupported, iPlaySoundDevice);
1244 void RMdaDevSound::CBody::GetPlayFormat(TCurrentSoundFormatBuf& aFormat)
1246 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
1247 GetFormat(aFormat, iPlaySoundDevice, iPlayFormatData);
1250 TInt RMdaDevSound::CBody::SetPlayFormat(const TCurrentSoundFormatBuf& aFormat)
1252 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
1253 return SetFormat(aFormat, iPlaySoundDevice, iPlayFormatData);
1256 void RMdaDevSound::CBody::RecordFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported)
1258 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
1259 FormatsSupported(aFormatsSupported, iRecordSoundDevice);
1262 void RMdaDevSound::CBody::GetRecordFormat(TCurrentSoundFormatBuf& aFormat)
1264 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
1265 GetFormat(aFormat, iRecordSoundDevice, iRecordFormatData);
1268 TInt RMdaDevSound::CBody::SetRecordFormat(const TCurrentSoundFormatBuf& aFormat)
1270 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
1271 return SetFormat(aFormat, iRecordSoundDevice, iRecordFormatData);
1274 void RMdaDevSound::CBody::Close()
1276 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1277 RDebug::Printf("void RMdaDevSound::CBody::Close() started");
1282 if(iPlaySoundDevice.Handle() != KNullHandle)
1284 // Make sure all player objects are idle
1287 iPlaySoundDevice.Close();
1290 if(iRecordSoundDevice.Handle() != KNullHandle)
1293 iRecordChunk.Close();
1294 iRecordSoundDevice.Close();
1298 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1299 RDebug::Printf("void RMdaDevSound::CBody::Close() ended");
1303 TInt RMdaDevSound::CBody::Handle()
1304 {//This method is actually used to check whether the device is opened. Below logic should work
1305 if(iPlaySoundDevice.Handle())
1307 return iPlaySoundDevice.Handle();
1309 if(iRecordSoundDevice.Handle())
1311 return iRecordSoundDevice.Handle();
1317 void RMdaDevSound::CBody::PlayData(TRequestStatus& aStatus, const TDesC8& aData)
1319 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1320 RDebug::Printf("RMdaDevSound::CBody::PlayData(0x%x,%d) State=%s Handle=%d.",&aStatus,
1321 aData.Length(), iState.Name(), iPlayChunk.Handle());
1324 __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
1325 aStatus = KRequestPending;
1327 if((iClientPlayStatus != NULL) || InRecordMode())
1329 // We only support one outstanding request
1330 // No support for simultaneous play and record in RMdaDevSound
1331 TRequestStatus *pRequest = &aStatus;
1332 User::RequestComplete(pRequest, KErrInUse);
1335 iClientPlayStatus = &aStatus;//store the status of datapath player
1337 if(iPlayFormatData.iConverter || iSavedTrailingData.Length() != 0)
1339 // Need a conversion buffer
1340 // Needs to hold any trailing data truncated from the previous request (due
1341 // to alignment requirements) and either the new data, or the new rate adapted data
1342 TUint32 spaceRequired = iSavedTrailingData.Length();
1343 if(iPlayFormatData.iConverter)
1345 // Doing rate conversion so also need space for the converted data
1346 spaceRequired += iPlayFormatData.iConverter->MaxConvertBufferSize(aData.Length());
1350 // Not doing rate adaptation therefore only need to allow for the new incoming data
1351 spaceRequired += aData.Length();
1353 // Check if existing buffer exists and is big enough
1354 if(iConvertedPlayData.MaxLength() < spaceRequired)
1356 iConvertedPlayData.Close();
1357 TInt err = iConvertedPlayData.Create(spaceRequired);
1360 User::RequestComplete(iClientPlayStatus, err);
1365 // Truncate iConvertedPlayData and copy in saved trailing data (if any)
1366 iConvertedPlayData = iSavedTrailingData;
1367 iSavedTrailingData.SetLength(0);
1369 // Now append rate adapted data or incoming data
1370 if (iPlayFormatData.iConverter)
1372 // The convertor will panic if it fails to convert any data, therefore
1373 // we avoid passing it an empty source buffer
1374 if(aData.Length() != 0)
1376 TPtr8 destPtr((TUint8 *)iConvertedPlayData.Ptr()+iConvertedPlayData.Length(), 0, iConvertedPlayData.MaxLength()-iConvertedPlayData.Length());
1377 TInt len = iPlayFormatData.iConverter->Convert(aData, destPtr);
1378 iConvertedPlayData.SetLength(iConvertedPlayData.Length() + destPtr.Length());
1379 if(len != aData.Length())
1381 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1382 RDebug::Printf("RMdaDevSound::CBody::PlayData converted %d but expected to convert %d", len, aData.Length());
1389 iConvertedPlayData.Append(aData);
1391 iClientPlayData.Set(iConvertedPlayData);
1395 // Do not need a conversion buffer so just aim the descriptor at the data
1396 iClientPlayData.Set(aData);
1398 iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse;
1400 // All driver requests must be an exact multiple of iRequestMinSize
1401 TUint32 trailingDataLen = iClientPlayData.Length() % iRequestMinSize;
1404 // Not a multiple of iRequestMinSize, so need to truncate current request, and save trailing bytes for
1405 // inclusion at the beginning of the next request
1406 iSavedTrailingData = iClientPlayData.Right(trailingDataLen);
1407 iClientPlayData.Set(iClientPlayData.Left(iClientPlayData.Length()-trailingDataLen));
1410 #ifdef SYMBIAN_FORCE_32BIT_LENGTHS
1411 if (iClientPlayData.Length()%4 != 0)
1413 // simulate the limitation of some hardware, where -6 is generated if the
1414 // buffer length is not divisible by 4.
1415 TRequestStatus *pRequest = &aStatus;
1416 User::RequestComplete(pRequest, KErrArgument);
1420 iRecordChunk.Close();
1421 if(!iPlayChunk.Handle())
1423 //This is where we setup to play.
1424 //Configure the shared chunk for two buffers with iBufferSize each
1425 iPlayBufferConfig.iNumBuffers = KPlaySharedChunkBuffers;
1426 iDeviceBufferLength = KPlaySharedChunkBufferSize;
1427 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1428 RDebug::Printf("iDeviceBufferLength %d", iDeviceBufferLength);
1430 iPlayBufferConfig.iFlags = 0;//data will be continuous
1431 // If required, use rate converter etc
1432 iPlayBufferConfig.iBufferSizeInBytes = iDeviceBufferLength;
1433 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1434 RDebug::Printf("number of buffers: [%d]",iPlayBufferConfig.iNumBuffers);
1435 RDebug::Printf("BufferSize in Bytes [%d]",iPlayBufferConfig.iBufferSizeInBytes);
1437 TPckg<TPlaySharedChunkBufConfig> bufferConfigBuf(iPlayBufferConfig);
1438 TInt error = iPlaySoundDevice.SetBufferChunkCreate(bufferConfigBuf,iPlayChunk);
1439 if(error == KErrNone)
1441 iPlaySoundDevice.GetBufferConfig(bufferConfigBuf);
1445 SoundDeviceError(error);
1450 StartPlayersAndUpdateState();
1455 void RMdaDevSound::CBody::RecordData(TRequestStatus& aStatus, TDes8& aData)
1457 __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
1458 aStatus = KRequestPending;
1459 if((iClientPlayStatus != NULL) || InPlayMode())
1461 // We only support one outstanding request
1462 // No support for simultaneous play and record in RMdaDevSound
1463 TRequestStatus *pRequest = &aStatus;
1464 User::RequestComplete(pRequest, KErrInUse);
1467 iClientRecordStatus = &aStatus;
1468 iClientRecordData = &aData;
1469 iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse;
1472 if(!iRecordChunk.Handle())
1474 //Configure the shared chunk for two buffers with iBufferSize each
1475 iRecordBufferConfig.iNumBuffers = KRecordMaxSharedChunkBuffers;
1476 iDeviceBufferLength = KRecordSharedChunkBufferSize; // initial size - resize if needs be
1477 if (iRecordFormatData.iConverter)
1479 // if number of channels used differs from request, resize buffer
1480 // assume we have nice rounded values for buffer.
1481 if (iRecordFormatData.iActualChannels>iRecordFormatData.iRequestedChannels)
1483 iDeviceBufferLength *= 2; // will record at stereo and convert to mono
1485 else if (iRecordFormatData.iActualChannels<iRecordFormatData.iRequestedChannels)
1487 iDeviceBufferLength /= 2; // will record at mono and convert to stereo
1490 iRecordBufferConfig.iBufferSizeInBytes = iDeviceBufferLength;
1491 iRecordBufferConfig.iFlags = 0;
1492 TPckg<TRecordSharedChunkBufConfig> bufferConfigBuf(iRecordBufferConfig);
1493 TInt error = iRecordSoundDevice.SetBufferChunkCreate(bufferConfigBuf,iRecordChunk);
1494 if(error == KErrNone)
1496 iRecordSoundDevice.GetBufferConfig(bufferConfigBuf);
1500 SoundDeviceError(error);
1503 iState = ERecording;
1505 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1506 RDebug::Printf("RMdaDevSound::CBody::RecordData,iBufferOffset[%d]",iBufferOffset);
1517 // Either idle or recording is in progress, therefore we can issue another request
1518 StartRecordRequest();
1521 case ERecordingPausedInHw:
1522 // Driver is paused, therefore we can issue a request which will immediately return buffered data
1523 // or be aborted (in the driver) with KErrCancelled if there is no more data). nb. That KErrCancelled should not be
1524 // returned to the client because the old RMdaDevSound driver would have completed with KErrNone and zero data length.
1525 StartRecordRequest();
1528 case ERecordingPausedInSw:
1529 // Paused in s/w but driver is not paused, therefore can not issue a new request to driver because
1530 // it would re-start recording.
1531 // This implies we were paused whilst the h/w was not recording, so there is no buffered data.
1533 // Complete the request with KErrNone and no data.
1534 iClientRecordData->SetLength(0);
1535 User::RequestComplete(iClientRecordStatus, KErrNone);
1539 case EPlayingPausedInHw:
1540 case EPlayingPausedInSw:
1541 case EPlayingUnderrun:
1552 Notify client of error.
1554 Note that we continue playing/recording if possible.
1556 We do not maintain information which could map the error back to a particular client play/record request
1557 and therefore we have to notify the client of error every time it happens.
1559 nb. A client play/record request is completed with KErrNone if it queues ok - All errors are reported via the Notify*Error
1562 void RMdaDevSound::CBody::SoundDeviceError(TInt aError)
1564 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1565 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError: Error[%d] state %s", aError, iState.Name());
1568 ASSERT(aError != KErrNone);
1570 if(iClientPlayErrorStatus)
1572 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1573 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iPlayerErrorStatus");
1576 User::RequestComplete(iClientPlayErrorStatus, aError); // nb call also sets iClientPlayErrorStatus to NULL
1579 if(iClientRecordErrorStatus)
1581 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1582 RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iClientRecordErrorStatus");
1584 User::RequestComplete(iClientRecordErrorStatus, aError); // nb call also sets iClientRecordErrorStatus to NULL
1590 void RMdaDevSound::CBody::NotifyRecordError(TRequestStatus& aStatus)
1592 aStatus = KRequestPending;
1593 iClientRecordErrorStatus = &aStatus;
1596 void RMdaDevSound::CBody::NotifyPlayError(TRequestStatus& aStatus)
1598 aStatus = KRequestPending;
1599 iClientPlayErrorStatus = &aStatus;
1602 void RMdaDevSound::CBody::CancelNotifyPlayError()
1604 if(iClientPlayErrorStatus)
1606 User::RequestComplete(iClientPlayErrorStatus, KErrCancel);
1610 void RMdaDevSound::CBody::CancelNotifyRecordError()
1612 if(iClientRecordErrorStatus)
1614 User::RequestComplete(iClientRecordErrorStatus, KErrCancel);
1618 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1619 RDebug::Printf("msp BufferEmptied but iClientPlayStatus==NULL");
1624 void RMdaDevSound::CBody::FlushPlayBuffer()
1626 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1627 RDebug::Printf("RMdaDevSound::CBody::FlushPlayBuffer calling CancelPlayData");
1632 RSoundSc& RMdaDevSound::CBody::PlaySoundDevice()
1634 return iPlaySoundDevice;
1637 RSoundSc& RMdaDevSound::CBody::RecordSoundDevice()
1639 return iRecordSoundDevice;
1642 const RMdaDevSound::CBody::TState &RMdaDevSound::CBody::State() const
1648 void RMdaDevSound::CBody::BufferFilled(TInt aBufferOffset)
1650 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1651 RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled:"));
1654 ASSERT(aBufferOffset>=0 || aBufferOffset==KErrCancel);
1655 ASSERT(iClientRecordData); // request should not get this without
1657 if(aBufferOffset==KErrCancel)
1659 //we can get KErrCancel when we call pause and there is no more data left with the driver
1660 //we send the empty buffer to the HwDevice, where this should trigger the shutdown mechanism
1661 iClientRecordData->SetLength(0);
1662 User::RequestComplete(iClientRecordStatus, KErrNone);
1663 iClientRecordStatus = NULL;
1667 iBufferOffset = aBufferOffset;
1668 //when last buffer is flushed, new driver sometimes gives buffer size of odd number. One of our codecs
1669 //expects that the buffer size should always be even. Base suggested that we fix in multimedia
1670 //as it is quite complicated to fix in overthere.
1671 iBufferLength = iBufferLength & 0xfffffffe;
1672 TPtr8 dataPtr(iRecordChunk.Base()+ iBufferOffset, iBufferLength, iClientRecordData->MaxLength());
1673 if (iRecordFormatData.iConverter)
1675 iRecordFormatData.iConverter->Convert(dataPtr, *iClientRecordData);
1679 iClientRecordData->Copy(dataPtr);
1681 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1682 RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled: BufferOffset[%d] BufferLen[%d]"), iBufferOffset, iBufferLength);
1684 if(iBufferOffset >= 0)
1686 iRecordSoundDevice.ReleaseBuffer(iBufferOffset);
1688 if(iClientRecordStatus)
1690 User::RequestComplete(iClientRecordStatus, KErrNone);
1691 iClientRecordStatus = NULL;
1695 RDebug::Printf("msp PlayCancelled but iClientPlayStatus==NULL");
1700 This function is called to notify us that a CPlayer's request has completed and what its status was.
1702 It is important to note that:-
1703 1) RSoundSc driver PlayData requests are guaranteed to complete in order, oldest first
1704 2) If we are overloaded, it is possible for more than one request to complete before any CPlayer::RunL is ran. In
1705 this situation the CPlayer::RunL functions, and hence this callback, maybe invoked in non-oldest first order
1709 a) It is impossible for callback for the second oldest CPlayer to occur before the driver request for the oldest has
1710 been complete (because of 1)
1711 b) We will always get exactly one callback for every complete request.
1713 Therefore this callback notifies us of two subtly separate things:-
1715 i) The oldest request has been completed (so we can reduce can increase the bytes played counter by its length
1716 ii) CPlayer aPlayerIndex is free for re-use
1718 but we can not assume that aPlayerIndex is the oldest request, therefore we save the play request lengths outside of
1721 void RMdaDevSound::CBody::PlayRequestHasCompleted(CPlayer *aPlayer, TInt aStatus, TBool aDueToCancelCommand)
1723 // CPlayer is done so put it on the free queue
1724 iFreePlayers.Push(aPlayer);
1726 TUint32 bytesPlayed = iActivePlayRequestSizes.Pop();
1727 // Request has finished therefore now timing the following request to simulate bytes played
1728 iStartTime = CurrentTimeInMsec();
1729 if(aDueToCancelCommand)
1731 // Callback due to CPlayer::Cancel/DoCancel being called, therefore we
1732 // do not want to update bytes played, process state, report a error or start new players
1736 // Update iBytesPlayed by the length of the oldest request (which might not be the one that CPlayer was
1738 iBytesPlayed += bytesPlayed;
1739 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1740 RDebug::Printf("PlayRequestHasCompleted increasing iBytesPlayed by %d to %d", bytesPlayed, iBytesPlayed);
1747 Panic(EDeviceNotOpened);
1751 // Will happen if we are doing CancelPlayData processing with active players
1755 case ERecordingPausedInHw:
1756 case ERecordingPausedInSw:
1764 case EPlayingPausedInHw:
1765 // H/W was/is paused, but there must have been an already complete request that we had not
1767 // There must be at least one more pending request on h/w, otherwise the h/w would have refused to pause
1768 // I would expect this be rare, but it happens quite often...
1769 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1770 ASSERT(iActivePlayRequestSizes.Length() != 0);
1772 // Need to update the start and pause time to now because we have just updated the actual iBytesPlayed
1773 // and logically the h/w is paused at the beginning of the next request
1774 iStartTime = CurrentTimeInMsec();
1775 iPauseTime = iStartTime;
1778 case EPlayingPausedInSw:
1779 // This will happen if there is only a single hw request outstanding, and the hardware has finished it, but the
1780 // corresponding RunL has not run yet (in which case PausePlayBuffer will have attempted to use h/w pause,
1781 // but the driver call would have failed, and the state changed to EPlayingPausedInSw).
1782 iStartTime = CurrentTimeInMsec();
1783 iPauseTime = iStartTime;
1785 case EPlayingUnderrun:
1794 // If we have an error, report it to the client
1795 // We NEVER report driver underflow, instead we report KErrUnderflow if we run out of data to pass to driver.
1796 if( (aStatus != KErrNone) && (aStatus != KErrUnderflow) )
1798 SoundDeviceError(aStatus);
1801 // If appropriate start more players
1802 StartPlayersAndUpdateState();
1806 RMdaDevSound::CBody::CPlayer::CPlayer(TInt aPriority, RMdaDevSound::CBody& aParent, TInt aIndex):
1807 CActive(aPriority), iParent(aParent), iIndex(aIndex), iBufferOffset(-1), iBufferLength(0)
1809 CActiveScheduler::Add(this);
1812 RMdaDevSound::CBody::CPlayer::~CPlayer()
1818 void RMdaDevSound::CBody::CPlayer::RunL()
1820 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1821 RDebug::Printf("****RMdaDevSound::CBody::CPlayer(%d)::RunL: Error[%d] ParentState[%s]",
1822 iIndex, iStatus.Int(), iParent.State().Name());
1823 RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (including this one as active)",
1824 iParent.iActivePlayRequestSizes.Length(),
1825 iParent.iFreePlayers.Length());
1827 iParent.PlayRequestHasCompleted(this, iStatus.Int(), EFalse);
1831 TInt RMdaDevSound::CBody::CPlayer::RunError(TInt aError)
1833 iParent.PlayRequestHasCompleted(this, aError, EFalse);
1837 void RMdaDevSound::CBody::CPlayer::DoCancel()
1839 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1840 RDebug::Printf("RMdaDevSound::CBody::CPlayer(%d)::DoCancel", iIndex);
1842 if(iStatus == KRequestPending)
1844 // Avoid cancelling requests which have already completed.
1845 // It wastes time, and might provoke a sound driver problem
1846 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1847 RDebug::Printf("RMdaDevSound::CBody::CPlayer::DoCancel - would have cancelled driver request");
1849 iParent.PlaySoundDevice().Cancel(iStatus);
1851 iParent.PlayRequestHasCompleted(this, KErrCancel, ETrue);
1854 void RMdaDevSound::CBody::CPlayer::PlayData(TUint aChunkOffset, TInt aLength)
1856 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1857 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::PlayData : IsActive[%d]"),
1858 iIndex, IsActive());
1859 RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (inc this player)",
1860 iParent.iActivePlayRequestSizes.Length(),
1861 iParent.iFreePlayers.Length());
1864 iBufferOffset = aChunkOffset;
1865 iBufferLength = aLength;
1867 //Make sure the length is a multiple of 4 to work around an h6 limitation.
1868 iBufferLength = iBufferLength & 0xfffffffc;
1870 // Issue the RSoundSc request
1871 iParent.PlaySoundDevice().PlayData(iStatus, iBufferOffset, iBufferLength, EFalse);
1876 TUint RMdaDevSound::CBody::CPlayer::GetPlayerIndex() const
1881 RMdaDevSound::CBody::CRecorder::CRecorder(TInt aPriority, RMdaDevSound::CBody& aParent):
1882 CActive(aPriority), iParent(aParent), iBufferOffset(-1), iBufferLength(0)
1884 CActiveScheduler::Add(this);
1887 RMdaDevSound::CBody::CRecorder::~CRecorder()
1892 void RMdaDevSound::CBody::CRecorder::RecordData(TInt& aBufferLength)
1896 iStatus = KRequestPending;
1897 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1898 RDebug::Printf("Recording request: BufferLength[%d]", aBufferLength);
1900 iParent.RecordSoundDevice().RecordData(iStatus, aBufferLength);
1905 void RMdaDevSound::CBody::CRecorder::RunL()
1907 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1908 RDebug::Printf("****RMdaDevSound::CBody::CRecorder()::RunL: Error[%d] ParentState[%s]",
1909 iStatus.Int(), iParent.State().Name());
1913 TInt error = iStatus.Int();
1915 if((error >= 0) || (error == KErrCancel))
1916 {//we can get KErrCancel when we call pause and there is no more data left with the driver
1917 iParent.BufferFilled(error);
1921 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1922 RDebug::Print(_L("RMdaDevSound::CBody::CPlayer()::RunL: Error[%d]"), error);
1924 iParent.SoundDeviceError(error);
1929 TInt RMdaDevSound::CBody::CRecorder::RunError(TInt aError)
1931 iParent.SoundDeviceError(aError);
1935 void RMdaDevSound::CBody::CRecorder::DoCancel()
1937 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
1938 RDebug::Printf("RMdaDevSound::CBody::CRecorder()::DoCancel");
1940 iParent.RecordSoundDevice().Cancel(iStatus);