devsound/sounddevbt/src/swcodecwrapper/mmfBtSwCodecPlayDataPath.cpp
changeset 0 40261b775718
--- /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();
+	}
+
+