omxilcomp/omxilaudioemulator/pcmrenderer/src/omxilpcmrendererprocessingfunction.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 02 Sep 2010 20:13:57 +0300
changeset 0 58be5850fb6c
child 1 e0d606d6e3b1
permissions -rw-r--r--
Revision: 2010wk32 Kit: 201035

/*
* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/


/**
 @file
 @internalComponent
*/

#include <openmax/il/khronos/v1_x/OMX_Audio.h>

#include <d32soundsc.h>

#include "log.h"
#include <openmax/il/common/omxilcallbacknotificationif.h>
#include <openmax/il/common/omxilclockcomponentcmdsif.h>
#include <openmax/il/extensions/omxilsymbianaudiopcmextensions.h>
#include "omxilpcmrendererprocessingfunction.h"

const TInt COmxILPcmRendererProcessingFunction::CPFHelper::KMaxMsgQueueEntries;

COmxILPcmRendererProcessingFunction*
COmxILPcmRendererProcessingFunction::NewL(MOmxILCallbackNotificationIf& aCallbacks, 
										MOmxILClockComponentCmdsIf& aClientClockPort)
	{
    DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::NewL"));

	COmxILPcmRendererProcessingFunction* self =
		new (ELeave)COmxILPcmRendererProcessingFunction(aCallbacks, aClientClockPort);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;

	}

void
COmxILPcmRendererProcessingFunction::ConstructL()
	{
    DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::ConstructL"));

    iAudioDevice = CAudioDevice::NewL(*this);
    iPFHelper = CPFHelper::NewL(*iAudioDevice);
	}

COmxILPcmRendererProcessingFunction::COmxILPcmRendererProcessingFunction(
	MOmxILCallbackNotificationIf& aCallbacks, 
	MOmxILClockComponentCmdsIf& aClientClockPort)
	:
	COmxILProcessingFunction(aCallbacks),
	iClientClockPortPtr(&aClientClockPort)
	{
    DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::COmxILPcmRendererProcessingFunction"));

	}

COmxILPcmRendererProcessingFunction::~COmxILPcmRendererProcessingFunction()
	{
    DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::~COmxILPcmRendererProcessingFunction"));

	// Check in case the Sound Device has not been closed. That would happen in
	// an scenario where the component is not being deleted in an orderer way.
	if(iAudioDevice && iPFHelper &&
	   (iState == OMX_StateInvalid   ||
	    iState == OMX_StateExecuting ||
	    iState == OMX_StatePause))
		{
		// Ignore error if the following call fails
		iPFHelper->CloseDeviceOnError();
		}

	// Buffer headers are not owned by the processing function
    iBuffersToEmpty.Close();
    iBuffersEmptied.Close();
    delete iAudioDevice;
    delete iPFHelper;
	}


OMX_ERRORTYPE
COmxILPcmRendererProcessingFunction::StateTransitionIndication(TStateIndex aNewState)
	{
    DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::StateTransitionIndication"));

    OMX_ERRORTYPE err = OMX_ErrorNone;
	switch(aNewState)
		{
	case EStateExecuting:
		{
		DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StateExecuting"));
		if (iPFHelper->Execute() != KErrNone)
			{
			return OMX_ErrorInsufficientResources;
			}
		}
		break;
	case EStateInvalid:
		{
		DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StateInvalid"));
		if (iPFHelper->Stop() != KErrNone)
			{ // InsufficientResources to stop???
			return OMX_ErrorInsufficientResources;
			}
		}
		break;
	case EStatePause:
		{
		DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StatePause"));
		err = iAudioDevice->MoveToPausedState();
		}
		break;
	case EStateIdle:
		{
		DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StateIdle"));
		iBuffersToEmpty.Reset();
		if (iPFHelper->Stop() != KErrNone)
			{ // InsufficientResources to stop???
			return OMX_ErrorInsufficientResources;
			}
		}
		break;
	case EStateLoaded:
	case EStateWaitForResources:
		{
		DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StateLoaded, OMX_StateWaitForResources"));
		if (iPFHelper->Stop() != KErrNone)
			{ // InsufficientResources to stop???
			return OMX_ErrorInsufficientResources;
			}
		}
		break;
	case ESubStateLoadedToIdle:
		{
		DEBUG_PRINTF(_L8("StateTransitionIndication : ESubStateLoadedToIdle"));
		if (iPFHelper->OpenDevice() != KErrNone)
			{
			return OMX_ErrorInsufficientResources;
			}
		}
		break;
	case ESubStateIdleToLoaded:
		{
		DEBUG_PRINTF(_L8("StateTransitionIndication : ESubStateIdleToLoaded"));
		if (iPFHelper->CloseDevice() != KErrNone)
			{ // InsufficientResources to close???
			return OMX_ErrorInsufficientResources;
			}
		}
		break;
	case ESubStateExecutingToIdle:
	case ESubStatePauseToIdle:
		{
		// Ignore these transitions...
		return OMX_ErrorNone;
		}
	default:
		{
		// Always ASSERT; This would be a problem in the framework.
		ASSERT(0);
		return OMX_ErrorIncorrectStateTransition;
		}
		};

	return err;

	}


OMX_ERRORTYPE
COmxILPcmRendererProcessingFunction::BufferFlushingIndication(
	TUint32 aPortIndex,
	OMX_DIRTYPE aDirection)
	{
    DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::BufferFlushingIndication : aPortIndex[%d]"), aPortIndex);

    if ((aPortIndex == OMX_ALL && aDirection == OMX_DirMax) ||
		(aPortIndex == KPCMRENDERER_APB0PORT_INDEX && aDirection == OMX_DirInput))
		{
		// If we are currently processing a buffer then cancel
		if (iPFHelper->CancelDevice() != KErrNone)
			{
			return OMX_ErrorInsufficientResources;
			}

		// Send BufferDone notifications for each emptied buffer...
		FlushBufferList(iBuffersEmptied);

	    // Send BufferDone notifications for each pending buffer...
		FlushBufferList(iBuffersToEmpty);
		
		return OMX_ErrorNone;
		}
	else if (aPortIndex == KPCMRENDERER_OPB0PORT_INDEX && aDirection == OMX_DirInput)
		{
		// Since the clock port buffers are returned immediately, 
		// there's nothing to flush for the port
		return OMX_ErrorNone;
		}
	else
		{
		// Always ASSERT; This would be a problem in the framework.
		ASSERT(0);
		return OMX_ErrorBadParameter;
		}
	}

void
COmxILPcmRendererProcessingFunction::FlushBufferList(
	RPointerArray<OMX_BUFFERHEADERTYPE>& aBufferList)
	{
    DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::FlushBufferList : [%s]"),
				  &aBufferList == &iBuffersToEmpty ? "iBuffersToEmpty" : "iBuffersEmptied");

	const TUint bufferCount = aBufferList.Count();
	OMX_BUFFERHEADERTYPE* pBufferHeader = 0;
	// We know there is only one input port...
	OMX_DIRTYPE portDirection = OMX_DirInput;

	for (TUint i=0; i<bufferCount; ++i)
		{
		pBufferHeader = aBufferList[i];
		pBufferHeader->nFilledLen = 0;
		iCallbacks.
			BufferDoneNotification(
				pBufferHeader,
				pBufferHeader->nInputPortIndex,
				portDirection
				);
		}

	// Empty buffer list...
	aBufferList.Reset();

	}


OMX_ERRORTYPE
COmxILPcmRendererProcessingFunction::ParamIndication(
	OMX_INDEXTYPE aParamIndex,
	const TAny* apComponentParameterStructure)
	{
    DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::ParamIndication"));

    OMX_ERRORTYPE err = OMX_ErrorNone;
    switch(aParamIndex)
	{
	case OMX_IndexParamAudioPcm:
		{
		const OMX_AUDIO_PARAM_PCMMODETYPE* pPcmProfile
			= static_cast<const OMX_AUDIO_PARAM_PCMMODETYPE*>(
				apComponentParameterStructure);

		if((pPcmProfile->nChannels == 1 || pPcmProfile->nChannels == 2) &&
		  (pPcmProfile->eNumData == OMX_NumericalDataSigned) &&
		  (pPcmProfile->eEndian == OMX_EndianBig) &&
		  (pPcmProfile->bInterleaved == OMX_TRUE) &&
		  (pPcmProfile->nBitPerSample == 16) &&
		  ((pPcmProfile->nSamplingRate == 8000)  ||
		   (pPcmProfile->nSamplingRate == 11025) ||
		   (pPcmProfile->nSamplingRate == 12000) ||
		   (pPcmProfile->nSamplingRate == 16000) ||
		   (pPcmProfile->nSamplingRate == 22050) ||
		   (pPcmProfile->nSamplingRate == 24000) ||
		   (pPcmProfile->nSamplingRate == 32000) ||
		   (pPcmProfile->nSamplingRate == 44100) ||
		   (pPcmProfile->nSamplingRate == 48000)) &&
		  (pPcmProfile->ePCMMode == OMX_AUDIO_PCMModeLinear) &&
		  (pPcmProfile->eChannelMapping[0] == OMX_AUDIO_ChannelLF) &&
		  (pPcmProfile->eChannelMapping[1] == OMX_AUDIO_ChannelRF))
			{
			if (iPFHelper->ParamIndication(pPcmProfile) != KErrNone)
				{
				err = OMX_ErrorInsufficientResources;
				}
			}
		else
			{
			err = OMX_ErrorBadParameter;
			}
		}
		break;
	default:
		{
		// Ignore other port param changes...
		}
	};

	return err;

	}

OMX_ERRORTYPE
COmxILPcmRendererProcessingFunction::ConfigIndication(OMX_INDEXTYPE aConfigIndex,
													  const TAny* apComponentConfigStructure)
	{
    DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::ConfigIndication %X"), aConfigIndex);

    OMX_ERRORTYPE err = OMX_ErrorNone;
    switch(aConfigIndex)
	{
	case OMX_SymbianIndexConfigAudioPcmVolumeRamp:
		{
		const OMX_SYMBIAN_AUDIO_CONFIG_PCM_VOLUMERAMP*
			pPcmVolumeRamp
			= static_cast<
			const OMX_SYMBIAN_AUDIO_CONFIG_PCM_VOLUMERAMP*>(
					apComponentConfigStructure);

		if (iPFHelper->SetVolumeRamp(pPcmVolumeRamp->nRampDuration) != KErrNone)
			{
			err = OMX_ErrorInsufficientResources;
			}
		}
		break;

	case OMX_IndexConfigAudioVolume:
		{
		const OMX_AUDIO_CONFIG_VOLUMETYPE* pVolumeType
			= static_cast<const OMX_AUDIO_CONFIG_VOLUMETYPE*>(
					apComponentConfigStructure);

        if (pVolumeType->bLinear == OMX_TRUE)
            {
            // Some configuration structures contain read-only fields. The 
            // OMX_SetConfig method will preserve read-only fields in configuration
            // structures that contain them, and shall not generate an error when 
            // the caller attempts to change the value of a read-only field.
            err = OMX_ErrorNone;
            break;
            }
			
		if ((pVolumeType->sVolume.nValue <= pVolumeType->sVolume.nMax) &&
		   (pVolumeType->sVolume.nValue >= pVolumeType->sVolume.nMin))
			{
			if (iPFHelper->SetVolume(pVolumeType->sVolume.nValue) != KErrNone)
				{
				err = OMX_ErrorInsufficientResources;
				}
			}
		else
			{
			err = OMX_ErrorBadParameter;
			}
		}
		break;

	case OMX_IndexConfigAudioMute:
		{
		const OMX_AUDIO_CONFIG_MUTETYPE* pVolumeType
		= static_cast<const OMX_AUDIO_CONFIG_MUTETYPE*>(
				apComponentConfigStructure);

		if (iPFHelper->SetMuted(pVolumeType->bMute) != KErrNone)
			{
			err = OMX_ErrorInsufficientResources;
			}
		}
		break;

	default:
		{
		// Ignore other port config changes...
		}
	};

	return err;

	}

OMX_ERRORTYPE
COmxILPcmRendererProcessingFunction::BufferIndication(
	OMX_BUFFERHEADERTYPE* apBufferHeader,
	OMX_DIRTYPE aDirection)
	{
    DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::BufferIndication : [%X]"), apBufferHeader);

	if (aDirection != OMX_DirInput)
		{
		return OMX_ErrorBadParameter;
		}
    
	if (iBuffersToEmpty.Append(apBufferHeader) != KErrNone)
		{
		return OMX_ErrorInsufficientResources;
		}

	// If we are not in an executing state or if the audio device is busy, delay playing back the buffer
	if (iState != OMX_StateExecuting || iAudioDevice->IsActive())
		{
		return OMX_ErrorNone;
		}
				
	if (iPFHelper->BufferIndication() != KErrNone)
		{
		return OMX_ErrorInsufficientResources;
		}

	return OMX_ErrorNone;
	}

OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::MediaTimeIndication(const OMX_TIME_MEDIATIMETYPE& aMediaTimeType)
	{
	// Received a requested media time notification.
	DEBUG_PRINTF5(_L8("MediaTimeIndication : eUpdateType = %d eState = %d xScale = %d nMediaTimestamp = %d "), 
						aMediaTimeType.eUpdateType, aMediaTimeType.eState, aMediaTimeType.xScale, aMediaTimeType.nMediaTimestamp);
	
	iPFHelper->MediaTimeIndication(aMediaTimeType);
	return OMX_ErrorNone;
	}

OMX_BOOL
COmxILPcmRendererProcessingFunction::BufferRemovalIndication(
	OMX_BUFFERHEADERTYPE* apBufferHeader,
	OMX_DIRTYPE /* aDirection */)
	{
    DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::BufferRemovalIndication : BUFFER [%X]"), apBufferHeader);

    TBool headerDeletionResult = ETrue;
    // Check if the buffer we want to remove is the one is being currently processed
    if (iAudioDevice->IsActive() && iAudioDevice->GetCurrentBuffer() == apBufferHeader)
		{
		if (iPFHelper->CancelDevice() != KErrNone)
			{
			return OMX_FALSE;
			}

		// if you cancel the audio device then you send the buffer to the other end of the tunnel
		// so you shouldn't say that you had the buffer in the processing function in this situation.
		headerDeletionResult = EFalse;
		}
    else
		{
		TInt headerIndexInArray = KErrNotFound;
		if (KErrNotFound !=
			(headerIndexInArray =
			 iBuffersToEmpty.Find(apBufferHeader)))
			{
			iBuffersToEmpty.Remove(headerIndexInArray);
			}
		else if(KErrNotFound !=
				(headerIndexInArray =
				 iBuffersEmptied.Find(apBufferHeader)))
			{
			iBuffersEmptied.Remove(headerIndexInArray);
			}
		else
			{
			headerDeletionResult = EFalse;
			}
		}

    DEBUG_PRINTF2(_L8("BufferRemovalIndication : Removal result [%s]"), (headerDeletionResult ? "YES" : "NO"));
    return (headerDeletionResult ? OMX_TRUE : OMX_FALSE);
	}

TInt
COmxILPcmRendererProcessingFunction::GetBytesPlayed() const
	{
	return iAudioDevice->GetBytesPlayed();
	}


COmxILPcmRendererProcessingFunction::CAudioDevice* COmxILPcmRendererProcessingFunction::CAudioDevice::NewL(COmxILPcmRendererProcessingFunction& aParent)
	{
	CAudioDevice* self = new (ELeave) CAudioDevice(aParent);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

COmxILPcmRendererProcessingFunction::CAudioDevice::CAudioDevice(COmxILPcmRendererProcessingFunction& aParent)
: CActive(EPriorityUserInput),
  iParent(aParent),
  iSampleRate(KDefaultSampleRate),
  iChannels(KDefaultNumberChannels),
  iEncoding(KDefaultEncoding),
  iVolume(KDefaultVolume),
  iMuted(KDefaultMuted),
  iBufferSize(KBufferSize),
  iClockStateRunning(EFalse),
  iPausedClockViaScale(EFalse),
  iIsStartTimeFlagSet(EFalse)
	{
	CActiveScheduler::Add(this);
	}


void COmxILPcmRendererProcessingFunction::CAudioDevice::ConstructL()
    {
	iCachedPlayBuffer.CreateL(0);
	}

COmxILPcmRendererProcessingFunction::CAudioDevice::~CAudioDevice()
	{
	delete iPeriodic;
	Cancel();
	iCachedPlayBuffer.Close();
	}

void COmxILPcmRendererProcessingFunction::CAudioDevice::RunL()
	{
	DEBUG_PRINTF(_L8("CAudioDevice::RunL : "));
	if (iStatus != KErrNone)
		{
		switch(iStatus.Int())
			{
		case KErrUnderflow:
			DEBUG_PRINTF(_L8("CAudioDevice::RunL : KErrUnderflow"));
			iParent.iCallbacks.ErrorEventNotification(OMX_ErrorUnderflow);
			break;

		case KErrOverflow:
			DEBUG_PRINTF(_L8("CAudioDevice::RunL : KErrOverflow"));
			iParent.iCallbacks.ErrorEventNotification(OMX_ErrorOverflow);
			break;

		default:
			DEBUG_PRINTF2(_L8("CAudioDevice::RunL : [%d] -> OMX_ErrorHardware"), iStatus.Int());
			iParent.iCallbacks.ErrorEventNotification(OMX_ErrorHardware);
			};
		}

	ASSERT(iCurrentBuffer);
	// Update the last value of bytes played...
	iLastBytesPlayedValue = iSoundDevice.BytesPlayed();

	// Return the emptied buffer to the IL Client or the tunnelled
	// component..
	SignalBufferCompletion(iCurrentBuffer);
	iCurrentBuffer = 0;
		
	// Make sure to clear the aggregated cache buffer, if it was used
	iCachedPlayBuffer.Zero();
	}

void COmxILPcmRendererProcessingFunction::CAudioDevice::SignalBufferCompletion(
	OMX_BUFFERHEADERTYPE* apCurrentBuffer)
	{
	DEBUG_PRINTF2(_L8("CAudioDevice::SignalBufferCompletion : BUFFER = [%X]"), apCurrentBuffer);

	iParent.iBuffersEmptied.Append(apCurrentBuffer);

	// Process the queue only if in executing state...
	if (iParent.iState == OMX_StateExecuting)
		{
		const TUint bufferCount = iParent.iBuffersEmptied.Count();
		OMX_BUFFERHEADERTYPE* pBufferHeader = 0;
		for (TUint i=0; i<bufferCount; ++i)
			{
			pBufferHeader = iParent.iBuffersEmptied[i];
			TBool lastBuffer = EFalse;
			if (pBufferHeader->nFlags & OMX_BUFFERFLAG_EOS)
				{
				lastBuffer = ETrue;
				}

			pBufferHeader->nFilledLen = 0;
			iParent.iCallbacks.BufferDoneNotification(pBufferHeader,
													  pBufferHeader->nInputPortIndex,
													  OMX_DirInput);
			if (lastBuffer)
				{
				pBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS;
				// propagate the EOS flag
				iParent.iCallbacks.EventNotification(
					OMX_EventBufferFlag,
					KPCMRENDERER_APB0PORT_INDEX,
					pBufferHeader->nFlags,
					NULL);
				}
			}

		// Empty list...
		iParent.iBuffersEmptied.Reset();
		}

	if (iParent.iBuffersToEmpty.Count() > 0)
		{
		DEBUG_PRINTF2(_L8("CAudioDevice::RunL : iBuffersToEmpty.Count = [%d]"),
					  iParent.iBuffersToEmpty.Count());
		iParent.iPFHelper->BufferIndication();
		}		
		
	}


TBool COmxILPcmRendererProcessingFunction::CAudioDevice::ConstructAndStartUpdateTimer()
    {
    // Need this check if:
    // - The component state transitions from Execution-Idle-Execution
    // - The Clock's state transitions from Running-Stop-Running
    if (iPeriodic == NULL)
        {
        iPeriodic = CPeriodic::New(EPriorityStandard);
        
        if (iPeriodic == NULL)
            {
            iParent.iCallbacks.ErrorEventNotification(OMX_ErrorInsufficientResources);
            return EFalse;
            }
        }
    
    StartUpdateTimer();
    
    return ETrue;
    }


void COmxILPcmRendererProcessingFunction::CAudioDevice::ProcessNextBuffer()
	{
	if (iParent.iBuffersToEmpty.Count() == 0)
		return;

	// To implement A/V Sync, we should start playing only once the clock component gives the appopriate command
	// If the clock component is not available, we start playing immediately
	// Since the PCM Renderer supplies the reference clock, we make sure to initialise the clock component with
	// the reference when we receive the first buffer
	
	if (!iParent.iClientClockPortPtr->IsClockComponentAvailable())
		{
		PlayData();
		return;
		}

	OMX_BUFFERHEADERTYPE* bufferPtr = iParent.iBuffersToEmpty[0];
	
	TBool bufferHasStartTime = bufferPtr->nFlags & OMX_BUFFERFLAG_STARTTIME;
	
    if (!iClockStateRunning)
        {
        if (!bufferHasStartTime)
            {
            // Connected with the Clock but OMX_BUFFERFLAG_STARTTIME isn't set; simply queue the buffer
            return;
            }
        else
            {
            OMX_ERRORTYPE err = iParent.iClientClockPortPtr->SetStartTime(static_cast<OMX_TICKS>(bufferPtr->nTimeStamp));
            
            if (err == OMX_ErrorNone)
                {
                // Clear the returning buffer's flag
                bufferPtr->nFlags &= ~OMX_BUFFERFLAG_STARTTIME;
                }
            else
                {
                // NOTE: If the Clock is not in OMX_TIME_ClockStateWaitingForStartTime,
                //       currently SetStartTime will return OMX_ErrorIncorrectStateOperation
                
                // It is not the PCM renderer to flag a Clock component error;
                // therefore, ignore the error.
                // 
                // As the Clock is not in OMX_TIME_ClockStateRunning state, the Renderer needs  
                // to keep the OMX_BUFFERFLAG_STARTTIME in the first buffer until the Clock 
                // moves into OMX_TIME_ClockStateWaitingForStartTime or OMX_TIME_ClockStateRunning
                // state
                DEBUG_PRINTF2(_L8("CAudioDevice::ProcessNextBuffer SetStartTime() return %d"), err);
                }
            
            // Update the iStartMediaTime
            iParent.iStartMediaTime = static_cast<OMX_TICKS>(bufferPtr->nTimeStamp);
            iIsStartTimeFlagSet = ETrue;
            }
        } // (!iClockStateRunning)
    else
		{
        if (bufferHasStartTime)
            {
            // The Clock moves straight into OMX_TIME_ClockStateRunning state,
            // clear the returning buffer's flag.
            bufferPtr->nFlags &= ~OMX_BUFFERFLAG_STARTTIME;
            }
        
        if (!iPlayData)
            {
            // Not allowed to render audio. This could be due to:
            //  - The renderer is waiting for a time completion notification from the Clock;
            return;
            }
		
		if (!iIsStartTimeFlagSet)
		    {
		    // As the StartTimeFlag is not mandatory; therefore it might be missing from the first audio buffer
		    // In such a case, we use the first buffer's timestamp as the StartMediaTime.
		    //
		    // NOTE: Since the Clock is running, calling SetStartTime() to the Clock is meaningless
		    
		    // Update the iStartMediaTime
		    iParent.iStartMediaTime = static_cast<OMX_TICKS>(bufferPtr->nTimeStamp);
		    iIsStartTimeFlagSet = ETrue;
		    
            // Cross checking the Clock's media timestamp with iStartMediaTime to see 
            // data can be rendered straight away 
            if (!CanPlayNow())
                {
                return;
                }
            
            if (!ConstructAndStartUpdateTimer())
                {
                return;
                }
		    }
		
		DEBUG_PRINTF3(_L8("ProcessNextBuffer : iStartMediaTime = %d nTimeStamp = %d"), 
	            I64LOW(iParent.iStartMediaTime), I64LOW(bufferPtr->nTimeStamp));
		
		if (!iPausedClockViaScale)	//if clock scale is zero then we are effectively paused
			{
			PlayData();
			}
		} // else
	}


void COmxILPcmRendererProcessingFunction::CAudioDevice::PlayData()
	{
	DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::CAudioDevice::PlayData()++"));
	if (iParent.iBuffersToEmpty.Count() == 0 || iSoundDevice.Handle() == 0 || IsActive())
		{
		DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::CAudioDevice::PlayData() nothing to play, or there is an outstanding request"));
		return;
		}

	iCurrentBuffer = iParent.iBuffersToEmpty[0];

	iParent.iBuffersToEmpty.Remove(0);

	CMMFDataBuffer* mmfSrcBuffer = static_cast<CMMFDataBuffer*>(iCurrentBuffer->pInputPortPrivate);
	mmfSrcBuffer->Data().SetLength(iCurrentBuffer->nFilledLen);

	// Attenuate the amplitude of the samples if volume ramping has been changed
	if (iRampAudioSample)
		{
		iRampAudioSample = RampAudio(mmfSrcBuffer);
		}

	// First, check whether the buffer length is sufficient not to cause underflows in the device driver
	TBool isFilledLengthSufficient = IsBufferLengthSufficient(iCurrentBuffer->nFilledLen);
	// This variable defines whether we should send the data to the driver directly, or append it to an aggregated buffer
	// We append the buffer instead of sending it directly, if (i) the buffer is too small, or (ii)  we have already aggregated something.
	TBool appendBuffer = (!isFilledLengthSufficient || iCachedPlayBuffer.Length() > 0) ? ETrue : EFalse;
	if (!appendBuffer)
		{
		SendBufferToSoundDevice(mmfSrcBuffer->Data());
		}
	else
		{
		// Check if we need to allocate the cached buffer
		if (iCachedPlayBuffer.MaxLength() == 0)
			{
			// The RMdaDevSound shim allocates the shared chunk according to the maxLength of the descriptor it receives
			// For this reason, we must allocate our buffer conservatively, otherwise the chunk may be insufficient and the RMdaDevSound shim will panic			
			TInt err = iCachedPlayBuffer.ReAlloc(GetMinBufferLength() + iCurrentBuffer->nAllocLen);
			if  (err != KErrNone)
				{
				iParent.iCallbacks.ErrorEventNotification(OMX_ErrorInsufficientResources);
				return;
				}
			}
		
		iCachedPlayBuffer.Append(mmfSrcBuffer->Data());
				
		// If we have sufficient length aggregated, play the cached buffer
		// Also if this is the last buffer, we have to play it now, there's nothing left to cache
		if (IsBufferLengthSufficient(iCachedPlayBuffer.Length()) || iCurrentBuffer->nFlags & OMX_BUFFERFLAG_EOS)
			{
			SendBufferToSoundDevice(iCachedPlayBuffer);
			}
		// If not, make sure to notify that we notify that the buffer is done for the OMX tunnnel, so that the port will be able to reuse it
		else
			{
			SignalBufferCompletion(iCurrentBuffer);
			iCurrentBuffer = NULL;
			}
		}
	}

void COmxILPcmRendererProcessingFunction::CAudioDevice::SendBufferToSoundDevice(TDes8& aBuffer) 
	{
	ASSERT(!IsActive());
	iStatus = KRequestPending;
	DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::CAudioDevice::SendBufferToSoundDevice() PlayData [%d]"), aBuffer.Length());
	iSoundDevice.PlayData(iStatus, aBuffer);
	if (iResumeAfterNextPlay)
		{
		iResumeAfterNextPlay = EFalse;
		iSoundDevice.ResumePlaying();
		}
	SetActive();				
	}
	
TInt COmxILPcmRendererProcessingFunction::CAudioDevice::GetMinBufferLength() const
	{
	TInt minBufSize = KMinBufferMilliseconds * iSampleRate * iChannels / 1000;
	if (iEncoding == RMdaDevSound::EMdaSoundEncoding16BitPCM)
		{
		minBufSize *= 2; // All other encodings use 1 byte per sample
		}
	return minBufSize;
	}
	
TBool COmxILPcmRendererProcessingFunction::CAudioDevice::IsBufferLengthSufficient(TInt aBufferLength) const
	{
	return aBufferLength >= GetMinBufferLength() ? ETrue : EFalse;
	}


void COmxILPcmRendererProcessingFunction::CAudioDevice::ProcessMediaTimeIndication(OMX_TIME_MEDIATIMETYPE& aMediaTimeType)
	{
	switch (aMediaTimeType.eUpdateType)
		{
		case OMX_TIME_UpdateClockStateChanged:
			{
			HandleClockStateChanged(aMediaTimeType);
			}
			break;
			
		case OMX_TIME_UpdateRequestFulfillment:
			{
			// As the Clock was using another earlier start time;
			// it is time for the PCM renderer to start playing the audio.
			iPlayData = ETrue;
			PlayData();
			
			if (!ConstructAndStartUpdateTimer())
			    {
			    return;
			    }
			}
			break;
			
		case OMX_TIME_UpdateScaleChanged:
			{
			HandleClockScaleChanged(aMediaTimeType);
			}
			break;
			
		default:
			{
			// Do nothing here
			}
		}
	}


void COmxILPcmRendererProcessingFunction::CAudioDevice::StartUpdateTimer()
	{
	if (!iIsStartTimeFlagSet)
		{
		DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::CAudioDevice::StartUpdateTimer() iIsStartTimeFlagSet == EFalse!!"));
		return;
		}
	
	// In case the timer already started
	ASSERT(iPeriodic);
	iPeriodic->Cancel();
	
	TCallBack callback(UpdateClockMediaTime, this);
	
	// Start updating the Clock comp. every KPcmRendererTimePlayedDelay
	iPeriodic->Start(KPcmRendererTimePlayedDelay, KPcmRendererTimePlayedDelay, callback);
	}


TBool COmxILPcmRendererProcessingFunction::CAudioDevice::CanPlayNow()
    {
    if (iParent.iState != OMX_StateExecuting)
        {
        return EFalse;
        }
    
    if (iClockMediaTime < iParent.iStartMediaTime)
        {
        // Once all required Clock's clients have responded, the clock component starts 
        // the media clock using the earliest client start time.
        // Therefore, start playing the audio only when such time comes; send a time
        // completion request to the Clock component
        OMX_ERRORTYPE err = iParent.iClientClockPortPtr->MediaTimeRequest(NULL, iParent.iStartMediaTime, 0);
        
        if (err != OMX_ErrorNone)
            {
            DEBUG_PRINTF2(_L8("CAudioDevice::CanPlayNow() MediaTimeRequest() return %d"), err);
            }
        
        // Indicate that processing a new buffer should not trigger a false start on playback.
        iPlayData = EFalse;
        return EFalse;
        }
    else if (iClockMediaTime > iParent.iStartMediaTime)
        {
        // NOTE: The spec. states that the clock should use the minimum of the received start times
        //       Therefore the Clock should NOT jump forwards with timestamp greater than the PCM
        //       Renderer iStartMediaTime 
        DEBUG_PRINTF3(_L8("CanPlayNow() nMediaTimestamp(%d) > iStartMediaTime(%d) IGNORE this use case"), 
                I64LOW(iClockMediaTime), I64LOW(iParent.iStartMediaTime));
        
        // However if the Clock sends out a timestamp greater than the buffer's timestamp
        // drop the buffers that fall outside the Clock specified MediaTimestamp;
        
        OMX_BUFFERHEADERTYPE* bufferPtr = iParent.iBuffersToEmpty[0];
        iParent.iBuffersToEmpty.Remove(0);
        SignalBufferCompletion(bufferPtr);
        bufferPtr = NULL;
        
        // Since the iStartMediaTime doesn't make sense anymore, reset iIsStartTimeFlagSet until the buffer's timestamp 
        // is within the the Clock's Media timestamp
        iIsStartTimeFlagSet = EFalse;
        return EFalse;
        }
    
    return ETrue;
    }


void COmxILPcmRendererProcessingFunction::CAudioDevice::HandleClockStateChanged(const OMX_TIME_MEDIATIMETYPE& aMediaTimeType)
	{	
	switch (aMediaTimeType.eState)
	    {
	    case OMX_TIME_ClockStateRunning:
	        {
	        iClockMediaTime = aMediaTimeType.nMediaTimestamp;
	        
	        // There are two possibilities:
	        // case 1 - The clock goes straight into running and the PCM renderer processes the  
	        //          StateChanged notification prior the audio buffer
	         
	        if (iIsStartTimeFlagSet)
	            {
	            // OR
	            // case 2 - The PCM recieves the audio buffer, and the clock StateChanged notification
	            // comes later
	            
	            // Clear the returning buffer's flag.
	            iParent.iBuffersToEmpty[0]->nFlags &= ~OMX_BUFFERFLAG_STARTTIME;
	            
	            // Cross checking the Clock's media timestamp with iStartMediaTime to see 
	            // data can be rendered straight away
	            if (!CanPlayNow())
	                {
	                break;
	                }
	            
	            // Start playing the audio and start updating the Clock media time regularly 
	            PlayData();

	            if (!ConstructAndStartUpdateTimer())
	                {
	                return;
	                }
	            }
			else if ((!iClockStateRunning) && (iParent.iState == OMX_StateExecuting))
				{
				// The clock has gone to running but no start time flag was received. This would
				// indicate that the client moved it straight from stopped to running. As we may
				// have received buffers while in stopped state, we need to start processing
				// them now.
                DEBUG_PRINTF(_L8("HandleClockStateChanged() Gone to running without start time flag set"));
				iClockStateRunning = ETrue;
				ProcessNextBuffer();
				}
	        
	        // Otherwise, the queue is empty;
	        //
	        // NOTE: When !iIsStartTimeFlagSet && !iClockStateRunning would drop the incoming buffers;
	        //       if the first buffer does not have the OMX_BUFFERFLAG_STARTTIME set
	        }
	        break;
	    
        case OMX_TIME_ClockStateWaitingForStartTime:
            {
            if (iClockStateRunning)
                {
                DEBUG_PRINTF(_L8("HandleClockStateChanged() OMX_TIME_ClockStateRunning -> OMX_TIME_ClockStateWaitingForStartTime IGNORED!!"));
                }
            else
                {
                // Let's try to process buffers (if any).
                ProcessNextBuffer();
                }
            }
            break;

        case OMX_TIME_ClockStateStopped:
            {
            if (iClockStateRunning)
                {
                // The Clock was in "Running" state but not anymore; stop the audio
                if (IsActive())
                    {
                    Cancel();
                    }
                else
                    {
                    DoCancel();
                    }

                iPlayData = ETrue;
                if (iIsStartTimeFlagSet)
                    {
                    iIsStartTimeFlagSet = EFalse;
                    iPeriodic->Cancel();
                    }
                }
            
            // Otherwise, ignore other possibilities
            }
            
            break;
         
        default:
            {
            DEBUG_PRINTF2(_L8("HandleClockStateChanged() aMediaTimeType.eState = %d IGNORED!!"), aMediaTimeType.eState);
            }
         break;
	    }
   
	iClockStateRunning = (aMediaTimeType.eState == OMX_TIME_ClockStateRunning) ? ETrue : EFalse;
	}	


void COmxILPcmRendererProcessingFunction::CAudioDevice::HandleClockScaleChanged(const OMX_TIME_MEDIATIMETYPE& aMediaTimeType)
	{
	if (aMediaTimeType.xScale == 0)
		{
		PauseAudio();
		iPausedClockViaScale = ETrue;
        DEBUG_PRINTF2(_L8("HandleClockScaleChanged() pausing iPausedClockViaScale = %d"), iPausedClockViaScale);
 		}
	else if (aMediaTimeType.xScale == 0x10000)
		{
		// The scale is a Q16 value
		iPausedClockViaScale = EFalse;
		DEBUG_PRINTF2(_L8("HandleClockScaleChanged() resuming iPausedClockViaScale = %d"), iPausedClockViaScale);

		// If we are active then there is an outstanding PlayData() request so we need to call ResumePlaying().
		// However calling ResumePlaying() without an outstanding PlayData() request can cause the TimePLayed() API
		// to go awry, so we should defer calling ResumePlaying() until the next PlayData() call.
		if (IsActive())
			{
			iSoundDevice.ResumePlaying();
			}
		else
			{
			iResumeAfterNextPlay = ETrue;
			}
		iParent.iPFHelper->BufferIndication();	//handles the race condition where both 1) iSoundDevice was paused after the last PlayData() completed and before it was passed any data & 2) BufferIndication()s came in for all the IL buffers while in this paused state
		StartUpdateTimer();
		}

	// TODO: Handle the rest of the scale values

	}


void COmxILPcmRendererProcessingFunction::CAudioDevice::DoCancel()
	{
	if (iSoundDevice.Handle() != 0)
		{
		iSoundDevice.CancelPlayData();
		iSoundDevice.FlushPlayBuffer();
		}
	
	if (iCurrentBuffer)
	    {
		iCurrentBuffer->nFilledLen = 0;
		
		iParent.iCallbacks.BufferDoneNotification(iCurrentBuffer,
		        iCurrentBuffer->nInputPortIndex,
		        OMX_DirInput);
		
		iCachedPlayBuffer.Zero();
		iCurrentBuffer = NULL;
		}
	}


OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::OpenDevice()
	{
	OMX_ERRORTYPE err = OMX_ErrorNone;
	if(!iSoundDevice.Handle())
		{
		if(KErrNone != iSoundDevice.Open(KSoundScTxUnit0))
			{
			err = OMX_ErrorHardware;
			}
		}
	return err;
	}

OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::CloseDevice()
	{
	OMX_ERRORTYPE err = OMX_ErrorNone;
	if(iSoundDevice.Handle())
		{
		iSoundDevice.Close();
		iResumeAfterNextPlay = EFalse;
		}
	return err;
	}

OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::Execute()
	{
	if(!iSoundDevice.Handle())
		{
		if(KErrNone != iSoundDevice.Open())
			{
			return OMX_ErrorHardware;
			}
		}

	if(iParent.iState == OMX_StatePause)
		{
		// Now we can send BufferDone notifications for each emptied
		// buffer...
		iParent.FlushBufferList(iParent.iBuffersEmptied);
		// If we are active then there is an outstanding PlayData() request so we need to call ResumePlaying().
		// However calling ResumePlaying() without an outstanding PlayData() request can cause the TimePLayed() API
		// to go awry, so we should defer calling ResumePlaying() until the next PlayData() call.
		if (IsActive())
			{
			iSoundDevice.ResumePlaying();
			}
		else
			{
			iResumeAfterNextPlay = ETrue;
			}
		StartUpdateTimer();
		}
	else
		{
		// Set play format
		RMdaDevSound::TCurrentSoundFormatBuf buf;
		iSoundDevice.GetPlayFormat(buf);
		buf().iRate = iSampleRate;
		buf().iChannels = iChannels;
		buf().iBufferSize = iBufferSize;
		buf().iEncoding = iEncoding;
		if(KErrNone != iSoundDevice.SetPlayFormat(buf))
			{
			return OMX_ErrorHardware;
			}
		
		if (iMuted)
		    {
		    iSoundDevice.SetVolume(0);
		    }
		else
		    {
		    iSoundDevice.SetVolume(iVolume);
		    }
		}

	iParent.iState = OMX_StateExecuting;		
		
	// Make sure to start processing of queued up buffers (if any)
	if (!IsActive() && iParent.iBuffersToEmpty.Count() > 0)
		{
		ProcessNextBuffer();
		}

	return OMX_ErrorNone;
	}

OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::MoveToPausedState()
	{
	iParent.iState = OMX_StatePause;
	PauseAudio();

	return OMX_ErrorNone;
	}

OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::Stop()
	{
	if(iParent.iState == OMX_StateExecuting || iParent.iState == OMX_StatePause)
		{
		// Cancel and flush the device driver
		Cancel();

		// Cancel timer to stop calling RSoundSc::TimePlayed()
		if (iIsStartTimeFlagSet)
			{
			ASSERT(iPeriodic);
			iPeriodic->Cancel();
			}
		
		iParent.iState = OMX_StateIdle;

		// If the audio device is still open, store the last value of bytes
		// played before closing the audio device...
		if(iSoundDevice.Handle() != 0)
			{
			iLastBytesPlayedValue = iSoundDevice.BytesPlayed();
			// Close the sound device
			iSoundDevice.Close();
			iResumeAfterNextPlay = EFalse;
			}
		}
	
	iClockStateRunning = EFalse;
	iIsStartTimeFlagSet = EFalse;
	iPlayData = ETrue;

	return OMX_ErrorNone;
	}


void COmxILPcmRendererProcessingFunction::CAudioDevice::PauseAudio()
	{
	// Cancel timer to stop calling RSoundSc::TimePlayed()
	if (iIsStartTimeFlagSet)
		{
		ASSERT(iPeriodic);
		iPeriodic->Cancel();
		}
	
	if (iSoundDevice.Handle())
		{
		iSoundDevice.PausePlayBuffer();
		}
	}


OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetChannels(TUint aChannels)
	{
	iChannels = aChannels;
	return SetPlayFormat();
	}

OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetSampleRate(TUint aSampleRate)
	{
	iSampleRate = aSampleRate;
	return SetPlayFormat();
	}

TInt ConvertOmxToSymbianVolume(TInt aVolume)
	{
	// OpenMax volume is in millibels while Symbian volume is in 0.5 db increments in the [0..255] range
	// We divide by 50 as 0.5 dB = 50 millibells
	TInt res = KMedianVolume + aVolume / 50;
	if (res < 0)
		return 0;
	if (res > KMaxVolume)
		return KMaxVolume;
	return res;
	}
	
OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetVolume(TInt aVolume)
	{
	iVolume = ConvertOmxToSymbianVolume(aVolume);
	if ((!iMuted) && (iSoundDevice.Handle() != 0))
		{
		iSoundDevice.SetVolume(iVolume);
		}
	return OMX_ErrorNone;
	}

OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetVolumeRamp(const TTimeIntervalMicroSeconds& aRampDuration)
	{
	iVolumeRamp = aRampDuration;
	if(iVolumeRamp.Int64() != 0)
		{
		iRampAudioSample = ETrue;
		ConfigAudioRamper(
			iVolumeRamp.Int64());
		}
	else
		{
		iRampAudioSample = EFalse;
		}
	return OMX_ErrorNone;
	}

OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetMuted(TBool aMuted)
	{
	iMuted = aMuted;
	
	if (iSoundDevice.Handle() == 0)
	    {
		// Just cache the value; the value will be set once the the device is opened
	    return OMX_ErrorNone;
	    }
	
	if (iMuted)
		{
		iSoundDevice.SetVolume(0);
		}
	else
		{
		iSoundDevice.SetVolume(iVolume);
		}
	return OMX_ErrorNone;
	}

OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetPlayFormat()
	{
	if (iParent.iState == OMX_StateExecuting)
		{
		RMdaDevSound::TCurrentSoundFormatBuf buf;
		iSoundDevice.GetPlayFormat(buf);
		buf().iRate = iSampleRate;
		buf().iChannels = iChannels;
		buf().iBufferSize = iBufferSize;
		buf().iEncoding = iEncoding;
		if(KErrNone != iSoundDevice.SetPlayFormat(buf))
			{
			return OMX_ErrorHardware;
			}
		}
	return OMX_ErrorNone;
	}

OMX_BUFFERHEADERTYPE* COmxILPcmRendererProcessingFunction::CAudioDevice::GetCurrentBuffer()
	{
	return iCurrentBuffer;
	}

TInt COmxILPcmRendererProcessingFunction::CAudioDevice::GetBytesPlayed()
	{
	if(iSoundDevice.Handle() != 0)
		{
		return iSoundDevice.BytesPlayed();
		}

	return iLastBytesPlayedValue;
	}

void COmxILPcmRendererProcessingFunction::CAudioDevice::ConfigAudioRamper(TInt64 aRampTime)
	{
	iRampSamples = I64LOW(((TInt64(iSampleRate) * aRampTime) /1000000 )); // Add this
	iRampSamplesLeft = iRampSamples;
	iRampIncr = 0;
	iSkip = ETrue;
	}

TBool COmxILPcmRendererProcessingFunction::CAudioDevice::RampAudio(CMMFDataBuffer* aBuffer)
	{
	TInt i=0;
	TInt length = aBuffer->Data().Length()>>1;
	if (length == 0)
		{
		return EFalse;
		}
		
	TInt16* sample = REINTERPRET_CAST(TInt16*,&aBuffer->Data()[0]);
	TInt64 theResult(0);
	while ((i < length) && (iRampIncr < iRampSamples))
		{
		theResult = sample[i];
		theResult *= iRampIncr;
		theResult /= iRampSamples;
		sample[i] = STATIC_CAST(TInt16, I64LOW(theResult) );

		if ((iChannels == 1) || (!iSkip))
			{
			iRampIncr++;
			}
		iSkip = !iSkip;
		i++;
		}

	if (iRampIncr < iRampSamples)
		return ETrue;
	else
		return EFalse;
	}


TInt COmxILPcmRendererProcessingFunction::CAudioDevice::UpdateClockMediaTime(TAny* aPtr)
	{
	CAudioDevice* ptr = (CAudioDevice*)aPtr;
	TTimeIntervalMicroSeconds playedTime(0);
  	
	if (ptr->iSoundDevice.GetTimePlayed(playedTime) != KErrNone)
		{
		ptr->iParent.iCallbacks.ErrorEventNotification(OMX_ErrorHardware);
		return EFalse;
		}
	
  	OMX_ERRORTYPE err;
  	
	// Update the clock component audio reference clock
  	err = ptr->iParent.iClientClockPortPtr->SetAudioReference(ptr->iParent.iStartMediaTime + playedTime.Int64());

	if (err != OMX_ErrorNone)
		{
		ptr->iParent.iCallbacks.ErrorEventNotification(err);
		return EFalse;
		}
	
	DEBUG_PRINTF2(_L8("CAudioDevice::UpdateClockMediaTime : playedTime = %d"), 
					I64LOW(playedTime.Int64()));

	return ETrue;
	}


void COmxILPcmRendererProcessingFunction::CAudioDevice::ProcessParamIndication(const OMX_AUDIO_PARAM_PCMMODETYPE& aPcmModeType)
	{
	if(SetChannels(aPcmModeType.nChannels) != OMX_ErrorNone)
		{
		iParent.iCallbacks.ErrorEventNotification(OMX_ErrorHardware);
		return;
		}
	
	if (SetSampleRate(aPcmModeType.nSamplingRate) != OMX_ErrorNone)
		{
		iParent.iCallbacks.ErrorEventNotification(OMX_ErrorHardware);
		return;
		}
	}

COmxILPcmRendererProcessingFunction::CPFHelper* COmxILPcmRendererProcessingFunction::CPFHelper::NewL(CAudioDevice& aAudioDevice)
	{
	CPFHelper* self = new (ELeave) CPFHelper(aAudioDevice);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

COmxILPcmRendererProcessingFunction::CPFHelper::CPFHelper(CAudioDevice& aAudioDevice)
: CActive(EPriorityUserInput),
  iAudioDevice(aAudioDevice)
	{
	}
	
void COmxILPcmRendererProcessingFunction::CPFHelper::ConstructL()
	{
	CActiveScheduler::Add(this);
	User::LeaveIfError(iCallerSemaphore.CreateGlobal(KNullDesC, 0));
	User::LeaveIfError(iMsgQueue.CreateLocal(KMaxMsgQueueEntries));
	iMsgQueue.NotifyDataAvailable(iStatus);
	RThread thisThread;
	iHelperThreadId = thisThread.Id();
	thisThread.Close();
	SetActive();
	}
	
COmxILPcmRendererProcessingFunction::CPFHelper::~CPFHelper()
	{
	Cancel(); 
	iMsgQueue.Close();
	iCallerSemaphore.Close();
	}

void COmxILPcmRendererProcessingFunction::CPFHelper::RunL()
	{

	TProcMessage msg;
	while (iMsgQueue.Receive(msg)==KErrNone)
		{
		switch (msg.iType)
			{
			case EOpenDevice:
				{
				iAudioDevice.OpenDevice();
				break;
				}
			case ECloseDevice:
				{
				iAudioDevice.CloseDevice();
				break;
				}
			case ECloseDeviceOnError:
				{
				iAudioDevice.Cancel();
				iAudioDevice.CloseDevice();
				iCallerSemaphore.Signal();
				break;
				}
			case EExecuteCommand:
				{
				iAudioDevice.Execute();	
				break;
				}				

			case EStopCommand:
				{
				iAudioDevice.Stop();	
				break;
				}
				
			case ECancelCommand:
				{
				iAudioDevice.Cancel();	
				break;
				}
				
			case EBufferIndication:
				{
				iAudioDevice.ProcessNextBuffer();	
				break;
				}
			case EMediaTimeIndication:
				{
				iAudioDevice.ProcessMediaTimeIndication(msg.iMediaTimeType);	
				break;
				}
			case EParamIndication:
				{
				iAudioDevice.ProcessParamIndication(msg.iPcmModeType);
				break;
				}
			case ESetVolumeRamp:
				{
				iAudioDevice.SetVolumeRamp(TTimeIntervalMicroSeconds(msg.iRampDuration));
				break;
				}
			case ESetVolume:
				{
				iAudioDevice.SetVolume(msg.iVolumeValue);
				break;
				}
			case ESetMuted:
				{
				iAudioDevice.SetMuted(msg.iMuted);
				break;
				}
			default:
				{
				break;
				}					
			}
		}
	DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::CPFHelper::RunL : msg.iType[%d]"), msg.iType);		
	// setup for next callbacks		
	iMsgQueue.NotifyDataAvailable(iStatus);
	SetActive();
	}

void COmxILPcmRendererProcessingFunction::CPFHelper::DoCancel()
	{
	if (iMsgQueue.Handle()!=NULL)
		{
		iMsgQueue.CancelDataAvailable();
		}
	}

TInt COmxILPcmRendererProcessingFunction::CPFHelper::OpenDevice()
	{
	TProcMessage message;
	message.iType = EOpenDevice;
	return iMsgQueue.Send(message);
	}

TInt COmxILPcmRendererProcessingFunction::CPFHelper::CloseDevice()
	{
	DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::CPFHelper::CloseDevice : IsActive[%s]"),
				  (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = ECloseDevice;
	return iMsgQueue.Send(message);
	}

TInt COmxILPcmRendererProcessingFunction::CPFHelper::CloseDeviceOnError()
	{
	DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::CPFHelper::CloseDeviceOnError : IsActive[%d]"), (IsActive() ? 1 : 0));

	RThread thisThread;
	if (thisThread.Id() == iHelperThreadId)
		{
		// Just do it...
		iAudioDevice.Cancel();
		iAudioDevice.CloseDevice();
		}
	else
		{

		TProcMessage message;
		message.iType = ECloseDeviceOnError;
		TInt error = iMsgQueue.Send(message);
		if (KErrNone != error)
			{
			// only wait if the message was sent into the queue...
			iCallerSemaphore.Wait();
			}
		}

	thisThread.Close();

	return KErrNone;

	}

TInt COmxILPcmRendererProcessingFunction::CPFHelper::Execute()
	{
	DEBUG_PRINTF2(_L8("CPFHelper::Execute : IsActive[%s]"), (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = EExecuteCommand;
	return iMsgQueue.Send(message);
	}
		
TInt COmxILPcmRendererProcessingFunction::CPFHelper::Stop()
	{
	DEBUG_PRINTF2(_L8("CPFHelper::Stop : IsActive[%s]"), (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = EStopCommand;
	return iMsgQueue.Send(message);
	}
	
TInt COmxILPcmRendererProcessingFunction::CPFHelper::CancelDevice()
	{
	DEBUG_PRINTF2(_L8("CPFHelper::CancelDevice : IsActive[%s]"), (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = ECancelCommand;
	return iMsgQueue.Send(message);
	}
	
TInt COmxILPcmRendererProcessingFunction::CPFHelper::BufferIndication()
	{
	DEBUG_PRINTF2(_L8("CPFHelper::BufferIndication : IsActive[%s]"), (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = EBufferIndication;
	return iMsgQueue.Send(message);
	}

TInt COmxILPcmRendererProcessingFunction::CPFHelper::MediaTimeIndication(const OMX_TIME_MEDIATIMETYPE& aMediaTimeType)
	{
	DEBUG_PRINTF2(_L8("CPFHelper::MediaTimeIndication : IsActive[%s]"), (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = EMediaTimeIndication;
	message.iMediaTimeType.eUpdateType = aMediaTimeType.eUpdateType;
	message.iMediaTimeType.eState = aMediaTimeType.eState;
	message.iMediaTimeType.xScale = aMediaTimeType.xScale;	
	message.iMediaTimeType.nMediaTimestamp = aMediaTimeType.nMediaTimestamp;
	
	return iMsgQueue.Send(message);
	}

TInt COmxILPcmRendererProcessingFunction::CPFHelper::ParamIndication(const OMX_AUDIO_PARAM_PCMMODETYPE* aPcmModeType)
	{
	DEBUG_PRINTF2(_L8("CPFHelper::ParamIndication : IsActive[%s]"), (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = EParamIndication;
	message.iPcmModeType.nChannels =  aPcmModeType->nChannels;
	message.iPcmModeType.nSamplingRate =  aPcmModeType->nSamplingRate;
	
	return iMsgQueue.Send(message);
	}

TInt COmxILPcmRendererProcessingFunction::CPFHelper::SetVolumeRamp(const OMX_U64 aRampDuration)
	{
	DEBUG_PRINTF2(_L8("CPFHelper::SetVolumeRamp : IsActive[%s]"), (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = ESetVolumeRamp;
	message.iRampDuration = aRampDuration;
	
	return iMsgQueue.Send(message);
	}

TInt COmxILPcmRendererProcessingFunction::CPFHelper::SetVolume(const OMX_S32 aVolumeValue)
	{
	DEBUG_PRINTF2(_L8("CPFHelper::SetVolume : IsActive[%s]"), (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = ESetVolume;
	message.iVolumeValue = aVolumeValue;
	
	return iMsgQueue.Send(message);	
	}

TInt COmxILPcmRendererProcessingFunction::CPFHelper::SetMuted(const OMX_BOOL aMuted)
	{
	DEBUG_PRINTF2(_L8("CPFHelper::SetMuted : IsActive[%s]"), (IsActive() ? "YES" : "NO"));
	TProcMessage message;
	message.iType = ESetMuted;
	message.iMuted = aMuted;
	
	return iMsgQueue.Send(message);
	}