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