mmhais/refacladapt/src/tonehwdevice/tonedatapath.cpp
changeset 0 79dd3e2336a0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmhais/refacladapt/src/tonehwdevice/tonedatapath.cpp	Fri Oct 08 19:40:43 2010 +0100
@@ -0,0 +1,704 @@
+// Copyright (c) 2003-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:
+//
+
+#include "tonedatapath.h"
+
+
+CToneDataPath* CToneDataPath::NewL()
+	{
+	CToneDataPath* self = new(ELeave) CToneDataPath;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+void CToneDataPath::ConstructL()
+	{
+	iAudioPlayer = new (ELeave) CToneDataPathPlayer(*this,CActive::EPriorityUserInput);
+	iSoundDeviceErrorReceiver = new (ELeave) CToneSoundDevPlayErrorReceiver(*this, CActive::EPriorityUserInput);
+	}
+
+
+CToneDataPath::~CToneDataPath()
+	{
+	delete iAudioPlayer;
+	delete iSoundDeviceErrorReceiver;
+	
+	iSoundDevice.Close();
+
+	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 CToneDataPath::SetObserver(MMMFHwDeviceObserver& aObserver)
+	{
+	TInt error;
+	if (iHwDeviceObserver)
+		{
+		error =  KErrAlreadyExists;
+		}
+	else
+		{
+		iHwDeviceObserver = &aObserver;
+		error  = KErrNone;
+		}
+	return error;
+	}
+
+
+TInt CToneDataPath::AddCodec(CToneCodec& 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 CToneDataPath::Start()
+	{
+	TInt startError = KErrNone;
+
+	if (!iCodec) 
+		{//check that a codec has been added
+		startError = KErrNotReady;
+		}
+	if ((!iSoundDevice.Handle())&&(!startError))
+    	{//check that the sound drivers can be opened
+   		startError = iSoundDevice.Open();
+		}
+
+	if (iState == EPaused)
+		{//we are paused so need to resume play
+		if (!startError)
+			{
+#ifdef _SCW_DEBUG
+			RDebug::Print(_L("CToneDataPath::Start-Resume"));
+#endif
+			iAudioPlayer->ResumePlaying();
+			iState = EPlaying;
+			}
+		}
+	else if (!startError)
+		{
+#ifdef _SCW_DEBUG
+		RDebug::Print(_L("CToneDataPath::Start-Normal"));
+#endif
+		// get sample rate and channels from RMdaDevSound
+		RMdaDevSound::TCurrentSoundFormatBuf format;
+		iSoundDevice.GetPlayFormat(format);
+		iSampleRate = format().iRate;
+		iChannels = format().iChannels;
+		
+		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 CToneDataPath::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 CToneDataPath::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("CToneDataPath::BufferFilledL"));
+#endif
+
+	//need to check that the buffer size is not 0 - if so assume we've reached the end of the data
+	iBuffSize = iSourceBuffer->BufferSize();
+	if (!iBuffSize)
+		{//no buffer  - could be end of source or could be that the source has no data??
+		iNoMoreSourceData = ETrue;
+#ifdef _SCW_DEBUG
+		RDebug::Print(_L("CToneDataPath::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	
+	
+	iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers
+	}
+
+
+void CToneDataPath::FillSoundDeviceBufferL()
+	{//use CToneCodec to fill the sound device buffer
+	
+	CToneCodec::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 CToneCodec::TCodecProcessResult::EProcessComplete:
+		//finished procesing source data - all data in sink buffer
+			{
+			iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full	
+			}
+		break;
+#ifdef SYMBIAN_VARIABLE_BITRATE_CODEC
+		case CToneCodec::TCodecProcessResult::EProcessIncomplete:
+		//finished procesing source data - all data in sink buffer
+			{
+			iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full	
+			}
+		break;
+#endif
+		case CToneCodec::TCodecProcessResult::EDstNotFilled:
+		//could be the last buffer in which case dst might not get filled
+			{
+			iSoundDeviceBuffer->SetStatus(EFull);	//sink buffer is full	
+			}
+		break;
+		case CToneCodec::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
+			break;
+			}
+		}
+	}
+
+
+void CToneDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer)
+	{//call back from CToneDataPathPlayer when the sound device buffer has been emptied
+	if (&aBuffer != iSoundDeviceBuffer) 
+		{
+		Panic(EToneBadBuffer);
+		}
+
+	if (!iNoMoreSourceData) 
+		{
+		FillSourceBufferL();
+		}
+	}
+
+//*** End of Main Play Loop ***
+
+
+void CToneDataPath::Stop()
+	{
+	iAudioPlayer->Cancel();
+	iSoundDeviceErrorReceiver->Cancel();
+    iSoundDevice.Close();
+
+#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 CToneDataPath::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.PausePlayBuffer(); //needs new LDD
+		iState = EPaused;
+#ifdef _SCW_DEBUG
+		RDebug::Print(_L("Pause"));
+#endif
+		}
+	else
+		{//an error must have occured 
+		iState = EStopped;
+		}
+	}
+	
+	
+TInt CToneDataPath::EmptyBuffers()
+	{
+	TInt error = KErrNone;
+	if (iSoundDevice.Handle() == 0)
+		{
+		error = KErrNotReady;		
+		}
+	else
+		{
+		iSoundDevice.FlushPlayBuffer();
+		}
+	return error;
+	}	
+
+
+RMdaDevSound& CToneDataPath::Device()
+	{
+	return iSoundDevice;
+	}
+
+
+void CToneDataPath::SoundDeviceException(TInt aError)
+	{
+	if(iIgnoreUnderflow)
+		{
+		if((aError == KErrUnderflow) && (!iNoMoreSourceData))
+			{
+			//ignore underflow
+			return;
+			}
+		}
+
+	//this sends a request to the hw device observer
+	//to update the bytes played
+	//it is done here so that the sound driver can be closed prior to
+	//updating the policy and sending the error back
+	TUid uidUpdateBytesPlayed;
+	uidUpdateBytesPlayed.iUid = KToneHwDeviceObserverUpdateBytesPlayed;
+	TPtrC8 dummy(0,0);
+	
+	ASSERT(iHwDeviceObserver);
+	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);
+	
+	RDebug::Print(_L("CToneDataPath::iHwDeviceObserver->Error(%d)"),aError);
+	}
+
+/**
+Retrieves a custom interface to the device.
+The reference CToneDataPath supports three  custom interfaces,
+MEmptyBuffersCustomInterface, MSetVbrFlagCustomInterface and MIgnoreUnderflowEventsCustomInterface
+
+@param	aInterface
+		Interface UID, defined with the custom interface.
+		aInterface = KMmfUidEmptyBuffersCustomInterface for MEmptyBuffersCustomInterface,
+					 KSetVbrFlagCustomInterfaceTypeUid for MSetVbrFlagCustomInterface
+		
+@return A pointer to the interface implementation, or NULL if the device can not
+		implement the interface requested. The return value must be cast to the
+		correct type by the user.
+*/
+TAny* CToneDataPath::CustomInterface(TUid aInterface)
+	{
+	TAny* ret = NULL;
+
+	if (aInterface == KIgnoreUnderflowCustomInterfaceTypeUid)
+		{
+		MIgnoreUnderflowEventsCustomInterface* result = static_cast<MIgnoreUnderflowEventsCustomInterface*> (this);
+		ret = static_cast<TAny*>(result);
+		}
+	return ret;
+	}
+
+
+void CToneDataPath::IgnoreUnderflowEvents()
+	{
+	iIgnoreUnderflow = ETrue;
+	}
+
+
+
+/************************************************************************
+ *				CDataPathPlayer											*
+ ************************************************************************/
+
+CToneDataPathPlayer::CToneDataPathPlayer(CToneDataPath& aParent, TInt aPriority)
+: CActive(aPriority), iParent(aParent)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+
+CToneDataPathPlayer::~CToneDataPathPlayer()
+	{
+	Cancel();
+	}
+
+
+void CToneDataPathPlayer::Start()
+	{
+	// No implementation
+	}
+
+
+void CToneDataPathPlayer::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 CToneDataPathPlayer::PlayData(const CMMFDataBuffer& aData)
+	{
+	iDataFromSource = &aData;
+	if (!IsActive())
+		{
+#ifdef _SCW_DEBUG
+		RDebug::Print(_L("CToneDataPathPlayer::PlayData"));
+#endif
+		iParent.Device().PlayData(iStatus,(static_cast<const CMMFDataBuffer*> (iDataFromSource))->Data());
+		SetActive();
+		}
+	}
+
+
+void CToneDataPathPlayer::Stop()
+	{
+	if (!IsActive())
+		{
+		iParent.Device().FlushPlayBuffer(); // Otherwise won't be flushed
+		}
+	Cancel();
+	iParent.SoundDeviceException(KErrCancel);
+	}
+
+
+void CToneDataPathPlayer::RunL()
+	{
+#ifdef _SCW_DEBUG
+	RDebug::Print(_L("CToneDataPathPlayer::RunL error[%d]"), iStatus.Int());
+#endif
+	if (iStatus.Int()!=KErrNone)
+		{
+		iParent.SoundDeviceException(iStatus.Int());
+		}
+	else
+		{
+		iParent.BufferEmptiedL(static_cast<const CMMFDataBuffer&>(*iDataFromSource));
+		iResumePlaying = EFalse;
+		}
+	}
+
+
+TInt CToneDataPathPlayer::RunError(TInt aError)
+	{
+	Error(aError);
+	return KErrNone;
+	}
+
+
+void CToneDataPathPlayer::DoCancel()
+	{
+	if (iParent.Device().Handle())
+		{
+		iParent.Device().CancelPlayData();
+		iParent.Device().FlushPlayBuffer();
+		}
+	}
+
+
+void CToneDataPathPlayer::Error(TInt aError)
+	{ 
+	iParent.SoundDeviceException(aError);
+	}
+
+
+
+/************************************************************************
+ *				CToneSoundDevPlayErrorReceiver							*
+ ************************************************************************/
+
+CToneSoundDevPlayErrorReceiver::CToneSoundDevPlayErrorReceiver(CToneDataPath& aParent, TInt aPriority)
+: CActive(aPriority), iParent(aParent)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+CToneSoundDevPlayErrorReceiver::~CToneSoundDevPlayErrorReceiver()
+	{
+	Cancel();
+	}
+
+void CToneSoundDevPlayErrorReceiver::Start()
+	{
+	iParent.Device().NotifyPlayError(iStatus);
+	SetActive();
+	}
+
+void CToneSoundDevPlayErrorReceiver::Stop()
+	{
+	Cancel();
+	}
+
+void CToneSoundDevPlayErrorReceiver::RunL()
+	{
+	TInt reason = iStatus.Int();
+	Start();
+
+	// An error has been returned
+#ifdef _SCW_DEBUG
+	RDebug::Print(_L("CToneSoundDevPlayErrorReceiver::RunL[%d]"), reason);
+#endif
+	iParent.SoundDeviceException(reason);
+	}
+
+void CToneSoundDevPlayErrorReceiver::DoCancel()
+	{
+	iParent.Device().CancelNotifyPlayError();
+	}
+
+
+
+/*
+ * CycleAudioBufferL
+ *
+ * Sets up a usable buffer for passing to MMF
+ *	
+ * This method has been written such that it must allocate a new buffer before
+ * replacing the existing one. The purpose of this is to force creation of a 
+ * new buffer. Simply deleting and then re-allocing may result in the same 
+ * address being used.
+ * 
+ * Only cycles if there is enough memory
+ *
+ */
+#ifdef __CYCLE_MMF_DATABUFFERS__
+CMMFDataBuffer* CToneDataPath::CycleAudioBuffer(CMMFDataBuffer* aBuffer)
+	{
+	CMMFDataBuffer* buffer = NULL;
+	TUint bufferSize = aBuffer->Data().MaxLength();
+
+#ifdef __USE_MMF_TRANSFERBUFFERS__
+	TRAPD(err, buffer = CreateTransferBufferL(bufferSize, static_cast<CMMFTransferBuffer*>(aBuffer)));
+#else
+	TRAPD(err,buffer = CMMFDataBuffer::NewL(bufferSize));
+
+	if (err == KErrNone)
+		{
+		delete aBuffer;
+		}
+#endif
+	if (err != KErrNone)
+		{//there was a problem creating buffer eg OOM so use same buffer
+		buffer = aBuffer;
+		}
+
+	return buffer;
+
+	}
+#endif
+
+/*
+ * DoCleanupRHandleBase
+ *
+ * This method will initially Close the handle and then delete it.
+ *
+ */
+#ifdef __USE_MMF_TRANSFERBUFFERS__
+inline static void DoCleanupRHandleBase(TAny* aRHandleBase)
+	{
+	ASSERT(aRHandleBase);
+	RHandleBase* rHandleBase = static_cast<RHandleBase*> (aRHandleBase);
+	TRAPD(error, rHandleBase->Close());
+	delete aRHandleBase;
+	}
+
+CMMFTransferBuffer* CToneDataPath::CreateTransferBufferL(TUint aBufferSize, CMMFTransferBuffer* aOldBuffer)
+	{
+	CMMFTransferBuffer* buffer = NULL;
+
+	RTransferBuffer* transBuffer = new  (ELeave) RTransferBuffer;
+	
+	TCleanupItem bufferCleanupItem(DoCleanupRHandleBase, transBuffer); //closes and deletes.
+	CleanupStack::PushL(bufferCleanupItem);
+
+	RTransferWindow* transWindow = new (ELeave) RTransferWindow;
+	
+	TCleanupItem windowCleanupItem(DoCleanupRHandleBase, transWindow); //closes and deletes.
+	CleanupStack::PushL(windowCleanupItem);
+
+	User::LeaveIfError(transBuffer->Create(aBufferSize));
+	User::LeaveIfError(transWindow->Create(aBufferSize));
+	User::LeaveIfError(transWindow->MapInBuffer(*transBuffer));
+
+	buffer = CMMFTransferBuffer::NewL(*transWindow);
+
+	delete aOldBuffer; //closes RTransferWindow
+	delete iTransferWindow;
+
+	if(iTransferBuffer)
+		{
+		iTransferBuffer->Close();
+		}
+	delete iTransferBuffer;
+
+	iTransferBuffer = transBuffer;
+	iTransferWindow = transWindow;
+
+	CleanupStack::Pop(transWindow); 
+	CleanupStack::Pop(transBuffer); 
+
+	return buffer;
+	}
+#endif
+
+
+#ifdef __USE_MMF_PTRBUFFERS__
+CMMFPtrBuffer* CToneDataPath::CreatePtrBufferL(TUint aBufferSize)
+	{
+	CMMFPtrBuffer* buffer = NULL;
+	if (iPtrBufferMemoryBlock)
+		{
+		delete iPtrBufferMemoryBlock;//incase already exisits
+		}
+	iPtrBufferMemoryBlock = HBufC8::NewL(aBufferSize);
+	TPtr8 ptrMemoryBlock(iPtrBufferMemoryBlock->Des());
+	buffer = CMMFPtrBuffer::NewL(ptrMemoryBlock);
+	return buffer;
+	}
+#endif  // __USE_MMF_PTRBUFFERS__
+
+
+