sl@0: // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: #include "mdasoundadapterconsts.h" sl@0: #include "mdasoundadapterbody.h" sl@0: #include sl@0: sl@0: #include "mmf/utils/rateconvert.h" // if we need to resample sl@0: sl@0: #include sl@0: sl@0: _LIT(KPddFileName,"SOUNDSC.PDD"); sl@0: _LIT(KLddFileName,"ESOUNDSC.LDD"); sl@0: sl@0: sl@0: const TInt KBytesPerSample = 2; sl@0: const TInt KMinBufferSize = 2; sl@0: sl@0: /** sl@0: This function raises a panic sl@0: EDeviceNotOpened is raised when any of the RMdaDevSound APIs are called before opening the device. sl@0: */ sl@0: GLDEF_C void Panic(TSoundAdapterPanicCodes aPanicCode) sl@0: { sl@0: User::Panic(KSoundAdapterPanicCategory, aPanicCode); sl@0: } sl@0: sl@0: sl@0: const TText8 *RMdaDevSound::CBody::TState::Name() const sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: switch(iState) sl@0: { sl@0: case ENotReady: return _S8("ENotReady"); sl@0: case EStopped: return _S8("EStopped"); sl@0: case ERecording: return _S8("ERecording"); sl@0: case ERecordingPausedInHw: return _S8("ERecordingPausedInHw"); sl@0: case ERecordingPausedInSw: return _S8("ERecordingPausedInSw"); sl@0: case EPlaying: return _S8("EPlaying"); sl@0: case EPlayingPausedInHw: return _S8("EPlayingPausedInHw"); sl@0: case EPlayingPausedInSw: return _S8("EPlayingPausedInSw"); sl@0: case EPlayingUnderrun: return _S8("EPlayingUnderrun"); sl@0: } sl@0: return _S8("CorruptState"); sl@0: #else sl@0: return _S8(""); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: sl@0: RMdaDevSound::CBody::TState &RMdaDevSound::CBody::TState::operator=(TStateEnum aNewState) sl@0: { sl@0: if(iState != aNewState) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound state %s -> %s", Name(), TState(aNewState).Name()); sl@0: #endif sl@0: iState = aNewState; sl@0: } sl@0: return *this; sl@0: } sl@0: sl@0: RMdaDevSound::CBody* RMdaDevSound::CBody::NewL() sl@0: { sl@0: CBody* self = new(ELeave) CBody(); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(); sl@0: CleanupStack::Pop(); sl@0: return self; sl@0: } sl@0: sl@0: RMdaDevSound::CBody::~CBody() sl@0: { sl@0: for(TInt i = 0; i < KPlaySharedChunkBuffers; i++) sl@0: { sl@0: delete iPlayers[i]; sl@0: iPlayers[i] = NULL; sl@0: } sl@0: delete iRecorder; sl@0: iRecorder = NULL; sl@0: delete iPlayFormatData.iConverter; sl@0: delete iRecordFormatData.iConverter; sl@0: iPlayChunk.Close(); sl@0: iPlaySoundDevice.Close(); sl@0: iRecordChunk.Close(); sl@0: iRecordSoundDevice.Close(); sl@0: iConvertedPlayData.Close(); sl@0: iSavedTrailingData.Close(); sl@0: iBufferedRecordData.Close(); sl@0: } sl@0: sl@0: RMdaDevSound::CBody::CBody() sl@0: :iState(ENotReady), iBufferOffset(-1) sl@0: { sl@0: sl@0: } sl@0: sl@0: TVersion RMdaDevSound::CBody::VersionRequired() const sl@0: { sl@0: if(iPlaySoundDevice.Handle()) sl@0: { sl@0: return iPlaySoundDevice.VersionRequired(); sl@0: } sl@0: else sl@0: { sl@0: return TVersion(); sl@0: } sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::IsMdaSound() sl@0: { sl@0: return ETrue; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::ConstructL() sl@0: { sl@0: // Try to load the audio physical driver sl@0: TInt err = User::LoadPhysicalDevice(KPddFileName); sl@0: if ((err!=KErrNone) && (err!=KErrAlreadyExists)) sl@0: { sl@0: User::Leave(err); sl@0: } sl@0: // Try to load the audio logical driver sl@0: err = User::LoadLogicalDevice(KLddFileName); sl@0: if ((err!=KErrNone) && (err!=KErrAlreadyExists)) sl@0: { sl@0: User::Leave(err); sl@0: } sl@0: for(TInt i=0; i=0 && aVolume<=KSoundMaxVolume) sl@0: { sl@0: iPlaySoundDevice.SetVolume(KLinerToDbConstantLookup[aVolume].iDBValue); sl@0: } sl@0: } sl@0: void RMdaDevSound::CBody::SetVolume(TInt aLogarithmicVolume) sl@0: { sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: if(aLogarithmicVolume >= 0 && aLogarithmicVolume <= KSoundMaxVolume) sl@0: { sl@0: iPlaySoundDevice.SetVolume(aLogarithmicVolume); sl@0: } sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::CancelPlayData() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::CancelPlayData: state %s", iState.Name()); sl@0: #endif sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: sl@0: // If there is a client request, cancel it sl@0: // Must do this before canceling players because otherwise they may just restart! sl@0: if(iClientPlayStatus) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("msp PlayCancelled complete iClientPlayStatus"); sl@0: #endif sl@0: User::RequestComplete(iClientPlayStatus, KErrCancel); // Call also sets iClientPlayStatus to NULL sl@0: } sl@0: sl@0: // Discard any buffered data sl@0: iClientPlayData.Set(0,0); sl@0: // Discard any saved trailing data (ie. data saved due driver requiring all requests to be a multiple of iRequestMinSize). sl@0: iSavedTrailingData.SetLength(0); sl@0: sl@0: // Emulator RSoundSc PDD when running without a soundcard has a major sl@0: // issue with cancelling whilst paused. It will not clear the pending sl@0: // list (because the timer is not active) and therefore this list will sl@0: // later overflow causing hep corruption. sl@0: // This means that, for now, we MUST Resume before calling CancelPlayData sl@0: // to avoid kernel panics... sl@0: sl@0: // The device driver will not cancel a request which is in progress... sl@0: // So, if we are paused in hw, we must resume before cancelling the sl@0: // player otherwise it will hang in CActive::Cancel sl@0: if(iState == EPlayingPausedInHw) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("msp Resume to avoid hang"); sl@0: #endif sl@0: (void) iPlaySoundDevice.Resume(); sl@0: } sl@0: sl@0: // Update state sl@0: iState = EStopped; sl@0: sl@0: sl@0: // The RSoundSc driver will not cancel a request which is in progress (or paused). sl@0: // If we just loop across the players, cancelling each individual request and waiting for it to complete, sl@0: // several of them will actually play, which is both wrong and time consuming.... sl@0: // Issue a block cancel upfront to avoid this sl@0: iPlaySoundDevice.CancelPlayData(); sl@0: sl@0: // Cancel all players sl@0: for (TUint playerIndex=0; playerIndexCancel(); sl@0: } sl@0: sl@0: iBufferOffset = -1; sl@0: iBufferLength = 0; sl@0: sl@0: return; sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::RecordLevel() sl@0: { sl@0: __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: return iRecordSoundDevice.Volume(); sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::SetRecordLevel(TInt aLevel) sl@0: { sl@0: __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: iRecordSoundDevice.SetVolume(aLevel); sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::CancelRecordData() sl@0: { sl@0: __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::CancelRecordData: state %s", iState.Name()); sl@0: #endif sl@0: sl@0: // Stop recorder object (and its request) sl@0: iRecorder->Cancel(); sl@0: sl@0: // Stop driver from recording sl@0: iRecordSoundDevice.CancelRecordData(); sl@0: sl@0: // If there is a client request, cancel it sl@0: if(iClientRecordStatus) sl@0: { sl@0: User::RequestComplete(iClientRecordStatus, KErrNone); // Call also sets iClientPlayStatus to NULL sl@0: } sl@0: sl@0: iState = EStopped; sl@0: return; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::FlushRecordBuffer() sl@0: { sl@0: __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Print(_L("RMdaDevSound::CBody::FlushRecordBuffer - implemented by calling PauseRecordBuffer")); sl@0: #endif sl@0: sl@0: PauseRecordBuffer(); sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::BytesPlayed() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::BytesPlayed %s", iState.Name()); sl@0: #endif sl@0: sl@0: return I64LOW(BytesPlayed64()); sl@0: } sl@0: sl@0: sl@0: TUint64 RMdaDevSound::CBody::BytesPlayed64() sl@0: { sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: sl@0: TUint64 currentBytesPlayed = KMaxTUint64; sl@0: sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: Panic(EDeviceNotOpened); sl@0: break; sl@0: sl@0: case EStopped: sl@0: currentBytesPlayed = iBytesPlayed; sl@0: break; sl@0: sl@0: case ERecording: sl@0: case ERecordingPausedInHw: sl@0: case ERecordingPausedInSw: sl@0: Panic(EBadState); sl@0: break; sl@0: sl@0: case EPlayingPausedInHw: // ie. Play request pending on h/w and paused sl@0: // Paused, so use pause time sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("EPlayingPausedInHw: iPausedBytes %x %x", I64HIGH(iPausedBytesPlayed), I64LOW(iPausedBytesPlayed)); sl@0: #endif sl@0: currentBytesPlayed = iPausedBytesPlayed; sl@0: break; sl@0: sl@0: case EPlayingPausedInSw: // ie. Driver not playing or paused sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("EPlayingPausedInSw: iPausedBytesPlayed %x %x", I64HIGH(iPausedBytesPlayed), I64LOW(iPausedBytesPlayed)); sl@0: #endif sl@0: currentBytesPlayed = iPausedBytesPlayed; sl@0: break; sl@0: case EPlayingUnderrun: sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("EPlayingUnderrun: iBytesPlayed %x %x", I64HIGH(iBytesPlayed), I64LOW(iBytesPlayed)); sl@0: #endif sl@0: currentBytesPlayed = iBytesPlayed; sl@0: break; sl@0: sl@0: case EPlaying: sl@0: { sl@0: // Playing so calculate time since last update to iBytesPlayed sl@0: TUint32 curTime = CurrentTimeInMsec(); sl@0: TUint32 curRequestSize = iActivePlayRequestSizes.Peek(); sl@0: sl@0: TUint32 extraPlayTime = (curTime >= iStartTime) ? (curTime-iStartTime) : (KMaxTUint32 - (iStartTime-curTime)); sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("iStartTime %d curTime %d extraPlayTime %d", iStartTime, curTime, extraPlayTime); sl@0: sl@0: RDebug::Printf("iPlayFormatData.iSampleRate %d KBytesPerSample %d iNTickPeriodInUsec %d", sl@0: iPlayFormatData.iSampleRate, KBytesPerSample, iNTickPeriodInUsec); sl@0: #endif sl@0: TUint32 extraBytesPlayed = TUint32((TUint64(extraPlayTime) * iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample)/1000); sl@0: if(extraBytesPlayed > curRequestSize) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("caping extraBytes played from %d to %d", extraBytesPlayed, curRequestSize); sl@0: #endif sl@0: extraBytesPlayed = curRequestSize; sl@0: } sl@0: sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("iBytesPlayed %d extraBytesPlayed %d (curRequestSize %d) -> currentBytesPlayed %x %x", sl@0: iBytesPlayed, extraBytesPlayed, curRequestSize, I64HIGH(currentBytesPlayed), I64LOW(currentBytesPlayed)); sl@0: #endif sl@0: sl@0: currentBytesPlayed = iBytesPlayed + extraBytesPlayed; sl@0: break; sl@0: } sl@0: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: sl@0: sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("iPlayFormatData.iConverter %x", iPlayFormatData.iConverter); sl@0: #endif sl@0: sl@0: if (iPlayFormatData.iConverter) sl@0: { sl@0: // need to scale bytes played to fit with requested rate and channels, not actual sl@0: if (iPlayFormatData.iActualChannels != iPlayFormatData.iRequestedChannels) sl@0: { sl@0: if (iPlayFormatData.iActualChannels == 2) sl@0: { sl@0: // requested was mono, we have stereo sl@0: currentBytesPlayed /= 2; sl@0: } sl@0: else sl@0: { sl@0: // requested was stereo, we have mono sl@0: currentBytesPlayed *= 2; sl@0: } sl@0: } sl@0: if (iPlayFormatData.iSampleRate != iPlayFormatData.iActualRate) sl@0: { sl@0: currentBytesPlayed = TUint64(currentBytesPlayed* sl@0: TReal(iPlayFormatData.iSampleRate)/TReal(iPlayFormatData.iActualRate)); // don't round sl@0: } sl@0: } sl@0: sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("currentBytesPlayed %x %x", I64HIGH(currentBytesPlayed), I64LOW(currentBytesPlayed)); sl@0: #endif sl@0: return currentBytesPlayed; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::ResetBytesPlayed() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::ResetBytesPlayed %s", iState.Name()); sl@0: #endif sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: iBytesPlayed = 0; sl@0: iPlaySoundDevice.ResetBytesTransferred(); sl@0: return; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::PausePlayBuffer() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::PausePlayBuffer %s", iState.Name()); sl@0: #endif sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: Panic(EDeviceNotOpened); sl@0: break; sl@0: sl@0: case EStopped: sl@0: // Driver is not playing so pause in s/w sl@0: break; sl@0: sl@0: case ERecording: sl@0: case ERecordingPausedInHw: sl@0: case ERecordingPausedInSw: sl@0: Panic(EBadState); sl@0: break; sl@0: sl@0: case EPlayingPausedInHw: // ie. Play request pending on h/w and paused sl@0: case EPlayingPausedInSw: // ie. Driver not playing or paused sl@0: // Already paused so nothing to do. sl@0: break; sl@0: case EPlayingUnderrun: sl@0: iState = EPlayingPausedInSw; sl@0: break; sl@0: sl@0: case EPlaying: sl@0: { sl@0: iPauseTime = CurrentTimeInMsec(); sl@0: iPausedBytesPlayed = BytesPlayed64(); sl@0: TInt res = iPlaySoundDevice.Pause(); sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("iPlaySoundDevice.Pause res = %d", res); sl@0: #endif sl@0: if(res == KErrNone) sl@0: { sl@0: iState = EPlayingPausedInHw; sl@0: } sl@0: else sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("msp PausePlayBuffer hw pause unexpectedly failed, doing sw pause"); sl@0: #endif sl@0: iState = EPlayingPausedInSw; sl@0: } sl@0: break; sl@0: } sl@0: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: sl@0: return; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::ResumePlaying() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::ResumePlaying %s", iState.Name()); sl@0: #endif sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: Panic(EDeviceNotOpened); sl@0: break; sl@0: sl@0: case EStopped: sl@0: // No change sl@0: break; sl@0: sl@0: case ERecording: sl@0: case ERecordingPausedInHw: sl@0: case ERecordingPausedInSw: sl@0: Panic(EBadState); sl@0: break; sl@0: sl@0: case EPlaying: sl@0: // No change sl@0: break; sl@0: sl@0: case EPlayingPausedInHw: // ie. Play request pending on h/w and paused sl@0: { sl@0: // Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing). sl@0: iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; sl@0: sl@0: TInt res = iPlaySoundDevice.Resume(); sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("ResumePlayBuffer EPlayingPausedInHw res = %d", res); sl@0: #endif sl@0: if(res == KErrNone) sl@0: { sl@0: // Resume ok so a pending request will complete sl@0: iState = EPlaying; sl@0: // Update iStartTime to allow for time spent paused sl@0: TUint32 curTime = CurrentTimeInMsec(); sl@0: TUint32 timePaused = (curTime >= iPauseTime) ? (curTime-iPauseTime) : (KMaxTUint32 - (iPauseTime-curTime)); sl@0: iStartTime += timePaused; // nb. It is harmless if this wraps. sl@0: } sl@0: else sl@0: { sl@0: // Resume failed, therefore driver is not playing sl@0: // No need to update iStartTime/iPauseTime because these are only used within a driver request sl@0: // Change state to Stopped sl@0: iState = EStopped; sl@0: // Attempt to start a new (pending) request. sl@0: StartPlayersAndUpdateState(); sl@0: } sl@0: break; sl@0: } sl@0: case EPlayingPausedInSw: // ie. Driver not playing/paused sl@0: { sl@0: // Driver not playing sl@0: // Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing). sl@0: iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; sl@0: // No need to update iStartTime/iPauseTime because these are only used within a driver request sl@0: // Change state to Stopped sl@0: iState = EStopped; sl@0: // Attempt to start a new (pending) request. sl@0: StartPlayersAndUpdateState(); sl@0: break; sl@0: } sl@0: case EPlayingUnderrun: sl@0: break; sl@0: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: sl@0: return; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::PauseRecordBuffer() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::PauseRecordBuffer %s", iState.Name()); sl@0: #endif sl@0: sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: Panic(EDeviceNotOpened); sl@0: break; sl@0: sl@0: case EStopped: sl@0: // Driver is not recording so pause in s/w sl@0: // Do not pause because that will cause problems when CAudioDevice::Pause calls sl@0: break; sl@0: sl@0: case ERecording: sl@0: { sl@0: TInt res = iRecordSoundDevice.Pause(); sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("PauseRecordBuffer EPlaying res = %d", res); sl@0: #endif sl@0: if(res == KErrNone) sl@0: { sl@0: iState = ERecordingPausedInHw; sl@0: } sl@0: else sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("PauseRecordBuffer hw pause unexpectedly failed, doing sw pause"); sl@0: #endif sl@0: iState = ERecordingPausedInSw; sl@0: } sl@0: break; sl@0: } sl@0: sl@0: case ERecordingPausedInHw: sl@0: case ERecordingPausedInSw: sl@0: // Already paused so nothing to do. sl@0: break; sl@0: sl@0: case EPlaying: sl@0: case EPlayingPausedInHw: // ie. Play request pending on h/w and paused sl@0: Panic(EBadState); sl@0: break; sl@0: sl@0: case EPlayingPausedInSw: sl@0: // This is an ugly hack to maintain compatibility with CAudioDevice::Pause which sl@0: // calls both PausePlayBuffer and PauseRecordBuffer whilst in stopped, then later calls ResumePlaying sl@0: break; sl@0: case EPlayingUnderrun: // ie. Play request pending on h/w and paused sl@0: Panic(EBadState); sl@0: break; sl@0: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: sl@0: return; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::ResumeRecording() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::ResumeRecording %s", iState.Name()); sl@0: #endif sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: Panic(EDeviceNotOpened); sl@0: break; sl@0: sl@0: case EStopped: sl@0: // No change sl@0: break; sl@0: sl@0: case ERecording: sl@0: // No change sl@0: break; sl@0: sl@0: case ERecordingPausedInHw: sl@0: { sl@0: TInt res = iRecordSoundDevice.Resume(); sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("ResumeRecordBuffer ERecordingPausedInHw res = %d", res); sl@0: #endif sl@0: if(res == KErrNone) sl@0: { sl@0: // Resume ok so a pending request will complete sl@0: iState = ERecording; sl@0: } sl@0: else sl@0: { sl@0: iState = EStopped; sl@0: // Resume failed, so attempt to start a new (pending) request. sl@0: // If this works, it will update the state to ERecording. sl@0: StartRecordRequest(); sl@0: } sl@0: break; sl@0: } sl@0: case ERecordingPausedInSw: sl@0: { sl@0: // Update state to stopped and attempt to start any pending request sl@0: iState = EStopped; sl@0: // If this works, it will update the state to ERecording. sl@0: StartRecordRequest(); sl@0: break; sl@0: } sl@0: sl@0: case EPlaying: sl@0: case EPlayingPausedInHw: // ie. Play request pending on h/w and paused sl@0: case EPlayingPausedInSw: // ie. Driver not playing/paused sl@0: case EPlayingUnderrun: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: sl@0: return; sl@0: sl@0: sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::GetTimePlayed(TTimeIntervalMicroSeconds& aTimePlayed) sl@0: { sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: sl@0: sl@0: TUint64 bytesPlayed = BytesPlayed64(); sl@0: sl@0: TUint64 timePlayed = 1000 * 1000 * bytesPlayed / (iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample); sl@0: sl@0: aTimePlayed = TTimeIntervalMicroSeconds(timePlayed); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: void RMdaDevSound::CBody::FormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported, RSoundSc& aSoundDevice) sl@0: { sl@0: TSoundFormatsSupportedV02Buf supportedFormat; sl@0: aSoundDevice.Caps(supportedFormat); sl@0: TUint32 rates = supportedFormat().iRates; sl@0: sl@0: for(TInt i = KNumSampleRates-1; i > 0 ;i--)//min to max sl@0: { sl@0: if(rates & KRateEnumLookup[i].iRateConstant) sl@0: { sl@0: aFormatsSupported().iMinRate = KRateEnumLookup[i].iRate; sl@0: break; sl@0: } sl@0: } sl@0: for(TInt i = 0; i < KNumSampleRates; i++)//max to min sl@0: { sl@0: if(rates & KRateEnumLookup[i].iRateConstant) sl@0: { sl@0: aFormatsSupported().iMaxRate = KRateEnumLookup[i].iRate; sl@0: break; sl@0: } sl@0: } sl@0: TUint32 enc = supportedFormat().iEncodings; sl@0: sl@0: if (enc & KSoundEncoding16BitPCM) sl@0: { sl@0: aFormatsSupported().iEncodings = EMdaSoundEncoding16BitPCM;// Always defaults to this sl@0: } sl@0: if (enc & KSoundEncoding8BitPCM) sl@0: { sl@0: aFormatsSupported().iEncodings |= EMdaSoundEncoding8BitPCM; sl@0: } sl@0: TUint32 channels = supportedFormat().iChannels; sl@0: sl@0: if (channels & KSoundStereoChannel) sl@0: { sl@0: aFormatsSupported().iChannels = 2; sl@0: } sl@0: else sl@0: { sl@0: aFormatsSupported().iChannels = 1; sl@0: } sl@0: aFormatsSupported().iMinBufferSize = supportedFormat().iRequestMinSize; sl@0: aFormatsSupported().iMaxBufferSize = KMaxBufferSize; sl@0: aFormatsSupported().iMinVolume = 0; sl@0: aFormatsSupported().iMaxVolume = KSoundMaxVolume; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::GetFormat(TCurrentSoundFormatBuf& aFormat, sl@0: RSoundSc& /*aSoundDevice*/, sl@0: const TFormatData &aFormatData) sl@0: { sl@0: // always return the requested, or the initial, not current device setting sl@0: aFormat().iChannels = aFormatData.iRequestedChannels; // never clear if this is bitmap or value, but effectively the same sl@0: aFormat().iRate = aFormatData.iSampleRate; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::StartPlayersAndUpdateState() sl@0: { sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: Panic(EDeviceNotOpened); sl@0: break; sl@0: sl@0: case EStopped: sl@0: // Allow following code to queue more driver play requests and check for stopped sl@0: break; sl@0: sl@0: case ERecording: sl@0: case ERecordingPausedInHw: sl@0: case ERecordingPausedInSw: sl@0: Panic(EBadState); sl@0: break; sl@0: sl@0: case EPlaying: sl@0: // Allow following code to queue more driver play requests and check for stopped sl@0: break; sl@0: case EPlayingPausedInHw: // ie. Play request pending on h/w and paused sl@0: // Allow following code to queue more driver play requests sl@0: break; sl@0: sl@0: case EPlayingPausedInSw: sl@0: // Paused but driver not playing+paused, therefore do not queue new requests until ResumePlaying sl@0: return; sl@0: case EPlayingUnderrun: sl@0: break; sl@0: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: sl@0: // iState is now either EStopped, EPlaying or EPlayingPausedInHw sl@0: __ASSERT_DEBUG(((iState == EStopped) || (iState == EPlaying) || (iState == EPlayingPausedInHw) || (iState == EPlayingUnderrun)), Panic(EBadState)); sl@0: sl@0: while( (iClientPlayData.Length() != 0) && (! iFreePlayers.IsEmpty())) sl@0: { sl@0: // More data to play and more players, so issue another request sl@0: sl@0: bool wasIdle = iFreePlayers.IsFull(); sl@0: // Get a free player sl@0: CPlayer *player = iFreePlayers.Pop(); sl@0: // Calculate length of request sl@0: TUint32 lengthToPlay = iClientPlayData.Length(); sl@0: if(lengthToPlay > iDeviceBufferLength) sl@0: { sl@0: lengthToPlay = iDeviceBufferLength; sl@0: } sl@0: sl@0: // Remember request length, so we can update bytes played when it finishes sl@0: iActivePlayRequestSizes.Push(lengthToPlay); sl@0: sl@0: // Find offset to copy data to sl@0: TUint playerIndex = player->GetPlayerIndex(); sl@0: ASSERT(playerIndex < KPlaySharedChunkBuffers); sl@0: TUint chunkOffset = iPlayBufferConfig.iBufferOffsetList[playerIndex]; sl@0: sl@0: // Copy data sl@0: TPtr8 destPtr(iPlayChunk.Base()+ chunkOffset, 0, iDeviceBufferLength); sl@0: destPtr.Copy(iClientPlayData.Mid(0, lengthToPlay)); sl@0: sl@0: // Update iClientPlayData to remove the data just queued sl@0: iClientPlayData.Set(iClientPlayData.Right(iClientPlayData.Length()-lengthToPlay)); sl@0: sl@0: // Start the CPlayer sl@0: player->PlayData(chunkOffset, lengthToPlay); sl@0: if(wasIdle) sl@0: { sl@0: iState = EPlaying; sl@0: iStartTime = CurrentTimeInMsec(); sl@0: sl@0: } sl@0: sl@0: } sl@0: sl@0: // Check if the client request is now complete sl@0: if(iClientPlayData.Length() == 0 && iClientPlayStatus) sl@0: { sl@0: // We have queued all the client play data to the driver so we can now complete the client request. sl@0: // If actual playback fails, we will notify the client via the Play Error notification mechanism. sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState completing client request"); sl@0: #endif sl@0: User::RequestComplete(iClientPlayStatus, KErrNone); // This call also sets iClientPlayStatus to NULL sl@0: } sl@0: sl@0: //nb. iState is now either EStopped, EPlaying or EPlayingPausedInHw (see previous switch and assert) sl@0: if(iState != EPlayingPausedInHw) sl@0: { sl@0: if(iFreePlayers.IsFull()) sl@0: { sl@0: // Free fifo is full, therefore there are no active players sl@0: iState = EPlayingUnderrun; sl@0: if(! iUnderFlowReportedSinceLastPlayOrRecordRequest) sl@0: { sl@0: // We report KErrUnderflow if we have not already reported it since the last PlayData call. sl@0: // Note that sl@0: // i) We do NOT report driver underflows. sl@0: // ii) We report underflow when we run out of data to pass to the driver. sl@0: // iii) We throttle this reporting sl@0: // iv) We WILL report KErrUnderflow if already stopped and asked to play a zero length buffer sl@0: // The last point is required because the client maps a manual stop command into a devsound play with a sl@0: // zero length buffer and the last buffer flag set, this in turn is mapped to a Playdata calll with an empty buffer sl@0: // which is expected to complete ok and cause a KErrUnderflow error to be reported... sl@0: iUnderFlowReportedSinceLastPlayOrRecordRequest = ETrue; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState stopped and iUnderFlowReportedSinceLastPlayOrRecordRequest false so raising KErrUnderflow"); sl@0: #endif sl@0: sl@0: // Discard any saved trailing data (ie. data saved due driver requiring all requests to be a multiple of iRequestMinSize). sl@0: // This maybe because client is delibrately letting us underflow to play silence. In that case we do not want to sl@0: // play the trailing data at the beginning of the new data issued after the silence... sl@0: iSavedTrailingData.SetLength(0); sl@0: sl@0: SoundDeviceError(KErrUnderflow); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // Free fifo not full, therefore there are active players sl@0: iState = EPlaying; sl@0: } sl@0: } sl@0: return; sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::SetFormat(const TCurrentSoundFormatBuf& aFormat, sl@0: RSoundSc& aSoundDevice, sl@0: TFormatData &aFormatData) sl@0: { sl@0: TInt err = KErrNotFound; sl@0: TCurrentSoundFormatV02Buf formatBuf; sl@0: sl@0: delete aFormatData.iConverter; sl@0: aFormatData.iConverter = NULL; // setting this to NULL indicates we are not using converter. No other flag sl@0: iConvertedPlayData.Close(); sl@0: sl@0: TInt wantedRate = aFormat().iRate; sl@0: for(TInt index = 0; index < KNumSampleRates; index++ ) sl@0: { sl@0: if(wantedRate == KRateEnumLookup[index].iRate) sl@0: { sl@0: formatBuf().iRate = KRateEnumLookup[index].iRateEnum; sl@0: aFormatData.iSampleRate = wantedRate; sl@0: err = KErrNone; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: if(err == KErrNone) sl@0: { sl@0: // Assume, for now, we support the requested channels and rate sl@0: aFormatData.iActualChannels = aFormatData.iRequestedChannels; sl@0: aFormatData.iActualRate = aFormatData.iSampleRate; sl@0: sl@0: // Attempt to configure driver sl@0: formatBuf().iChannels = aFormat().iChannels; sl@0: formatBuf().iEncoding = ESoundEncoding16BitPCM; sl@0: formatBuf().iDataFormat = ESoundDataFormatInterleaved; sl@0: err = aSoundDevice.SetAudioFormat(formatBuf); sl@0: #if defined(SYMBIAN_SOUNDADAPTER_FORCECDRATES) || defined (SYMBIAN_SOUNDADAPTER_FORCESTEREO) sl@0: err = KErrNotSupported; // force Negotiate - for debugging sl@0: #endif sl@0: if (err==KErrNotSupported) sl@0: { sl@0: // don't support directly. Perhaps can rate convert? sl@0: err = NegotiateFormat(aFormat, aSoundDevice, aFormatData); sl@0: } sl@0: } sl@0: return err; sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::NegotiateFormat(const TCurrentSoundFormatBuf& aFormat, sl@0: RSoundSc& aSoundDevice, sl@0: TFormatData &aFormatData) sl@0: { sl@0: ASSERT(!aFormatData.iConverter); // we don't clear on fail - so assuming NULL to start with sl@0: sl@0: TInt err = KErrNotFound; sl@0: TCurrentSoundFormatV02Buf formatBuf; sl@0: sl@0: // find out first what the driver supports sl@0: TSoundFormatsSupportedV02Buf supportedFormat; sl@0: aSoundDevice.Caps(supportedFormat); sl@0: TUint32 supportedRates = supportedFormat().iRates; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_FORCECDRATES sl@0: supportedRates &= KSoundRate11025Hz| KSoundRate22050Hz | KSoundRate44100Hz; // only use CD rates - for debugging sl@0: #endif sl@0: sl@0: // For PlayCase: sl@0: // first try to find the first rate below or equal to the requested that is supported sl@0: // initially go down and be fussy, but if we pass the requested rate find the first that sl@0: // is supported sl@0: // For RecordCase: sl@0: // We want the next rate above consistently - we go down from this to the requested rate. sl@0: // If there is one, we don't support - we _never_ upsample. sl@0: // note that the table is given in descending order, so we start with the highest sl@0: TInt wantedRate = aFormat().iRate; sl@0: TInt takeTheFirst = EFalse; sl@0: TInt nextUpValidIndex = -1; sl@0: for(TInt index = 0; index < KNumSampleRates; index++ ) sl@0: { sl@0: TBool lookingAtRequestedRate = wantedRate == KRateEnumLookup[index].iRate; sl@0: TSoundRate wantedEnum = KRateEnumLookup[index].iRateEnum; sl@0: TUint32 equivBitmap = KRateEnumLookup[index].iRateConstant; sl@0: TBool isSupported = (equivBitmap & supportedRates) != EFalse; sl@0: if (lookingAtRequestedRate || takeTheFirst) sl@0: { sl@0: if (isSupported) sl@0: { sl@0: // this rate is supported sl@0: formatBuf().iRate = wantedEnum; sl@0: aFormatData.iActualRate = KRateEnumLookup[index].iRate; sl@0: err = KErrNone; sl@0: break; sl@0: } sl@0: } sl@0: else if (!takeTheFirst) sl@0: { sl@0: // while we are still looking for the rate, want to cache any supported index sl@0: // at end of loop, this will be the first rate above ours that is supported sl@0: // use for fallback if required sl@0: if (isSupported) sl@0: { sl@0: nextUpValidIndex = index; sl@0: } sl@0: } sl@0: if (lookingAtRequestedRate) sl@0: { sl@0: // if we get this far we've gone passed the wanted rate. For play we enable sl@0: // "takeTheFirst". For record we just abort. sl@0: if (&aSoundDevice==&iPlaySoundDevice) sl@0: { sl@0: takeTheFirst = ETrue; sl@0: } sl@0: else sl@0: { sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: sl@0: if (err) sl@0: { sl@0: // if there is one above the requested rate, use that sl@0: if (nextUpValidIndex>=0) sl@0: { sl@0: TSoundRate wantedEnum = KRateEnumLookup[nextUpValidIndex].iRateEnum; sl@0: formatBuf().iRate = wantedEnum; sl@0: aFormatData.iActualRate = KRateEnumLookup[nextUpValidIndex].iRate; sl@0: err = KErrNone; sl@0: } sl@0: } sl@0: sl@0: if (err) sl@0: { sl@0: // should have something! sl@0: return err; sl@0: } sl@0: sl@0: aFormatData.iSampleRate = wantedRate; // iSampleRate is our requested/apparent rate, not the device rate. sl@0: sl@0: TUint32 channelsSupported = supportedFormat().iChannels; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_FORCESTEREO sl@0: channelsSupported &= KSoundStereoChannel; // don't use mono - for debugging sl@0: #endif sl@0: if(KSoundAdapterForceStereo==1) sl@0: { sl@0: channelsSupported &= KSoundStereoChannel; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Print(_L("Added stereo support.")); sl@0: #endif sl@0: } sl@0: if (aFormat().iChannels == 1) sl@0: { sl@0: aFormatData.iRequestedChannels = 1; sl@0: // want mono sl@0: if (channelsSupported & KSoundMonoChannel) sl@0: { sl@0: // mono is supported, as usual sl@0: aFormatData.iActualChannels = 1; sl@0: } sl@0: else if (channelsSupported & KSoundStereoChannel) sl@0: { sl@0: aFormatData.iActualChannels = 2; sl@0: } sl@0: else sl@0: { sl@0: return KErrNotSupported; // should not get this far for real sl@0: } sl@0: } sl@0: else if (aFormat().iChannels == 2) sl@0: { sl@0: aFormatData.iRequestedChannels = 2; sl@0: // want stereo sl@0: if (channelsSupported & KSoundStereoChannel) sl@0: { sl@0: // stereo is supported, as usual sl@0: aFormatData.iActualChannels = 2; sl@0: } sl@0: else if (channelsSupported & KSoundMonoChannel) sl@0: { sl@0: aFormatData.iActualChannels = 1; sl@0: } sl@0: else sl@0: { sl@0: return KErrNotSupported; // should not get this far for real sl@0: } sl@0: } sl@0: else sl@0: { sl@0: return KErrNotSupported; // unknown number of channels requested! sl@0: } sl@0: sl@0: formatBuf().iChannels = aFormatData.iActualChannels; sl@0: sl@0: formatBuf().iEncoding = ESoundEncoding16BitPCM; sl@0: formatBuf().iDataFormat = ESoundDataFormatInterleaved; sl@0: err = aSoundDevice.SetAudioFormat(formatBuf); sl@0: sl@0: if (!err) sl@0: { sl@0: ASSERT(!aFormatData.iConverter); // pre-condition at top of function anyway sl@0: if (&aSoundDevice==&iPlaySoundDevice) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), sl@0: aFormatData.iSampleRate, aFormatData.iRequestedChannels, sl@0: aFormatData.iActualRate, aFormatData.iActualChannels); sl@0: #endif sl@0: // when playing we convert from requested to actual sl@0: TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iSampleRate, sl@0: aFormatData.iRequestedChannels, sl@0: aFormatData.iActualRate, sl@0: aFormatData.iActualChannels)); sl@0: } sl@0: else sl@0: { sl@0: // when recording we convert from actual to requested sl@0: TInt outputRateToUse = aFormatData.iSampleRate; sl@0: #ifdef SYMBIAN_SKIP_RESAMPLE_ON_RECORD sl@0: // with this macro just channel convert at most sl@0: outputRateToUse = aFormatData.iActualRate; sl@0: #endif sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), sl@0: aFormatData.iActualRate, aFormatData.iActualChannels, sl@0: aFormatData.iSampleRate, aFormatData.iRequestedChannels); sl@0: #endif sl@0: TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iActualRate, sl@0: aFormatData.iActualChannels, sl@0: outputRateToUse, sl@0: aFormatData.iRequestedChannels)); sl@0: } sl@0: } sl@0: if(err != KErrNone) sl@0: { sl@0: delete aFormatData.iConverter; sl@0: aFormatData.iConverter= NULL; sl@0: iConvertedPlayData.Close(); sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::StartRecordRequest() sl@0: { sl@0: __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: sl@0: iRecorder->RecordData(iBufferLength); sl@0: } sl@0: sl@0: // Note both InRecordMode and InPlayMode return EFalse for ENotReady and EStopped sl@0: TBool RMdaDevSound::CBody::InRecordMode()const sl@0: { sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: case EStopped: sl@0: return EFalse; sl@0: sl@0: case ERecording: sl@0: case ERecordingPausedInHw: sl@0: case ERecordingPausedInSw: sl@0: return ETrue; sl@0: sl@0: case EPlaying: sl@0: case EPlayingPausedInHw: sl@0: case EPlayingPausedInSw: sl@0: case EPlayingUnderrun: sl@0: return EFalse; sl@0: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: return EFalse; sl@0: } sl@0: sl@0: TBool RMdaDevSound::CBody::InPlayMode() const sl@0: { sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: case EStopped: sl@0: return EFalse; sl@0: sl@0: case ERecording: sl@0: case ERecordingPausedInHw: sl@0: case ERecordingPausedInSw: sl@0: return EFalse; sl@0: sl@0: case EPlaying: sl@0: case EPlayingPausedInHw: sl@0: case EPlayingPausedInSw: sl@0: case EPlayingUnderrun: sl@0: return ETrue; sl@0: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: sl@0: return EFalse; sl@0: } sl@0: sl@0: sl@0: TUint32 RMdaDevSound::CBody::CurrentTimeInMsec() const sl@0: { sl@0: TUint64 tmp = User::NTickCount(); sl@0: tmp *= iNTickPeriodInUsec; sl@0: tmp /= 1000; sl@0: return TUint32(tmp); sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::PlayFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported) sl@0: { sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: FormatsSupported(aFormatsSupported, iPlaySoundDevice); sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::GetPlayFormat(TCurrentSoundFormatBuf& aFormat) sl@0: { sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: GetFormat(aFormat, iPlaySoundDevice, iPlayFormatData); sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::SetPlayFormat(const TCurrentSoundFormatBuf& aFormat) sl@0: { sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: return SetFormat(aFormat, iPlaySoundDevice, iPlayFormatData); sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::RecordFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported) sl@0: { sl@0: __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: FormatsSupported(aFormatsSupported, iRecordSoundDevice); sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::GetRecordFormat(TCurrentSoundFormatBuf& aFormat) sl@0: { sl@0: __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: GetFormat(aFormat, iRecordSoundDevice, iRecordFormatData); sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::SetRecordFormat(const TCurrentSoundFormatBuf& aFormat) sl@0: { sl@0: __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: return SetFormat(aFormat, iRecordSoundDevice, iRecordFormatData); sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::Close() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("void RMdaDevSound::CBody::Close() started"); sl@0: #endif sl@0: iBufferOffset = -1; sl@0: iBufferLength = 0; sl@0: sl@0: if(iPlaySoundDevice.Handle() != KNullHandle) sl@0: { sl@0: // Make sure all player objects are idle sl@0: CancelPlayData(); sl@0: iPlayChunk.Close(); sl@0: iPlaySoundDevice.Close(); sl@0: } sl@0: sl@0: if(iRecordSoundDevice.Handle() != KNullHandle) sl@0: { sl@0: CancelRecordData(); sl@0: iRecordChunk.Close(); sl@0: iRecordSoundDevice.Close(); sl@0: } sl@0: sl@0: iState = ENotReady; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("void RMdaDevSound::CBody::Close() ended"); sl@0: #endif sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::Handle() sl@0: {//This method is actually used to check whether the device is opened. Below logic should work sl@0: if(iPlaySoundDevice.Handle()) sl@0: { sl@0: return iPlaySoundDevice.Handle(); sl@0: } sl@0: if(iRecordSoundDevice.Handle()) sl@0: { sl@0: return iRecordSoundDevice.Handle(); sl@0: } sl@0: return 0; sl@0: } sl@0: sl@0: sl@0: void RMdaDevSound::CBody::PlayData(TRequestStatus& aStatus, const TDesC8& aData) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::PlayData(0x%x,%d) State=%s Handle=%d.",&aStatus, sl@0: aData.Length(), iState.Name(), iPlayChunk.Handle()); sl@0: #endif sl@0: sl@0: __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: aStatus = KRequestPending; sl@0: sl@0: if((iClientPlayStatus != NULL) || InRecordMode()) sl@0: { sl@0: // We only support one outstanding request sl@0: // No support for simultaneous play and record in RMdaDevSound sl@0: TRequestStatus *pRequest = &aStatus; sl@0: User::RequestComplete(pRequest, KErrInUse); sl@0: return; sl@0: } sl@0: iClientPlayStatus = &aStatus;//store the status of datapath player sl@0: sl@0: if(iPlayFormatData.iConverter || iSavedTrailingData.Length() != 0) sl@0: { sl@0: // Need a conversion buffer sl@0: // Needs to hold any trailing data truncated from the previous request (due sl@0: // to alignment requirements) and either the new data, or the new rate adapted data sl@0: TUint32 spaceRequired = iSavedTrailingData.Length(); sl@0: if(iPlayFormatData.iConverter) sl@0: { sl@0: // Doing rate conversion so also need space for the converted data sl@0: spaceRequired += iPlayFormatData.iConverter->MaxConvertBufferSize(aData.Length()); sl@0: } sl@0: else sl@0: { sl@0: // Not doing rate adaptation therefore only need to allow for the new incoming data sl@0: spaceRequired += aData.Length(); sl@0: } sl@0: // Check if existing buffer exists and is big enough sl@0: if(iConvertedPlayData.MaxLength() < spaceRequired) sl@0: { sl@0: iConvertedPlayData.Close(); sl@0: TInt err = iConvertedPlayData.Create(spaceRequired); sl@0: if(err) sl@0: { sl@0: User::RequestComplete(iClientPlayStatus, err); sl@0: return; sl@0: } sl@0: } sl@0: sl@0: // Truncate iConvertedPlayData and copy in saved trailing data (if any) sl@0: iConvertedPlayData = iSavedTrailingData; sl@0: iSavedTrailingData.SetLength(0); sl@0: sl@0: // Now append rate adapted data or incoming data sl@0: if (iPlayFormatData.iConverter) sl@0: { sl@0: // The convertor will panic if it fails to convert any data, therefore sl@0: // we avoid passing it an empty source buffer sl@0: if(aData.Length() != 0) sl@0: { sl@0: TPtr8 destPtr((TUint8 *)iConvertedPlayData.Ptr()+iConvertedPlayData.Length(), 0, iConvertedPlayData.MaxLength()-iConvertedPlayData.Length()); sl@0: TInt len = iPlayFormatData.iConverter->Convert(aData, destPtr); sl@0: iConvertedPlayData.SetLength(iConvertedPlayData.Length() + destPtr.Length()); sl@0: if(len != aData.Length()) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::PlayData converted %d but expected to convert %d", len, aData.Length()); sl@0: #endif sl@0: } sl@0: } sl@0: } sl@0: else sl@0: { sl@0: iConvertedPlayData.Append(aData); sl@0: } sl@0: iClientPlayData.Set(iConvertedPlayData); sl@0: } sl@0: else sl@0: { sl@0: // Do not need a conversion buffer so just aim the descriptor at the data sl@0: iClientPlayData.Set(aData); sl@0: } sl@0: iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; sl@0: sl@0: // All driver requests must be an exact multiple of iRequestMinSize sl@0: TUint32 trailingDataLen = iClientPlayData.Length() % iRequestMinSize; sl@0: if(trailingDataLen) sl@0: { sl@0: // Not a multiple of iRequestMinSize, so need to truncate current request, and save trailing bytes for sl@0: // inclusion at the beginning of the next request sl@0: iSavedTrailingData = iClientPlayData.Right(trailingDataLen); sl@0: iClientPlayData.Set(iClientPlayData.Left(iClientPlayData.Length()-trailingDataLen)); sl@0: } sl@0: sl@0: #ifdef SYMBIAN_FORCE_32BIT_LENGTHS sl@0: if (iClientPlayData.Length()%4 != 0) sl@0: { sl@0: // simulate the limitation of some hardware, where -6 is generated if the sl@0: // buffer length is not divisible by 4. sl@0: TRequestStatus *pRequest = &aStatus; sl@0: User::RequestComplete(pRequest, KErrArgument); sl@0: } sl@0: #endif sl@0: sl@0: iRecordChunk.Close(); sl@0: if(!iPlayChunk.Handle()) sl@0: { sl@0: //This is where we setup to play. sl@0: //Configure the shared chunk for two buffers with iBufferSize each sl@0: iPlayBufferConfig.iNumBuffers = KPlaySharedChunkBuffers; sl@0: iDeviceBufferLength = KPlaySharedChunkBufferSize; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("iDeviceBufferLength %d", iDeviceBufferLength); sl@0: #endif sl@0: iPlayBufferConfig.iFlags = 0;//data will be continuous sl@0: // If required, use rate converter etc sl@0: iPlayBufferConfig.iBufferSizeInBytes = iDeviceBufferLength; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("number of buffers: [%d]",iPlayBufferConfig.iNumBuffers); sl@0: RDebug::Printf("BufferSize in Bytes [%d]",iPlayBufferConfig.iBufferSizeInBytes); sl@0: #endif sl@0: TPckg bufferConfigBuf(iPlayBufferConfig); sl@0: TInt error = iPlaySoundDevice.SetBufferChunkCreate(bufferConfigBuf,iPlayChunk); sl@0: if(error == KErrNone) sl@0: { sl@0: iPlaySoundDevice.GetBufferConfig(bufferConfigBuf); sl@0: } sl@0: if (error) sl@0: { sl@0: SoundDeviceError(error); sl@0: return; sl@0: } sl@0: } sl@0: sl@0: StartPlayersAndUpdateState(); sl@0: sl@0: return; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::RecordData(TRequestStatus& aStatus, TDes8& aData) sl@0: { sl@0: __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); sl@0: aStatus = KRequestPending; sl@0: if((iClientPlayStatus != NULL) || InPlayMode()) sl@0: { sl@0: // We only support one outstanding request sl@0: // No support for simultaneous play and record in RMdaDevSound sl@0: TRequestStatus *pRequest = &aStatus; sl@0: User::RequestComplete(pRequest, KErrInUse); sl@0: return; sl@0: } sl@0: iClientRecordStatus = &aStatus; sl@0: iClientRecordData = &aData; sl@0: iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; sl@0: sl@0: iPlayChunk.Close(); sl@0: if(!iRecordChunk.Handle()) sl@0: { sl@0: //Configure the shared chunk for two buffers with iBufferSize each sl@0: iRecordBufferConfig.iNumBuffers = KRecordMaxSharedChunkBuffers; sl@0: iDeviceBufferLength = KRecordSharedChunkBufferSize; // initial size - resize if needs be sl@0: if (iRecordFormatData.iConverter) sl@0: { sl@0: // if number of channels used differs from request, resize buffer sl@0: // assume we have nice rounded values for buffer. sl@0: if (iRecordFormatData.iActualChannels>iRecordFormatData.iRequestedChannels) sl@0: { sl@0: iDeviceBufferLength *= 2; // will record at stereo and convert to mono sl@0: } sl@0: else if (iRecordFormatData.iActualChannels bufferConfigBuf(iRecordBufferConfig); sl@0: TInt error = iRecordSoundDevice.SetBufferChunkCreate(bufferConfigBuf,iRecordChunk); sl@0: if(error == KErrNone) sl@0: { sl@0: iRecordSoundDevice.GetBufferConfig(bufferConfigBuf); sl@0: } sl@0: else sl@0: { sl@0: SoundDeviceError(error); sl@0: return; sl@0: } sl@0: iState = ERecording; sl@0: } sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::RecordData,iBufferOffset[%d]",iBufferOffset); sl@0: #endif sl@0: sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: Panic(EBadState); sl@0: break; sl@0: sl@0: case EStopped: sl@0: case ERecording: sl@0: // Either idle or recording is in progress, therefore we can issue another request sl@0: StartRecordRequest(); sl@0: break; sl@0: sl@0: case ERecordingPausedInHw: sl@0: // Driver is paused, therefore we can issue a request which will immediately return buffered data sl@0: // or be aborted (in the driver) with KErrCancelled if there is no more data). nb. That KErrCancelled should not be sl@0: // returned to the client because the old RMdaDevSound driver would have completed with KErrNone and zero data length. sl@0: StartRecordRequest(); sl@0: break; sl@0: sl@0: case ERecordingPausedInSw: sl@0: // Paused in s/w but driver is not paused, therefore can not issue a new request to driver because sl@0: // it would re-start recording. sl@0: // This implies we were paused whilst the h/w was not recording, so there is no buffered data. sl@0: sl@0: // Complete the request with KErrNone and no data. sl@0: iClientRecordData->SetLength(0); sl@0: User::RequestComplete(iClientRecordStatus, KErrNone); sl@0: break; sl@0: sl@0: case EPlaying: sl@0: case EPlayingPausedInHw: sl@0: case EPlayingPausedInSw: sl@0: case EPlayingUnderrun: sl@0: Panic(EBadState); sl@0: break; sl@0: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Notify client of error. sl@0: sl@0: Note that we continue playing/recording if possible. sl@0: sl@0: We do not maintain information which could map the error back to a particular client play/record request sl@0: and therefore we have to notify the client of error every time it happens. sl@0: sl@0: nb. A client play/record request is completed with KErrNone if it queues ok - All errors are reported via the Notify*Error sl@0: mechanism. sl@0: */ sl@0: void RMdaDevSound::CBody::SoundDeviceError(TInt aError) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError: Error[%d] state %s", aError, iState.Name()); sl@0: #endif sl@0: sl@0: ASSERT(aError != KErrNone); sl@0: sl@0: if(iClientPlayErrorStatus) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iPlayerErrorStatus"); sl@0: #endif sl@0: sl@0: User::RequestComplete(iClientPlayErrorStatus, aError); // nb call also sets iClientPlayErrorStatus to NULL sl@0: } sl@0: sl@0: if(iClientRecordErrorStatus) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iClientRecordErrorStatus"); sl@0: #endif sl@0: User::RequestComplete(iClientRecordErrorStatus, aError); // nb call also sets iClientRecordErrorStatus to NULL sl@0: } sl@0: sl@0: return; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::NotifyRecordError(TRequestStatus& aStatus) sl@0: { sl@0: aStatus = KRequestPending; sl@0: iClientRecordErrorStatus = &aStatus; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::NotifyPlayError(TRequestStatus& aStatus) sl@0: { sl@0: aStatus = KRequestPending; sl@0: iClientPlayErrorStatus = &aStatus; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::CancelNotifyPlayError() sl@0: { sl@0: if(iClientPlayErrorStatus) sl@0: { sl@0: User::RequestComplete(iClientPlayErrorStatus, KErrCancel); sl@0: } sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::CancelNotifyRecordError() sl@0: { sl@0: if(iClientRecordErrorStatus) sl@0: { sl@0: User::RequestComplete(iClientRecordErrorStatus, KErrCancel); sl@0: } sl@0: else sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("msp BufferEmptied but iClientPlayStatus==NULL"); sl@0: #endif sl@0: } sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::FlushPlayBuffer() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::FlushPlayBuffer calling CancelPlayData"); sl@0: #endif sl@0: CancelPlayData(); sl@0: } sl@0: sl@0: RSoundSc& RMdaDevSound::CBody::PlaySoundDevice() sl@0: { sl@0: return iPlaySoundDevice; sl@0: } sl@0: sl@0: RSoundSc& RMdaDevSound::CBody::RecordSoundDevice() sl@0: { sl@0: return iRecordSoundDevice; sl@0: } sl@0: sl@0: const RMdaDevSound::CBody::TState &RMdaDevSound::CBody::State() const sl@0: { sl@0: return iState; sl@0: } sl@0: sl@0: sl@0: void RMdaDevSound::CBody::BufferFilled(TInt aBufferOffset) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled:")); sl@0: #endif sl@0: sl@0: ASSERT(aBufferOffset>=0 || aBufferOffset==KErrCancel); sl@0: ASSERT(iClientRecordData); // request should not get this without sl@0: sl@0: if(aBufferOffset==KErrCancel) sl@0: { sl@0: //we can get KErrCancel when we call pause and there is no more data left with the driver sl@0: //we send the empty buffer to the HwDevice, where this should trigger the shutdown mechanism sl@0: iClientRecordData->SetLength(0); sl@0: User::RequestComplete(iClientRecordStatus, KErrNone); sl@0: iClientRecordStatus = NULL; sl@0: return; sl@0: } sl@0: sl@0: iBufferOffset = aBufferOffset; sl@0: //when last buffer is flushed, new driver sometimes gives buffer size of odd number. One of our codecs sl@0: //expects that the buffer size should always be even. Base suggested that we fix in multimedia sl@0: //as it is quite complicated to fix in overthere. sl@0: iBufferLength = iBufferLength & 0xfffffffe; sl@0: TPtr8 dataPtr(iRecordChunk.Base()+ iBufferOffset, iBufferLength, iClientRecordData->MaxLength()); sl@0: if (iRecordFormatData.iConverter) sl@0: { sl@0: iRecordFormatData.iConverter->Convert(dataPtr, *iClientRecordData); sl@0: } sl@0: else sl@0: { sl@0: iClientRecordData->Copy(dataPtr); sl@0: } sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled: BufferOffset[%d] BufferLen[%d]"), iBufferOffset, iBufferLength); sl@0: #endif sl@0: if(iBufferOffset >= 0) sl@0: { sl@0: iRecordSoundDevice.ReleaseBuffer(iBufferOffset); sl@0: } sl@0: if(iClientRecordStatus) sl@0: { sl@0: User::RequestComplete(iClientRecordStatus, KErrNone); sl@0: iClientRecordStatus = NULL; sl@0: } sl@0: else sl@0: { sl@0: RDebug::Printf("msp PlayCancelled but iClientPlayStatus==NULL"); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: This function is called to notify us that a CPlayer's request has completed and what its status was. sl@0: sl@0: It is important to note that:- sl@0: 1) RSoundSc driver PlayData requests are guaranteed to complete in order, oldest first sl@0: 2) If we are overloaded, it is possible for more than one request to complete before any CPlayer::RunL is ran. In sl@0: this situation the CPlayer::RunL functions, and hence this callback, maybe invoked in non-oldest first order sl@0: sl@0: but sl@0: sl@0: a) It is impossible for callback for the second oldest CPlayer to occur before the driver request for the oldest has sl@0: been complete (because of 1) sl@0: b) We will always get exactly one callback for every complete request. sl@0: sl@0: Therefore this callback notifies us of two subtly separate things:- sl@0: sl@0: i) The oldest request has been completed (so we can reduce can increase the bytes played counter by its length sl@0: ii) CPlayer aPlayerIndex is free for re-use sl@0: sl@0: but we can not assume that aPlayerIndex is the oldest request, therefore we save the play request lengths outside of sl@0: the CPlayer object. sl@0: */ sl@0: void RMdaDevSound::CBody::PlayRequestHasCompleted(CPlayer *aPlayer, TInt aStatus, TBool aDueToCancelCommand) sl@0: { sl@0: // CPlayer is done so put it on the free queue sl@0: iFreePlayers.Push(aPlayer); sl@0: sl@0: TUint32 bytesPlayed = iActivePlayRequestSizes.Pop(); sl@0: // Request has finished therefore now timing the following request to simulate bytes played sl@0: iStartTime = CurrentTimeInMsec(); sl@0: if(aDueToCancelCommand) sl@0: { sl@0: // Callback due to CPlayer::Cancel/DoCancel being called, therefore we sl@0: // do not want to update bytes played, process state, report a error or start new players sl@0: return; sl@0: } sl@0: sl@0: // Update iBytesPlayed by the length of the oldest request (which might not be the one that CPlayer was sl@0: // handling). sl@0: iBytesPlayed += bytesPlayed; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("PlayRequestHasCompleted increasing iBytesPlayed by %d to %d", bytesPlayed, iBytesPlayed); sl@0: #endif sl@0: sl@0: // Process state sl@0: switch(iState) sl@0: { sl@0: case ENotReady: sl@0: Panic(EDeviceNotOpened); sl@0: break; sl@0: sl@0: case EStopped: sl@0: // Will happen if we are doing CancelPlayData processing with active players sl@0: break; sl@0: sl@0: case ERecording: sl@0: case ERecordingPausedInHw: sl@0: case ERecordingPausedInSw: sl@0: Panic(EBadState); sl@0: break; sl@0: sl@0: case EPlaying: sl@0: // Normal situation sl@0: break; sl@0: sl@0: case EPlayingPausedInHw: sl@0: // H/W was/is paused, but there must have been an already complete request that we had not sl@0: // processed yet. sl@0: // There must be at least one more pending request on h/w, otherwise the h/w would have refused to pause sl@0: // I would expect this be rare, but it happens quite often... sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: ASSERT(iActivePlayRequestSizes.Length() != 0); sl@0: #endif sl@0: // Need to update the start and pause time to now because we have just updated the actual iBytesPlayed sl@0: // and logically the h/w is paused at the beginning of the next request sl@0: iStartTime = CurrentTimeInMsec(); sl@0: iPauseTime = iStartTime; sl@0: break; sl@0: sl@0: case EPlayingPausedInSw: sl@0: // This will happen if there is only a single hw request outstanding, and the hardware has finished it, but the sl@0: // corresponding RunL has not run yet (in which case PausePlayBuffer will have attempted to use h/w pause, sl@0: // but the driver call would have failed, and the state changed to EPlayingPausedInSw). sl@0: iStartTime = CurrentTimeInMsec(); sl@0: iPauseTime = iStartTime; sl@0: return; sl@0: case EPlayingUnderrun: sl@0: break; sl@0: sl@0: default: sl@0: Panic(EBadState); sl@0: break; sl@0: } sl@0: sl@0: sl@0: // If we have an error, report it to the client sl@0: // We NEVER report driver underflow, instead we report KErrUnderflow if we run out of data to pass to driver. sl@0: if( (aStatus != KErrNone) && (aStatus != KErrUnderflow) ) sl@0: { sl@0: SoundDeviceError(aStatus); sl@0: } sl@0: sl@0: // If appropriate start more players sl@0: StartPlayersAndUpdateState(); sl@0: return; sl@0: } sl@0: sl@0: RMdaDevSound::CBody::CPlayer::CPlayer(TInt aPriority, RMdaDevSound::CBody& aParent, TInt aIndex): sl@0: CActive(aPriority), iParent(aParent), iIndex(aIndex), iBufferOffset(-1), iBufferLength(0) sl@0: { sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: RMdaDevSound::CBody::CPlayer::~CPlayer() sl@0: { sl@0: Cancel(); sl@0: } sl@0: sl@0: sl@0: void RMdaDevSound::CBody::CPlayer::RunL() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("****RMdaDevSound::CBody::CPlayer(%d)::RunL: Error[%d] ParentState[%s]", sl@0: iIndex, iStatus.Int(), iParent.State().Name()); sl@0: RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (including this one as active)", sl@0: iParent.iActivePlayRequestSizes.Length(), sl@0: iParent.iFreePlayers.Length()); sl@0: #endif sl@0: iParent.PlayRequestHasCompleted(this, iStatus.Int(), EFalse); sl@0: return; sl@0: } sl@0: sl@0: TInt RMdaDevSound::CBody::CPlayer::RunError(TInt aError) sl@0: { sl@0: iParent.PlayRequestHasCompleted(this, aError, EFalse); sl@0: return KErrNone; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::CPlayer::DoCancel() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::CPlayer(%d)::DoCancel", iIndex); sl@0: #endif sl@0: if(iStatus == KRequestPending) sl@0: { sl@0: // Avoid cancelling requests which have already completed. sl@0: // It wastes time, and might provoke a sound driver problem sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::CPlayer::DoCancel - would have cancelled driver request"); sl@0: #endif sl@0: iParent.PlaySoundDevice().Cancel(iStatus); sl@0: } sl@0: iParent.PlayRequestHasCompleted(this, KErrCancel, ETrue); sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::CPlayer::PlayData(TUint aChunkOffset, TInt aLength) sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::PlayData : IsActive[%d]"), sl@0: iIndex, IsActive()); sl@0: RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (inc this player)", sl@0: iParent.iActivePlayRequestSizes.Length(), sl@0: iParent.iFreePlayers.Length()); sl@0: #endif sl@0: sl@0: iBufferOffset = aChunkOffset; sl@0: iBufferLength = aLength; sl@0: sl@0: //Make sure the length is a multiple of 4 to work around an h6 limitation. sl@0: iBufferLength = iBufferLength & 0xfffffffc; sl@0: sl@0: // Issue the RSoundSc request sl@0: iParent.PlaySoundDevice().PlayData(iStatus, iBufferOffset, iBufferLength, EFalse); sl@0: SetActive(); sl@0: return; sl@0: } sl@0: sl@0: TUint RMdaDevSound::CBody::CPlayer::GetPlayerIndex() const sl@0: { sl@0: return iIndex; sl@0: } sl@0: sl@0: RMdaDevSound::CBody::CRecorder::CRecorder(TInt aPriority, RMdaDevSound::CBody& aParent): sl@0: CActive(aPriority), iParent(aParent), iBufferOffset(-1), iBufferLength(0) sl@0: { sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: RMdaDevSound::CBody::CRecorder::~CRecorder() sl@0: { sl@0: Cancel(); sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::CRecorder::RecordData(TInt& aBufferLength) sl@0: { sl@0: if (!IsActive()) sl@0: { sl@0: iStatus = KRequestPending; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("Recording request: BufferLength[%d]", aBufferLength); sl@0: #endif sl@0: iParent.RecordSoundDevice().RecordData(iStatus, aBufferLength); sl@0: SetActive(); sl@0: } sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::CRecorder::RunL() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("****RMdaDevSound::CBody::CRecorder()::RunL: Error[%d] ParentState[%s]", sl@0: iStatus.Int(), iParent.State().Name()); sl@0: #endif sl@0: sl@0: sl@0: TInt error = iStatus.Int(); sl@0: sl@0: if((error >= 0) || (error == KErrCancel)) sl@0: {//we can get KErrCancel when we call pause and there is no more data left with the driver sl@0: iParent.BufferFilled(error); sl@0: } sl@0: else sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Print(_L("RMdaDevSound::CBody::CPlayer()::RunL: Error[%d]"), error); sl@0: #endif sl@0: iParent.SoundDeviceError(error); sl@0: } sl@0: } sl@0: sl@0: sl@0: TInt RMdaDevSound::CBody::CRecorder::RunError(TInt aError) sl@0: { sl@0: iParent.SoundDeviceError(aError); sl@0: return KErrNone; sl@0: } sl@0: sl@0: void RMdaDevSound::CBody::CRecorder::DoCancel() sl@0: { sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Printf("RMdaDevSound::CBody::CRecorder()::DoCancel"); sl@0: #endif sl@0: iParent.RecordSoundDevice().Cancel(iStatus); sl@0: } sl@0: sl@0: sl@0: // End of file