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