os/mm/mmlibs/mmfw/src/Client/Audio/mmfclientaudiooutputstream.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 //
    15 
    16 #include <mdaaudiooutputstream.h>
    17 #include <mda/common/audio.h>
    18 #include "mmfclientaudiooutputstream.h"
    19 #include "mmfclientaudiostreamutils.h"
    20 #include "MmfFifo.h"
    21 #include <mmf/common/mmfpaniccodes.h>
    22 
    23 #define WAIT_FOR_READY_ACTIVE_OBJECTS
    24 
    25 const TInt KMicroSecsInOneSec = 1000000; 
    26 const TInt KUnknownVolume = -1; // means "don't set", must be negative
    27 const TInt KShutDownTimeInterval = 100000; //100 milli seconds
    28 
    29 /**
    30  *
    31  * Static NewL
    32  *
    33  * @return CMdaAudioOutputStream*
    34  *
    35  */
    36 EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback,
    37 															CMdaServer* /*aServer = NULL*/)
    38 	{
    39 	return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality);
    40 	}
    41 
    42 /**
    43  *
    44  * Static NewL
    45  *
    46  * @return CMdaAudioOutputStream*
    47  *
    48  */
    49 EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback,
    50 															TInt aPriority,
    51 															TInt aPref /*=EMdaPriorityPreferenceTimeAndQuality*/)
    52 	{
    53 	CMdaAudioOutputStream* self = new(ELeave) CMdaAudioOutputStream();
    54 	CleanupStack::PushL(self);
    55 	self->iProperties = CMMFMdaAudioOutputStream::NewL(aCallback, aPriority, aPref);
    56 	CleanupStack::Pop(self);
    57 	return self;
    58 	}
    59 
    60 CMdaAudioOutputStream::CMdaAudioOutputStream()
    61 	{
    62 	}
    63 
    64 CMdaAudioOutputStream::~CMdaAudioOutputStream()
    65 	{
    66 	delete iProperties;
    67 	}
    68 
    69 void CMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
    70 	{
    71 	ASSERT(iProperties);
    72 	iProperties->SetAudioPropertiesL(aSampleRate, aChannels);
    73 	}
    74 
    75 void CMdaAudioOutputStream::Open(TMdaPackage* aSettings)
    76 	{
    77 	ASSERT(iProperties);
    78 	iProperties->Open(aSettings);
    79 	}
    80 
    81 TInt CMdaAudioOutputStream::MaxVolume()
    82 	{
    83 	ASSERT(iProperties);
    84 	return iProperties->MaxVolume();
    85 	}
    86 
    87 TInt CMdaAudioOutputStream::Volume()
    88 	{
    89 	ASSERT(iProperties);
    90 	return iProperties->Volume();
    91 	}
    92 
    93 void CMdaAudioOutputStream::SetVolume(const TInt aNewVolume)
    94 	{
    95 	ASSERT(iProperties);
    96 	iProperties->SetVolume(aNewVolume);
    97 	}
    98 
    99 void CMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref)
   100 	{
   101 	ASSERT(iProperties);
   102 	iProperties->SetPriority(aPriority, aPref);
   103 	}
   104 
   105 void CMdaAudioOutputStream::WriteL(const TDesC8& aData)
   106 	{
   107 	ASSERT(iProperties);
   108 	iProperties->WriteL(aData);
   109 	}
   110 
   111 void CMdaAudioOutputStream::Stop()
   112 	{
   113 	ASSERT(iProperties);
   114 	iProperties->Stop();
   115 	}
   116 
   117 EXPORT_C TInt CMdaAudioOutputStream::Pause()
   118 	{
   119 	ASSERT(iProperties);
   120 	return iProperties->Pause();
   121 	}
   122 
   123 EXPORT_C TInt CMdaAudioOutputStream::Resume()
   124 	{
   125 	ASSERT(iProperties);
   126 	return iProperties->Resume();
   127 	}
   128 
   129 const TTimeIntervalMicroSeconds& CMdaAudioOutputStream::Position()
   130 	{
   131 	ASSERT(iProperties);
   132 	return iProperties->Position();
   133 	}
   134 
   135 EXPORT_C void CMdaAudioOutputStream::SetBalanceL(TInt aBalance)
   136 	{
   137 	ASSERT(iProperties);
   138 	iProperties->SetBalanceL(aBalance);
   139 	}
   140 
   141 EXPORT_C TInt CMdaAudioOutputStream::GetBalanceL() const
   142 	{
   143 	ASSERT(iProperties);
   144 	return iProperties->GetBalanceL();
   145 	}
   146 
   147 EXPORT_C TInt CMdaAudioOutputStream::GetBytes()
   148 	{
   149 	ASSERT(iProperties);
   150 	return iProperties->GetBytes();
   151 	}
   152 
   153 EXPORT_C TInt CMdaAudioOutputStream::KeepOpenAtEnd()
   154 	{
   155 	ASSERT(iProperties);
   156 	return iProperties->KeepOpenAtEnd();
   157 	}
   158 
   159 EXPORT_C TInt CMdaAudioOutputStream::RequestStop()
   160 	{
   161 	ASSERT(iProperties);
   162 	return iProperties->RequestStop();
   163 	}	
   164 	
   165 /**
   166 
   167 */
   168 EXPORT_C void CMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType)
   169 	{
   170 	ASSERT(iProperties);
   171 	iProperties->SetDataTypeL(aAudioType);
   172 	}	
   173 
   174 
   175 /**
   176 
   177 */
   178 EXPORT_C TFourCC CMdaAudioOutputStream::DataType() const
   179 	{
   180 	ASSERT(iProperties);
   181 	return iProperties->DataType();
   182 	}
   183 	
   184 EXPORT_C TAny* CMdaAudioOutputStream::CustomInterface(TUid aInterfaceId)
   185 	{
   186 	ASSERT(iProperties);
   187 	return iProperties->CustomInterface(aInterfaceId);
   188 	}
   189 	
   190 
   191 /**
   192 Registers the Event for Notification when resource is avaliable.
   193 
   194 @param  aCallback
   195 		The audio player observer interface.
   196 @param  aNotificationEventUid
   197 		The event uid to notify the client.
   198 @param  aNotificationRegistrationData
   199 		Notification registration specific data.
   200 
   201 @return An error code indicating if the registration was successful. KErrNone on success, 
   202 		otherwise another of the system-wide error codes.
   203 */
   204 EXPORT_C TInt CMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback,
   205 																		TUid aNotificationEventUid,
   206 																		const TDesC8& aNotificationRegistrationData)
   207 	{
   208 	ASSERT(iProperties);
   209 	return iProperties->RegisterAudioResourceNotification(aCallback,aNotificationEventUid,aNotificationRegistrationData);
   210 	}
   211 
   212 /**
   213 Cancels the registered notification event.
   214 
   215 @param  aNotificationEventUid
   216 	The Event to notify the client.
   217 	
   218 @return An error code indicating if the registration was successful. KErrNone on success, 
   219 	otherwise another of the system-wide error codes.
   220 */
   221 EXPORT_C TInt CMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventUid)	
   222 	{
   223 	ASSERT(iProperties);
   224 	return iProperties->CancelRegisterAudioResourceNotification(aNotificationEventUid);
   225 	}
   226 
   227 /**
   228 Waits for the client to resume the play even after the default timer expires.
   229 
   230 @return An error code indicating if the registration was successful. KErrNone on success, 
   231 		otherwise another of the system-wide error codes.
   232 */
   233 EXPORT_C TInt CMdaAudioOutputStream::WillResumePlay()	
   234 	{
   235 	ASSERT(iProperties);
   236 	return iProperties->WillResumePlay();
   237 	}
   238 
   239 enum TMmAosPanic
   240 	{
   241 	EToneFinishedNotSupported,
   242 	EBufferToBeEmptiedNotSupported,
   243 	ERecordErrorNotSupported,
   244 	EConvertErrorNotSupported,
   245 	EDeviceMessageNotSupported,
   246 	EReservedCall,
   247 	EAOSStoppingError
   248 	};
   249 
   250 _LIT(KMmfMdaAosCategory, "CMmfMdaAudioOutputStream");
   251 LOCAL_C void Panic(const TMmAosPanic aReason)
   252 	{
   253 	User::Panic(KMmfMdaAosCategory, aReason);
   254 	}
   255 
   256 /**
   257  *
   258  * 2 phase construction function
   259  *
   260  */
   261 CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback)
   262 	{
   263 	return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality);
   264 	}
   265 
   266 CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback, TInt aPriority, TInt aPref)
   267 	{
   268 	CMMFMdaAudioOutputStream* self = new(ELeave) CMMFMdaAudioOutputStream(aCallback);
   269 	CleanupStack::PushL(self);
   270 	self->ConstructL(aPriority, aPref);
   271 	CleanupStack::Pop(); // self
   272 	return self;
   273 	}
   274 
   275 /**
   276  *
   277  * Construct
   278  *
   279  * @param	"MMdaAudioOutputStreamCallback&"
   280  *			a reference to MMdaAudioOutputStreamCallback
   281  * @param	"TInt aPriority"
   282  *			a priority value			
   283  * @param	"TInt aPref"
   284  *			a perference value
   285  *
   286  */
   287 CMMFMdaAudioOutputStream::CMMFMdaAudioOutputStream(MMdaAudioOutputStreamCallback& aCallback)
   288 	: iCallback(aCallback), iState(EStopped)
   289 	{
   290 	iDataTypeCode.Set(TFourCC(' ','P','1','6'));
   291 	}
   292 
   293 /**
   294  *
   295  *	Second phase constructor
   296  *
   297  */
   298 void CMMFMdaAudioOutputStream::ConstructL(TInt aPriority, TInt aPref)
   299 	{
   300 	iDevSound = CMMFDevSound::NewL();
   301 	SetPriority(aPriority, aPref);
   302 	iDevSoundIgnoresUnderflow = iDevSound->QueryIgnoresUnderflow();
   303 	iFifo = new(ELeave) CMMFFifo<const TDesC8>();
   304 	iActiveCallback = new(ELeave) CActiveCallback(iCallback);
   305 	iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait;
   306 	iShutDownTimer = CPeriodic::NewL(CActive::EPriorityStandard);
   307 	}
   308 
   309 /**
   310  *
   311  *	Destructor
   312  *
   313  */
   314 CMMFMdaAudioOutputStream::~CMMFMdaAudioOutputStream()
   315 	{
   316 	delete iFifo;
   317 	delete iDevSound;
   318 	delete iActiveCallback;
   319 	delete iActiveSchedulerWait;
   320 	delete iShutDownTimer;
   321 	}
   322 
   323 /**
   324  *
   325  *  Set audio output stream properties	
   326  *
   327  *	@param	"TInt aSampleRate"	
   328  *			a specified priority value
   329  *	@param	"TInt aChannels"		
   330  *			a specified preference value
   331  *
   332  */
   333 void CMMFMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
   334 	{
   335 	if (iIsOpenState==EIsOpen)
   336 		{
   337 		RealSetAudioPropertiesL(aSampleRate, aChannels);
   338 		}
   339 	else
   340 		{
   341 		// cache params for application later
   342 		iSampleRate = aSampleRate;
   343 		iChannels = aChannels;
   344 		iVolume = KUnknownVolume;
   345 		iValuesCached = ETrue;
   346 		}
   347 	}
   348 
   349 void CMMFMdaAudioOutputStream::RealSetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
   350 	{
   351 	TMMFCapabilities config = iDevSound->Config();
   352 	config.iChannels = StreamUtils::MapChannelsMdaToMMFL(aChannels);
   353 	config.iRate = StreamUtils::MapSampleRateMdaToMMFL(aSampleRate);
   354 	iDevSound->SetConfigL(config);
   355 	}
   356 
   357 /**
   358  *
   359  *  Open a audio ouptut stream	
   360  *
   361  *	@param	"TMdaPackage* Settings"	
   362  *			a pointer point to TMdaPackage
   363  *
   364  */
   365 void CMMFMdaAudioOutputStream::Open(TMdaPackage* aSettings)
   366 	{
   367 	iIsOpenState = EIsOpening;
   368 	
   369 	// Use settings to set audio properties after the dev sound has been
   370 	// successfully initialised. Note if the InitializeL() fails, something calls
   371 	// InitializeComplete() and the iValuesCached flag is cleared. Also note
   372 	// that if SetAudioPropertiesL() has already been called, there may already
   373 	// be cached values
   374 	if (aSettings && aSettings->Type().iUid == KUidMdaDataTypeSettingsDefine)
   375 		{
   376 		TMdaAudioDataSettings& audioSettings = *STATIC_CAST(TMdaAudioDataSettings*, aSettings);
   377 		//Caching these values, which are later set in InitializeComplete
   378 		iSampleRate = audioSettings.iSampleRate;
   379 		iChannels = audioSettings.iChannels;
   380 		iVolume = audioSettings.iVolume;
   381 		iValuesCached = ETrue;
   382 		}
   383 
   384 	TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying));
   385 	if (err != KErrNone)
   386 		{
   387 		InitializeComplete(err);
   388 		}
   389 	}
   390 
   391 /**
   392  *
   393  *  To get the maximum volume level	
   394  *
   395  *	@return	"TInt"	
   396  *			the maximum volume value in integer
   397  *
   398  */
   399 TInt CMMFMdaAudioOutputStream::MaxVolume()
   400 	{
   401 	return iDevSound->MaxVolume();
   402 	}
   403 
   404 /**
   405  *
   406  *  To get the current volume level	
   407  *
   408  *	@return	"TInt"	
   409  *			the current volume value in integer
   410  *
   411  */
   412 TInt CMMFMdaAudioOutputStream::Volume()
   413 	{
   414 	return iDevSound->Volume();
   415 	} 
   416 
   417 /**
   418  *
   419  *  Set audio output stream volume to the specified value
   420  *
   421  *	@param	"TInt aVolume"	
   422  *			a specified volume value
   423  *
   424  */
   425 void CMMFMdaAudioOutputStream::SetVolume(TInt aVolume)
   426 	{
   427 	iDevSound->SetVolume(aVolume);
   428 	}
   429 
   430 /**
   431  *
   432  *  Set audio output stream balance	
   433  *
   434  *	@param	"TInt aBalance"	
   435  *			a specified balance value
   436  *
   437  */
   438 void CMMFMdaAudioOutputStream::SetBalanceL(TInt aBalance)
   439 	{
   440 	// test and clip balance to min / max range [-100 <-> 100]
   441 	// clip rather than leave as this isn't a leaving function
   442 	if (aBalance < KMMFBalanceMaxLeft) aBalance = KMMFBalanceMaxLeft;
   443 	if (aBalance > KMMFBalanceMaxRight) aBalance = KMMFBalanceMaxRight;
   444 
   445 	// separate out left and right balance
   446 	TInt left  = 0;
   447 	TInt right = 0;
   448 	StreamUtils::CalculateLeftRightBalance( left, right, aBalance );
   449 
   450 	// send the balance to SoundDevice
   451 	iDevSound->SetPlayBalanceL(left, right);
   452 	}
   453 
   454 /**
   455  *
   456  *  To get the current balance value. This function may not return the same value 
   457  *	as passed to SetBalanceL depending on the internal implementation in 
   458  *	the underlying components.
   459  *	
   460  *	@return	"TInt"	
   461  *			the current balance value in integer
   462  *
   463  */
   464 TInt CMMFMdaAudioOutputStream::GetBalanceL() const
   465 	{
   466 	TInt rightBalance = 0;
   467 	TInt leftBalance  = 0;
   468 	iDevSound->GetPlayBalanceL(leftBalance, rightBalance);
   469 	TInt balance  = 0;
   470 	StreamUtils::CalculateBalance( balance, leftBalance, rightBalance );
   471 	return balance;
   472 	}
   473 
   474 /**
   475  *
   476  *  Set audio output stream priority	
   477  *
   478  *	@param	"TInt aPriority"	
   479  *			a specified priority value
   480  *	@param	"TMdaPriorityPreference aPref"		
   481  *			a specified preference value
   482  *
   483  */
   484 void CMMFMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref)
   485 	{
   486 	TMMFPrioritySettings settings;
   487 	settings.iPriority = aPriority;
   488 	settings.iPref = aPref;
   489 	iDevSound->SetPrioritySettings(settings);
   490 	}
   491 
   492 /**
   493  *
   494  *  To write data to output stream 	
   495  *  Note that if framed data eg gsm610 is being streamed then the buffer
   496  *  size of aData should contain an intiger number of frames
   497  *
   498  *	@param	"const TDesC8& aData"	
   499  *			a stream data 
   500  *
   501  */
   502 void CMMFMdaAudioOutputStream::WriteL(const TDesC8& aData)
   503 	{
   504 	if(iState==EStopping)
   505 		{
   506 		User::Leave(KErrNotReady);
   507 		}
   508 	iShutDownTimer->Cancel();
   509 	
   510 	TMMFFifoItem<const TDesC8>* item = new(ELeave) TMMFFifoItem<const TDesC8>(aData);
   511 	iFifo->AddToFifo(*item);
   512 
   513 	if(iState == EStopped)
   514 		StartPlayL();
   515 	else if((iState == EPlaying) && (iBuffer != NULL))
   516 		{ //if we are playing and we have a buffer waiting for data, use it.
   517 		BufferToBeFilled(iBuffer);
   518 		iBuffer = NULL;
   519 		}
   520 	if(iEventHolder != KNullUid)
   521 		{
   522 		TInt err = iDevSound->RegisterAsClient(iEventHolder,iNotificationDataHolder);
   523 		iEventHolder = KNullUid;
   524 		iNotificationDataHolder = KNullDesC8;
   525 		if(err != KErrNone)
   526 			{
   527 			iCallback.MaoscPlayComplete(err);
   528 			}
   529 		}
   530 	}
   531 
   532 /**
   533  *
   534  * If the audio stream is stopped, then notify the CDevSound to initilised play.
   535  * The CDevSound will automatically start calling back for buffers.
   536  */
   537 void CMMFMdaAudioOutputStream::StartPlayL()
   538 {
   539 	if (iState == EStopped)
   540 		{
   541 		iFifoItemPos = 0;
   542 		iCurrentSamplesPlayed = 0;
   543 		iDevSound->PlayInitL();
   544 		iState = EPlaying;
   545 		iBuffer = NULL;
   546 		}
   547 }
   548 
   549 
   550 /**
   551  *
   552  *  To stop write data to stream 	
   553  *
   554  */
   555 void CMMFMdaAudioOutputStream::Stop()
   556 	{
   557 	iShutDownTimer->Cancel();
   558 	EmptyFifo(KErrAbort);
   559 
   560 	if(iState == EStopped)
   561 		{
   562 		return;
   563 		}
   564 		
   565 	// stop the operation
   566 	iDevSound->Stop();
   567 	iState = EStopped;
   568 	iBuffer = NULL;
   569 
   570 	iCallback.MaoscPlayComplete(KErrCancel);
   571 	}
   572 
   573 TInt CMMFMdaAudioOutputStream::RequestStop()
   574 	{
   575 	if(!iKeepOpenAtEnd)
   576 		{
   577 		return KErrNotSupported;
   578 		}
   579 	
   580 	if(iState==EStopped || iState==EStopping)
   581 		{
   582 		return KErrNotReady;
   583 		}
   584 		
   585 	if(iBuffer)
   586 		{//means all the client buffers are used up and waiting for more data
   587 		CMMFDataBuffer* dataBuffer = static_cast<CMMFDataBuffer*>(iBuffer);
   588 		dataBuffer->Data().SetLength(0);
   589 		dataBuffer->SetLastBuffer(ETrue);
   590 		iDevSound->PlayData();
   591 		iBuffer=NULL;
   592 		}
   593 	iState = EStopping;
   594 	return KErrNone;
   595 	}
   596 
   597 TInt CMMFMdaAudioOutputStream::KeepOpenAtEnd()
   598 	{
   599 	if(!iDevSoundIgnoresUnderflow)
   600 		{
   601 		return KErrNotSupported;
   602 		}
   603 	else
   604 		{
   605 		iKeepOpenAtEnd = ETrue;
   606 		return KErrNone;
   607 		}
   608 	}
   609 
   610 /**
   611  *
   612  *  To pause send data to stream 	
   613  *
   614  */
   615 TInt CMMFMdaAudioOutputStream::Pause()
   616 	{
   617 	if(iState != EPlaying)
   618 		{
   619 		return KErrNotReady;
   620 		}
   621 
   622 	else if(!iDevSound->IsResumeSupported())
   623 		{
   624 		return KErrNotSupported;
   625 		}
   626 	
   627 	// pause the operation
   628 	iDevSound->Pause();
   629 	iState = EPaused;
   630 	return KErrNone;
   631 	}
   632 
   633 /**
   634  *
   635  *  To resume send data to stream 	
   636  *
   637  */
   638 TInt CMMFMdaAudioOutputStream::Resume()
   639 	{
   640 	TInt err = KErrNone;
   641 	if(iState != EPaused)
   642 		{
   643 		err = KErrNotReady;
   644 		}
   645 	else if(!iDevSound->IsResumeSupported())
   646 		{
   647 		err = KErrNotSupported;
   648 		}
   649 	
   650 	// resume the operation
   651 	if(err == KErrNone)
   652 		{
   653 		err = iDevSound->Resume();
   654 		}
   655 	if(err == KErrNone)
   656 		{
   657 		iState = EPlaying;
   658 		}
   659 
   660 	return err;
   661 	}
   662 
   663 
   664 /**
   665  *
   666  *  To get the current position in the data stream	
   667  *
   668  *	@return	"TTimeIntervalMicroSeconds&"	
   669  *			the current position in integer
   670  *
   671  */
   672 const TTimeIntervalMicroSeconds& CMMFMdaAudioOutputStream::Position()
   673 	{
   674 	TInt64 position = iDevSound->SamplesPlayed();
   675 	position = position * KMicroSecsInOneSec / StreamUtils::SampleRateAsValue(iDevSound->Config());
   676 	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
   677 	return iPosition;
   678 	}
   679 
   680 /**
   681  *
   682  *  To return the current number of bytes rendered by audio hardware
   683  *	@return "the current current number of bytes rendered by audio hardware in integer"	
   684  *
   685  */
   686 TInt CMMFMdaAudioOutputStream::GetBytes()
   687 	{
   688 	return iDevSound->SamplesPlayed() * StreamUtils::BytesPerSample(iDevSound->Config());
   689 	}
   690 
   691 /**
   692 
   693 */
   694 void CMMFMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType)
   695 	{
   696 	if(iState != EStopped)
   697 		User::Leave(KErrServerBusy);
   698 
   699 	if(aAudioType == iDataTypeCode)
   700 		return;
   701 
   702 	TMMFPrioritySettings prioritySettings; 	
   703 	prioritySettings.iState = EMMFStatePlaying;
   704 	RArray<TFourCC> supportedDataTypes;
   705 		
   706 	CleanupClosePushL(supportedDataTypes);
   707 	
   708 	TRAPD(err, iDevSound->GetSupportedInputDataTypesL(supportedDataTypes, prioritySettings));
   709 
   710 	if (err == KErrNone)
   711 		{
   712 		if (supportedDataTypes.Find(aAudioType) == KErrNotFound)
   713 			{
   714 			User::Leave(KErrNotSupported);	
   715 			}
   716 		//if match, set the 4CC of AudioType to match
   717 		iDataTypeCode.Set(aAudioType);
   718 		}
   719 	else  //we had a real leave error from GetSupportedOuputDataTypesL
   720 		{
   721 		User::Leave(err);
   722 		}
   723 	
   724 	CleanupStack::PopAndDestroy(&supportedDataTypes);
   725 
   726 	if(iIsOpenState!=EIsNotOpen)
   727 		{
   728 		// need to recall or restart InitializeL() process
   729 		iDevSound->CancelInitialize(); // call just in case mid-InitializeL. No harm if not. 
   730 									   // if not supported then assume old DevSound behaviour anyway
   731 									   // where InitializeL() implicitly cancels, so no harm either way
   732 		iIsOpenState = EIsOpening;
   733 		iInitCallFrmSetDataType = ETrue;
   734 		TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying));
   735 		if (err != KErrNone)
   736 			{
   737 			// Leave if error.
   738 			iIsOpenState = EIsNotOpen;
   739 			iInitCallFrmSetDataType = EFalse;
   740 			User::Leave(err);
   741 			}
   742 		// In some implementations InitializeComplete is sent 
   743 		// in context, so check before starting activeSchedulerWait.
   744 		else if(iIsOpenState == EIsOpening)
   745 			{
   746 			iInitializeState = KRequestPending;
   747 			iActiveSchedulerWait->Start();
   748 			}
   749 		iInitCallFrmSetDataType = EFalse;
   750 		User::LeaveIfError(iInitializeState);
   751 		}	
   752 	}
   753 
   754 /**
   755 
   756 */
   757 TFourCC CMMFMdaAudioOutputStream::DataType() const
   758 	{
   759 	return iDataTypeCode;
   760 	}
   761 
   762 TInt CMMFMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback,TUid aNotificationEventUid,const TDesC8& aNotificationRegistrationData)
   763 	{
   764 	iAudioResourceNotificationCallBack = &aCallback;
   765 	TInt err = iDevSound->RegisterAsClient(aNotificationEventUid,aNotificationRegistrationData);
   766 	if(err == KErrNotReady)
   767 		{
   768 		iEventHolder = 	aNotificationEventUid;
   769 		iNotificationDataHolder = aNotificationRegistrationData;
   770 		return KErrNone;
   771 		}
   772 	iEventHolder = KNullUid;
   773 	iNotificationDataHolder = KNullDesC8;
   774 	return err;
   775 	}
   776 
   777 TInt CMMFMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventId)
   778 	{
   779 	TInt err = iDevSound->CancelRegisterAsClient(aNotificationEventId);
   780 	if(err == KErrNotReady)
   781 		{
   782 		if(aNotificationEventId != KMMFEventCategoryAudioResourceAvailable)	
   783 			{
   784 			return KErrNotSupported;
   785 			}
   786 		if(iEventHolder == KNullUid)	
   787 			{
   788 			return KErrCancel;
   789 			}		
   790 		iEventHolder = KNullUid;
   791 		iNotificationDataHolder = KNullDesC8;
   792 		return KErrNone;
   793 		}
   794 	return err;
   795 	}
   796 	
   797 TInt CMMFMdaAudioOutputStream::WillResumePlay()
   798 	{
   799 	return iDevSound->WillResumePlay();
   800 	}
   801 
   802 /**
   803  *
   804  *  To be called when intialize stream complete	
   805  *
   806  *	@param	"TInt aError"	
   807  *			error code, initialize stream succeed when aError = 0
   808  *
   809  */
   810 void CMMFMdaAudioOutputStream::InitializeComplete(TInt aError)
   811 	{
   812 	TInt err = aError;
   813 	if(err == KErrNone && iValuesCached)
   814 		{
   815 		TRAP(err, RealSetAudioPropertiesL(iSampleRate, iChannels));
   816 		if(err == KErrNone && iVolume>=0)
   817 			{
   818 			SetVolume(iVolume);
   819 			}
   820 		}
   821 	iValuesCached = EFalse; // whatever clear our cache
   822 	if(iIsOpenState == EIsOpening)
   823 		{
   824 		// Signal for the MaoscOpenComplete callback to be called asynchronously.Ignore if InitializeL is called from SetDataTypeL
   825 		if(!iInitCallFrmSetDataType)
   826 			{
   827 			iActiveCallback->Signal(err);
   828 			}
   829 		iIsOpenState = err ? EIsNotOpen : EIsOpen;
   830 		if(iInitializeState == KRequestPending)
   831 			{
   832 			iInitializeState = err;
   833 			iActiveSchedulerWait->AsyncStop();
   834 			}
   835 		else
   836 			{
   837 			iInitializeState = err;//Set the error if InitializeComplete is called in context of InitializeL.
   838 			}
   839 		}
   840 	}
   841 
   842 /**
   843  *
   844  *  Do not support
   845  *
   846  */
   847 void CMMFMdaAudioOutputStream::ToneFinished(TInt /*aError*/)
   848 	{
   849 	Panic(EToneFinishedNotSupported);
   850 	}
   851 
   852 /**
   853  *
   854  *  Called when sound device need data	
   855  *
   856  *	@param	"CMMFBuffer* aBuffer"	
   857  *			a pointer point to CMMFBuffer, which is used to stored data
   858  *
   859  */
   860 void CMMFMdaAudioOutputStream::BufferToBeFilled(CMMFBuffer* aBuffer)
   861 	{
   862 	if (iState == EPlaying || iState == EStopping)
   863 		{
   864 		TMMFFifoItem<const TDesC8>* firstItem = iFifo->Get();
   865 		//ASSERT firstItem != NULL
   866 		if (iFifoItemPos >= firstItem->GetData().Length())
   867 			{
   868 			// We've played all segments of the first buffer in the fifo so we can delete it
   869 			iFifo->RemoveFirstItem();
   870 			iCallback.MaoscBufferCopied(KErrNone, firstItem->GetData());
   871 			delete firstItem;
   872 			iFifoItemPos = 0;
   873 			}
   874 		}
   875 	TMMFFifoItem<const TDesC8>* firstItem = iFifo->Get();
   876 
   877 	if (firstItem)
   878 		{
   879 		// Fill aBuffer with the next segment of the first buffer in the fifo
   880 		TDes8& fillDes = STATIC_CAST(CMMFDataBuffer*, aBuffer)->Data();
   881 		const TDesC8& readDes = firstItem->GetData();
   882 		TInt readLen = Min(readDes.Length()-iFifoItemPos, aBuffer->RequestSize());
   883 		fillDes = readDes.Mid(iFifoItemPos, readLen);
   884 		iFifoItemPos+=readLen; // so we know where the next segment in the fifo item starts
   885 		// Notify iDevSound the buffer is ready to be played
   886 		iDevSound->PlayData();
   887 		}
   888 	else 
   889 		{
   890 		if(iBuffer ==NULL)
   891 			{
   892 			//keep a record of the supplied buffer and use it when/if more user data is in the FIFO
   893 			iBuffer = aBuffer; 
   894 			}
   895 		if(!iKeepOpenAtEnd)
   896 			{
   897 			if (iDevSoundIgnoresUnderflow)
   898 				{
   899 				iCurrentSamplesPlayed = iDevSound->SamplesPlayed();
   900 				StartShutDownTimer();
   901 				}
   902 			}
   903 		else if(iState==EStopping)
   904 			{
   905 			CMMFDataBuffer* dataBuffer = static_cast<CMMFDataBuffer*>(aBuffer);
   906 			dataBuffer->Data().SetLength(0);
   907 			dataBuffer->SetLastBuffer(ETrue);
   908 			iDevSound->PlayData();
   909 			iBuffer=NULL;
   910 			}
   911 		}
   912 	 
   913 	}
   914 
   915 void CMMFMdaAudioOutputStream::StartShutDownTimer()
   916 	{
   917 	iShutDownTimer->Start(KShutDownTimeInterval, KShutDownTimeInterval, TCallBack(ShutDownTimerComplete,this));
   918 	}
   919 	
   920 TInt CMMFMdaAudioOutputStream::ShutDownTimerComplete(TAny* aAudioOutputStream)
   921 	{
   922 	CMMFMdaAudioOutputStream* audioOutputStream = static_cast<CMMFMdaAudioOutputStream*>(aAudioOutputStream);
   923 	audioOutputStream->DoShutDownTimerComplete();
   924 	return KErrNone;
   925 	}
   926 
   927 void CMMFMdaAudioOutputStream::DoShutDownTimerComplete()
   928 	{
   929 	iShutDownTimer->Cancel();
   930 	TInt samplesPlayed = iDevSound->SamplesPlayed();
   931 	if (samplesPlayed == iCurrentSamplesPlayed)
   932 		{
   933 		iDevSound->Stop();
   934 		iState = EStopped;
   935 		iBuffer = NULL;
   936 		iCallback.MaoscPlayComplete(KErrUnderflow);
   937 		}
   938 	else
   939 		{//desvound has not yet finished playing all the data. So wait for one more cycle
   940 		//Restart Timer
   941 		iCurrentSamplesPlayed = samplesPlayed;
   942 		StartShutDownTimer();
   943 		}
   944 	}
   945 
   946 /**
   947  *
   948  *  Called when play operation complete, successfully or otherwise	
   949  *
   950  *	@param	"TInt aError"	
   951  *			an error value which will indicate playing successfully complete
   952  *			if error value is 0
   953  *
   954  */
   955 void CMMFMdaAudioOutputStream::PlayError(TInt aError)
   956 	{
   957 	TInt err=aError;
   958 	 // if iDevSoundIgnoresUnderflow is true, then KErrUnderflow should not be produced by DevSound when we are not stopping 
   959 	__ASSERT_DEBUG(!iDevSoundIgnoresUnderflow || !(err==KErrUnderflow && iState!=EStopping), Panic(EAOSStoppingError));
   960 	iState = EStopped;
   961 	iBuffer = NULL;
   962 	iShutDownTimer->Cancel();
   963 	if (err == KErrNone || err == KErrUnderflow)
   964 		{
   965 		if (!iFifo->IsEmpty())
   966 			{
   967 			// We live again - the Fifo still has some data. The sound device
   968 			// will have to be opened again though so there might be an
   969 			// audible click.
   970 			TRAP(err, StartPlayL());
   971 			if (err == KErrNone)
   972 				{
   973 				return;
   974 				}
   975 			}
   976 		}
   977 	
   978 	EmptyFifo(err);
   979 	
   980 	// Note - KErrUnderflow will be reported even when all the buffers have
   981 	// successfully played
   982 	iCallback.MaoscPlayComplete(err);
   983 	}
   984 
   985 void CMMFMdaAudioOutputStream::EmptyFifo(TInt aError)
   986 	{
   987 	// Delete all buffers in the fifo and notify the observer
   988 	TMMFFifoItem<const TDesC8>* firstItem;
   989 	while((firstItem = iFifo->Get()) != NULL)
   990 		{
   991 		iFifo->RemoveFirstItem();
   992 		iCallback.MaoscBufferCopied(aError, firstItem->GetData());
   993 		delete firstItem;
   994 		}
   995 	}
   996 
   997 void CMMFMdaAudioOutputStream::SendEventToClient(const TMMFEvent& aEvent)
   998 	{
   999 	if (aEvent.iEventType == KMMFEventCategoryAudioResourceAvailable)
  1000 		{
  1001 		// Retrieve the number of samples played
  1002 		// For the event type KMMFEventCategoryAudioResourceAvailable GetResourceNotificationData() returns
  1003 		// a package buffer as TMMFTimeIntervalMicroSecondsPckg, but the contents should be 
  1004 		// converted to an integer and interpreted as the data returned is samples played,
  1005 		// but not as a microsecond value.
  1006 		TBuf8<TMMFAudioConfig::KNotificationDataBufferSize> notificationData;
  1007 		if (KErrNone != iDevSound->GetResourceNotificationData(aEvent.iEventType, notificationData))
  1008 			{
  1009 			notificationData.SetLength(0);
  1010 			}
  1011 		iAudioResourceNotificationCallBack->MarncResourceAvailable(aEvent.iEventType, notificationData);
  1012 		}
  1013 	}
  1014 
  1015 CMMFMdaAudioOutputStream::CActiveCallback::~CActiveCallback()
  1016 	{
  1017 	Cancel();
  1018 	}
  1019 
  1020 CMMFMdaAudioOutputStream::CActiveCallback::CActiveCallback(MMdaAudioOutputStreamCallback& aCallback)
  1021 	: CActive(EPriorityStandard), iCallback(aCallback)
  1022 	{
  1023 	CActiveScheduler::Add(this);
  1024 	}
  1025 
  1026 void CMMFMdaAudioOutputStream::CActiveCallback::RunL()
  1027 	{
  1028 	iCallback.MaoscOpenComplete(iStatus.Int());
  1029 	}
  1030 
  1031 void CMMFMdaAudioOutputStream::CActiveCallback::DoCancel()
  1032 	{
  1033 	}
  1034 
  1035 void CMMFMdaAudioOutputStream::CActiveCallback::Signal(const TInt aReason)
  1036 	{
  1037 	ASSERT(!IsActive());
  1038 
  1039 	// Signal ourselves to run with the given completion code
  1040 	TRequestStatus* status = &iStatus;
  1041 	User::RequestComplete(status, aReason);
  1042 	SetActive();
  1043 	}
  1044 
  1045 
  1046 /**
  1047  *
  1048  *  Do not support
  1049  *
  1050  */
  1051 void CMMFMdaAudioOutputStream::BufferToBeEmptied(CMMFBuffer* /*aBuffer*/)
  1052 	{
  1053 	Panic(EBufferToBeEmptiedNotSupported);
  1054 	}
  1055 
  1056 /**
  1057  *
  1058  *  Do not support
  1059  *
  1060  */
  1061 void CMMFMdaAudioOutputStream::RecordError(TInt /*aError*/)
  1062 	{
  1063 	Panic(ERecordErrorNotSupported);
  1064 	}
  1065 
  1066 /**
  1067  *
  1068  *  Do not support
  1069  *
  1070  */
  1071 void CMMFMdaAudioOutputStream::ConvertError(TInt /*aError*/)
  1072 	{
  1073 	Panic(EConvertErrorNotSupported);
  1074 	}
  1075 
  1076 /**
  1077  *
  1078  *  Do not support
  1079  *
  1080  */
  1081 void CMMFMdaAudioOutputStream::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/)
  1082 	{
  1083 	Panic(EDeviceMessageNotSupported);
  1084 	}
  1085 
  1086 // CustomInferface - just pass on to DevSound. 
  1087 TAny* CMMFMdaAudioOutputStream::CustomInterface(TUid aInterfaceId)
  1088 	{
  1089 	return iDevSound->CustomInterface(aInterfaceId);
  1090 	}
  1091