--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/omxilcomp/omxilaudioemulator/pcmrenderer/src/omxilpcmrendererprocessingfunction.cpp Thu Sep 02 20:13:57 2010 +0300
@@ -0,0 +1,1685 @@
+/*
+* 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);
+ }