os/mm/mmlibs/mmfw/src/Plugin/AudioOutput/MmfAudioOutput.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 2005-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 "MmfAudioOutput.h"
    17 #include <ecom/implementationproxy.h>
    18 #include <mmf/server/mmfformat.h>
    19 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
    20 #include <mmf/common/mmfhelper.h>
    21 #endif
    22 #include <mmf/plugin/mmfaudioiointerfaceuids.hrh>
    23 
    24 void Panic(TInt aPanicCode)
    25 	{
    26 	_LIT(KMMFAudioOutputPanicCategory, "MMFAudioOutput");
    27 	User::Panic(KMMFAudioOutputPanicCategory, aPanicCode);
    28 	}
    29 
    30 /**
    31 Allocates and constructs a new audio output sink.
    32 
    33 Static standard SymbianOS 2 phase constuction method.
    34 
    35 @return A pointer to the new sink.
    36 */
    37 MDataSink* CMMFAudioOutput::NewSinkL()
    38 	{
    39 	CMMFAudioOutput* self = new (ELeave) CMMFAudioOutput ;
    40 	CleanupStack::PushL(self);
    41 	self->ConstructL();
    42 	CleanupStack::Pop();
    43 	return STATIC_CAST( MDataSink*, self ) ;
    44 	}
    45 
    46 /**
    47 Standard SymbianOS ConstructL.
    48 
    49 Used to initialise member varibles with device specific behaviour.
    50 */
    51 void CMMFAudioOutput::ConstructL()
    52 	{
    53 	iInitializeState = KErrNone;
    54 	iDataTypeCode = KMMFFourCCCodePCM16;
    55 	iNeedsSWConversion = EFalse;
    56 	iSourceSampleRate = 0;
    57 	iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait;
    58 	}
    59 
    60 /**
    61 Overridable constuction specific to this datasource.
    62 
    63 The default implementation does nothing.
    64 
    65 @param  aInitData
    66         The initialisation data.
    67 */
    68 void CMMFAudioOutput::ConstructSinkL( const TDesC8& /*aInitData*/ )
    69 	{
    70 	}
    71 
    72 
    73 /**
    74 @deprecated
    75 
    76 Gets audio from hardware device abstracted MMFDevsound (not used).
    77 
    78 @param  aBuffer
    79         The data to write out to a Hardware Device.
    80 @param  aSupplier
    81         The MDataSource consuming the data contained in aBuffer
    82 */
    83 void CMMFAudioOutput::HWEmptyBufferL(CMMFBuffer* /*aBuffer*/, MDataSource* /*aSupplier*/)
    84 	{
    85 	}
    86 
    87 /**
    88 Sends audio to MMFDevsound.
    89 
    90 @param  aBuffer
    91         The data to write out.
    92 @param  aSupplier
    93         The search criteria for the supplier.
    94 @param  aMediaId
    95         The type of data supplied - currently ignored.
    96 */
    97 void CMMFAudioOutput::EmptyBufferL(CMMFBuffer* aBuffer, MDataSource* aSupplier, TMediaId /*aMediaId*/)
    98 	{
    99 	iSupplier = aSupplier;
   100 
   101 	if (!iMMFDevSound)
   102 		Panic(EMMFAudioOutputDevSoundNotLoaded);
   103 
   104 	if (aSupplier == NULL)
   105 		User::Leave(KErrArgument);
   106 
   107 	// In order to avoid changes at the datapath data transfer state machine 
   108 	// EmptyBufferL is still being called even
   109 	// if the first time there is no buffer to send
   110 	if (!iCanSendBuffers)
   111 		{
   112 		
   113 		iCanSendBuffers = ETrue;
   114 		return;
   115 		}
   116 
   117 	if (iNeedsSWConversion)
   118 		{//need to perform channel & sample rate conversion before writing to clip
   119 		CMMFDataBuffer* audio;
   120 
   121 		//need to make sure aBuffer is not null before it is used
   122 		if (aBuffer == NULL)
   123 			{
   124 			User::Leave(KErrArgument);
   125 			}
   126 		
   127 		//buffer check is placed here before possible use of the buffer
   128 		if (!CMMFBuffer::IsSupportedDataBuffer(aBuffer->Type()))
   129 			{
   130 			User::Leave(KErrNotSupported);
   131 			}
   132 		
   133 		iConvertBuffer = CMMFDataBuffer::NewL(((CMMFDataBuffer*)aBuffer)->Data().Length());
   134 		iChannelAndSampleRateConverter->Convert(*(CMMFDataBuffer*)aBuffer, *iConvertBuffer);
   135 		audio = iConvertBuffer;
   136 
   137 		//copy our converted data back into the real buffer to return to datapath
   138 		TDes8& dest = STATIC_CAST( CMMFDataBuffer*, aBuffer )->Data() ;
   139 		dest.SetLength(0);
   140 		dest.Copy(audio->Data());
   141 		}
   142 
   143 	if (iState != EDevSoundReady && iState != EPaused) // If we're paused we still feed a buffer to DevSound
   144 		User::Leave(KErrNotReady);
   145 
   146 	iMMFDevSound->PlayData();
   147 
   148 #if defined(__AUDIO_PROFILING)
   149 	RDebug::ProfileEnd(0);
   150 	User::Leave(KErrEof);
   151 #endif  // defined(__AUDIO_PROFILING)
   152 
   153 
   154 	// The following will never have been allocated unless
   155 	// software conversion was required, and due to certain DevSound
   156 	// implementations, this requirement can change dynamically.
   157 	delete iConvertBuffer;
   158 	iConvertBuffer = NULL;
   159 	}
   160 
   161 
   162 
   163 /**
   164 Negotiates with the source to set, for example, the sample rate and number of channels.
   165 
   166 Called if the sink's setup depends on source.
   167 
   168 @param  aSource
   169         The data source with which to negotiate.
   170 */
   171 void CMMFAudioOutput::NegotiateL(MDataSource& aSource)
   172 	{
   173 	if (aSource.DataSourceType() == KUidMmfFormatDecode)
   174 		{//source is a clip so for now set sink settings to match source
   175 		iSourceSampleRate = ((CMMFFormatDecode&)aSource).SampleRate();
   176 		iSourceChannels = ((CMMFFormatDecode&)aSource).NumChannels(); 
   177 		iSourceFourCC.Set(aSource.SourceDataTypeCode(TMediaId(KUidMediaTypeAudio)));
   178 	
   179 		((CMMFFormatDecode&)aSource).SuggestSourceBufferSize(KAudioOutputDefaultFrameSize);
   180 		}
   181 
   182 	// Query DevSound capabilities and Try to use DevSound sample rate and 
   183 	// mono/stereo capability
   184 	if (!iMMFDevSound)
   185 		Panic(EMMFAudioOutputDevSoundNotLoaded);
   186 
   187 	TMMFState prioritySettingsState = iPrioritySettings.iState; //should be EMMFStatePlaying
   188 	//to use the GetSupportedInputDatatypes but we'll save it just in case it's not
   189 	iPrioritySettings.iState = EMMFStatePlaying; //if playing does not support any output data types
   190 	RArray<TFourCC> supportedDataTypes;
   191 	//note Input data types becuase if we are playing audio ie audio output
   192 	//the data is sent as an input to DevSound
   193 	TRAPD(err, iMMFDevSound->GetSupportedInputDataTypesL(supportedDataTypes, iPrioritySettings));
   194 	iPrioritySettings.iState = prioritySettingsState;
   195 	if (err == KErrNone)
   196 		{
   197 		if (supportedDataTypes.Find(iSourceFourCC) == KErrNotFound)
   198 			{//the source fourCC code could not be found in the list of 
   199 			//data types supported by the Devsound therefor default to pcm16
   200 			iDataTypeCode = KMMFFourCCCodePCM16;	
   201 			}
   202 		else
   203 			{
   204 			//the DevSound does support the same datatype as the source 
   205 			//so set the fourcc to that of the source
   206 			iDataTypeCode = iSourceFourCC;
   207 			}
   208 		}
   209 	supportedDataTypes.Close();
   210 	if (err == KErrNotSupported)
   211 		{//if the Devsound does not support the GetSupportedOutputDataTypesL method
   212 		//then assume that the DevSound is pcm16 only
   213 		iDataTypeCode = KMMFFourCCCodePCM16;	
   214 		}
   215 	else if (err != KErrNone) //we had a real leave error from GetSupportedOuputDataTypesL
   216 		{
   217 		User::Leave(err);
   218 		}
   219 		
   220 	// Prevent defect when SinkPrimeL is called before NegotiateL()
   221 	// since characterization is ambiguous
   222 	if(iState == EDevSoundReady)
   223 		{
   224 		iState = EIdle;
   225 		}
   226 	//INC40817 do the initialize here as capabilities may depend on datatype
   227 	//note this implies client of audiooutput must call negotiate
   228 	iMMFDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying);
   229 
   230 	// In some implementations InitializeComplete is sent
   231 	// in context, so check before starting activeSchedulerWait.
   232 	if (iState != EDevSoundReady)
   233 		{
   234 		iInitializeState = KRequestPending;
   235 		iActiveSchedulerWait->Start();
   236 		}
   237 		
   238 	User::LeaveIfError(iInitializeState);
   239 	
   240 	// Reset the following flag in case DevSound's capabilities have
   241 	// changed since we were last here: INC037165
   242 	iNeedsSWConversion = EFalse;
   243 	TMMFCapabilities devSoundCaps;
   244 	devSoundCaps = iMMFDevSound->Capabilities();
   245 	// Default PCM16
   246 	iDevSoundConfig.iEncoding = EMMFSoundEncoding16BitPCM;
   247 	// 1 = Monophonic and 2 == Stereo
   248 	if (((iSourceChannels == 1) && (devSoundCaps.iChannels & EMMFMono)) ||
   249 		((iSourceChannels == 2) && (devSoundCaps.iChannels & EMMFStereo)))
   250 		iDevSoundConfig.iChannels = iSourceChannels;
   251 	else //default or SW conversion, e.g. stereo on mono support
   252 		{
   253 		iDevSoundConfig.iChannels = EMMFMono;
   254 		iNeedsSWConversion = ETrue;
   255 		iSWConvertChannels = 1;
   256 		iSWConvertSampleRate = iSourceSampleRate;
   257 		}
   258 
   259 	// Check for std sample rates.
   260 	if ((iSourceSampleRate == 96000) && (devSoundCaps.iRate & EMMFSampleRate96000Hz))
   261 		iDevSoundConfig.iRate = EMMFSampleRate96000Hz;
   262 	else if ((iSourceSampleRate == 88200) && (devSoundCaps.iRate & EMMFSampleRate88200Hz))
   263 		iDevSoundConfig.iRate = EMMFSampleRate88200Hz;
   264 	else if ((iSourceSampleRate == 64000) && (devSoundCaps.iRate & EMMFSampleRate64000Hz))
   265 		iDevSoundConfig.iRate = EMMFSampleRate64000Hz;
   266 	else if ((iSourceSampleRate == 48000) && (devSoundCaps.iRate & EMMFSampleRate48000Hz))
   267 		iDevSoundConfig.iRate = EMMFSampleRate48000Hz;
   268 	else if ((iSourceSampleRate == 44100) && (devSoundCaps.iRate & EMMFSampleRate44100Hz))
   269 		iDevSoundConfig.iRate = EMMFSampleRate44100Hz;
   270 	else if ((iSourceSampleRate == 32000) && (devSoundCaps.iRate & EMMFSampleRate32000Hz))
   271 		iDevSoundConfig.iRate = EMMFSampleRate32000Hz;
   272 	else if ((iSourceSampleRate == 24000) && (devSoundCaps.iRate & EMMFSampleRate24000Hz))
   273 		iDevSoundConfig.iRate = EMMFSampleRate24000Hz;
   274 	else if ((iSourceSampleRate == 22050) && (devSoundCaps.iRate & EMMFSampleRate22050Hz))
   275 		iDevSoundConfig.iRate = EMMFSampleRate22050Hz;
   276 	else if ((iSourceSampleRate == 16000) && (devSoundCaps.iRate & EMMFSampleRate16000Hz))
   277 		iDevSoundConfig.iRate = EMMFSampleRate16000Hz;
   278 	else if ((iSourceSampleRate == 12000) && (devSoundCaps.iRate & EMMFSampleRate12000Hz))
   279 		iDevSoundConfig.iRate = EMMFSampleRate12000Hz;
   280 	else if ((iSourceSampleRate == 11025) && (devSoundCaps.iRate & EMMFSampleRate11025Hz))
   281 		iDevSoundConfig.iRate = EMMFSampleRate11025Hz;
   282 	else if ((iSourceSampleRate == 8000) && (devSoundCaps.iRate & EMMFSampleRate8000Hz))
   283 		iDevSoundConfig.iRate = EMMFSampleRate8000Hz;
   284 	else // non standard sample rate
   285 		{
   286 		iNeedsSWConversion = ETrue;
   287 		// we need to choose to the closest, and smaller standard sample rate
   288 		// and eventually convert the audio samples to this standard sample rate
   289 		//NB: this list must be in ascending order
   290 		const TInt KNumSampleRates = 12;
   291 		static const TUint supportedSR[KNumSampleRates][2] = {{8000,  EMMFSampleRate8000Hz},
   292 									{11025, EMMFSampleRate11025Hz},
   293 									{12000, EMMFSampleRate12000Hz},
   294 									{16000, EMMFSampleRate16000Hz},
   295 									{22050, EMMFSampleRate22050Hz},
   296 									{24000, EMMFSampleRate24000Hz},
   297 									{32000, EMMFSampleRate32000Hz},
   298 									{44100, EMMFSampleRate44100Hz},
   299 									{48000, EMMFSampleRate48000Hz},
   300 									{64000, EMMFSampleRate64000Hz},
   301 									{88200, EMMFSampleRate88200Hz},
   302 									{96000, EMMFSampleRate96000Hz}};
   303 
   304 		//Only support down sampling
   305    		if (iSourceSampleRate < supportedSR[0][0]) 
   306    			User::Leave(KErrNotSupported);
   307    		
   308    		
   309    		TInt sampleRateIndex = KNumSampleRates;
   310    
   311    		//find the source sampleRateIndex
   312    		for (sampleRateIndex--; sampleRateIndex > -1; sampleRateIndex--)
   313    			{
   314    			if(iSourceSampleRate >= supportedSR[sampleRateIndex][0])
   315    				{
   316    				break;
   317    				}
   318    			}
   319    
   320    		//find the highest sink sample rate below the source rate
   321    		for (; sampleRateIndex > -1; sampleRateIndex--)
   322    			{
   323    			if(devSoundCaps.iRate & supportedSR[sampleRateIndex][1])
   324    				{
   325    				iSWConvertSampleRate = supportedSR[sampleRateIndex][0];
   326    				iDevSoundConfig.iRate = supportedSR[sampleRateIndex][1];
   327    				break;
   328    				}
   329    			}
   330    
   331    		//if a suitable sink sample rate is not available
   332    		if (sampleRateIndex < 0) 
   333    			User::Leave(KErrNotSupported);
   334    
   335    		// set the channels as well
   336    		iSWConvertChannels = iDevSoundConfig.iChannels;
   337    		} // else // non standard sample rate
   338 
   339 	if (iNeedsSWConversion)
   340 		{//can only software convert if datatype is pcm16
   341 		//note cannot set non standard sample rates on DevSound API
   342 		//as the API does not allow this
   343 		//we need to reinitialize the devsound with pcm16 in this case
   344 		iDataTypeCode = KMMFFourCCCodePCM16;
   345 		iState = EIdle;
   346 		iMMFDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying);
   347 		
   348 		// In some implementations InitializeComplete is called
   349 		// in context, so check before starting activeSchedulerWait.
   350 		if (iState != EDevSoundReady)
   351 			{
   352 			iInitializeState = KRequestPending;
   353 			iActiveSchedulerWait->Start();
   354 			}
   355 			
   356 		User::LeaveIfError(iInitializeState);
   357 		}
   358 	}
   359 
   360 /**
   361 Sets the sink's priority settings.
   362 
   363 @param  aPrioritySettings
   364         The sink's priority settings. Takes enumerations to determine audio playback priority. 
   365         Higher numbers mean high priority (can interrupt lower priorities).
   366 */
   367 void CMMFAudioOutput::SetSinkPrioritySettings(const TMMFPrioritySettings& aPrioritySettings)
   368 	{
   369 	iPrioritySettings = aPrioritySettings;
   370 	if (!iMMFDevSound)
   371 		Panic(EMMFAudioOutputDevSoundNotLoaded);
   372 	else
   373 		iMMFDevSound->SetPrioritySettings(iPrioritySettings);
   374 	}
   375 
   376 /**
   377 Gets the sink's data type code.
   378 
   379 Used by datapath MDataSource / MDataSink for codec matching.
   380 
   381 @param  aMediaId
   382         The Media ID. Optional parameter to specifiy specific stream when datasource contains more
   383         than one stream of data.
   384 
   385 @return	The 4CC of the data expected by this sink.
   386 */
   387 TFourCC CMMFAudioOutput::SinkDataTypeCode(TMediaId /*aMediaId*/)
   388 	{
   389 	return iDataTypeCode;
   390 	}
   391 
   392 /**
   393 Sets the sink's data type code.
   394 
   395 @param  aSinkFourCC
   396         The 4CC of the data to be supplied to this sink.
   397 @param  aMediaId
   398         The Media ID. Optional parameter to specifiy specific stream when datasource contains more 
   399         than one stream of data.
   400 
   401 @return An error code indicating if the function call was successful. KErrNone on success, otherwise
   402         another of the system-wide error codes.
   403 */
   404 TInt CMMFAudioOutput::SetSinkDataTypeCode(TFourCC aSinkFourCC, TMediaId /*aMediaId*/)
   405 	{//will check with devsound to see if aSinkFourCC is supported
   406 	 //when this is added to devsound
   407 	iDataTypeCode = aSinkFourCC;
   408 	return KErrNone;
   409 	}
   410 
   411 /**
   412 Primes the sink.
   413 
   414 This is a virtual function that each derived class must implement, but may be left blank for default 
   415 behaviour.
   416 
   417 Called by CMMFDataPath::PrimeL().
   418 */
   419 void CMMFAudioOutput::SinkPrimeL()
   420 	{
   421 	if(iPlayStarted != EFalse)
   422 		{
   423 		return;
   424 		}
   425 	iPlayStarted = EFalse;
   426 	iCanSendBuffers = EFalse;
   427 	if (iState == EIdle)
   428 		{
   429 		if (!iMMFDevSound) 
   430 			{
   431 			User::Leave(KErrNotReady);
   432 			}
   433 		iState = EDevSoundReady;
   434 		}
   435 	}
   436 
   437 /**
   438 Pauses the sink.
   439 
   440 This is a virtual function that each derived class must implement, but may be left blank for default 
   441 behaviour.
   442 */
   443 void CMMFAudioOutput::SinkPauseL()
   444 	{
   445 	if (!iMMFDevSound)
   446 		Panic(EMMFAudioOutputDevSoundNotLoaded);
   447 	else
   448 		iMMFDevSound->Pause();
   449 	iPlayStarted = EFalse;
   450 	iState = EPaused;
   451 	}
   452 
   453 /**
   454 Starts playing the sink.
   455 
   456 This is a virtual function that each derived class must implement, but may be left blank for default
   457 behaviour.
   458 */
   459 void CMMFAudioOutput::SinkPlayL()
   460 	{
   461 	if (iState == EPaused)
   462 		{
   463 		TBool isResumeSupported = iMMFDevSound->IsResumeSupported();
   464 		// CMMFAudioOutput is not supposed to be paused 
   465 		// if Resume is not supported by DevSound
   466 		if(!isResumeSupported)
   467 			{
   468 			User::Leave(KErrNotSupported);
   469 			}
   470 		
   471 		User::LeaveIfError(iMMFDevSound->Resume());
   472 		iPlayStarted = ETrue;
   473 		}
   474 	else if (iPlayStarted == EFalse)
   475 		{
   476 		ConfigDevSoundL();	
   477 
   478 		// This is a one-shot to "prime" MMFDevSound as first buffer uninitialised
   479 		iMMFDevSound->PlayInitL();
   480 		iPlayStarted = ETrue;
   481 		}
   482 	if ((iNeedsSWConversion)&&(iSourceChannels>0))
   483 		{//can only do SW convert  - therefore need to do a conversion
   484 		//currently only pcm16 is supported so return with an error if format not pcm16
   485 		if (!iChannelAndSampleRateConverterFactory)
   486 			{
   487 			iChannelAndSampleRateConverterFactory
   488 				= new(ELeave)CMMFChannelAndSampleRateConverterFactory;
   489 			iChannelAndSampleRateConverter =
   490 				iChannelAndSampleRateConverterFactory->CreateConverterL( iSourceSampleRate, iSourceChannels,
   491 																		iSWConvertSampleRate, iSWConvertChannels);
   492 			}
   493 		//need to create an intermediate buffer in which to place the converted data
   494 		}
   495 	iState = EDevSoundReady;
   496 	}
   497 
   498 /**
   499 Stops the sink.
   500 
   501 This is a virtual function that each derived class must implement, but may be left blank for default
   502 behaviour.
   503 */
   504 void CMMFAudioOutput::SinkStopL()
   505 	{
   506 	if (iState == EDevSoundReady)
   507 		{//not waiting on a buffer being played so stop devsound now
   508 		iState = EIdle;
   509 		if (iPlayStarted)
   510 			{
   511 			if (!iMMFDevSound)
   512 				{
   513 				Panic(EMMFAudioOutputDevSoundNotLoaded);
   514 				}
   515 			else
   516 				{
   517 				iPlayStarted = EFalse;
   518 				iMMFDevSound->Stop();
   519 				}
   520 			}
   521 		}
   522 	else if (iState == EPaused)	//DEF46250 need to handle pause separately as we should always stop regardless of the state of iFirstBufferSent
   523 		{
   524 		iPlayStarted = EFalse;
   525 		iMMFDevSound->Stop();
   526 		iState = EIdle; 
   527 		}
   528 	iCanSendBuffers = EFalse;
   529 	} 
   530 
   531 /**
   532 Returns the playback state (EStopped, EPlaying, EPaused etc) of this sink
   533 */
   534 TInt CMMFAudioOutput::State()
   535 	{
   536 	return iState;
   537 	}
   538 
   539 /**
   540 Logs on the sink's thread.
   541 
   542 Thread specific initialization procedure for this device. Runs automatically on thread construction.
   543 
   544 @param  aEventHandler
   545         The event handler.
   546 
   547 @return An error code indicating if the function call was successful. KErrNone on success, otherwise
   548         another of the system-wide error codes.
   549 */
   550 TInt CMMFAudioOutput::SinkThreadLogon(MAsyncEventHandler& aEventHandler)
   551 	{
   552 	iEventHandler = &aEventHandler;
   553 	TInt err = KErrNone;
   554 	if (!iDevSoundLoaded)
   555 		TRAP(err, LoadL());
   556 	return err;
   557 	}
   558 
   559 /**
   560 Logs off the sink thread.
   561 
   562 Thread specific destruction procedure for this device. Runs automatically on thread destruction.
   563 */
   564 void CMMFAudioOutput::SinkThreadLogoff()
   565 	{
   566 	if(iMMFDevSound)
   567 		{
   568 		iMMFDevSound->Stop();
   569 		delete iMMFDevSound;
   570 		iMMFDevSound = NULL;
   571 		}
   572 	iDevSoundLoaded = EFalse;
   573 	iState = EIdle;
   574 	}
   575 
   576 /**
   577 Called by MDataSource to pass back a full buffer to the sink. 
   578 
   579 Should never be called by a sink, as sinks empty buffers, not fill them.
   580 
   581 @param  aBuffer
   582         The filled buffer.
   583 */
   584 void CMMFAudioOutput::BufferFilledL(CMMFBuffer* /*aBuffer*/)
   585 	{
   586 	Panic(EMMFAudioOutputPanicBufferFilledLNotSupported);
   587 	}
   588 
   589 /**
   590 Tests whether a sink buffer can be created.
   591 
   592 The default implementation returns true.
   593 
   594 @return A boolean indicating if the sink buffer can be created. ETrue if it can, otherwise EFalse.
   595 */
   596 TBool CMMFAudioOutput::CanCreateSinkBuffer()
   597 	{
   598 	return ETrue;
   599 	}
   600 
   601 /**
   602 Creates a sink buffer.
   603 
   604 Intended for asynchronous usage (buffers supplied by Devsound device)
   605 
   606 @param  aMediaId
   607         The Media ID.
   608 @param	aReference
   609 		A boolean indicating if MDataSink owns the buffer. ETrue if does, otherwise EFalse.
   610 
   611 @return A sink buffer.
   612 */
   613 CMMFBuffer* CMMFAudioOutput::CreateSinkBufferL(TMediaId /*aMediaId*/, TBool &aReference)
   614 	{
   615 	//iDevSoundBuffer = CMMFDataBuffer::NewL(KAudioOutputDefaultFrameSize);
   616 	iDevSoundBuffer = NULL;		//DevSound supplies this buffer in first callback
   617 	aReference = ETrue;
   618 	if ( iNeedsSWConversion )
   619 		return iConvertBuffer;
   620 	else
   621 		return iDevSoundBuffer;
   622 	}
   623 
   624 /**
   625 Standard SymbianOS destructor.
   626 */
   627 CMMFAudioOutput::~CMMFAudioOutput()
   628 	{
   629 	// The following will never have been allocated unless
   630 	// software conversion was required, and due to certain DevSound
   631 	// implementations, this requirement can change dynamically.
   632 	delete iChannelAndSampleRateConverterFactory;
   633 	delete iConvertBuffer;
   634 
   635 	if (iMMFDevSound)
   636 		{
   637 		iMMFDevSound->Stop();
   638 		delete iMMFDevSound;
   639 		}
   640 	delete iActiveSchedulerWait;
   641 	}
   642 
   643 void CMMFAudioOutput::ConfigDevSoundL()
   644 	{
   645 	iMMFDevSound->SetConfigL(iDevSoundConfig);
   646 	}
   647 
   648 
   649 /**
   650 @deprecated
   651 
   652 This method should not be used - it is provided to maintain SC with v7.0s.
   653 
   654 @param  aAudioType
   655         The 4CC of the data supplied by this source.
   656 */
   657 void CMMFAudioOutput::SetDataTypeL(TFourCC aAudioType)
   658 	{
   659 	if (aAudioType != KMMFFourCCCodePCM16)
   660 		{
   661 		User::Leave(KErrNotSupported);
   662 		}
   663 	}
   664 
   665 
   666 /**
   667 @deprecated
   668 
   669 This method should not be used - it is provided to maintain SC with v7.0s.
   670 
   671 @return The 4CC of the data supplied by this source.
   672 */
   673 TFourCC CMMFAudioOutput::DataType() const
   674 	{
   675 	return KMMFFourCCCodePCM16;
   676 	}
   677 
   678 /**
   679 Queries about DevSound resume support
   680 
   681 @return ETrue if DevSound does support resume, EFalse otherwise
   682 */
   683 TBool CMMFAudioOutput::IsResumeSupported()
   684 	{
   685 	TBool isSupported = EFalse;
   686 	if (iMMFDevSound)
   687 		{
   688 		isSupported = iMMFDevSound->IsResumeSupported();
   689 		}
   690 	return isSupported;
   691 	}
   692 
   693 
   694 /**
   695 Loads audio device drivers and initialise this device.
   696 */
   697 void CMMFAudioOutput::LoadL()
   698 	{
   699 	iPlayStarted = EFalse;
   700 	if (iState != EDevSoundReady)
   701 		iState = EIdle;
   702 
   703 	iMMFDevSound = CMMFDevSound::NewL();
   704 	
   705 	//This is done to maintain compatibility with the media server
   706 	iMMFDevSound->SetVolume(iMMFDevSound->MaxVolume() - 1);
   707 
   708 	//note cannot perform further initlaisation here untill the datatype is known
   709 
   710 	iDevSoundLoaded = ETrue;
   711 	}
   712 
   713 /**
   714 DeviceMessage MMFDevSoundObserver
   715 */
   716 void CMMFAudioOutput::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/)
   717 	{
   718 	}
   719 
   720 
   721 /**
   722 ToneFinished MMFDevSoundObserver called when a tone has finished or interrupted
   723 
   724 Should never get called.
   725 */
   726 void CMMFAudioOutput::ToneFinished(TInt /*aError*/)
   727 	{
   728 	//we should never get a tone error in MMFAudioOutput!
   729 	__ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicToneFinishedNotSupported));
   730 	}
   731 
   732 
   733 /**
   734 RecordError MMFDevSoundObserver called when recording has halted.
   735 
   736 Should never get called.
   737 */
   738 void CMMFAudioOutput::RecordError(TInt /*aError*/)
   739 	{
   740 	//we should never get a recording error in MMFAudioOutput!
   741 	__ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicRecordErrorNotSupported));
   742 	}
   743 
   744 /**
   745 InitializeComplete MMFDevSoundObserver called when devsound initialisation completed.
   746 */
   747 void CMMFAudioOutput::InitializeComplete(TInt aError)
   748 	{
   749 	
   750 	if (aError == KErrNone)
   751 		{
   752 		iState = EDevSoundReady;
   753 		}
   754 	
   755 	if(iInitializeState == KRequestPending)
   756 		{
   757 		iInitializeState = aError;
   758 		iActiveSchedulerWait->AsyncStop();
   759 		}
   760 	}
   761 
   762 /**
   763 BufferToBeEmptied MMFDevSoundObserver - should never get called.
   764 */
   765 void CMMFAudioOutput::BufferToBeEmptied(CMMFBuffer* /*aBuffer*/)
   766 	{
   767 	__ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicRecordErrorNotSupported));
   768 	}
   769 
   770 /**
   771 BufferToBeFilled MMFDevSoundObserver.
   772 Called when buffer used up.
   773 */
   774 void CMMFAudioOutput::BufferToBeFilled(CMMFBuffer* aBuffer)
   775 	{
   776 	TInt err = KErrNone;
   777 
   778 	TRAP(err, iSupplier->BufferEmptiedL(aBuffer));
   779 
   780 	//This error needs handling properly
   781 	__ASSERT_DEBUG(!err, Panic(err));
   782 	}
   783 
   784 /**
   785 PlayError MMFDevSoundObserver.
   786 
   787 Called when stopped due to error or EOF.
   788 */
   789 void CMMFAudioOutput::PlayError(TInt aError)
   790 	{
   791 	iMMFDevsoundError = aError;
   792 
   793 	//send EOF to client
   794 	TMMFEvent event(KMMFEventCategoryPlaybackComplete, aError);
   795 	SendEventToClient(event);
   796 
   797 	//stop stack overflow / looping problem - AD
   798 	if (aError == KErrCancel)
   799 		return;
   800 
   801 	// NB KErrInUse, KErrDied OR KErrAccessDenied may be returned by the policy server
   802 	// to indicate that the sound device is in use by another higher priority client.
   803 	if (aError == KErrInUse || aError == KErrDied || aError == KErrAccessDenied)
   804 		return;
   805 
   806 	if (iState == EIdle)
   807 		{//probably have stopped audio output and have got an underflow from last buffer
   808 		iMMFDevSound->Stop();
   809 		iPlayStarted = EFalse;
   810 		iCanSendBuffers = EFalse;
   811 		}
   812 	}
   813 
   814 
   815 /**
   816 ConvertError MMFDevSoundObserver.
   817 
   818 Should never get called.
   819 */
   820 void CMMFAudioOutput::ConvertError(TInt /*aError*/)
   821 	{
   822 	}
   823 
   824 
   825 /**
   826 Returns the number of bytes played.
   827 
   828 @return	The number of bytes played. If 16-bit divide this number returned by 2 to get word length.
   829 */
   830 TInt CMMFAudioOutput::BytesPlayed()
   831 	{
   832 	if (!iMMFDevSound)
   833 		Panic(EMMFAudioOutputDevSoundNotLoaded);
   834 	return iMMFDevSound->SamplesPlayed();
   835 	}
   836 
   837 /**
   838 Returns the sound device.
   839 
   840 Accessor function exposing public CMMFDevsound methods.
   841 
   842 @return	A reference to a CMMFDevSound objector.
   843 */
   844 CMMFDevSound& CMMFAudioOutput::SoundDevice()
   845 	{
   846 	if (!iMMFDevSound)
   847 		Panic(EMMFAudioOutputDevSoundNotLoaded);
   848 	return *iMMFDevSound;
   849 	}
   850 
   851 void CMMFAudioOutput::SendEventToClient(const TMMFEvent& aEvent)
   852 	{
   853 	iEventHandler->SendEventToClient(aEvent);
   854 	}
   855 // __________________________________________________________________________
   856 // Exported proxy for instantiation method resolution
   857 // Define the interface UIDs
   858 
   859 
   860 
   861 const TImplementationProxy ImplementationTable[] =
   862 	{
   863 		IMPLEMENTATION_PROXY_ENTRY(KMmfUidAudioOutputInterface,	CMMFAudioOutput::NewSinkL)
   864 	};
   865 
   866 EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
   867 	{
   868 	aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
   869 
   870 	return ImplementationTable;
   871 	}
   872