--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devsound/sounddevbt/src/swcodecwrapper/mmfBtSwCodecPlayDataPath.cpp Tue Feb 02 01:56:55 2010 +0200
@@ -0,0 +1,565 @@
+// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// source\server\MmfBtSwCodecPlayDatapath.cpp
+//
+//
+
+#include "mmfBtSwCodecPlayDataPath.h"
+#include "mmfbtswcodecwrapper.h"
+#include "mmfbtswcodecwrappercustominterfacesuids.hrh"
+#include <mmfpaniccodes.h>
+#include "mmfBtSwCodecUtility.h"
+
+#include "MMFBtRoutingSoundDevice.h"
+#include "A2dpBTHeadsetAudioIfClientServer.h" // for TRange (will be deprecated)
+
+CMMFSwCodecPlayDataPath* CMMFSwCodecPlayDataPath::NewL( CRoutingSoundPlayDevice* aSoundDevice,
+ TUid aDeviceUid)
+ {
+ CMMFSwCodecPlayDataPath* self = new(ELeave) CMMFSwCodecPlayDataPath(aSoundDevice,
+ aDeviceUid);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+
+void CMMFSwCodecPlayDataPath::ConstructL()
+ {
+ iAudioPlayer = new (ELeave) CDataPathPlayer(*this,CActive::EPriorityUserInput);
+ iSoundDeviceErrorReceiver = new (ELeave) CSoundDevPlayErrorReceiver(*this, CActive::EPriorityUserInput);
+ iUtility = CMMFSwCodecUtility::NewL();
+ }
+
+
+CMMFSwCodecPlayDataPath::~CMMFSwCodecPlayDataPath()
+ {
+ delete iAudioPlayer;
+ delete iSoundDeviceErrorReceiver;
+ delete iUtility;
+
+ TRequestStatus status;
+ iSoundDevice->CloseDevice(iDeviceUid, status);
+ //TODO there should be a timeout for the line below
+ User::WaitForRequest(status);
+
+ if (iCodec)
+ {
+ delete iSourceBuffer;
+ if (!iCodec->IsNullCodec())
+ {
+ delete iSoundDeviceBuffer;
+ }
+ }
+
+#ifdef __USE_MMF_TRANSFERBUFFERS__
+ delete iTransferWindow;
+
+ if(iTransferBuffer)
+ {
+ iTransferBuffer->Close();
+ delete iTransferBuffer;
+ }
+#endif
+
+#ifdef __USE_MMF_PTRBUFFERS__
+ delete iPtrBufferMemoryBlock;
+#endif
+ }
+
+
+TInt CMMFSwCodecPlayDataPath::SetObserver(MMMFHwDeviceObserver& aObserver)
+ {
+ TInt error;
+ if (iHwDeviceObserver)
+ {
+ error = KErrAlreadyExists;
+ }
+ else
+ {
+ iHwDeviceObserver = &aObserver;
+ error = KErrNone;
+ }
+ return error;
+ }
+
+
+TInt CMMFSwCodecPlayDataPath::AddCodec(CMMFSwCodec& aCodec)
+ {
+ if (iCodec)
+ {
+ return KErrNotSupported; //doesn't support multiple codecs
+ }
+
+ TInt err = KErrNone;
+
+ iCodec = &aCodec;
+
+ // Allocate data buffer
+ iSourceBufferSize = iCodec->SourceBufferSize();
+ iSoundDevBufferSize = iCodec->SinkBufferSize();
+
+ if ((!iSourceBufferSize) || (!iSoundDevBufferSize))
+ {
+ err = KErrArgument; //codec plugin has not specified buffer size
+ }
+
+ if (err == KErrNone)
+ {
+#ifdef __USE_MMF_TRANSFERBUFFERS__
+ TRAP(err,iSourceBuffer = CreateTransferBufferL(iSourceBufferSize, static_cast<CMMFTransferBuffer*>(iSourceBuffer)));
+#endif
+#ifdef __USE_MMF_PTRBUFFERS__
+ TRAP(err,iSourceBuffer = CreatePtrBufferL(iSourceBufferSize));
+#else
+ TRAP(err,iSourceBuffer = CMMFDataBuffer::NewL(iSourceBufferSize));
+#endif
+ }
+
+ if (err == KErrNone)
+ {
+ if (iCodec->IsNullCodec())
+ {//use source buffer for sound device buffer
+ iSoundDeviceBuffer = NULL;
+ }
+ else
+ {//codec needs separate source and sound device buffers
+ TRAP(err,iSoundDeviceBuffer = CMMFDataBuffer::NewL(iSoundDevBufferSize));
+ }
+ }
+ return err;
+ }
+
+
+TInt CMMFSwCodecPlayDataPath::Start()
+ {
+ TInt startError = KErrNone;
+
+ if (!iCodec || (!iSoundDevice->Handle()))
+ {
+ //check that a codec has been added and the sound device is open
+ startError = KErrNotReady;
+ }
+
+ if (iState == EPaused)
+ {//we are paused so need to resume play
+ if (!startError)
+ {
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Resume"));
+#endif
+ iAudioPlayer->ResumePlaying();
+ iState = EPlaying;
+ }
+ }
+ else if (!startError)
+ {
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Normal"));
+#endif
+ if (!startError)
+ {
+ iNoMoreSourceData = EFalse;
+ iSourceBuffer->SetLastBuffer(EFalse);
+ iState = EPlaying;
+ iSoundDeviceErrorReceiver->Start();
+ TRAP(startError, FillSourceBufferL()); //get initial buffer of audio data
+ if (startError == KErrNone)
+ {
+ // Start the player objects
+ iAudioPlayer->Start();
+ }
+ else
+ {//failed to start up correctly go back to stopped state
+ iState = EStopped;
+ iSoundDeviceErrorReceiver->Stop();
+ }
+ }
+ }
+ return startError;
+ }
+
+
+// *** Main Play Loop ***
+
+void CMMFSwCodecPlayDataPath::FillSourceBufferL()
+ {//asks observer to fill the source buffer
+ // Ask immediately for data from the observer
+#ifdef __CYCLE_MMF_DATABUFFERS__
+ // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
+ // If the creation fails, we carry on regardless as the original buffer will not have been
+ // destroyed. Must do this as alloc fail tests will not run.
+ if(iSourceBuffer)
+ {
+ iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
+ }
+#endif // __CYCLE_MMF_DATABUFFERS__
+ User::LeaveIfError(iHwDeviceObserver->FillThisHwBuffer(*iSourceBuffer));
+
+ }
+
+
+void CMMFSwCodecPlayDataPath::BufferFilledL(CMMFDataBuffer& aBuffer)
+ {//call back from observer to indicate buffer has been filled
+ if (iState == EStopped)
+ User::Leave(KErrNotReady);//ok if paused?
+
+ iSourceBuffer = &aBuffer;
+ iSourceBuffer->SetStatus(EFull);
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL"));
+#endif
+
+ //need to check that the buffer size is not 0 - if so assume we've reached the end of the data
+ if (!iSourceBuffer->BufferSize())
+ {//no buffer - could be end of source or could be that the source has no data??
+ iNoMoreSourceData = ETrue;
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-NoMoreSourceData"));
+#endif
+ }
+ //even if the buffer size is 0 we still
+ //need to perform the following to get the sound device callback
+ FillSoundDeviceBufferL(); //get buffer in pcm16 format for sound device
+
+ // attenuate the amplitude of the samples if volume ramping has been changed
+ // and is non-zero
+ if (iCustomInterface)
+ {
+ TTimeIntervalMicroSeconds volumeRamp = iCustomInterface->VolumeRamp();
+ if (volumeRamp != iVolumeRamp)
+ {
+ iVolumeRamp = volumeRamp;
+ if (iVolumeRamp.Int64() != 0)
+ {
+ iUtility->ConfigAudioRamper(
+ iVolumeRamp.Int64(),
+ iSampleRate,
+ iChannels);
+ iRampAudioSample = ETrue;
+ }
+ else
+ {
+ iRampAudioSample = EFalse;
+ }
+ }
+ if (iRampAudioSample)
+ {
+ iRampAudioSample = iUtility->RampAudio(iSoundDeviceBuffer);
+ }
+ }
+
+ iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers
+
+ if (iSourceBuffer->LastBuffer())//check last buffer flag
+ {
+ iNoMoreSourceData = ETrue;
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-LBNoMoreSourceData"));
+#endif
+ }
+ }
+
+
+void CMMFSwCodecPlayDataPath::FillSoundDeviceBufferL()
+ {//use CMMFSwCodec to fill the sound device buffer
+
+ CMMFSwCodec::TCodecProcessResult codecProcessResult;
+
+ if (iCodec->IsNullCodec())
+ {//no codec so data can be sent direct to sink
+ iSoundDeviceBuffer = iSourceBuffer;
+ iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
+ }
+ else
+ {
+ //pass buffer to codec for processing
+ codecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSoundDeviceBuffer);
+ if (iSourceBuffer->LastBuffer()) //if source is last buffer so is sound dev
+ iSoundDeviceBuffer->SetLastBuffer(ETrue);
+ if ((!iSoundDeviceBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded))
+ {//the codec has added data but not set the buffer length
+ iSoundDeviceBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded);
+ }
+ //only supports EProcessComplete
+ switch (codecProcessResult.iCodecProcessStatus)
+ {
+ case CMMFSwCodec::TCodecProcessResult::EProcessComplete:
+ //finished procesing source data - all data in sink buffer
+ {
+ iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
+ }
+ break;
+ case CMMFSwCodec::TCodecProcessResult::EDstNotFilled:
+ //could be the last buffer in which case dst might not get filled
+ {
+ iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
+ }
+ break;
+ case CMMFSwCodec::TCodecProcessResult::EEndOfData:
+ //no more data - send what we've got to the sink
+ //note we can't always rely on this - in many cases the codec will not know when
+ //it has reached the end of data.
+ {
+ iSoundDeviceBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get
+ iNoMoreSourceData = ETrue;
+ //doesn't matter if sink buffer is not full
+ }
+ break;
+ default:
+ Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec
+ }
+ }
+ }
+
+
+void CMMFSwCodecPlayDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer)
+ {//call back from CDataPathPlayer when the sound device buffer has been emptied
+ if (&aBuffer != iSoundDeviceBuffer)
+ Panic(EMMFSwCodecWrapperBadBuffer);
+ if (!iNoMoreSourceData)
+ FillSourceBufferL();
+ }
+
+//*** End of Main Play Loop ***
+
+
+void CMMFSwCodecPlayDataPath::Stop()
+ {
+ iAudioPlayer->Cancel();
+ iSoundDeviceErrorReceiver->Cancel();
+ TRequestStatus status;
+ iSoundDevice->CloseDevice(iDeviceUid, status);
+ User::WaitForRequest(status);
+
+#ifdef __CYCLE_MMF_DATABUFFERS__
+ // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
+ // If the creation fails, we carry on regardless as the original buffer will not have been
+ // destroyed. Must do this as alloc fail tests will not run.
+ if(iSourceBuffer)
+ {
+ iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
+ }
+#endif // __CYCLE_MMF_DATABUFFERS__
+
+ iState = EStopped;
+ }
+
+
+void CMMFSwCodecPlayDataPath::Pause()
+ {
+ //since a pause can happen anyway in the datatransfer -need to set to a known
+ //state so that when play is resumed the behaviour is predictable
+ if (iSoundDevice->Handle())
+ {
+ iSoundDevice->PauseBuffer(); // ignores return value?
+ iState = EPaused;
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("Pause"));
+#endif
+ }
+ else
+ {//an error must have occured
+ iState = EStopped;
+ }
+ }
+
+
+CRoutingSoundPlayDevice* CMMFSwCodecPlayDataPath::Device()
+ {
+ return iSoundDevice;
+ }
+
+void CMMFSwCodecPlayDataPath::SoundDeviceException(TInt aError)
+ {
+ //this sends a request to the hw device observer usually Devsound
+ //to update the bytes played
+ //it is done here so that the sound driver can be closed prior to
+ //updating the plicy and sending the error back
+ TUid uidUpdateBytesPlayed;
+ uidUpdateBytesPlayed.iUid = KMmfHwDeviceObserverUpdateBytesPlayed;
+ TPtrC8 dummy(0,0);
+ iHwDeviceObserver->MsgFromHwDevice(uidUpdateBytesPlayed,dummy);
+
+ //this closes RMdaDevSound.
+ Stop();
+
+ //inform devsound so it can update policy
+ iHwDeviceObserver->Stopped();
+
+ // Inform the observer of the exception condition
+ // We inform the hw device observer after the policy has been
+ // updated incase the observer relied on the error to assume
+ // the policy has been updated
+ iHwDeviceObserver->Error(aError);
+ }
+
+
+void CMMFSwCodecPlayDataPath::SetPlayCustomInterface(MPlayCustomInterface& aCustomInterface)
+ {
+ iCustomInterface = &aCustomInterface;
+ }
+
+void CMMFSwCodecPlayDataPath::SetConfigForAudioRamp(TUint aSampleRate, TUint aChannels)
+ {
+ iSampleRate = aSampleRate;
+ iChannels = aChannels;
+ }
+
+/************************************************************************
+ * CDataPathPlayer *
+ ************************************************************************/
+
+CDataPathPlayer::CDataPathPlayer(CMMFSwCodecPlayDataPath& aParent, TInt aPriority)
+: CActive(aPriority), iParent(aParent)
+ {
+ CActiveScheduler::Add(this);
+ }
+
+
+CDataPathPlayer::~CDataPathPlayer()
+ {
+ Cancel();
+ }
+
+
+void CDataPathPlayer::Start()
+ {
+ // No implementation
+ }
+
+
+void CDataPathPlayer::ResumePlaying()
+ {
+ if (iParent.Device()->Handle())
+ {
+ //should be ok to call this even if we are active
+ iParent.Device()->ResumePlaying();
+ iResumePlaying = ETrue;
+ }
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("Playing Resumed"));
+#endif
+ }
+
+
+void CDataPathPlayer::PlayData(const CMMFDataBuffer& aData)
+ {
+ iDataFromSource = &aData;
+ if (!IsActive())
+ {
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("CDataPathPlayer::PlayData"));
+#endif
+ iParent.Device()->PlayData(aData.Data(), iStatus);
+ SetActive();
+ }
+ }
+
+
+void CDataPathPlayer::Stop()
+ {
+ if (!IsActive())
+ {
+ iParent.Device()->FlushBuffer();
+ }
+ Cancel();
+ iParent.SoundDeviceException(KErrCancel);
+ }
+
+
+void CDataPathPlayer::RunL()
+ {
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("CDataPathPlayer::RunL error[%d]"), iStatus.Int());
+#endif
+ if (!iStatus.Int())
+ {
+ iParent.BufferEmptiedL(static_cast<const CMMFDataBuffer&>(*iDataFromSource));
+ iResumePlaying = EFalse;
+ }
+ //if we don't have a sound driver handle then we have stopped
+ //but the client still thinks we are recording so swallow error
+ else if (iStatus.Int()!= KErrBadHandle)
+ {
+ iParent.SoundDeviceException(iStatus.Int());
+ }
+ }
+
+
+TInt CDataPathPlayer::RunError(TInt aError)
+ {
+ Error(aError);
+ return KErrNone;
+ }
+
+
+void CDataPathPlayer::DoCancel()
+ {
+ if (iParent.Device()->Handle())
+ {
+ iParent.Device()->CancelPlayData();
+ iParent.Device()->FlushBuffer();
+ }
+ }
+
+
+void CDataPathPlayer::Error(TInt aError)
+ {
+ iParent.SoundDeviceException(aError);
+ }
+
+
+/************************************************************************
+ * CSoundDevPlayErrorReceiver *
+ ************************************************************************/
+
+CSoundDevPlayErrorReceiver::CSoundDevPlayErrorReceiver(CMMFSwCodecPlayDataPath& aParent, TInt aPriority)
+: CActive(aPriority), iParent(aParent)
+ {
+ CActiveScheduler::Add(this);
+ }
+
+CSoundDevPlayErrorReceiver::~CSoundDevPlayErrorReceiver()
+ {
+ Cancel();
+ }
+
+void CSoundDevPlayErrorReceiver::Start()
+ {
+ iParent.Device()->NotifyError(iStatus);
+ SetActive();
+ }
+
+void CSoundDevPlayErrorReceiver::Stop()
+ {
+ Cancel();
+ }
+
+void CSoundDevPlayErrorReceiver::RunL()
+ {
+ // An error has been returned
+#ifdef _SCW_DEBUG
+ RDebug::Print(_L("CSoundDevPlayErrorReceiver::RunL[%d]"), iStatus.Int());
+#endif
+ iParent.SoundDeviceException(iStatus.Int());
+ }
+
+void CSoundDevPlayErrorReceiver::DoCancel()
+ {
+ iParent.Device()->CancelNotifyError();
+ }
+
+