diff -r 000000000000 -r 40261b775718 mmhais/refacladapt/src/tonehwdevice/tonedatapath.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmhais/refacladapt/src/tonehwdevice/tonedatapath.cpp Tue Feb 02 01:56:55 2010 +0200 @@ -0,0 +1,704 @@ +// Copyright (c) 2003-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: +// + +#include "tonedatapath.h" + + +CToneDataPath* CToneDataPath::NewL() + { + CToneDataPath* self = new(ELeave) CToneDataPath; + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + + +void CToneDataPath::ConstructL() + { + iAudioPlayer = new (ELeave) CToneDataPathPlayer(*this,CActive::EPriorityUserInput); + iSoundDeviceErrorReceiver = new (ELeave) CToneSoundDevPlayErrorReceiver(*this, CActive::EPriorityUserInput); + } + + +CToneDataPath::~CToneDataPath() + { + delete iAudioPlayer; + delete iSoundDeviceErrorReceiver; + + iSoundDevice.Close(); + + if (iCodec) + { + delete iSourceBuffer; + if (!iCodec->IsNullCodec()) + { + delete iSoundDeviceBuffer; + } + } + +#ifdef __USE_MMF_TRANSFERBUFFERS__ + delete iTransferWindow; + + if(iTransferBuffer) + { + iTransferBuffer->Close(); + delete iTransferBuffer; + } +#endif + +#ifdef __USE_MMF_PTRBUFFERS__ + delete iPtrBufferMemoryBlock; +#endif + } + + +TInt CToneDataPath::SetObserver(MMMFHwDeviceObserver& aObserver) + { + TInt error; + if (iHwDeviceObserver) + { + error = KErrAlreadyExists; + } + else + { + iHwDeviceObserver = &aObserver; + error = KErrNone; + } + return error; + } + + +TInt CToneDataPath::AddCodec(CToneCodec& aCodec) + { + if (iCodec) + { + return KErrNotSupported; //doesn't support multiple codecs + } + + TInt err = KErrNone; + + iCodec = &aCodec; + + // Allocate data buffer + iSourceBufferSize = iCodec->SourceBufferSize(); + iSoundDevBufferSize = iCodec->SinkBufferSize(); + + if ((!iSourceBufferSize)||(!iSoundDevBufferSize)) + { + err = KErrArgument; //codec plugin has not specified buffer size + } + + if (err == KErrNone) + { +#ifdef __USE_MMF_TRANSFERBUFFERS__ + TRAP(err,iSourceBuffer = CreateTransferBufferL(iSourceBufferSize, static_cast(iSourceBuffer))); +#endif +#ifdef __USE_MMF_PTRBUFFERS__ + TRAP(err,iSourceBuffer = CreatePtrBufferL(iSourceBufferSize)); +#else + TRAP(err,iSourceBuffer = CMMFDataBuffer::NewL(iSourceBufferSize)); +#endif + } + + if (err == KErrNone) + { + if (iCodec->IsNullCodec()) + {//use source buffer for sound device buffer + iSoundDeviceBuffer = NULL; + } + else + {//codec needs separate source and sound device buffers + TRAP(err,iSoundDeviceBuffer = CMMFDataBuffer::NewL(iSoundDevBufferSize)); + } + } + return err; + } + +TInt CToneDataPath::Start() + { + TInt startError = KErrNone; + + if (!iCodec) + {//check that a codec has been added + startError = KErrNotReady; + } + if ((!iSoundDevice.Handle())&&(!startError)) + {//check that the sound drivers can be opened + startError = iSoundDevice.Open(); + } + + if (iState == EPaused) + {//we are paused so need to resume play + if (!startError) + { +#ifdef _SCW_DEBUG + RDebug::Print(_L("CToneDataPath::Start-Resume")); +#endif + iAudioPlayer->ResumePlaying(); + iState = EPlaying; + } + } + else if (!startError) + { +#ifdef _SCW_DEBUG + RDebug::Print(_L("CToneDataPath::Start-Normal")); +#endif + // get sample rate and channels from RMdaDevSound + RMdaDevSound::TCurrentSoundFormatBuf format; + iSoundDevice.GetPlayFormat(format); + iSampleRate = format().iRate; + iChannels = format().iChannels; + + iNoMoreSourceData = EFalse; + iSourceBuffer->SetLastBuffer(EFalse); + iState = EPlaying; + iSoundDeviceErrorReceiver->Start(); + TRAP(startError,FillSourceBufferL()); //get initial buffer of audio data + if (startError == KErrNone) + { + // Start the player objects + iAudioPlayer->Start(); + } + else + {//failed to start up correctly go back to stopped state + iState = EStopped; + iSoundDeviceErrorReceiver->Stop(); + } + } + return startError; + } + + +// *** Main Play Loop *** + +void CToneDataPath::FillSourceBufferL() + {//asks observer to fill the source buffer + // Ask immediately for data from the observer +#ifdef __CYCLE_MMF_DATABUFFERS__ + // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics + // If the creation fails, we carry on regardless as the original buffer will not have been + // destroyed. Must do this as alloc fail tests will not run. + if(iSourceBuffer) + { + iSourceBuffer = CycleAudioBuffer(iSourceBuffer); + } +#endif // __CYCLE_MMF_DATABUFFERS__ + User::LeaveIfError(iHwDeviceObserver->FillThisHwBuffer(*iSourceBuffer)); + + } + + +void CToneDataPath::BufferFilledL(CMMFDataBuffer& aBuffer) + {//call back from observer to indicate buffer has been filled + + if (iState == EStopped) + { + User::Leave(KErrNotReady);//ok if paused? + } + + iSourceBuffer = &aBuffer; + iSourceBuffer->SetStatus(EFull); +#ifdef _SCW_DEBUG + RDebug::Print(_L("CToneDataPath::BufferFilledL")); +#endif + + //need to check that the buffer size is not 0 - if so assume we've reached the end of the data + iBuffSize = iSourceBuffer->BufferSize(); + if (!iBuffSize) + {//no buffer - could be end of source or could be that the source has no data?? + iNoMoreSourceData = ETrue; +#ifdef _SCW_DEBUG + RDebug::Print(_L("CToneDataPath::BufferFilledL-NoMoreSourceData")); +#endif + } + //even if the buffer size is 0 we still + //need to perform the following to get the sound device callback + FillSoundDeviceBufferL(); //get buffer in pcm16 format for sound device + + iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers + } + + +void CToneDataPath::FillSoundDeviceBufferL() + {//use CToneCodec to fill the sound device buffer + + CToneCodec::TCodecProcessResult codecProcessResult; + + if (iCodec->IsNullCodec()) + {//no codec so data can be sent direct to sink + iSoundDeviceBuffer = iSourceBuffer; + iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full + } + else + { + //pass buffer to codec for processing + codecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSoundDeviceBuffer); + + if (iSourceBuffer->LastBuffer()) //if source is last buffer so is sound dev + { + iSoundDeviceBuffer->SetLastBuffer(ETrue); + } + if ((!iSoundDeviceBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded)) + {//the codec has added data but not set the buffer length + iSoundDeviceBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded); + } + //only supports EProcessComplete + switch (codecProcessResult.iCodecProcessStatus) + { + case CToneCodec::TCodecProcessResult::EProcessComplete: + //finished procesing source data - all data in sink buffer + { + iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full + } + break; +#ifdef SYMBIAN_VARIABLE_BITRATE_CODEC + case CToneCodec::TCodecProcessResult::EProcessIncomplete: + //finished procesing source data - all data in sink buffer + { + iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full + } + break; +#endif + case CToneCodec::TCodecProcessResult::EDstNotFilled: + //could be the last buffer in which case dst might not get filled + { + iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full + } + break; + case CToneCodec::TCodecProcessResult::EEndOfData: + //no more data - send what we've got to the sink + //note we can't always rely on this - in many cases the codec will not know when + //it has reached the end of data. + { + iSoundDeviceBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get + iNoMoreSourceData = ETrue; + //doesn't matter if sink buffer is not full + } + break; + default: + //Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec + break; + } + } + } + + +void CToneDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer) + {//call back from CToneDataPathPlayer when the sound device buffer has been emptied + if (&aBuffer != iSoundDeviceBuffer) + { + Panic(EToneBadBuffer); + } + + if (!iNoMoreSourceData) + { + FillSourceBufferL(); + } + } + +//*** End of Main Play Loop *** + + +void CToneDataPath::Stop() + { + iAudioPlayer->Cancel(); + iSoundDeviceErrorReceiver->Cancel(); + iSoundDevice.Close(); + +#ifdef __CYCLE_MMF_DATABUFFERS__ + // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics + // If the creation fails, we carry on regardless as the original buffer will not have been + // destroyed. Must do this as alloc fail tests will not run. + if(iSourceBuffer) + { + iSourceBuffer = CycleAudioBuffer(iSourceBuffer); + } +#endif // __CYCLE_MMF_DATABUFFERS__ + + iState = EStopped; + } + + +void CToneDataPath::Pause() + { + //since a pause can happen anyway in the datatransfer -need to set to a known + //state so that when play is resumed the behaviour is predictable + if (iSoundDevice.Handle()) + { + iSoundDevice.PausePlayBuffer(); //needs new LDD + iState = EPaused; +#ifdef _SCW_DEBUG + RDebug::Print(_L("Pause")); +#endif + } + else + {//an error must have occured + iState = EStopped; + } + } + + +TInt CToneDataPath::EmptyBuffers() + { + TInt error = KErrNone; + if (iSoundDevice.Handle() == 0) + { + error = KErrNotReady; + } + else + { + iSoundDevice.FlushPlayBuffer(); + } + return error; + } + + +RMdaDevSound& CToneDataPath::Device() + { + return iSoundDevice; + } + + +void CToneDataPath::SoundDeviceException(TInt aError) + { + if(iIgnoreUnderflow) + { + if((aError == KErrUnderflow) && (!iNoMoreSourceData)) + { + //ignore underflow + return; + } + } + + //this sends a request to the hw device observer + //to update the bytes played + //it is done here so that the sound driver can be closed prior to + //updating the policy and sending the error back + TUid uidUpdateBytesPlayed; + uidUpdateBytesPlayed.iUid = KToneHwDeviceObserverUpdateBytesPlayed; + TPtrC8 dummy(0,0); + + ASSERT(iHwDeviceObserver); + iHwDeviceObserver->MsgFromHwDevice(uidUpdateBytesPlayed,dummy); + + //this closes RMdaDevSound. + Stop(); + + //inform devsound so it can update policy + iHwDeviceObserver->Stopped(); + + // Inform the observer of the exception condition + // We inform the hw device observer after the policy has been + // updated incase the observer relied on the error to assume + // the policy has been updated + iHwDeviceObserver->Error(aError); + + RDebug::Print(_L("CToneDataPath::iHwDeviceObserver->Error(%d)"),aError); + } + +/** +Retrieves a custom interface to the device. +The reference CToneDataPath supports three custom interfaces, +MEmptyBuffersCustomInterface, MSetVbrFlagCustomInterface and MIgnoreUnderflowEventsCustomInterface + +@param aInterface + Interface UID, defined with the custom interface. + aInterface = KMmfUidEmptyBuffersCustomInterface for MEmptyBuffersCustomInterface, + KSetVbrFlagCustomInterfaceTypeUid for MSetVbrFlagCustomInterface + +@return A pointer to the interface implementation, or NULL if the device can not + implement the interface requested. The return value must be cast to the + correct type by the user. +*/ +TAny* CToneDataPath::CustomInterface(TUid aInterface) + { + TAny* ret = NULL; + + if (aInterface == KIgnoreUnderflowCustomInterfaceTypeUid) + { + MIgnoreUnderflowEventsCustomInterface* result = static_cast (this); + ret = static_cast(result); + } + return ret; + } + + +void CToneDataPath::IgnoreUnderflowEvents() + { + iIgnoreUnderflow = ETrue; + } + + + +/************************************************************************ + * CDataPathPlayer * + ************************************************************************/ + +CToneDataPathPlayer::CToneDataPathPlayer(CToneDataPath& aParent, TInt aPriority) +: CActive(aPriority), iParent(aParent) + { + CActiveScheduler::Add(this); + } + + +CToneDataPathPlayer::~CToneDataPathPlayer() + { + Cancel(); + } + + +void CToneDataPathPlayer::Start() + { + // No implementation + } + + +void CToneDataPathPlayer::ResumePlaying() + { + if (iParent.Device().Handle()) + { + //should be ok to call this even if we are active + iParent.Device().ResumePlaying(); + iResumePlaying = ETrue; + } +#ifdef _SCW_DEBUG + RDebug::Print(_L("Playing Resumed")); +#endif + } + + +void CToneDataPathPlayer::PlayData(const CMMFDataBuffer& aData) + { + iDataFromSource = &aData; + if (!IsActive()) + { +#ifdef _SCW_DEBUG + RDebug::Print(_L("CToneDataPathPlayer::PlayData")); +#endif + iParent.Device().PlayData(iStatus,(static_cast (iDataFromSource))->Data()); + SetActive(); + } + } + + +void CToneDataPathPlayer::Stop() + { + if (!IsActive()) + { + iParent.Device().FlushPlayBuffer(); // Otherwise won't be flushed + } + Cancel(); + iParent.SoundDeviceException(KErrCancel); + } + + +void CToneDataPathPlayer::RunL() + { +#ifdef _SCW_DEBUG + RDebug::Print(_L("CToneDataPathPlayer::RunL error[%d]"), iStatus.Int()); +#endif + if (iStatus.Int()!=KErrNone) + { + iParent.SoundDeviceException(iStatus.Int()); + } + else + { + iParent.BufferEmptiedL(static_cast(*iDataFromSource)); + iResumePlaying = EFalse; + } + } + + +TInt CToneDataPathPlayer::RunError(TInt aError) + { + Error(aError); + return KErrNone; + } + + +void CToneDataPathPlayer::DoCancel() + { + if (iParent.Device().Handle()) + { + iParent.Device().CancelPlayData(); + iParent.Device().FlushPlayBuffer(); + } + } + + +void CToneDataPathPlayer::Error(TInt aError) + { + iParent.SoundDeviceException(aError); + } + + + +/************************************************************************ + * CToneSoundDevPlayErrorReceiver * + ************************************************************************/ + +CToneSoundDevPlayErrorReceiver::CToneSoundDevPlayErrorReceiver(CToneDataPath& aParent, TInt aPriority) +: CActive(aPriority), iParent(aParent) + { + CActiveScheduler::Add(this); + } + +CToneSoundDevPlayErrorReceiver::~CToneSoundDevPlayErrorReceiver() + { + Cancel(); + } + +void CToneSoundDevPlayErrorReceiver::Start() + { + iParent.Device().NotifyPlayError(iStatus); + SetActive(); + } + +void CToneSoundDevPlayErrorReceiver::Stop() + { + Cancel(); + } + +void CToneSoundDevPlayErrorReceiver::RunL() + { + TInt reason = iStatus.Int(); + Start(); + + // An error has been returned +#ifdef _SCW_DEBUG + RDebug::Print(_L("CToneSoundDevPlayErrorReceiver::RunL[%d]"), reason); +#endif + iParent.SoundDeviceException(reason); + } + +void CToneSoundDevPlayErrorReceiver::DoCancel() + { + iParent.Device().CancelNotifyPlayError(); + } + + + +/* + * CycleAudioBufferL + * + * Sets up a usable buffer for passing to MMF + * + * This method has been written such that it must allocate a new buffer before + * replacing the existing one. The purpose of this is to force creation of a + * new buffer. Simply deleting and then re-allocing may result in the same + * address being used. + * + * Only cycles if there is enough memory + * + */ +#ifdef __CYCLE_MMF_DATABUFFERS__ +CMMFDataBuffer* CToneDataPath::CycleAudioBuffer(CMMFDataBuffer* aBuffer) + { + CMMFDataBuffer* buffer = NULL; + TUint bufferSize = aBuffer->Data().MaxLength(); + +#ifdef __USE_MMF_TRANSFERBUFFERS__ + TRAPD(err, buffer = CreateTransferBufferL(bufferSize, static_cast(aBuffer))); +#else + TRAPD(err,buffer = CMMFDataBuffer::NewL(bufferSize)); + + if (err == KErrNone) + { + delete aBuffer; + } +#endif + if (err != KErrNone) + {//there was a problem creating buffer eg OOM so use same buffer + buffer = aBuffer; + } + + return buffer; + + } +#endif + +/* + * DoCleanupRHandleBase + * + * This method will initially Close the handle and then delete it. + * + */ +#ifdef __USE_MMF_TRANSFERBUFFERS__ +inline static void DoCleanupRHandleBase(TAny* aRHandleBase) + { + ASSERT(aRHandleBase); + RHandleBase* rHandleBase = static_cast (aRHandleBase); + TRAPD(error, rHandleBase->Close()); + delete aRHandleBase; + } + +CMMFTransferBuffer* CToneDataPath::CreateTransferBufferL(TUint aBufferSize, CMMFTransferBuffer* aOldBuffer) + { + CMMFTransferBuffer* buffer = NULL; + + RTransferBuffer* transBuffer = new (ELeave) RTransferBuffer; + + TCleanupItem bufferCleanupItem(DoCleanupRHandleBase, transBuffer); //closes and deletes. + CleanupStack::PushL(bufferCleanupItem); + + RTransferWindow* transWindow = new (ELeave) RTransferWindow; + + TCleanupItem windowCleanupItem(DoCleanupRHandleBase, transWindow); //closes and deletes. + CleanupStack::PushL(windowCleanupItem); + + User::LeaveIfError(transBuffer->Create(aBufferSize)); + User::LeaveIfError(transWindow->Create(aBufferSize)); + User::LeaveIfError(transWindow->MapInBuffer(*transBuffer)); + + buffer = CMMFTransferBuffer::NewL(*transWindow); + + delete aOldBuffer; //closes RTransferWindow + delete iTransferWindow; + + if(iTransferBuffer) + { + iTransferBuffer->Close(); + } + delete iTransferBuffer; + + iTransferBuffer = transBuffer; + iTransferWindow = transWindow; + + CleanupStack::Pop(transWindow); + CleanupStack::Pop(transBuffer); + + return buffer; + } +#endif + + +#ifdef __USE_MMF_PTRBUFFERS__ +CMMFPtrBuffer* CToneDataPath::CreatePtrBufferL(TUint aBufferSize) + { + CMMFPtrBuffer* buffer = NULL; + if (iPtrBufferMemoryBlock) + { + delete iPtrBufferMemoryBlock;//incase already exisits + } + iPtrBufferMemoryBlock = HBufC8::NewL(aBufferSize); + TPtr8 ptrMemoryBlock(iPtrBufferMemoryBlock->Des()); + buffer = CMMFPtrBuffer::NewL(ptrMemoryBlock); + return buffer; + } +#endif // __USE_MMF_PTRBUFFERS__ + + +