mmlibs/mmfw/src/Client/Audio/mmfclientaudiooutputstream.cpp
changeset 0 b8ed18f6c07b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmlibs/mmfw/src/Client/Audio/mmfclientaudiooutputstream.cpp	Thu Oct 07 22:34:12 2010 +0100
@@ -0,0 +1,1091 @@
+// Copyright (c) 2002-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 <mdaaudiooutputstream.h>
+#include <mda/common/audio.h>
+#include "mmfclientaudiooutputstream.h"
+#include "mmfclientaudiostreamutils.h"
+#include "MmfFifo.h"
+#include <mmf/common/mmfpaniccodes.h>
+
+#define WAIT_FOR_READY_ACTIVE_OBJECTS
+
+const TInt KMicroSecsInOneSec = 1000000; 
+const TInt KUnknownVolume = -1; // means "don't set", must be negative
+const TInt KShutDownTimeInterval = 100000; //100 milli seconds
+
+/**
+ *
+ * Static NewL
+ *
+ * @return CMdaAudioOutputStream*
+ *
+ */
+EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback,
+															CMdaServer* /*aServer = NULL*/)
+	{
+	return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality);
+	}
+
+/**
+ *
+ * Static NewL
+ *
+ * @return CMdaAudioOutputStream*
+ *
+ */
+EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback,
+															TInt aPriority,
+															TInt aPref /*=EMdaPriorityPreferenceTimeAndQuality*/)
+	{
+	CMdaAudioOutputStream* self = new(ELeave) CMdaAudioOutputStream();
+	CleanupStack::PushL(self);
+	self->iProperties = CMMFMdaAudioOutputStream::NewL(aCallback, aPriority, aPref);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CMdaAudioOutputStream::CMdaAudioOutputStream()
+	{
+	}
+
+CMdaAudioOutputStream::~CMdaAudioOutputStream()
+	{
+	delete iProperties;
+	}
+
+void CMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
+	{
+	ASSERT(iProperties);
+	iProperties->SetAudioPropertiesL(aSampleRate, aChannels);
+	}
+
+void CMdaAudioOutputStream::Open(TMdaPackage* aSettings)
+	{
+	ASSERT(iProperties);
+	iProperties->Open(aSettings);
+	}
+
+TInt CMdaAudioOutputStream::MaxVolume()
+	{
+	ASSERT(iProperties);
+	return iProperties->MaxVolume();
+	}
+
+TInt CMdaAudioOutputStream::Volume()
+	{
+	ASSERT(iProperties);
+	return iProperties->Volume();
+	}
+
+void CMdaAudioOutputStream::SetVolume(const TInt aNewVolume)
+	{
+	ASSERT(iProperties);
+	iProperties->SetVolume(aNewVolume);
+	}
+
+void CMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref)
+	{
+	ASSERT(iProperties);
+	iProperties->SetPriority(aPriority, aPref);
+	}
+
+void CMdaAudioOutputStream::WriteL(const TDesC8& aData)
+	{
+	ASSERT(iProperties);
+	iProperties->WriteL(aData);
+	}
+
+void CMdaAudioOutputStream::Stop()
+	{
+	ASSERT(iProperties);
+	iProperties->Stop();
+	}
+
+EXPORT_C TInt CMdaAudioOutputStream::Pause()
+	{
+	ASSERT(iProperties);
+	return iProperties->Pause();
+	}
+
+EXPORT_C TInt CMdaAudioOutputStream::Resume()
+	{
+	ASSERT(iProperties);
+	return iProperties->Resume();
+	}
+
+const TTimeIntervalMicroSeconds& CMdaAudioOutputStream::Position()
+	{
+	ASSERT(iProperties);
+	return iProperties->Position();
+	}
+
+EXPORT_C void CMdaAudioOutputStream::SetBalanceL(TInt aBalance)
+	{
+	ASSERT(iProperties);
+	iProperties->SetBalanceL(aBalance);
+	}
+
+EXPORT_C TInt CMdaAudioOutputStream::GetBalanceL() const
+	{
+	ASSERT(iProperties);
+	return iProperties->GetBalanceL();
+	}
+
+EXPORT_C TInt CMdaAudioOutputStream::GetBytes()
+	{
+	ASSERT(iProperties);
+	return iProperties->GetBytes();
+	}
+
+EXPORT_C TInt CMdaAudioOutputStream::KeepOpenAtEnd()
+	{
+	ASSERT(iProperties);
+	return iProperties->KeepOpenAtEnd();
+	}
+
+EXPORT_C TInt CMdaAudioOutputStream::RequestStop()
+	{
+	ASSERT(iProperties);
+	return iProperties->RequestStop();
+	}	
+	
+/**
+
+*/
+EXPORT_C void CMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType)
+	{
+	ASSERT(iProperties);
+	iProperties->SetDataTypeL(aAudioType);
+	}	
+
+
+/**
+
+*/
+EXPORT_C TFourCC CMdaAudioOutputStream::DataType() const
+	{
+	ASSERT(iProperties);
+	return iProperties->DataType();
+	}
+	
+EXPORT_C TAny* CMdaAudioOutputStream::CustomInterface(TUid aInterfaceId)
+	{
+	ASSERT(iProperties);
+	return iProperties->CustomInterface(aInterfaceId);
+	}
+	
+
+/**
+Registers the Event for Notification when resource is avaliable.
+
+@param  aCallback
+		The audio player observer interface.
+@param  aNotificationEventUid
+		The event uid to notify the client.
+@param  aNotificationRegistrationData
+		Notification registration specific data.
+
+@return An error code indicating if the registration was successful. KErrNone on success, 
+		otherwise another of the system-wide error codes.
+*/
+EXPORT_C TInt CMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback,
+																		TUid aNotificationEventUid,
+																		const TDesC8& aNotificationRegistrationData)
+	{
+	ASSERT(iProperties);
+	return iProperties->RegisterAudioResourceNotification(aCallback,aNotificationEventUid,aNotificationRegistrationData);
+	}
+
+/**
+Cancels the registered notification event.
+
+@param  aNotificationEventUid
+	The Event to notify the client.
+	
+@return An error code indicating if the registration was successful. KErrNone on success, 
+	otherwise another of the system-wide error codes.
+*/
+EXPORT_C TInt CMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventUid)	
+	{
+	ASSERT(iProperties);
+	return iProperties->CancelRegisterAudioResourceNotification(aNotificationEventUid);
+	}
+
+/**
+Waits for the client to resume the play even after the default timer expires.
+
+@return An error code indicating if the registration was successful. KErrNone on success, 
+		otherwise another of the system-wide error codes.
+*/
+EXPORT_C TInt CMdaAudioOutputStream::WillResumePlay()	
+	{
+	ASSERT(iProperties);
+	return iProperties->WillResumePlay();
+	}
+
+enum TMmAosPanic
+	{
+	EToneFinishedNotSupported,
+	EBufferToBeEmptiedNotSupported,
+	ERecordErrorNotSupported,
+	EConvertErrorNotSupported,
+	EDeviceMessageNotSupported,
+	EReservedCall,
+	EAOSStoppingError
+	};
+
+_LIT(KMmfMdaAosCategory, "CMmfMdaAudioOutputStream");
+LOCAL_C void Panic(const TMmAosPanic aReason)
+	{
+	User::Panic(KMmfMdaAosCategory, aReason);
+	}
+
+/**
+ *
+ * 2 phase construction function
+ *
+ */
+CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback)
+	{
+	return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality);
+	}
+
+CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback, TInt aPriority, TInt aPref)
+	{
+	CMMFMdaAudioOutputStream* self = new(ELeave) CMMFMdaAudioOutputStream(aCallback);
+	CleanupStack::PushL(self);
+	self->ConstructL(aPriority, aPref);
+	CleanupStack::Pop(); // self
+	return self;
+	}
+
+/**
+ *
+ * Construct
+ *
+ * @param	"MMdaAudioOutputStreamCallback&"
+ *			a reference to MMdaAudioOutputStreamCallback
+ * @param	"TInt aPriority"
+ *			a priority value			
+ * @param	"TInt aPref"
+ *			a perference value
+ *
+ */
+CMMFMdaAudioOutputStream::CMMFMdaAudioOutputStream(MMdaAudioOutputStreamCallback& aCallback)
+	: iCallback(aCallback), iState(EStopped)
+	{
+	iDataTypeCode.Set(TFourCC(' ','P','1','6'));
+	}
+
+/**
+ *
+ *	Second phase constructor
+ *
+ */
+void CMMFMdaAudioOutputStream::ConstructL(TInt aPriority, TInt aPref)
+	{
+	iDevSound = CMMFDevSound::NewL();
+	SetPriority(aPriority, aPref);
+	iDevSoundIgnoresUnderflow = iDevSound->QueryIgnoresUnderflow();
+	iFifo = new(ELeave) CMMFFifo<const TDesC8>();
+	iActiveCallback = new(ELeave) CActiveCallback(iCallback);
+	iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait;
+	iShutDownTimer = CPeriodic::NewL(CActive::EPriorityStandard);
+	}
+
+/**
+ *
+ *	Destructor
+ *
+ */
+CMMFMdaAudioOutputStream::~CMMFMdaAudioOutputStream()
+	{
+	delete iFifo;
+	delete iDevSound;
+	delete iActiveCallback;
+	delete iActiveSchedulerWait;
+	delete iShutDownTimer;
+	}
+
+/**
+ *
+ *  Set audio output stream properties	
+ *
+ *	@param	"TInt aSampleRate"	
+ *			a specified priority value
+ *	@param	"TInt aChannels"		
+ *			a specified preference value
+ *
+ */
+void CMMFMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
+	{
+	if (iIsOpenState==EIsOpen)
+		{
+		RealSetAudioPropertiesL(aSampleRate, aChannels);
+		}
+	else
+		{
+		// cache params for application later
+		iSampleRate = aSampleRate;
+		iChannels = aChannels;
+		iVolume = KUnknownVolume;
+		iValuesCached = ETrue;
+		}
+	}
+
+void CMMFMdaAudioOutputStream::RealSetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
+	{
+	TMMFCapabilities config = iDevSound->Config();
+	config.iChannels = StreamUtils::MapChannelsMdaToMMFL(aChannels);
+	config.iRate = StreamUtils::MapSampleRateMdaToMMFL(aSampleRate);
+	iDevSound->SetConfigL(config);
+	}
+
+/**
+ *
+ *  Open a audio ouptut stream	
+ *
+ *	@param	"TMdaPackage* Settings"	
+ *			a pointer point to TMdaPackage
+ *
+ */
+void CMMFMdaAudioOutputStream::Open(TMdaPackage* aSettings)
+	{
+	iIsOpenState = EIsOpening;
+	
+	// Use settings to set audio properties after the dev sound has been
+	// successfully initialised. Note if the InitializeL() fails, something calls
+	// InitializeComplete() and the iValuesCached flag is cleared. Also note
+	// that if SetAudioPropertiesL() has already been called, there may already
+	// be cached values
+	if (aSettings && aSettings->Type().iUid == KUidMdaDataTypeSettingsDefine)
+		{
+		TMdaAudioDataSettings& audioSettings = *STATIC_CAST(TMdaAudioDataSettings*, aSettings);
+		//Caching these values, which are later set in InitializeComplete
+		iSampleRate = audioSettings.iSampleRate;
+		iChannels = audioSettings.iChannels;
+		iVolume = audioSettings.iVolume;
+		iValuesCached = ETrue;
+		}
+
+	TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying));
+	if (err != KErrNone)
+		{
+		InitializeComplete(err);
+		}
+	}
+
+/**
+ *
+ *  To get the maximum volume level	
+ *
+ *	@return	"TInt"	
+ *			the maximum volume value in integer
+ *
+ */
+TInt CMMFMdaAudioOutputStream::MaxVolume()
+	{
+	return iDevSound->MaxVolume();
+	}
+
+/**
+ *
+ *  To get the current volume level	
+ *
+ *	@return	"TInt"	
+ *			the current volume value in integer
+ *
+ */
+TInt CMMFMdaAudioOutputStream::Volume()
+	{
+	return iDevSound->Volume();
+	} 
+
+/**
+ *
+ *  Set audio output stream volume to the specified value
+ *
+ *	@param	"TInt aVolume"	
+ *			a specified volume value
+ *
+ */
+void CMMFMdaAudioOutputStream::SetVolume(TInt aVolume)
+	{
+	iDevSound->SetVolume(aVolume);
+	}
+
+/**
+ *
+ *  Set audio output stream balance	
+ *
+ *	@param	"TInt aBalance"	
+ *			a specified balance value
+ *
+ */
+void CMMFMdaAudioOutputStream::SetBalanceL(TInt aBalance)
+	{
+	// test and clip balance to min / max range [-100 <-> 100]
+	// clip rather than leave as this isn't a leaving function
+	if (aBalance < KMMFBalanceMaxLeft) aBalance = KMMFBalanceMaxLeft;
+	if (aBalance > KMMFBalanceMaxRight) aBalance = KMMFBalanceMaxRight;
+
+	// separate out left and right balance
+	TInt left  = 0;
+	TInt right = 0;
+	StreamUtils::CalculateLeftRightBalance( left, right, aBalance );
+
+	// send the balance to SoundDevice
+	iDevSound->SetPlayBalanceL(left, right);
+	}
+
+/**
+ *
+ *  To get the current balance value. This function may not return the same value 
+ *	as passed to SetBalanceL depending on the internal implementation in 
+ *	the underlying components.
+ *	
+ *	@return	"TInt"	
+ *			the current balance value in integer
+ *
+ */
+TInt CMMFMdaAudioOutputStream::GetBalanceL() const
+	{
+	TInt rightBalance = 0;
+	TInt leftBalance  = 0;
+	iDevSound->GetPlayBalanceL(leftBalance, rightBalance);
+	TInt balance  = 0;
+	StreamUtils::CalculateBalance( balance, leftBalance, rightBalance );
+	return balance;
+	}
+
+/**
+ *
+ *  Set audio output stream priority	
+ *
+ *	@param	"TInt aPriority"	
+ *			a specified priority value
+ *	@param	"TMdaPriorityPreference aPref"		
+ *			a specified preference value
+ *
+ */
+void CMMFMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref)
+	{
+	TMMFPrioritySettings settings;
+	settings.iPriority = aPriority;
+	settings.iPref = aPref;
+	iDevSound->SetPrioritySettings(settings);
+	}
+
+/**
+ *
+ *  To write data to output stream 	
+ *  Note that if framed data eg gsm610 is being streamed then the buffer
+ *  size of aData should contain an intiger number of frames
+ *
+ *	@param	"const TDesC8& aData"	
+ *			a stream data 
+ *
+ */
+void CMMFMdaAudioOutputStream::WriteL(const TDesC8& aData)
+	{
+	if(iState==EStopping)
+		{
+		User::Leave(KErrNotReady);
+		}
+	iShutDownTimer->Cancel();
+	
+	TMMFFifoItem<const TDesC8>* item = new(ELeave) TMMFFifoItem<const TDesC8>(aData);
+	iFifo->AddToFifo(*item);
+
+	if(iState == EStopped)
+		StartPlayL();
+	else if((iState == EPlaying) && (iBuffer != NULL))
+		{ //if we are playing and we have a buffer waiting for data, use it.
+		BufferToBeFilled(iBuffer);
+		iBuffer = NULL;
+		}
+	if(iEventHolder != KNullUid)
+		{
+		TInt err = iDevSound->RegisterAsClient(iEventHolder,iNotificationDataHolder);
+		iEventHolder = KNullUid;
+		iNotificationDataHolder = KNullDesC8;
+		if(err != KErrNone)
+			{
+			iCallback.MaoscPlayComplete(err);
+			}
+		}
+	}
+
+/**
+ *
+ * If the audio stream is stopped, then notify the CDevSound to initilised play.
+ * The CDevSound will automatically start calling back for buffers.
+ */
+void CMMFMdaAudioOutputStream::StartPlayL()
+{
+	if (iState == EStopped)
+		{
+		iFifoItemPos = 0;
+		iCurrentSamplesPlayed = 0;
+		iDevSound->PlayInitL();
+		iState = EPlaying;
+		iBuffer = NULL;
+		}
+}
+
+
+/**
+ *
+ *  To stop write data to stream 	
+ *
+ */
+void CMMFMdaAudioOutputStream::Stop()
+	{
+	iShutDownTimer->Cancel();
+	EmptyFifo(KErrAbort);
+
+	if(iState == EStopped)
+		{
+		return;
+		}
+		
+	// stop the operation
+	iDevSound->Stop();
+	iState = EStopped;
+	iBuffer = NULL;
+
+	iCallback.MaoscPlayComplete(KErrCancel);
+	}
+
+TInt CMMFMdaAudioOutputStream::RequestStop()
+	{
+	if(!iKeepOpenAtEnd)
+		{
+		return KErrNotSupported;
+		}
+	
+	if(iState==EStopped || iState==EStopping)
+		{
+		return KErrNotReady;
+		}
+		
+	if(iBuffer)
+		{//means all the client buffers are used up and waiting for more data
+		CMMFDataBuffer* dataBuffer = static_cast<CMMFDataBuffer*>(iBuffer);
+		dataBuffer->Data().SetLength(0);
+		dataBuffer->SetLastBuffer(ETrue);
+		iDevSound->PlayData();
+		iBuffer=NULL;
+		}
+	iState = EStopping;
+	return KErrNone;
+	}
+
+TInt CMMFMdaAudioOutputStream::KeepOpenAtEnd()
+	{
+	if(!iDevSoundIgnoresUnderflow)
+		{
+		return KErrNotSupported;
+		}
+	else
+		{
+		iKeepOpenAtEnd = ETrue;
+		return KErrNone;
+		}
+	}
+
+/**
+ *
+ *  To pause send data to stream 	
+ *
+ */
+TInt CMMFMdaAudioOutputStream::Pause()
+	{
+	if(iState != EPlaying)
+		{
+		return KErrNotReady;
+		}
+
+	else if(!iDevSound->IsResumeSupported())
+		{
+		return KErrNotSupported;
+		}
+	
+	// pause the operation
+	iDevSound->Pause();
+	iState = EPaused;
+	return KErrNone;
+	}
+
+/**
+ *
+ *  To resume send data to stream 	
+ *
+ */
+TInt CMMFMdaAudioOutputStream::Resume()
+	{
+	TInt err = KErrNone;
+	if(iState != EPaused)
+		{
+		err = KErrNotReady;
+		}
+	else if(!iDevSound->IsResumeSupported())
+		{
+		err = KErrNotSupported;
+		}
+	
+	// resume the operation
+	if(err == KErrNone)
+		{
+		err = iDevSound->Resume();
+		}
+	if(err == KErrNone)
+		{
+		iState = EPlaying;
+		}
+
+	return err;
+	}
+
+
+/**
+ *
+ *  To get the current position in the data stream	
+ *
+ *	@return	"TTimeIntervalMicroSeconds&"	
+ *			the current position in integer
+ *
+ */
+const TTimeIntervalMicroSeconds& CMMFMdaAudioOutputStream::Position()
+	{
+	TInt64 position = iDevSound->SamplesPlayed();
+	position = position * KMicroSecsInOneSec / StreamUtils::SampleRateAsValue(iDevSound->Config());
+	iPosition = (iState == EPlaying || iState == EStopping || iState == EPaused) ? position : 0; // Shouldn't need to check for playing but CMMFDevSound doesn't reset bytes played after a stop
+	return iPosition;
+	}
+
+/**
+ *
+ *  To return the current number of bytes rendered by audio hardware
+ *	@return "the current current number of bytes rendered by audio hardware in integer"	
+ *
+ */
+TInt CMMFMdaAudioOutputStream::GetBytes()
+	{
+	return iDevSound->SamplesPlayed() * StreamUtils::BytesPerSample(iDevSound->Config());
+	}
+
+/**
+
+*/
+void CMMFMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType)
+	{
+	if(iState != EStopped)
+		User::Leave(KErrServerBusy);
+
+	if(aAudioType == iDataTypeCode)
+		return;
+
+	TMMFPrioritySettings prioritySettings; 	
+	prioritySettings.iState = EMMFStatePlaying;
+	RArray<TFourCC> supportedDataTypes;
+		
+	CleanupClosePushL(supportedDataTypes);
+	
+	TRAPD(err, iDevSound->GetSupportedInputDataTypesL(supportedDataTypes, prioritySettings));
+
+	if (err == KErrNone)
+		{
+		if (supportedDataTypes.Find(aAudioType) == KErrNotFound)
+			{
+			User::Leave(KErrNotSupported);	
+			}
+		//if match, set the 4CC of AudioType to match
+		iDataTypeCode.Set(aAudioType);
+		}
+	else  //we had a real leave error from GetSupportedOuputDataTypesL
+		{
+		User::Leave(err);
+		}
+	
+	CleanupStack::PopAndDestroy(&supportedDataTypes);
+
+	if(iIsOpenState!=EIsNotOpen)
+		{
+		// need to recall or restart InitializeL() process
+		iDevSound->CancelInitialize(); // call just in case mid-InitializeL. No harm if not. 
+									   // if not supported then assume old DevSound behaviour anyway
+									   // where InitializeL() implicitly cancels, so no harm either way
+		iIsOpenState = EIsOpening;
+		iInitCallFrmSetDataType = ETrue;
+		TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying));
+		if (err != KErrNone)
+			{
+			// Leave if error.
+			iIsOpenState = EIsNotOpen;
+			iInitCallFrmSetDataType = EFalse;
+			User::Leave(err);
+			}
+		// In some implementations InitializeComplete is sent 
+		// in context, so check before starting activeSchedulerWait.
+		else if(iIsOpenState == EIsOpening)
+			{
+			iInitializeState = KRequestPending;
+			iActiveSchedulerWait->Start();
+			}
+		iInitCallFrmSetDataType = EFalse;
+		User::LeaveIfError(iInitializeState);
+		}	
+	}
+
+/**
+
+*/
+TFourCC CMMFMdaAudioOutputStream::DataType() const
+	{
+	return iDataTypeCode;
+	}
+
+TInt CMMFMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback,TUid aNotificationEventUid,const TDesC8& aNotificationRegistrationData)
+	{
+	iAudioResourceNotificationCallBack = &aCallback;
+	TInt err = iDevSound->RegisterAsClient(aNotificationEventUid,aNotificationRegistrationData);
+	if(err == KErrNotReady)
+		{
+		iEventHolder = 	aNotificationEventUid;
+		iNotificationDataHolder = aNotificationRegistrationData;
+		return KErrNone;
+		}
+	iEventHolder = KNullUid;
+	iNotificationDataHolder = KNullDesC8;
+	return err;
+	}
+
+TInt CMMFMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventId)
+	{
+	TInt err = iDevSound->CancelRegisterAsClient(aNotificationEventId);
+	if(err == KErrNotReady)
+		{
+		if(aNotificationEventId != KMMFEventCategoryAudioResourceAvailable)	
+			{
+			return KErrNotSupported;
+			}
+		if(iEventHolder == KNullUid)	
+			{
+			return KErrCancel;
+			}		
+		iEventHolder = KNullUid;
+		iNotificationDataHolder = KNullDesC8;
+		return KErrNone;
+		}
+	return err;
+	}
+	
+TInt CMMFMdaAudioOutputStream::WillResumePlay()
+	{
+	return iDevSound->WillResumePlay();
+	}
+
+/**
+ *
+ *  To be called when intialize stream complete	
+ *
+ *	@param	"TInt aError"	
+ *			error code, initialize stream succeed when aError = 0
+ *
+ */
+void CMMFMdaAudioOutputStream::InitializeComplete(TInt aError)
+	{
+	TInt err = aError;
+	if(err == KErrNone && iValuesCached)
+		{
+		TRAP(err, RealSetAudioPropertiesL(iSampleRate, iChannels));
+		if(err == KErrNone && iVolume>=0)
+			{
+			SetVolume(iVolume);
+			}
+		}
+	iValuesCached = EFalse; // whatever clear our cache
+	if(iIsOpenState == EIsOpening)
+		{
+		// Signal for the MaoscOpenComplete callback to be called asynchronously.Ignore if InitializeL is called from SetDataTypeL
+		if(!iInitCallFrmSetDataType)
+			{
+			iActiveCallback->Signal(err);
+			}
+		iIsOpenState = err ? EIsNotOpen : EIsOpen;
+		if(iInitializeState == KRequestPending)
+			{
+			iInitializeState = err;
+			iActiveSchedulerWait->AsyncStop();
+			}
+		else
+			{
+			iInitializeState = err;//Set the error if InitializeComplete is called in context of InitializeL.
+			}
+		}
+	}
+
+/**
+ *
+ *  Do not support
+ *
+ */
+void CMMFMdaAudioOutputStream::ToneFinished(TInt /*aError*/)
+	{
+	Panic(EToneFinishedNotSupported);
+	}
+
+/**
+ *
+ *  Called when sound device need data	
+ *
+ *	@param	"CMMFBuffer* aBuffer"	
+ *			a pointer point to CMMFBuffer, which is used to stored data
+ *
+ */
+void CMMFMdaAudioOutputStream::BufferToBeFilled(CMMFBuffer* aBuffer)
+	{
+	if (iState == EPlaying || iState == EStopping)
+		{
+		TMMFFifoItem<const TDesC8>* firstItem = iFifo->Get();
+		//ASSERT firstItem != NULL
+		if (iFifoItemPos >= firstItem->GetData().Length())
+			{
+			// We've played all segments of the first buffer in the fifo so we can delete it
+			iFifo->RemoveFirstItem();
+			iCallback.MaoscBufferCopied(KErrNone, firstItem->GetData());
+			delete firstItem;
+			iFifoItemPos = 0;
+			}
+		}
+	TMMFFifoItem<const TDesC8>* firstItem = iFifo->Get();
+
+	if (firstItem)
+		{
+		// Fill aBuffer with the next segment of the first buffer in the fifo
+		TDes8& fillDes = STATIC_CAST(CMMFDataBuffer*, aBuffer)->Data();
+		const TDesC8& readDes = firstItem->GetData();
+		TInt readLen = Min(readDes.Length()-iFifoItemPos, aBuffer->RequestSize());
+		fillDes = readDes.Mid(iFifoItemPos, readLen);
+		iFifoItemPos+=readLen; // so we know where the next segment in the fifo item starts
+		// Notify iDevSound the buffer is ready to be played
+		iDevSound->PlayData();
+		}
+	else 
+		{
+		if(iBuffer ==NULL)
+			{
+			//keep a record of the supplied buffer and use it when/if more user data is in the FIFO
+			iBuffer = aBuffer; 
+			}
+		if(!iKeepOpenAtEnd)
+			{
+			if (iDevSoundIgnoresUnderflow)
+				{
+				iCurrentSamplesPlayed = iDevSound->SamplesPlayed();
+				StartShutDownTimer();
+				}
+			}
+		else if(iState==EStopping)
+			{
+			CMMFDataBuffer* dataBuffer = static_cast<CMMFDataBuffer*>(aBuffer);
+			dataBuffer->Data().SetLength(0);
+			dataBuffer->SetLastBuffer(ETrue);
+			iDevSound->PlayData();
+			iBuffer=NULL;
+			}
+		}
+	 
+	}
+
+void CMMFMdaAudioOutputStream::StartShutDownTimer()
+	{
+	iShutDownTimer->Start(KShutDownTimeInterval, KShutDownTimeInterval, TCallBack(ShutDownTimerComplete,this));
+	}
+	
+TInt CMMFMdaAudioOutputStream::ShutDownTimerComplete(TAny* aAudioOutputStream)
+	{
+	CMMFMdaAudioOutputStream* audioOutputStream = static_cast<CMMFMdaAudioOutputStream*>(aAudioOutputStream);
+	audioOutputStream->DoShutDownTimerComplete();
+	return KErrNone;
+	}
+
+void CMMFMdaAudioOutputStream::DoShutDownTimerComplete()
+	{
+	iShutDownTimer->Cancel();
+	TInt samplesPlayed = iDevSound->SamplesPlayed();
+	if (samplesPlayed == iCurrentSamplesPlayed)
+		{
+		iDevSound->Stop();
+		iState = EStopped;
+		iBuffer = NULL;
+		iCallback.MaoscPlayComplete(KErrUnderflow);
+		}
+	else
+		{//desvound has not yet finished playing all the data. So wait for one more cycle
+		//Restart Timer
+		iCurrentSamplesPlayed = samplesPlayed;
+		StartShutDownTimer();
+		}
+	}
+
+/**
+ *
+ *  Called when play operation complete, successfully or otherwise	
+ *
+ *	@param	"TInt aError"	
+ *			an error value which will indicate playing successfully complete
+ *			if error value is 0
+ *
+ */
+void CMMFMdaAudioOutputStream::PlayError(TInt aError)
+	{
+	TInt err=aError;
+	 // if iDevSoundIgnoresUnderflow is true, then KErrUnderflow should not be produced by DevSound when we are not stopping 
+	__ASSERT_DEBUG(!iDevSoundIgnoresUnderflow || !(err==KErrUnderflow && iState!=EStopping), Panic(EAOSStoppingError));
+	iState = EStopped;
+	iBuffer = NULL;
+	iShutDownTimer->Cancel();
+	if (err == KErrNone || err == KErrUnderflow)
+		{
+		if (!iFifo->IsEmpty())
+			{
+			// We live again - the Fifo still has some data. The sound device
+			// will have to be opened again though so there might be an
+			// audible click.
+			TRAP(err, StartPlayL());
+			if (err == KErrNone)
+				{
+				return;
+				}
+			}
+		}
+	
+	EmptyFifo(err);
+	
+	// Note - KErrUnderflow will be reported even when all the buffers have
+	// successfully played
+	iCallback.MaoscPlayComplete(err);
+	}
+
+void CMMFMdaAudioOutputStream::EmptyFifo(TInt aError)
+	{
+	// Delete all buffers in the fifo and notify the observer
+	TMMFFifoItem<const TDesC8>* firstItem;
+	while((firstItem = iFifo->Get()) != NULL)
+		{
+		iFifo->RemoveFirstItem();
+		iCallback.MaoscBufferCopied(aError, firstItem->GetData());
+		delete firstItem;
+		}
+	}
+
+void CMMFMdaAudioOutputStream::SendEventToClient(const TMMFEvent& aEvent)
+	{
+	if (aEvent.iEventType == KMMFEventCategoryAudioResourceAvailable)
+		{
+		// Retrieve the number of samples played
+		// For the event type KMMFEventCategoryAudioResourceAvailable GetResourceNotificationData() returns
+		// a package buffer as TMMFTimeIntervalMicroSecondsPckg, but the contents should be 
+		// converted to an integer and interpreted as the data returned is samples played,
+		// but not as a microsecond value.
+		TBuf8<TMMFAudioConfig::KNotificationDataBufferSize> notificationData;
+		if (KErrNone != iDevSound->GetResourceNotificationData(aEvent.iEventType, notificationData))
+			{
+			notificationData.SetLength(0);
+			}
+		iAudioResourceNotificationCallBack->MarncResourceAvailable(aEvent.iEventType, notificationData);
+		}
+	}
+
+CMMFMdaAudioOutputStream::CActiveCallback::~CActiveCallback()
+	{
+	Cancel();
+	}
+
+CMMFMdaAudioOutputStream::CActiveCallback::CActiveCallback(MMdaAudioOutputStreamCallback& aCallback)
+	: CActive(EPriorityStandard), iCallback(aCallback)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+void CMMFMdaAudioOutputStream::CActiveCallback::RunL()
+	{
+	iCallback.MaoscOpenComplete(iStatus.Int());
+	}
+
+void CMMFMdaAudioOutputStream::CActiveCallback::DoCancel()
+	{
+	}
+
+void CMMFMdaAudioOutputStream::CActiveCallback::Signal(const TInt aReason)
+	{
+	ASSERT(!IsActive());
+
+	// Signal ourselves to run with the given completion code
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, aReason);
+	SetActive();
+	}
+
+
+/**
+ *
+ *  Do not support
+ *
+ */
+void CMMFMdaAudioOutputStream::BufferToBeEmptied(CMMFBuffer* /*aBuffer*/)
+	{
+	Panic(EBufferToBeEmptiedNotSupported);
+	}
+
+/**
+ *
+ *  Do not support
+ *
+ */
+void CMMFMdaAudioOutputStream::RecordError(TInt /*aError*/)
+	{
+	Panic(ERecordErrorNotSupported);
+	}
+
+/**
+ *
+ *  Do not support
+ *
+ */
+void CMMFMdaAudioOutputStream::ConvertError(TInt /*aError*/)
+	{
+	Panic(EConvertErrorNotSupported);
+	}
+
+/**
+ *
+ *  Do not support
+ *
+ */
+void CMMFMdaAudioOutputStream::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/)
+	{
+	Panic(EDeviceMessageNotSupported);
+	}
+
+// CustomInferface - just pass on to DevSound. 
+TAny* CMMFMdaAudioOutputStream::CustomInterface(TUid aInterfaceId)
+	{
+	return iDevSound->CustomInterface(aInterfaceId);
+	}
+