diff -r 735348f59235 -r 948c7f65f6d4 mmdevicefw/mdf/src/audio/mdasoundadapter/mdasoundadapterbody.cpp --- a/mmdevicefw/mdf/src/audio/mdasoundadapter/mdasoundadapterbody.cpp Tue Aug 31 16:43:06 2010 +0300 +++ b/mmdevicefw/mdf/src/audio/mdasoundadapter/mdasoundadapterbody.cpp Wed Sep 01 12:38:50 2010 +0100 @@ -18,7 +18,9 @@ #include "mmf/utils/rateconvert.h" // if we need to resample -#include +#ifdef SYMBIAN_SOUNDADAPTER_BYTESPLAYED + #include +#endif _LIT(KPddFileName,"SOUNDSC.PDD"); _LIT(KLddFileName,"ESOUNDSC.LDD"); @@ -35,43 +37,7 @@ { User::Panic(KSoundAdapterPanicCategory, aPanicCode); } - - -const TText8 *RMdaDevSound::CBody::TState::Name() const - { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - switch(iState) - { - case ENotReady: return _S8("ENotReady"); - case EStopped: return _S8("EStopped"); - case ERecording: return _S8("ERecording"); - case ERecordingPausedInHw: return _S8("ERecordingPausedInHw"); - case ERecordingPausedInSw: return _S8("ERecordingPausedInSw"); - case EPlaying: return _S8("EPlaying"); - case EPlayingPausedInHw: return _S8("EPlayingPausedInHw"); - case EPlayingPausedInSw: return _S8("EPlayingPausedInSw"); - case EPlayingUnderrun: return _S8("EPlayingUnderrun"); - } - return _S8("CorruptState"); - #else - return _S8(""); - #endif - } - - -RMdaDevSound::CBody::TState &RMdaDevSound::CBody::TState::operator=(TStateEnum aNewState) - { - if(iState != aNewState) - { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound state %s -> %s", Name(), TState(aNewState).Name()); - #endif - iState = aNewState; - } - return *this; - } - RMdaDevSound::CBody* RMdaDevSound::CBody::NewL() { CBody* self = new(ELeave) CBody(); @@ -83,26 +49,21 @@ RMdaDevSound::CBody::~CBody() { - for(TInt i = 0; i < KPlaySharedChunkBuffers; i++) + for(TInt i = 0; i < KNumPlayers; i++) { delete iPlayers[i]; iPlayers[i] = NULL; } - delete iRecorder; - iRecorder = NULL; - delete iPlayFormatData.iConverter; - delete iRecordFormatData.iConverter; - iPlayChunk.Close(); + iBufferRemaining.Close(); + delete iPlayData.iConverter; + delete iRecordData.iConverter; + iChunk.Close(); iPlaySoundDevice.Close(); - iRecordChunk.Close(); iRecordSoundDevice.Close(); - iConvertedPlayData.Close(); - iSavedTrailingData.Close(); - iBufferedRecordData.Close(); } RMdaDevSound::CBody::CBody() - :iState(ENotReady), iBufferOffset(-1) + :iState(ENotReady), iBufferIndex(-1), iBufferOffset(-1), iSecondPhaseData(0,0) { } @@ -138,17 +99,13 @@ { User::Leave(err); } - for(TInt i=0; iCancel(); - } - - iBufferOffset = -1; - iBufferLength = 0; - - return; + iPlaySoundDevice.CancelPlayData(); + iPauseDeviceDriverOnNewData = EFalse; + SoundDeviceError(KErrNone);//cancel the players } TInt RMdaDevSound::CBody::RecordLevel() @@ -294,135 +184,64 @@ { __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::CancelRecordData: state %s", iState.Name()); + RDebug::Print(_L("RMdaDevSound::CBody::CancelRecordData:")); #endif - - // Stop recorder object (and its request) - iRecorder->Cancel(); - - // Stop driver from recording - iRecordSoundDevice.CancelRecordData(); - - // If there is a client request, cancel it - if(iClientRecordStatus) - { - User::RequestComplete(iClientRecordStatus, KErrNone); // Call also sets iClientPlayStatus to NULL - } - - iState = EStopped; - return; + iRecordSoundDevice.CancelRecordData(); + SoundDeviceError(KErrNone); } void RMdaDevSound::CBody::FlushRecordBuffer() { __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Print(_L("RMdaDevSound::CBody::FlushRecordBuffer - implemented by calling PauseRecordBuffer")); + RDebug::Print(_L("RMdaDevSound::CBody::FlushRecordBuffer")); #endif - - PauseRecordBuffer(); + + if(iState == ERecording) + { + iRecordSoundDevice.Pause();//this is equivalent call in the new sound driver + } } TInt RMdaDevSound::CBody::BytesPlayed() { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::BytesPlayed %s", iState.Name()); - #endif - - return I64LOW(BytesPlayed64()); - } - - -TUint64 RMdaDevSound::CBody::BytesPlayed64() - { __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); - - TUint64 currentBytesPlayed = KMaxTUint64; - - switch(iState) - { - case ENotReady: - Panic(EDeviceNotOpened); - break; - - case EStopped: - currentBytesPlayed = iBytesPlayed; - break; - - case ERecording: - case ERecordingPausedInHw: - case ERecordingPausedInSw: - Panic(EBadState); - break; - - case EPlayingPausedInHw: // ie. Play request pending on h/w and paused - // Paused, so use pause time - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("EPlayingPausedInHw: iPausedBytes %x %x", I64HIGH(iPausedBytesPlayed), I64LOW(iPausedBytesPlayed)); - #endif - currentBytesPlayed = iPausedBytesPlayed; - break; + TInt currentBytesPlayed = 0; +#ifdef SYMBIAN_SOUNDADAPTER_BYTESPLAYED + if(iTimerActive) + { + TUint32 endTime = User::FastCounter(); + TUint timePlayed = 0; + if(endTime= iStartTime) ? (curTime-iStartTime) : (KMaxTUint32 - (iStartTime-curTime)); - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("iStartTime %d curTime %d extraPlayTime %d", iStartTime, curTime, extraPlayTime); - - RDebug::Printf("iPlayFormatData.iSampleRate %d KBytesPerSample %d iNTickPeriodInUsec %d", - iPlayFormatData.iSampleRate, KBytesPerSample, iNTickPeriodInUsec); + currentBytesPlayed = iBytesPlayed+I64LOW(bytesPlayed); + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Printf("EstimatedBytesPlayed[%d] Driver's bytesPlayed[%d]", currentBytesPlayed, iBytesPlayed); #endif - TUint32 extraBytesPlayed = TUint32((TUint64(extraPlayTime) * iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample)/1000); - if(extraBytesPlayed > curRequestSize) - { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("caping extraBytes played from %d to %d", extraBytesPlayed, curRequestSize); - #endif - extraBytesPlayed = curRequestSize; - } - - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("iBytesPlayed %d extraBytesPlayed %d (curRequestSize %d) -> currentBytesPlayed %x %x", - iBytesPlayed, extraBytesPlayed, curRequestSize, I64HIGH(currentBytesPlayed), I64LOW(currentBytesPlayed)); - #endif - - currentBytesPlayed = iBytesPlayed + extraBytesPlayed; - break; } + else + { + currentBytesPlayed = iPlaySoundDevice.BytesTransferred(); + } - default: - Panic(EBadState); - break; - } - - - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("iPlayFormatData.iConverter %x", iPlayFormatData.iConverter); - #endif - - if (iPlayFormatData.iConverter) +#else + currentBytesPlayed = iPlaySoundDevice.BytesTransferred(); +#endif + if (iPlayData.iConverter) { // need to scale bytes played to fit with requested rate and channels, not actual - if (iPlayFormatData.iActualChannels != iPlayFormatData.iRequestedChannels) + if (iPlayData.iActualChannels != iPlayData.iRequestedChannels) { - if (iPlayFormatData.iActualChannels == 2) + if (iPlayData.iActualChannels == 2) { // requested was mono, we have stereo currentBytesPlayed /= 2; @@ -433,307 +252,99 @@ currentBytesPlayed *= 2; } } - if (iPlayFormatData.iSampleRate != iPlayFormatData.iActualRate) + if (iPlayData.iSampleRate != iPlayData.iActualRate) { - currentBytesPlayed = TUint64(currentBytesPlayed* - TReal(iPlayFormatData.iSampleRate)/TReal(iPlayFormatData.iActualRate)); // don't round + currentBytesPlayed = TInt(currentBytesPlayed* + TReal(iPlayData.iSampleRate)/TReal(iPlayData.iActualRate)); // don't round } } - - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("currentBytesPlayed %x %x", I64HIGH(currentBytesPlayed), I64LOW(currentBytesPlayed)); - #endif return currentBytesPlayed; } void RMdaDevSound::CBody::ResetBytesPlayed() { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::ResetBytesPlayed %s", iState.Name()); - #endif __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); - iBytesPlayed = 0; - iPlaySoundDevice.ResetBytesTransferred(); - return; + return iPlaySoundDevice.ResetBytesTransferred(); } void RMdaDevSound::CBody::PausePlayBuffer() { -#ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::PausePlayBuffer %s", iState.Name()); -#endif - switch(iState) + if (iState == EPlaying) { - case ENotReady: - Panic(EDeviceNotOpened); - break; - - case EStopped: - // Driver is not playing so pause in s/w - break; - - case ERecording: - case ERecordingPausedInHw: - case ERecordingPausedInSw: - Panic(EBadState); - break; - - case EPlayingPausedInHw: // ie. Play request pending on h/w and paused - case EPlayingPausedInSw: // ie. Driver not playing or paused - // Already paused so nothing to do. - break; - case EPlayingUnderrun: - iState = EPlayingPausedInSw; - break; - - case EPlaying: + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("RMdaDevSound::CBody::PausePlayBuffer() offset = %d length = %d"), iBufferOffset, iBufferLength); + #endif + __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); + // If iPlayerStatus is NULL, we're not playing currently any data, and the device driver won't pause properly. In this case, + // we set a reminder to ourselves to pause the driver once we have data later + if (iPlayerStatus == NULL) { - iPauseTime = CurrentTimeInMsec(); - iPausedBytesPlayed = BytesPlayed64(); + iPauseDeviceDriverOnNewData = ETrue; + } + else + { TInt res = iPlaySoundDevice.Pause(); #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("iPlaySoundDevice.Pause res = %d", res); + RDebug::Printf("iPlaySoundDevice.Pause res = %d", res); #endif - if(res == KErrNone) - { - iState = EPlayingPausedInHw; - } - else - { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("msp PausePlayBuffer hw pause unexpectedly failed, doing sw pause"); - #endif - iState = EPlayingPausedInSw; - } - break; + (void)res; } - - default: - Panic(EBadState); - break; - } - - return; + iState = EPaused; + iTimerActive = EFalse; + } } void RMdaDevSound::CBody::ResumePlaying() { + __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); + iPauseDeviceDriverOnNewData = EFalse; #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::ResumePlaying %s", iState.Name()); + RDebug::Printf("RMdaDevSound::CBody::ResumePlaying"); #endif - __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); - - switch(iState) + if (iFlushCalledDuringPause) { - case ENotReady: - Panic(EDeviceNotOpened); - break; - - case EStopped: - // No change - break; - - case ERecording: - case ERecordingPausedInHw: - case ERecordingPausedInSw: - Panic(EBadState); - break; - - case EPlaying: - // No change - break; - - case EPlayingPausedInHw: // ie. Play request pending on h/w and paused - { - // Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing). - iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; - - TInt res = iPlaySoundDevice.Resume(); -#ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("ResumePlayBuffer EPlayingPausedInHw res = %d", res); -#endif - if(res == KErrNone) - { - // Resume ok so a pending request will complete - iState = EPlaying; - // Update iStartTime to allow for time spent paused - TUint32 curTime = CurrentTimeInMsec(); - TUint32 timePaused = (curTime >= iPauseTime) ? (curTime-iPauseTime) : (KMaxTUint32 - (iPauseTime-curTime)); - iStartTime += timePaused; // nb. It is harmless if this wraps. - } - else - { - // Resume failed, therefore driver is not playing - // No need to update iStartTime/iPauseTime because these are only used within a driver request - // Change state to Stopped - iState = EStopped; - // Attempt to start a new (pending) request. - StartPlayersAndUpdateState(); - } - break; - } - case EPlayingPausedInSw: // ie. Driver not playing/paused - { - // Driver not playing - // Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing). - iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; - // No need to update iStartTime/iPauseTime because these are only used within a driver request - // Change state to Stopped - iState = EStopped; - // Attempt to start a new (pending) request. - StartPlayersAndUpdateState(); - break; - } - case EPlayingUnderrun: - break; - - default: - Panic(EBadState); - break; + // if we resume having called flush, we can't just keep going as the driver does not work + // that way. Instead we cancel so that buffer play completes and a new buffer will be passed + iFlushCalledDuringPause = EFalse; + PlayCancelled(); } - - return; + else + { + iState = EPlaying; + iPlaySoundDevice.Resume(); + } } void RMdaDevSound::CBody::PauseRecordBuffer() { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::PauseRecordBuffer %s", iState.Name()); - #endif - - switch(iState) - { - case ENotReady: - Panic(EDeviceNotOpened); - break; - - case EStopped: - // Driver is not recording so pause in s/w - // Do not pause because that will cause problems when CAudioDevice::Pause calls - break; - - case ERecording: - { - TInt res = iRecordSoundDevice.Pause(); - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("PauseRecordBuffer EPlaying res = %d", res); - #endif - if(res == KErrNone) - { - iState = ERecordingPausedInHw; - } - else - { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("PauseRecordBuffer hw pause unexpectedly failed, doing sw pause"); - #endif - iState = ERecordingPausedInSw; - } - break; - } - - case ERecordingPausedInHw: - case ERecordingPausedInSw: - // Already paused so nothing to do. - break; - - case EPlaying: - case EPlayingPausedInHw: // ie. Play request pending on h/w and paused - Panic(EBadState); - break; - - case EPlayingPausedInSw: - // This is an ugly hack to maintain compatibility with CAudioDevice::Pause which - // calls both PausePlayBuffer and PauseRecordBuffer whilst in stopped, then later calls ResumePlaying - break; - case EPlayingUnderrun: // ie. Play request pending on h/w and paused - Panic(EBadState); - break; - - default: - Panic(EBadState); - break; - } - - return; + if(iState == ERecording) + { + __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Printf("RMdaDevSound::CBody::PauseRecordBuffer"); + #endif + iRecordSoundDevice.Pause(); + } } void RMdaDevSound::CBody::ResumeRecording() { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::ResumeRecording %s", iState.Name()); - #endif __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); - - switch(iState) - { - case ENotReady: - Panic(EDeviceNotOpened); - break; - - case EStopped: - // No change - break; - - case ERecording: - // No change - break; - - case ERecordingPausedInHw: - { - TInt res = iRecordSoundDevice.Resume(); - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("ResumeRecordBuffer ERecordingPausedInHw res = %d", res); - #endif - if(res == KErrNone) - { - // Resume ok so a pending request will complete - iState = ERecording; - } - else - { - iState = EStopped; - // Resume failed, so attempt to start a new (pending) request. - // If this works, it will update the state to ERecording. - StartRecordRequest(); - } - break; - } - case ERecordingPausedInSw: - { - // Update state to stopped and attempt to start any pending request - iState = EStopped; - // If this works, it will update the state to ERecording. - StartRecordRequest(); - break; - } - - case EPlaying: - case EPlayingPausedInHw: // ie. Play request pending on h/w and paused - case EPlayingPausedInSw: // ie. Driver not playing/paused - case EPlayingUnderrun: - default: - Panic(EBadState); - break; - } - - return; - - + iRecordSoundDevice.Resume(); } TInt RMdaDevSound::CBody::GetTimePlayed(TTimeIntervalMicroSeconds& aTimePlayed) { __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); - - - TUint64 bytesPlayed = BytesPlayed64(); + TTimeIntervalMicroSecondsBuf aTimePlayedBuf; + TInt err; + err = iPlaySoundDevice.TimePlayed(aTimePlayedBuf); + if (err == KErrNone) + { + aTimePlayed = aTimePlayedBuf(); + } - TUint64 timePlayed = 1000 * 1000 * bytesPlayed / (iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample); - - aTimePlayed = TTimeIntervalMicroSeconds(timePlayed); - - return KErrNone; + return err; } @@ -794,138 +405,6 @@ aFormat().iRate = aFormatData.iSampleRate; } -void RMdaDevSound::CBody::StartPlayersAndUpdateState() - { - __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); - - switch(iState) - { - case ENotReady: - Panic(EDeviceNotOpened); - break; - - case EStopped: - // Allow following code to queue more driver play requests and check for stopped - break; - - case ERecording: - case ERecordingPausedInHw: - case ERecordingPausedInSw: - Panic(EBadState); - break; - - case EPlaying: - // Allow following code to queue more driver play requests and check for stopped - break; - case EPlayingPausedInHw: // ie. Play request pending on h/w and paused - // Allow following code to queue more driver play requests - break; - - case EPlayingPausedInSw: - // Paused but driver not playing+paused, therefore do not queue new requests until ResumePlaying - return; - case EPlayingUnderrun: - break; - - default: - Panic(EBadState); - break; - } - - // iState is now either EStopped, EPlaying or EPlayingPausedInHw - __ASSERT_DEBUG(((iState == EStopped) || (iState == EPlaying) || (iState == EPlayingPausedInHw) || (iState == EPlayingUnderrun)), Panic(EBadState)); - - while( (iClientPlayData.Length() != 0) && (! iFreePlayers.IsEmpty())) - { - // More data to play and more players, so issue another request - - bool wasIdle = iFreePlayers.IsFull(); - // Get a free player - CPlayer *player = iFreePlayers.Pop(); - // Calculate length of request - TUint32 lengthToPlay = iClientPlayData.Length(); - if(lengthToPlay > iDeviceBufferLength) - { - lengthToPlay = iDeviceBufferLength; - } - - // Remember request length, so we can update bytes played when it finishes - iActivePlayRequestSizes.Push(lengthToPlay); - - // Find offset to copy data to - TUint playerIndex = player->GetPlayerIndex(); - ASSERT(playerIndex < KPlaySharedChunkBuffers); - TUint chunkOffset = iPlayBufferConfig.iBufferOffsetList[playerIndex]; - - // Copy data - TPtr8 destPtr(iPlayChunk.Base()+ chunkOffset, 0, iDeviceBufferLength); - destPtr.Copy(iClientPlayData.Mid(0, lengthToPlay)); - - // Update iClientPlayData to remove the data just queued - iClientPlayData.Set(iClientPlayData.Right(iClientPlayData.Length()-lengthToPlay)); - - // Start the CPlayer - player->PlayData(chunkOffset, lengthToPlay); - if(wasIdle) - { - iState = EPlaying; - iStartTime = CurrentTimeInMsec(); - - } - - } - - // Check if the client request is now complete - if(iClientPlayData.Length() == 0 && iClientPlayStatus) - { - // We have queued all the client play data to the driver so we can now complete the client request. - // If actual playback fails, we will notify the client via the Play Error notification mechanism. - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState completing client request"); - #endif - User::RequestComplete(iClientPlayStatus, KErrNone); // This call also sets iClientPlayStatus to NULL - } - - //nb. iState is now either EStopped, EPlaying or EPlayingPausedInHw (see previous switch and assert) - if(iState != EPlayingPausedInHw) - { - if(iFreePlayers.IsFull()) - { - // Free fifo is full, therefore there are no active players - iState = EPlayingUnderrun; - if(! iUnderFlowReportedSinceLastPlayOrRecordRequest) - { - // We report KErrUnderflow if we have not already reported it since the last PlayData call. - // Note that - // i) We do NOT report driver underflows. - // ii) We report underflow when we run out of data to pass to the driver. - // iii) We throttle this reporting - // iv) We WILL report KErrUnderflow if already stopped and asked to play a zero length buffer - // The last point is required because the client maps a manual stop command into a devsound play with a - // zero length buffer and the last buffer flag set, this in turn is mapped to a Playdata calll with an empty buffer - // which is expected to complete ok and cause a KErrUnderflow error to be reported... - iUnderFlowReportedSinceLastPlayOrRecordRequest = ETrue; - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState stopped and iUnderFlowReportedSinceLastPlayOrRecordRequest false so raising KErrUnderflow"); - #endif - - // Discard any saved trailing data (ie. data saved due driver requiring all requests to be a multiple of iRequestMinSize). - // This maybe because client is delibrately letting us underflow to play silence. In that case we do not want to - // play the trailing data at the beginning of the new data issued after the silence... - iSavedTrailingData.SetLength(0); - - SoundDeviceError(KErrUnderflow); - } - } - else - { - // Free fifo not full, therefore there are active players - iState = EPlaying; - } - } - return; - } - TInt RMdaDevSound::CBody::SetFormat(const TCurrentSoundFormatBuf& aFormat, RSoundSc& aSoundDevice, TFormatData &aFormatData) @@ -935,7 +414,6 @@ delete aFormatData.iConverter; aFormatData.iConverter = NULL; // setting this to NULL indicates we are not using converter. No other flag - iConvertedPlayData.Close(); TInt wantedRate = aFormat().iRate; for(TInt index = 0; index < KNumSampleRates; index++ ) @@ -951,11 +429,6 @@ if(err == KErrNone) { - // Assume, for now, we support the requested channels and rate - aFormatData.iActualChannels = aFormatData.iRequestedChannels; - aFormatData.iActualRate = aFormatData.iSampleRate; - - // Attempt to configure driver formatBuf().iChannels = aFormat().iChannels; formatBuf().iEncoding = ESoundEncoding16BitPCM; formatBuf().iDataFormat = ESoundDataFormatInterleaved; @@ -1155,86 +628,11 @@ aFormatData.iRequestedChannels)); } } - if(err != KErrNone) - { - delete aFormatData.iConverter; - aFormatData.iConverter= NULL; - iConvertedPlayData.Close(); - } return err; } -void RMdaDevSound::CBody::StartRecordRequest() - { - __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); - iRecorder->RecordData(iBufferLength); - } - -// Note both InRecordMode and InPlayMode return EFalse for ENotReady and EStopped -TBool RMdaDevSound::CBody::InRecordMode()const - { - switch(iState) - { - case ENotReady: - case EStopped: - return EFalse; - - case ERecording: - case ERecordingPausedInHw: - case ERecordingPausedInSw: - return ETrue; - - case EPlaying: - case EPlayingPausedInHw: - case EPlayingPausedInSw: - case EPlayingUnderrun: - return EFalse; - - default: - Panic(EBadState); - break; - } - return EFalse; - } - -TBool RMdaDevSound::CBody::InPlayMode() const - { - switch(iState) - { - case ENotReady: - case EStopped: - return EFalse; - - case ERecording: - case ERecordingPausedInHw: - case ERecordingPausedInSw: - return EFalse; - - case EPlaying: - case EPlayingPausedInHw: - case EPlayingPausedInSw: - case EPlayingUnderrun: - return ETrue; - - default: - Panic(EBadState); - break; - } - - return EFalse; - } - - -TUint32 RMdaDevSound::CBody::CurrentTimeInMsec() const - { - TUint64 tmp = User::NTickCount(); - tmp *= iNTickPeriodInUsec; - tmp /= 1000; - return TUint32(tmp); - } - void RMdaDevSound::CBody::PlayFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported) { __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); @@ -1244,13 +642,13 @@ void RMdaDevSound::CBody::GetPlayFormat(TCurrentSoundFormatBuf& aFormat) { __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); - GetFormat(aFormat, iPlaySoundDevice, iPlayFormatData); + GetFormat(aFormat, iPlaySoundDevice, iPlayData); } TInt RMdaDevSound::CBody::SetPlayFormat(const TCurrentSoundFormatBuf& aFormat) { __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); - return SetFormat(aFormat, iPlaySoundDevice, iPlayFormatData); + return SetFormat(aFormat, iPlaySoundDevice, iPlayData); } void RMdaDevSound::CBody::RecordFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported) @@ -1262,13 +660,13 @@ void RMdaDevSound::CBody::GetRecordFormat(TCurrentSoundFormatBuf& aFormat) { __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); - GetFormat(aFormat, iRecordSoundDevice, iRecordFormatData); + GetFormat(aFormat, iRecordSoundDevice, iRecordData); } TInt RMdaDevSound::CBody::SetRecordFormat(const TCurrentSoundFormatBuf& aFormat) { __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); - return SetFormat(aFormat, iRecordSoundDevice, iRecordFormatData); + return SetFormat(aFormat, iRecordSoundDevice, iRecordData); } void RMdaDevSound::CBody::Close() @@ -1276,169 +674,80 @@ #ifdef SYMBIAN_SOUNDADAPTER_DEBUG RDebug::Printf("void RMdaDevSound::CBody::Close() started"); #endif + iBufferIndex = -1; iBufferOffset = -1; iBufferLength = 0; - - if(iPlaySoundDevice.Handle() != KNullHandle) - { - // Make sure all player objects are idle - CancelPlayData(); - iPlayChunk.Close(); - iPlaySoundDevice.Close(); - } - - if(iRecordSoundDevice.Handle() != KNullHandle) - { - CancelRecordData(); - iRecordChunk.Close(); - iRecordSoundDevice.Close(); - } - + iCurrentPlayer = 0; + iTimerActive = EFalse; + iChunk.Close(); + iPlaySoundDevice.Close(); + iRecordSoundDevice.Close(); iState = ENotReady; #ifdef SYMBIAN_SOUNDADAPTER_DEBUG RDebug::Printf("void RMdaDevSound::CBody::Close() ended"); #endif } - + TInt RMdaDevSound::CBody::Handle() {//This method is actually used to check whether the device is opened. Below logic should work if(iPlaySoundDevice.Handle()) { return iPlaySoundDevice.Handle(); } - if(iRecordSoundDevice.Handle()) - { - return iRecordSoundDevice.Handle(); - } return 0; } - void RMdaDevSound::CBody::PlayData(TRequestStatus& aStatus, const TDesC8& aData) { #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::PlayData(0x%x,%d) State=%s Handle=%d.",&aStatus, - aData.Length(), iState.Name(), iPlayChunk.Handle()); + RDebug::Printf("RMdaDevSound::CBody::PlayData(0x%x,%d) State=%d Current=%d. Handle=%d.",&aStatus, + aData.Length(), iState, iCurrentPlayer, iChunk.Handle()); + RDebug::Printf("KPlayMaxSharedChunkBuffersMask=0x%x KNumPlayersMask=0x%x", + KPlayMaxSharedChunkBuffersMask, KNumPlayersMask); #endif - __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); aStatus = KRequestPending; - - if((iClientPlayStatus != NULL) || InRecordMode()) + iPlayerStatus = &aStatus;//store the status of datapath player + //No support for simultaneous play and record in RMdaDevSound + if(iState == ERecording) { - // We only support one outstanding request - // No support for simultaneous play and record in RMdaDevSound - TRequestStatus *pRequest = &aStatus; - User::RequestComplete(pRequest, KErrInUse); + SoundDeviceError(KErrInUse); return; } - iClientPlayStatus = &aStatus;//store the status of datapath player - - if(iPlayFormatData.iConverter || iSavedTrailingData.Length() != 0) - { - // Need a conversion buffer - // Needs to hold any trailing data truncated from the previous request (due - // to alignment requirements) and either the new data, or the new rate adapted data - TUint32 spaceRequired = iSavedTrailingData.Length(); - if(iPlayFormatData.iConverter) - { - // Doing rate conversion so also need space for the converted data - spaceRequired += iPlayFormatData.iConverter->MaxConvertBufferSize(aData.Length()); - } - else - { - // Not doing rate adaptation therefore only need to allow for the new incoming data - spaceRequired += aData.Length(); - } - // Check if existing buffer exists and is big enough - if(iConvertedPlayData.MaxLength() < spaceRequired) - { - iConvertedPlayData.Close(); - TInt err = iConvertedPlayData.Create(spaceRequired); - if(err) - { - User::RequestComplete(iClientPlayStatus, err); - return; - } - } - - // Truncate iConvertedPlayData and copy in saved trailing data (if any) - iConvertedPlayData = iSavedTrailingData; - iSavedTrailingData.SetLength(0); - - // Now append rate adapted data or incoming data - if (iPlayFormatData.iConverter) - { - // The convertor will panic if it fails to convert any data, therefore - // we avoid passing it an empty source buffer - if(aData.Length() != 0) - { - TPtr8 destPtr((TUint8 *)iConvertedPlayData.Ptr()+iConvertedPlayData.Length(), 0, iConvertedPlayData.MaxLength()-iConvertedPlayData.Length()); - TInt len = iPlayFormatData.iConverter->Convert(aData, destPtr); - iConvertedPlayData.SetLength(iConvertedPlayData.Length() + destPtr.Length()); - if(len != aData.Length()) - { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::PlayData converted %d but expected to convert %d", len, aData.Length()); - #endif - } - } - } - else - { - iConvertedPlayData.Append(aData); - } - iClientPlayData.Set(iConvertedPlayData); - } - else - { - // Do not need a conversion buffer so just aim the descriptor at the data - iClientPlayData.Set(aData); - } - iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; - - // All driver requests must be an exact multiple of iRequestMinSize - TUint32 trailingDataLen = iClientPlayData.Length() % iRequestMinSize; - if(trailingDataLen) - { - // Not a multiple of iRequestMinSize, so need to truncate current request, and save trailing bytes for - // inclusion at the beginning of the next request - iSavedTrailingData = iClientPlayData.Right(trailingDataLen); - iClientPlayData.Set(iClientPlayData.Left(iClientPlayData.Length()-trailingDataLen)); - } - - #ifdef SYMBIAN_FORCE_32BIT_LENGTHS - if (iClientPlayData.Length()%4 != 0) - { - // simulate the limitation of some hardware, where -6 is generated if the - // buffer length is not divisible by 4. - TRequestStatus *pRequest = &aStatus; - User::RequestComplete(pRequest, KErrArgument); - } - #endif - - iRecordChunk.Close(); - if(!iPlayChunk.Handle()) + const TDes8* data = static_cast(&aData); + + if(!iChunk.Handle()) { //This is where we setup to play. //Configure the shared chunk for two buffers with iBufferSize each - iPlayBufferConfig.iNumBuffers = KPlaySharedChunkBuffers; - iDeviceBufferLength = KPlaySharedChunkBufferSize; - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("iDeviceBufferLength %d", iDeviceBufferLength); - #endif - iPlayBufferConfig.iFlags = 0;//data will be continuous + iBufferConfig.iNumBuffers = KPlayMaxSharedChunkBuffers; + iDeviceBufferLength = data->MaxLength(); + iBufferConfig.iFlags = 0;//data will be continuous // If required, use rate converter etc - iPlayBufferConfig.iBufferSizeInBytes = iDeviceBufferLength; + if (iPlayData.iConverter) + { + iDeviceBufferLength = iPlayData.iConverter->MaxConvertBufferSize(iDeviceBufferLength, ETrue); + } + iBufferConfig.iBufferSizeInBytes = iDeviceBufferLength; #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("number of buffers: [%d]",iPlayBufferConfig.iNumBuffers); - RDebug::Printf("BufferSize in Bytes [%d]",iPlayBufferConfig.iBufferSizeInBytes); + RDebug::Printf("number of buffers: [%d]",iBufferConfig.iNumBuffers); + RDebug::Printf("BufferSize in Bytes [%d]",iBufferConfig.iBufferSizeInBytes); #endif - TPckg bufferConfigBuf(iPlayBufferConfig); - TInt error = iPlaySoundDevice.SetBufferChunkCreate(bufferConfigBuf,iPlayChunk); + TPckg bufferConfigBuf(iBufferConfig); + TInt error = iPlaySoundDevice.SetBufferChunkCreate(bufferConfigBuf,iChunk); if(error == KErrNone) { iPlaySoundDevice.GetBufferConfig(bufferConfigBuf); + TSoundFormatsSupportedV02Buf modnumber; + iPlaySoundDevice.Caps(modnumber); + TInt minBufferSize = KMinBufferSize; + #ifdef SYMBIAN_FORCE_32BIT_LENGTHS + minBufferSize = Max(minBufferSize, 4); // force to 32-bit buffer align + #endif + iRequestMinSize = Max(modnumber().iRequestMinSize, minBufferSize); + error = iBufferRemaining.Create(iRequestMinSize); + // work out mask so that x&iRequestMinMask is equiv to x/iRequestMinSize*iRequestMinSize + iRequestMinMask = ~(iRequestMinSize-1); // assume iRequestMinSize is power of 2 } if (error) { @@ -1446,43 +755,114 @@ return; } } + + iBufferIndex = (iBufferIndex+1) & KPlayMaxSharedChunkBuffersMask; + + TPtr8 dataPtr(iChunk.Base()+ iBufferConfig.iBufferOffsetList[iBufferIndex], 0, iDeviceBufferLength); - StartPlayersAndUpdateState(); + __ASSERT_DEBUG(!(iBufferRemaining.Length()>0 && iPlayData.iConverter), + Panic(EPanicPartialBufferConverterNotSupported)); // can't deal with conversion with restrictions on buffer size + + if (iBufferRemaining.Length() != 0) + { + // This checks if any data was left over from last times rounding and adds it to the dataPtr + dataPtr.Copy(iBufferRemaining); + iBufferRemaining.SetLength(0); + } + + if (iPlayData.iConverter) + { + iPlayData.iConverter->Convert(aData, dataPtr); + ASSERT(iSecondPhaseData.Length()==0); // assume this below, so check + ASSERT(iBufferRemaining.Length()==0); + } + else + { + TInt dataLength = aData.Length(); + + TInt lengthAlreadyInDeviceBuffer = dataPtr.Length(); + TInt desiredDeviceBufferLength = (lengthAlreadyInDeviceBuffer + dataLength) & iRequestMinMask; + if (desiredDeviceBufferLength > dataPtr.MaxLength()) + { + // the buffer would be two long to do in one go, so do as two phases + desiredDeviceBufferLength = (lengthAlreadyInDeviceBuffer + (dataLength/2)) & iRequestMinMask; + } - return; + TInt lengthToCopy = desiredDeviceBufferLength - lengthAlreadyInDeviceBuffer; + lengthToCopy = Max(lengthToCopy, 0); // ensure lengthToCopy is not negative + + TInt remainingToBeCopied = dataLength - lengthToCopy; + TInt secondPhaseLength = remainingToBeCopied & iRequestMinMask; + TInt remainingForNextRun = remainingToBeCopied - secondPhaseLength; + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Printf("dataLength: [%d]",dataLength); + RDebug::Printf("lengthAlreadyInDeviceBuffer: [%d]",lengthAlreadyInDeviceBuffer); + RDebug::Printf("desiredDeviceBufferLength: [%d]",desiredDeviceBufferLength); + RDebug::Printf("lengthToCopy: [%d]",lengthToCopy); + RDebug::Printf("remainingToBeCopied: [%d]",remainingToBeCopied); + RDebug::Printf("secondPhaseLength: [%d]",secondPhaseLength); + RDebug::Printf("remainingForNextRun: [%d]",remainingForNextRun); + #endif + dataPtr.Append(aData.Left(lengthToCopy)); + iSecondPhaseData.Set(aData.Mid(lengthToCopy, secondPhaseLength)); + iBufferRemaining.Copy(aData.Mid(lengthToCopy + secondPhaseLength, remainingForNextRun)); + iHaveSecondPhaseData = secondPhaseLength>0; + } + + if(iState == EOpened || iState == EPlayBuffersFlushed) + { + if(dataPtr.Length() > 0 && iSecondPhaseData.Length()==0) + { + // Note: if we have identified second phase, don't call BufferEmptied() here as we can't cope with a new PlayData() call + //Make sure that next player do not overtake the current player, especially when recovering from underflow + TInt otherPlayer = (iCurrentPlayer+1) & KNumPlayersMask; + iPlayers[otherPlayer]->Deque(); + CActiveScheduler::Add(iPlayers[otherPlayer]); + //Beginning we need to give two play requests for an uninterrupted playback with the new driver + BufferEmptied(); + } + iState = EPlaying; + } + #ifdef _DEBUG + TInt cachePlayer = iCurrentPlayer; // TODO: remove + #endif + iPlayers[iCurrentPlayer]->PlayData(iBufferConfig.iBufferOffsetList[iBufferIndex], dataPtr.Length()); + ASSERT(iCurrentPlayer==cachePlayer); // check have not changed since previous calc + iCurrentPlayer = (iCurrentPlayer+1) & KNumPlayersMask; + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Printf("RMdaDevSound::CBody::PlayData() Exit. Current=%d, Handle=%d.", + iCurrentPlayer, iChunk.Handle()); + #endif } void RMdaDevSound::CBody::RecordData(TRequestStatus& aStatus, TDes8& aData) { __ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened)); aStatus = KRequestPending; - if((iClientPlayStatus != NULL) || InPlayMode()) + iRecorderStatus = &aStatus; + //No support for simultaneous play and record in RMdaDevSound + if(iState == EPlaying) { - // We only support one outstanding request - // No support for simultaneous play and record in RMdaDevSound - TRequestStatus *pRequest = &aStatus; - User::RequestComplete(pRequest, KErrInUse); + SoundDeviceError(KErrInUse); return; } - iClientRecordStatus = &aStatus; - iClientRecordData = &aData; - iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse; - iPlayChunk.Close(); - if(!iRecordChunk.Handle()) + iData = &aData; + + if(!iChunk.Handle()) { //Configure the shared chunk for two buffers with iBufferSize each iRecordBufferConfig.iNumBuffers = KRecordMaxSharedChunkBuffers; - iDeviceBufferLength = KRecordSharedChunkBufferSize; // initial size - resize if needs be - if (iRecordFormatData.iConverter) + iDeviceBufferLength = iData->MaxLength(); // initial size - resize if needs be + if (iRecordData.iConverter) { // if number of channels used differs from request, resize buffer // assume we have nice rounded values for buffer. - if (iRecordFormatData.iActualChannels>iRecordFormatData.iRequestedChannels) + if (iRecordData.iActualChannels>iRecordData.iRequestedChannels) { iDeviceBufferLength *= 2; // will record at stereo and convert to mono } - else if (iRecordFormatData.iActualChannels bufferConfigBuf(iRecordBufferConfig); - TInt error = iRecordSoundDevice.SetBufferChunkCreate(bufferConfigBuf,iRecordChunk); + TInt error = iRecordSoundDevice.SetBufferChunkCreate(bufferConfigBuf,iChunk); if(error == KErrNone) { iRecordSoundDevice.GetBufferConfig(bufferConfigBuf); @@ -1505,130 +885,151 @@ #ifdef SYMBIAN_SOUNDADAPTER_DEBUG RDebug::Printf("RMdaDevSound::CBody::RecordData,iBufferOffset[%d]",iBufferOffset); #endif - - switch(iState) - { - case ENotReady: - Panic(EBadState); - break; - - case EStopped: - case ERecording: - // Either idle or recording is in progress, therefore we can issue another request - StartRecordRequest(); - break; - - case ERecordingPausedInHw: - // Driver is paused, therefore we can issue a request which will immediately return buffered data - // or be aborted (in the driver) with KErrCancelled if there is no more data). nb. That KErrCancelled should not be - // returned to the client because the old RMdaDevSound driver would have completed with KErrNone and zero data length. - StartRecordRequest(); - break; - - case ERecordingPausedInSw: - // Paused in s/w but driver is not paused, therefore can not issue a new request to driver because - // it would re-start recording. - // This implies we were paused whilst the h/w was not recording, so there is no buffered data. - - // Complete the request with KErrNone and no data. - iClientRecordData->SetLength(0); - User::RequestComplete(iClientRecordStatus, KErrNone); - break; - - case EPlaying: - case EPlayingPausedInHw: - case EPlayingPausedInSw: - case EPlayingUnderrun: - Panic(EBadState); - break; - - default: - Panic(EBadState); - break; - } + iPlayers[iCurrentPlayer]->RecordData(iBufferLength); } +void RMdaDevSound::CBody::SoundDeviceError(TInt aError, TInt /*aPlayerIndex*/) +// This is called by CPlayer objects when there is an error in RunL + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("RMdaDevSound::CBody::SoundDeviceError: aError[%d]"), aError); + #endif + + //When we get an underflow from one of the players and the other player is active, we are + //bound to get an underflow from the other player too. So we ignore the first and process + //the second + TInt otherPlayerIndex = (iCurrentPlayer+1) & KNumPlayersMask; + if (iPlayers[otherPlayerIndex]->IsActive() && aError==KErrUnderflow) + { + return; + } + SoundDeviceError(aError); + } /** - Notify client of error. - - Note that we continue playing/recording if possible. - - We do not maintain information which could map the error back to a particular client play/record request - and therefore we have to notify the client of error every time it happens. - - nb. A client play/record request is completed with KErrNone if it queues ok - All errors are reported via the Notify*Error - mechanism. + Note for maintainers: Please note that this method is called from + CancelPlayData and CancelRecordData with KErrNone as a parameter in order to + cancel the players and reset the internal state. */ void RMdaDevSound::CBody::SoundDeviceError(TInt aError) { #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError: Error[%d] state %s", aError, iState.Name()); + RDebug::Print(_L("RMdaDevSound::CBody::SoundDeviceError: Error[%d] CurrentPlayer[%d]"), aError, iCurrentPlayer); #endif - ASSERT(aError != KErrNone); - - if(iClientPlayErrorStatus) + for (TInt i=0; iStop(); + } + else + { + if (!iPlayers[i]->IsActive()) + { + iPlayers[i]->ResetPlayer(); + } + } + } + + iBufferRemaining.SetLength(0); + if(iPlayErrorStatus && aError!=KErrNone)//error receiver is only for errors { #ifdef SYMBIAN_SOUNDADAPTER_DEBUG RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iPlayerErrorStatus"); #endif - - User::RequestComplete(iClientPlayErrorStatus, aError); // nb call also sets iClientPlayErrorStatus to NULL + User::RequestComplete(iPlayErrorStatus, aError); + iPlayErrorStatus = NULL; } - - if(iClientRecordErrorStatus) + if(iPlayerStatus) + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iPlayerStatus"); + #endif + User::RequestComplete(iPlayerStatus, KErrNone); // effectively buffer emptied + iPlayerStatus = NULL; + } + if(iRecordErrorStatus && aError!=KErrNone) { #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iClientRecordErrorStatus"); + RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iRecordErrorStatus"); + #endif + User::RequestComplete(iRecordErrorStatus, aError); + iRecordErrorStatus = NULL; + } + else if(iRecorderStatus) + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iRecorderStatus"); #endif - User::RequestComplete(iClientRecordErrorStatus, aError); // nb call also sets iClientRecordErrorStatus to NULL + User::RequestComplete(iRecorderStatus, aError); + iRecorderStatus = NULL; } - - return; + iBufferIndex = -1; + iCurrentPlayer = 0; + iBufferOffset = -1; + iBufferLength = 0; + iTimerActive = EFalse; + iState = EOpened; } void RMdaDevSound::CBody::NotifyRecordError(TRequestStatus& aStatus) { aStatus = KRequestPending; - iClientRecordErrorStatus = &aStatus; + iRecordErrorStatus = &aStatus; } void RMdaDevSound::CBody::NotifyPlayError(TRequestStatus& aStatus) { aStatus = KRequestPending; - iClientPlayErrorStatus = &aStatus; + iPlayErrorStatus = &aStatus; } void RMdaDevSound::CBody::CancelNotifyPlayError() { - if(iClientPlayErrorStatus) + if(iPlayErrorStatus) { - User::RequestComplete(iClientPlayErrorStatus, KErrCancel); + User::RequestComplete(iPlayErrorStatus, KErrCancel); } } void RMdaDevSound::CBody::CancelNotifyRecordError() { - if(iClientRecordErrorStatus) + if(iRecordErrorStatus) { - User::RequestComplete(iClientRecordErrorStatus, KErrCancel); + User::RequestComplete(iRecordErrorStatus, KErrCancel); } - else - { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("msp BufferEmptied but iClientPlayStatus==NULL"); - #endif - } } void RMdaDevSound::CBody::FlushPlayBuffer() { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::FlushPlayBuffer calling CancelPlayData"); - #endif - CancelPlayData(); + __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened)); + //There is no equivalent of FlushPlaybuffer in the new sound driver. So use CancelPlayData + //to simulate the original behavior + if ((iState == EPlaying) || (iState == EPaused)) + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("RMdaDevSound::CBody::FlushPlayBuffers in Playing or Paused state")); + #endif + + if (iState == EPaused) + { + iFlushCalledDuringPause = ETrue; + } + + + iPlaySoundDevice.CancelPlayData(); + iBufferRemaining.SetLength(0); + iState = EPlayBuffersFlushed; + } + } + + + RSoundSc& RMdaDevSound::CBody::PlaySoundDevice() { return iPlaySoundDevice; @@ -1639,11 +1040,22 @@ return iRecordSoundDevice; } -const RMdaDevSound::CBody::TState &RMdaDevSound::CBody::State() const +RMdaDevSound::CBody::TState RMdaDevSound::CBody::State() { return iState; } +void RMdaDevSound::CBody::BufferEmptied() + { + if(iPlayerStatus) + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("***Buffer Emptied***")); + #endif + User::RequestComplete(iPlayerStatus, KErrNone); + iPlayerStatus = NULL; + } + } void RMdaDevSound::CBody::BufferFilled(TInt aBufferOffset) { @@ -1652,15 +1064,15 @@ #endif ASSERT(aBufferOffset>=0 || aBufferOffset==KErrCancel); - ASSERT(iClientRecordData); // request should not get this without + ASSERT(iData); // request should not get this without if(aBufferOffset==KErrCancel) { //we can get KErrCancel when we call pause and there is no more data left with the driver //we send the empty buffer to the HwDevice, where this should trigger the shutdown mechanism - iClientRecordData->SetLength(0); - User::RequestComplete(iClientRecordStatus, KErrNone); - iClientRecordStatus = NULL; + iData->SetLength(0); + User::RequestComplete(iRecorderStatus, KErrNone); + iRecorderStatus = NULL; return; } @@ -1669,14 +1081,14 @@ //expects that the buffer size should always be even. Base suggested that we fix in multimedia //as it is quite complicated to fix in overthere. iBufferLength = iBufferLength & 0xfffffffe; - TPtr8 dataPtr(iRecordChunk.Base()+ iBufferOffset, iBufferLength, iClientRecordData->MaxLength()); - if (iRecordFormatData.iConverter) + TPtr8 dataPtr(iChunk.Base()+ iBufferOffset, iBufferLength, iData->MaxLength()); + if (iRecordData.iConverter) { - iRecordFormatData.iConverter->Convert(dataPtr, *iClientRecordData); + iRecordData.iConverter->Convert(dataPtr, *iData); } else { - iClientRecordData->Copy(dataPtr); + iData->Copy(dataPtr); } #ifdef SYMBIAN_SOUNDADAPTER_DEBUG RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled: BufferOffset[%d] BufferLen[%d]"), iBufferOffset, iBufferLength); @@ -1685,124 +1097,52 @@ { iRecordSoundDevice.ReleaseBuffer(iBufferOffset); } - if(iClientRecordStatus) + if(iRecorderStatus) { - User::RequestComplete(iClientRecordStatus, KErrNone); - iClientRecordStatus = NULL; + User::RequestComplete(iRecorderStatus, KErrNone); + iRecorderStatus = NULL; } - else - { - RDebug::Printf("msp PlayCancelled but iClientPlayStatus==NULL"); - } } - -/* - This function is called to notify us that a CPlayer's request has completed and what its status was. - - It is important to note that:- - 1) RSoundSc driver PlayData requests are guaranteed to complete in order, oldest first - 2) If we are overloaded, it is possible for more than one request to complete before any CPlayer::RunL is ran. In - this situation the CPlayer::RunL functions, and hence this callback, maybe invoked in non-oldest first order - - but - - a) It is impossible for callback for the second oldest CPlayer to occur before the driver request for the oldest has - been complete (because of 1) - b) We will always get exactly one callback for every complete request. - - Therefore this callback notifies us of two subtly separate things:- + +void RMdaDevSound::CBody::PlayCancelled() + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("RMdaDevSound::CBody::PlayCancelled:")); + #endif - i) The oldest request has been completed (so we can reduce can increase the bytes played counter by its length - ii) CPlayer aPlayerIndex is free for re-use - - but we can not assume that aPlayerIndex is the oldest request, therefore we save the play request lengths outside of - the CPlayer object. -*/ -void RMdaDevSound::CBody::PlayRequestHasCompleted(CPlayer *aPlayer, TInt aStatus, TBool aDueToCancelCommand) - { - // CPlayer is done so put it on the free queue - iFreePlayers.Push(aPlayer); - - TUint32 bytesPlayed = iActivePlayRequestSizes.Pop(); - // Request has finished therefore now timing the following request to simulate bytes played - iStartTime = CurrentTimeInMsec(); - if(aDueToCancelCommand) - { - // Callback due to CPlayer::Cancel/DoCancel being called, therefore we - // do not want to update bytes played, process state, report a error or start new players - return; - } - - // Update iBytesPlayed by the length of the oldest request (which might not be the one that CPlayer was - // handling). - iBytesPlayed += bytesPlayed; - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("PlayRequestHasCompleted increasing iBytesPlayed by %d to %d", bytesPlayed, iBytesPlayed); - #endif - - // Process state - switch(iState) + for (TInt index=0; indexCancel(); + } + iBufferIndex = -1; + iCurrentPlayer = 0; + iBufferOffset = -1; + iBufferLength = 0; + iTimerActive = EFalse; + if(iPlayerStatus) + { + User::RequestComplete(iPlayerStatus, KErrNone); + iPlayerStatus = NULL; } - - - // If we have an error, report it to the client - // We NEVER report driver underflow, instead we report KErrUnderflow if we run out of data to pass to driver. - if( (aStatus != KErrNone) && (aStatus != KErrUnderflow) ) - { - SoundDeviceError(aStatus); - } - - // If appropriate start more players - StartPlayersAndUpdateState(); - return; + } + +void RMdaDevSound::CBody::UpdateTimeAndBytesPlayed() + { + iBytesPlayed = iPlaySoundDevice.BytesTransferred(); + iStartTime = User::FastCounter(); + iTimerActive=ETrue; + } + +TBool RMdaDevSound::CBody::TimerActive() + { + return iTimerActive; } +TBool RMdaDevSound::CBody::FlushCalledDuringPause() + { + return iFlushCalledDuringPause; + } + RMdaDevSound::CBody::CPlayer::CPlayer(TInt aPriority, RMdaDevSound::CBody& aParent, TInt aIndex): CActive(aPriority), iParent(aParent), iIndex(aIndex), iBufferOffset(-1), iBufferLength(0) { @@ -1814,82 +1154,135 @@ Cancel(); } - void RMdaDevSound::CBody::CPlayer::RunL() { #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("****RMdaDevSound::CBody::CPlayer(%d)::RunL: Error[%d] ParentState[%s]", - iIndex, iStatus.Int(), iParent.State().Name()); - RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (including this one as active)", - iParent.iActivePlayRequestSizes.Length(), - iParent.iFreePlayers.Length()); + RDebug::Print(_L("****RMdaDevSound::CBody::CPlayer(%d)::RunL: Error[%d] ParentState[%d] Outstanding[%d], pending[%d]"), + iIndex, iStatus.Int(), iParent.State(), iParent.iHaveSecondPhaseData, iRequestPending); #endif - iParent.PlayRequestHasCompleted(this, iStatus.Int(), EFalse); - return; + + //this is required to avoid receiving the request completions in the order diff. from the + //issued order + Deque(); + CActiveScheduler::Add(this); + + TInt error = iStatus.Int(); + + // It's fine to schedule buffers to the driver in the paused state (i.e. iRequestPending == EFalse) + if(!iRequestPending && (iParent.State() == EPlaying || iParent.State() == EPaused) && error == KErrNone) + { + //this is from playdata + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::RunL: Playing BufferOffset[%d] BufferLength[%d]"), iIndex, iBufferOffset, iBufferLength); + #endif + //Make sure the length is even. We may get odd for the last partial buffer. + iBufferLength = iBufferLength & 0xfffffffe; + + PlaySoundDevice(); + //Need this for the first time only + if(!iParent.TimerActive()) + { + iParent.UpdateTimeAndBytesPlayed(); + } + iRequestPending = ETrue; + } + // TODO: The case below shouldn't be valid under EPaused state, i.e. the driver shouldn't complete playback if it was paused. However due to a current problem in the driver we have to handle this case + else if (iRequestPending && (iParent.State() == EPlaying || iParent.State() == EPaused) && error == KErrNone) //this is from driver + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::RunL: Buffer emptied successfully"), iIndex); + #endif + if (iParent.iHaveSecondPhaseData) + { + TPtr8 dataPtr(iParent.iChunk.Base()+ iParent.iBufferConfig.iBufferOffsetList[iParent.iBufferIndex], 0, iParent.iDeviceBufferLength); + dataPtr.Copy(iParent.iSecondPhaseData); + + PlaySoundDevice(); + iParent.iCurrentPlayer = (iParent.iCurrentPlayer+1) & KNumPlayersMask; + iParent.UpdateTimeAndBytesPlayed(); + iParent.iHaveSecondPhaseData = EFalse; + } + else + { + iRequestPending = EFalse; + iParent.UpdateTimeAndBytesPlayed(); + iParent.BufferEmptied(); + } + } + else if(iParent.State() == EPlayBuffersFlushed && error == KErrCancel) + { + iRequestPending = EFalse; + if (!iParent.FlushCalledDuringPause()) + { + iParent.PlayCancelled(); + } + } + else if(iParent.State() == ERecording && (error >= 0 || error == KErrCancel)) + {//we can get KErrCancel when we call pause and there is no more data left with the driver + iParent.BufferFilled(error); + } + else + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::RunL: Error[%d] Outstanding[%d], pending[%d]"), + iIndex, error, iParent.iHaveSecondPhaseData,iRequestPending); + #endif + iParent.SoundDeviceError(error, iIndex); + } } TInt RMdaDevSound::CBody::CPlayer::RunError(TInt aError) { - iParent.PlayRequestHasCompleted(this, aError, EFalse); + iParent.SoundDeviceError(aError, iIndex); return KErrNone; } - + void RMdaDevSound::CBody::CPlayer::DoCancel() { + //nothing to do #ifdef SYMBIAN_SOUNDADAPTER_DEBUG RDebug::Printf("RMdaDevSound::CBody::CPlayer(%d)::DoCancel", iIndex); #endif - if(iStatus == KRequestPending) - { - // Avoid cancelling requests which have already completed. - // It wastes time, and might provoke a sound driver problem - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::CPlayer::DoCancel - would have cancelled driver request"); - #endif - iParent.PlaySoundDevice().Cancel(iStatus); - } - iParent.PlayRequestHasCompleted(this, KErrCancel, ETrue); + } + +void RMdaDevSound::CBody::CPlayer::ResetPlayer() + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::ResetPlayer: IsActive[%d] pending[%d] iBufferOffset[%d] iBufferLength[%d]"), iIndex, IsActive(), iRequestPending, iBufferOffset, iBufferLength); + #endif + + iRequestPending = EFalse; + iBufferOffset = -1; + iBufferLength = 0; } -void RMdaDevSound::CBody::CPlayer::PlayData(TUint aChunkOffset, TInt aLength) +void RMdaDevSound::CBody::CPlayer::Stop() { #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::PlayData : IsActive[%d]"), - iIndex, IsActive()); - RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (inc this player)", - iParent.iActivePlayRequestSizes.Length(), - iParent.iFreePlayers.Length()); + RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::Stop: IsActive[%d] pending[%d] iBufferOffset[%d] iBufferLength[%d]"), iIndex, IsActive(), iRequestPending, iBufferOffset, iBufferLength); + #endif + + ResetPlayer(); + Cancel(); + } + +void RMdaDevSound::CBody::CPlayer::PlayData(TInt aBufferOffset, TInt aLength) + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::PlayData : IsActive[%d]"), iIndex, IsActive()); #endif - - iBufferOffset = aChunkOffset; + + ASSERT(!IsActive()); // TODO: remove or replace redundant test + iBufferOffset = aBufferOffset; iBufferLength = aLength; - //Make sure the length is a multiple of 4 to work around an h6 limitation. - iBufferLength = iBufferLength & 0xfffffffc; - - // Issue the RSoundSc request - iParent.PlaySoundDevice().PlayData(iStatus, iBufferOffset, iBufferLength, EFalse); + iStatus = KRequestPending; SetActive(); - return; + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); } -TUint RMdaDevSound::CBody::CPlayer::GetPlayerIndex() const - { - return iIndex; - } - -RMdaDevSound::CBody::CRecorder::CRecorder(TInt aPriority, RMdaDevSound::CBody& aParent): - CActive(aPriority), iParent(aParent), iBufferOffset(-1), iBufferLength(0) - { - CActiveScheduler::Add(this); - } - -RMdaDevSound::CBody::CRecorder::~CRecorder() - { - Cancel(); - } - -void RMdaDevSound::CBody::CRecorder::RecordData(TInt& aBufferLength) +void RMdaDevSound::CBody::CPlayer::RecordData(TInt& aBufferLength) { if (!IsActive()) { @@ -1902,43 +1295,34 @@ } } -void RMdaDevSound::CBody::CRecorder::RunL() +void RMdaDevSound::CBody::CPlayer::PlaySoundDevice() { #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("****RMdaDevSound::CBody::CRecorder()::RunL: Error[%d] ParentState[%s]", - iStatus.Int(), iParent.State().Name()); - #endif + RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::PlaySoundDevice : IsActive[%d]"), iIndex, IsActive()); + #endif - - TInt error = iStatus.Int(); - - if((error >= 0) || (error == KErrCancel)) - {//we can get KErrCancel when we call pause and there is no more data left with the driver - iParent.BufferFilled(error); +#ifdef SYMBIAN_FORCE_32BIT_LENGTHS + if (iBufferLength%4 != 0) + { + // simulate the limitation of some hardware, where -6 is generated if the + // buffer length is not divisible by 4. + TRequestStatus*status = &iStatus; + User::RequestComplete(status, KErrArgument); } - else + else +#endif { - #ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Print(_L("RMdaDevSound::CBody::CPlayer()::RunL: Error[%d]"), error); - #endif - iParent.SoundDeviceError(error); + iParent.PlaySoundDevice().PlayData(iStatus, iBufferOffset, iBufferLength, EFalse); + // Pause was called when there was no data available. Now that we have data available, we should pause the driver + if (iParent.iPauseDeviceDriverOnNewData) + { + #ifdef SYMBIAN_SOUNDADAPTER_DEBUG + RDebug::Printf("Pausing the driver after receiving data to play"); + #endif + iParent.PlaySoundDevice().Pause(); + iParent.iPauseDeviceDriverOnNewData = EFalse; + } } + SetActive(); + } - - -TInt RMdaDevSound::CBody::CRecorder::RunError(TInt aError) - { - iParent.SoundDeviceError(aError); - return KErrNone; - } - -void RMdaDevSound::CBody::CRecorder::DoCancel() - { -#ifdef SYMBIAN_SOUNDADAPTER_DEBUG - RDebug::Printf("RMdaDevSound::CBody::CRecorder()::DoCancel"); -#endif - iParent.RecordSoundDevice().Cancel(iStatus); - } - - -// End of file