diff -r 000000000000 -r b8ed18f6c07b mmlibs/mmfw/src/Client/Audio/mmfclientaudiooutputstream.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmlibs/mmfw/src/Client/Audio/mmfclientaudiooutputstream.cpp Thu Oct 07 22:34:12 2010 +0100 @@ -0,0 +1,1091 @@ +// Copyright (c) 2002-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 +#include +#include "mmfclientaudiooutputstream.h" +#include "mmfclientaudiostreamutils.h" +#include "MmfFifo.h" +#include + +#define WAIT_FOR_READY_ACTIVE_OBJECTS + +const TInt KMicroSecsInOneSec = 1000000; +const TInt KUnknownVolume = -1; // means "don't set", must be negative +const TInt KShutDownTimeInterval = 100000; //100 milli seconds + +/** + * + * Static NewL + * + * @return CMdaAudioOutputStream* + * + */ +EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback, + CMdaServer* /*aServer = NULL*/) + { + return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality); + } + +/** + * + * Static NewL + * + * @return CMdaAudioOutputStream* + * + */ +EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback, + TInt aPriority, + TInt aPref /*=EMdaPriorityPreferenceTimeAndQuality*/) + { + CMdaAudioOutputStream* self = new(ELeave) CMdaAudioOutputStream(); + CleanupStack::PushL(self); + self->iProperties = CMMFMdaAudioOutputStream::NewL(aCallback, aPriority, aPref); + CleanupStack::Pop(self); + return self; + } + +CMdaAudioOutputStream::CMdaAudioOutputStream() + { + } + +CMdaAudioOutputStream::~CMdaAudioOutputStream() + { + delete iProperties; + } + +void CMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels) + { + ASSERT(iProperties); + iProperties->SetAudioPropertiesL(aSampleRate, aChannels); + } + +void CMdaAudioOutputStream::Open(TMdaPackage* aSettings) + { + ASSERT(iProperties); + iProperties->Open(aSettings); + } + +TInt CMdaAudioOutputStream::MaxVolume() + { + ASSERT(iProperties); + return iProperties->MaxVolume(); + } + +TInt CMdaAudioOutputStream::Volume() + { + ASSERT(iProperties); + return iProperties->Volume(); + } + +void CMdaAudioOutputStream::SetVolume(const TInt aNewVolume) + { + ASSERT(iProperties); + iProperties->SetVolume(aNewVolume); + } + +void CMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref) + { + ASSERT(iProperties); + iProperties->SetPriority(aPriority, aPref); + } + +void CMdaAudioOutputStream::WriteL(const TDesC8& aData) + { + ASSERT(iProperties); + iProperties->WriteL(aData); + } + +void CMdaAudioOutputStream::Stop() + { + ASSERT(iProperties); + iProperties->Stop(); + } + +EXPORT_C TInt CMdaAudioOutputStream::Pause() + { + ASSERT(iProperties); + return iProperties->Pause(); + } + +EXPORT_C TInt CMdaAudioOutputStream::Resume() + { + ASSERT(iProperties); + return iProperties->Resume(); + } + +const TTimeIntervalMicroSeconds& CMdaAudioOutputStream::Position() + { + ASSERT(iProperties); + return iProperties->Position(); + } + +EXPORT_C void CMdaAudioOutputStream::SetBalanceL(TInt aBalance) + { + ASSERT(iProperties); + iProperties->SetBalanceL(aBalance); + } + +EXPORT_C TInt CMdaAudioOutputStream::GetBalanceL() const + { + ASSERT(iProperties); + return iProperties->GetBalanceL(); + } + +EXPORT_C TInt CMdaAudioOutputStream::GetBytes() + { + ASSERT(iProperties); + return iProperties->GetBytes(); + } + +EXPORT_C TInt CMdaAudioOutputStream::KeepOpenAtEnd() + { + ASSERT(iProperties); + return iProperties->KeepOpenAtEnd(); + } + +EXPORT_C TInt CMdaAudioOutputStream::RequestStop() + { + ASSERT(iProperties); + return iProperties->RequestStop(); + } + +/** + +*/ +EXPORT_C void CMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType) + { + ASSERT(iProperties); + iProperties->SetDataTypeL(aAudioType); + } + + +/** + +*/ +EXPORT_C TFourCC CMdaAudioOutputStream::DataType() const + { + ASSERT(iProperties); + return iProperties->DataType(); + } + +EXPORT_C TAny* CMdaAudioOutputStream::CustomInterface(TUid aInterfaceId) + { + ASSERT(iProperties); + return iProperties->CustomInterface(aInterfaceId); + } + + +/** +Registers the Event for Notification when resource is avaliable. + +@param aCallback + The audio player observer interface. +@param aNotificationEventUid + The event uid to notify the client. +@param aNotificationRegistrationData + Notification registration specific data. + +@return An error code indicating if the registration was successful. KErrNone on success, + otherwise another of the system-wide error codes. +*/ +EXPORT_C TInt CMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback, + TUid aNotificationEventUid, + const TDesC8& aNotificationRegistrationData) + { + ASSERT(iProperties); + return iProperties->RegisterAudioResourceNotification(aCallback,aNotificationEventUid,aNotificationRegistrationData); + } + +/** +Cancels the registered notification event. + +@param aNotificationEventUid + The Event to notify the client. + +@return An error code indicating if the registration was successful. KErrNone on success, + otherwise another of the system-wide error codes. +*/ +EXPORT_C TInt CMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventUid) + { + ASSERT(iProperties); + return iProperties->CancelRegisterAudioResourceNotification(aNotificationEventUid); + } + +/** +Waits for the client to resume the play even after the default timer expires. + +@return An error code indicating if the registration was successful. KErrNone on success, + otherwise another of the system-wide error codes. +*/ +EXPORT_C TInt CMdaAudioOutputStream::WillResumePlay() + { + ASSERT(iProperties); + return iProperties->WillResumePlay(); + } + +enum TMmAosPanic + { + EToneFinishedNotSupported, + EBufferToBeEmptiedNotSupported, + ERecordErrorNotSupported, + EConvertErrorNotSupported, + EDeviceMessageNotSupported, + EReservedCall, + EAOSStoppingError + }; + +_LIT(KMmfMdaAosCategory, "CMmfMdaAudioOutputStream"); +LOCAL_C void Panic(const TMmAosPanic aReason) + { + User::Panic(KMmfMdaAosCategory, aReason); + } + +/** + * + * 2 phase construction function + * + */ +CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback) + { + return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality); + } + +CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback, TInt aPriority, TInt aPref) + { + CMMFMdaAudioOutputStream* self = new(ELeave) CMMFMdaAudioOutputStream(aCallback); + CleanupStack::PushL(self); + self->ConstructL(aPriority, aPref); + CleanupStack::Pop(); // self + return self; + } + +/** + * + * Construct + * + * @param "MMdaAudioOutputStreamCallback&" + * a reference to MMdaAudioOutputStreamCallback + * @param "TInt aPriority" + * a priority value + * @param "TInt aPref" + * a perference value + * + */ +CMMFMdaAudioOutputStream::CMMFMdaAudioOutputStream(MMdaAudioOutputStreamCallback& aCallback) + : iCallback(aCallback), iState(EStopped) + { + iDataTypeCode.Set(TFourCC(' ','P','1','6')); + } + +/** + * + * Second phase constructor + * + */ +void CMMFMdaAudioOutputStream::ConstructL(TInt aPriority, TInt aPref) + { + iDevSound = CMMFDevSound::NewL(); + SetPriority(aPriority, aPref); + iDevSoundIgnoresUnderflow = iDevSound->QueryIgnoresUnderflow(); + iFifo = new(ELeave) CMMFFifo(); + iActiveCallback = new(ELeave) CActiveCallback(iCallback); + iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait; + iShutDownTimer = CPeriodic::NewL(CActive::EPriorityStandard); + } + +/** + * + * Destructor + * + */ +CMMFMdaAudioOutputStream::~CMMFMdaAudioOutputStream() + { + delete iFifo; + delete iDevSound; + delete iActiveCallback; + delete iActiveSchedulerWait; + delete iShutDownTimer; + } + +/** + * + * Set audio output stream properties + * + * @param "TInt aSampleRate" + * a specified priority value + * @param "TInt aChannels" + * a specified preference value + * + */ +void CMMFMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels) + { + if (iIsOpenState==EIsOpen) + { + RealSetAudioPropertiesL(aSampleRate, aChannels); + } + else + { + // cache params for application later + iSampleRate = aSampleRate; + iChannels = aChannels; + iVolume = KUnknownVolume; + iValuesCached = ETrue; + } + } + +void CMMFMdaAudioOutputStream::RealSetAudioPropertiesL(TInt aSampleRate, TInt aChannels) + { + TMMFCapabilities config = iDevSound->Config(); + config.iChannels = StreamUtils::MapChannelsMdaToMMFL(aChannels); + config.iRate = StreamUtils::MapSampleRateMdaToMMFL(aSampleRate); + iDevSound->SetConfigL(config); + } + +/** + * + * Open a audio ouptut stream + * + * @param "TMdaPackage* Settings" + * a pointer point to TMdaPackage + * + */ +void CMMFMdaAudioOutputStream::Open(TMdaPackage* aSettings) + { + iIsOpenState = EIsOpening; + + // Use settings to set audio properties after the dev sound has been + // successfully initialised. Note if the InitializeL() fails, something calls + // InitializeComplete() and the iValuesCached flag is cleared. Also note + // that if SetAudioPropertiesL() has already been called, there may already + // be cached values + if (aSettings && aSettings->Type().iUid == KUidMdaDataTypeSettingsDefine) + { + TMdaAudioDataSettings& audioSettings = *STATIC_CAST(TMdaAudioDataSettings*, aSettings); + //Caching these values, which are later set in InitializeComplete + iSampleRate = audioSettings.iSampleRate; + iChannels = audioSettings.iChannels; + iVolume = audioSettings.iVolume; + iValuesCached = ETrue; + } + + TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying)); + if (err != KErrNone) + { + InitializeComplete(err); + } + } + +/** + * + * To get the maximum volume level + * + * @return "TInt" + * the maximum volume value in integer + * + */ +TInt CMMFMdaAudioOutputStream::MaxVolume() + { + return iDevSound->MaxVolume(); + } + +/** + * + * To get the current volume level + * + * @return "TInt" + * the current volume value in integer + * + */ +TInt CMMFMdaAudioOutputStream::Volume() + { + return iDevSound->Volume(); + } + +/** + * + * Set audio output stream volume to the specified value + * + * @param "TInt aVolume" + * a specified volume value + * + */ +void CMMFMdaAudioOutputStream::SetVolume(TInt aVolume) + { + iDevSound->SetVolume(aVolume); + } + +/** + * + * Set audio output stream balance + * + * @param "TInt aBalance" + * a specified balance value + * + */ +void CMMFMdaAudioOutputStream::SetBalanceL(TInt aBalance) + { + // test and clip balance to min / max range [-100 <-> 100] + // clip rather than leave as this isn't a leaving function + if (aBalance < KMMFBalanceMaxLeft) aBalance = KMMFBalanceMaxLeft; + if (aBalance > KMMFBalanceMaxRight) aBalance = KMMFBalanceMaxRight; + + // separate out left and right balance + TInt left = 0; + TInt right = 0; + StreamUtils::CalculateLeftRightBalance( left, right, aBalance ); + + // send the balance to SoundDevice + iDevSound->SetPlayBalanceL(left, right); + } + +/** + * + * To get the current balance value. This function may not return the same value + * as passed to SetBalanceL depending on the internal implementation in + * the underlying components. + * + * @return "TInt" + * the current balance value in integer + * + */ +TInt CMMFMdaAudioOutputStream::GetBalanceL() const + { + TInt rightBalance = 0; + TInt leftBalance = 0; + iDevSound->GetPlayBalanceL(leftBalance, rightBalance); + TInt balance = 0; + StreamUtils::CalculateBalance( balance, leftBalance, rightBalance ); + return balance; + } + +/** + * + * Set audio output stream priority + * + * @param "TInt aPriority" + * a specified priority value + * @param "TMdaPriorityPreference aPref" + * a specified preference value + * + */ +void CMMFMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref) + { + TMMFPrioritySettings settings; + settings.iPriority = aPriority; + settings.iPref = aPref; + iDevSound->SetPrioritySettings(settings); + } + +/** + * + * To write data to output stream + * Note that if framed data eg gsm610 is being streamed then the buffer + * size of aData should contain an intiger number of frames + * + * @param "const TDesC8& aData" + * a stream data + * + */ +void CMMFMdaAudioOutputStream::WriteL(const TDesC8& aData) + { + if(iState==EStopping) + { + User::Leave(KErrNotReady); + } + iShutDownTimer->Cancel(); + + TMMFFifoItem* item = new(ELeave) TMMFFifoItem(aData); + iFifo->AddToFifo(*item); + + if(iState == EStopped) + StartPlayL(); + else if((iState == EPlaying) && (iBuffer != NULL)) + { //if we are playing and we have a buffer waiting for data, use it. + BufferToBeFilled(iBuffer); + iBuffer = NULL; + } + if(iEventHolder != KNullUid) + { + TInt err = iDevSound->RegisterAsClient(iEventHolder,iNotificationDataHolder); + iEventHolder = KNullUid; + iNotificationDataHolder = KNullDesC8; + if(err != KErrNone) + { + iCallback.MaoscPlayComplete(err); + } + } + } + +/** + * + * If the audio stream is stopped, then notify the CDevSound to initilised play. + * The CDevSound will automatically start calling back for buffers. + */ +void CMMFMdaAudioOutputStream::StartPlayL() +{ + if (iState == EStopped) + { + iFifoItemPos = 0; + iCurrentSamplesPlayed = 0; + iDevSound->PlayInitL(); + iState = EPlaying; + iBuffer = NULL; + } +} + + +/** + * + * To stop write data to stream + * + */ +void CMMFMdaAudioOutputStream::Stop() + { + iShutDownTimer->Cancel(); + EmptyFifo(KErrAbort); + + if(iState == EStopped) + { + return; + } + + // stop the operation + iDevSound->Stop(); + iState = EStopped; + iBuffer = NULL; + + iCallback.MaoscPlayComplete(KErrCancel); + } + +TInt CMMFMdaAudioOutputStream::RequestStop() + { + if(!iKeepOpenAtEnd) + { + return KErrNotSupported; + } + + if(iState==EStopped || iState==EStopping) + { + return KErrNotReady; + } + + if(iBuffer) + {//means all the client buffers are used up and waiting for more data + CMMFDataBuffer* dataBuffer = static_cast(iBuffer); + dataBuffer->Data().SetLength(0); + dataBuffer->SetLastBuffer(ETrue); + iDevSound->PlayData(); + iBuffer=NULL; + } + iState = EStopping; + return KErrNone; + } + +TInt CMMFMdaAudioOutputStream::KeepOpenAtEnd() + { + if(!iDevSoundIgnoresUnderflow) + { + return KErrNotSupported; + } + else + { + iKeepOpenAtEnd = ETrue; + return KErrNone; + } + } + +/** + * + * To pause send data to stream + * + */ +TInt CMMFMdaAudioOutputStream::Pause() + { + if(iState != EPlaying) + { + return KErrNotReady; + } + + else if(!iDevSound->IsResumeSupported()) + { + return KErrNotSupported; + } + + // pause the operation + iDevSound->Pause(); + iState = EPaused; + return KErrNone; + } + +/** + * + * To resume send data to stream + * + */ +TInt CMMFMdaAudioOutputStream::Resume() + { + TInt err = KErrNone; + if(iState != EPaused) + { + err = KErrNotReady; + } + else if(!iDevSound->IsResumeSupported()) + { + err = KErrNotSupported; + } + + // resume the operation + if(err == KErrNone) + { + err = iDevSound->Resume(); + } + if(err == KErrNone) + { + iState = EPlaying; + } + + return err; + } + + +/** + * + * To get the current position in the data stream + * + * @return "TTimeIntervalMicroSeconds&" + * the current position in integer + * + */ +const TTimeIntervalMicroSeconds& CMMFMdaAudioOutputStream::Position() + { + TInt64 position = iDevSound->SamplesPlayed(); + position = position * KMicroSecsInOneSec / StreamUtils::SampleRateAsValue(iDevSound->Config()); + iPosition = (iState == EPlaying || iState == EStopping || iState == EPaused) ? position : 0; // Shouldn't need to check for playing but CMMFDevSound doesn't reset bytes played after a stop + return iPosition; + } + +/** + * + * To return the current number of bytes rendered by audio hardware + * @return "the current current number of bytes rendered by audio hardware in integer" + * + */ +TInt CMMFMdaAudioOutputStream::GetBytes() + { + return iDevSound->SamplesPlayed() * StreamUtils::BytesPerSample(iDevSound->Config()); + } + +/** + +*/ +void CMMFMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType) + { + if(iState != EStopped) + User::Leave(KErrServerBusy); + + if(aAudioType == iDataTypeCode) + return; + + TMMFPrioritySettings prioritySettings; + prioritySettings.iState = EMMFStatePlaying; + RArray supportedDataTypes; + + CleanupClosePushL(supportedDataTypes); + + TRAPD(err, iDevSound->GetSupportedInputDataTypesL(supportedDataTypes, prioritySettings)); + + if (err == KErrNone) + { + if (supportedDataTypes.Find(aAudioType) == KErrNotFound) + { + User::Leave(KErrNotSupported); + } + //if match, set the 4CC of AudioType to match + iDataTypeCode.Set(aAudioType); + } + else //we had a real leave error from GetSupportedOuputDataTypesL + { + User::Leave(err); + } + + CleanupStack::PopAndDestroy(&supportedDataTypes); + + if(iIsOpenState!=EIsNotOpen) + { + // need to recall or restart InitializeL() process + iDevSound->CancelInitialize(); // call just in case mid-InitializeL. No harm if not. + // if not supported then assume old DevSound behaviour anyway + // where InitializeL() implicitly cancels, so no harm either way + iIsOpenState = EIsOpening; + iInitCallFrmSetDataType = ETrue; + TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying)); + if (err != KErrNone) + { + // Leave if error. + iIsOpenState = EIsNotOpen; + iInitCallFrmSetDataType = EFalse; + User::Leave(err); + } + // In some implementations InitializeComplete is sent + // in context, so check before starting activeSchedulerWait. + else if(iIsOpenState == EIsOpening) + { + iInitializeState = KRequestPending; + iActiveSchedulerWait->Start(); + } + iInitCallFrmSetDataType = EFalse; + User::LeaveIfError(iInitializeState); + } + } + +/** + +*/ +TFourCC CMMFMdaAudioOutputStream::DataType() const + { + return iDataTypeCode; + } + +TInt CMMFMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback,TUid aNotificationEventUid,const TDesC8& aNotificationRegistrationData) + { + iAudioResourceNotificationCallBack = &aCallback; + TInt err = iDevSound->RegisterAsClient(aNotificationEventUid,aNotificationRegistrationData); + if(err == KErrNotReady) + { + iEventHolder = aNotificationEventUid; + iNotificationDataHolder = aNotificationRegistrationData; + return KErrNone; + } + iEventHolder = KNullUid; + iNotificationDataHolder = KNullDesC8; + return err; + } + +TInt CMMFMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventId) + { + TInt err = iDevSound->CancelRegisterAsClient(aNotificationEventId); + if(err == KErrNotReady) + { + if(aNotificationEventId != KMMFEventCategoryAudioResourceAvailable) + { + return KErrNotSupported; + } + if(iEventHolder == KNullUid) + { + return KErrCancel; + } + iEventHolder = KNullUid; + iNotificationDataHolder = KNullDesC8; + return KErrNone; + } + return err; + } + +TInt CMMFMdaAudioOutputStream::WillResumePlay() + { + return iDevSound->WillResumePlay(); + } + +/** + * + * To be called when intialize stream complete + * + * @param "TInt aError" + * error code, initialize stream succeed when aError = 0 + * + */ +void CMMFMdaAudioOutputStream::InitializeComplete(TInt aError) + { + TInt err = aError; + if(err == KErrNone && iValuesCached) + { + TRAP(err, RealSetAudioPropertiesL(iSampleRate, iChannels)); + if(err == KErrNone && iVolume>=0) + { + SetVolume(iVolume); + } + } + iValuesCached = EFalse; // whatever clear our cache + if(iIsOpenState == EIsOpening) + { + // Signal for the MaoscOpenComplete callback to be called asynchronously.Ignore if InitializeL is called from SetDataTypeL + if(!iInitCallFrmSetDataType) + { + iActiveCallback->Signal(err); + } + iIsOpenState = err ? EIsNotOpen : EIsOpen; + if(iInitializeState == KRequestPending) + { + iInitializeState = err; + iActiveSchedulerWait->AsyncStop(); + } + else + { + iInitializeState = err;//Set the error if InitializeComplete is called in context of InitializeL. + } + } + } + +/** + * + * Do not support + * + */ +void CMMFMdaAudioOutputStream::ToneFinished(TInt /*aError*/) + { + Panic(EToneFinishedNotSupported); + } + +/** + * + * Called when sound device need data + * + * @param "CMMFBuffer* aBuffer" + * a pointer point to CMMFBuffer, which is used to stored data + * + */ +void CMMFMdaAudioOutputStream::BufferToBeFilled(CMMFBuffer* aBuffer) + { + if (iState == EPlaying || iState == EStopping) + { + TMMFFifoItem* firstItem = iFifo->Get(); + //ASSERT firstItem != NULL + if (iFifoItemPos >= firstItem->GetData().Length()) + { + // We've played all segments of the first buffer in the fifo so we can delete it + iFifo->RemoveFirstItem(); + iCallback.MaoscBufferCopied(KErrNone, firstItem->GetData()); + delete firstItem; + iFifoItemPos = 0; + } + } + TMMFFifoItem* firstItem = iFifo->Get(); + + if (firstItem) + { + // Fill aBuffer with the next segment of the first buffer in the fifo + TDes8& fillDes = STATIC_CAST(CMMFDataBuffer*, aBuffer)->Data(); + const TDesC8& readDes = firstItem->GetData(); + TInt readLen = Min(readDes.Length()-iFifoItemPos, aBuffer->RequestSize()); + fillDes = readDes.Mid(iFifoItemPos, readLen); + iFifoItemPos+=readLen; // so we know where the next segment in the fifo item starts + // Notify iDevSound the buffer is ready to be played + iDevSound->PlayData(); + } + else + { + if(iBuffer ==NULL) + { + //keep a record of the supplied buffer and use it when/if more user data is in the FIFO + iBuffer = aBuffer; + } + if(!iKeepOpenAtEnd) + { + if (iDevSoundIgnoresUnderflow) + { + iCurrentSamplesPlayed = iDevSound->SamplesPlayed(); + StartShutDownTimer(); + } + } + else if(iState==EStopping) + { + CMMFDataBuffer* dataBuffer = static_cast(aBuffer); + dataBuffer->Data().SetLength(0); + dataBuffer->SetLastBuffer(ETrue); + iDevSound->PlayData(); + iBuffer=NULL; + } + } + + } + +void CMMFMdaAudioOutputStream::StartShutDownTimer() + { + iShutDownTimer->Start(KShutDownTimeInterval, KShutDownTimeInterval, TCallBack(ShutDownTimerComplete,this)); + } + +TInt CMMFMdaAudioOutputStream::ShutDownTimerComplete(TAny* aAudioOutputStream) + { + CMMFMdaAudioOutputStream* audioOutputStream = static_cast(aAudioOutputStream); + audioOutputStream->DoShutDownTimerComplete(); + return KErrNone; + } + +void CMMFMdaAudioOutputStream::DoShutDownTimerComplete() + { + iShutDownTimer->Cancel(); + TInt samplesPlayed = iDevSound->SamplesPlayed(); + if (samplesPlayed == iCurrentSamplesPlayed) + { + iDevSound->Stop(); + iState = EStopped; + iBuffer = NULL; + iCallback.MaoscPlayComplete(KErrUnderflow); + } + else + {//desvound has not yet finished playing all the data. So wait for one more cycle + //Restart Timer + iCurrentSamplesPlayed = samplesPlayed; + StartShutDownTimer(); + } + } + +/** + * + * Called when play operation complete, successfully or otherwise + * + * @param "TInt aError" + * an error value which will indicate playing successfully complete + * if error value is 0 + * + */ +void CMMFMdaAudioOutputStream::PlayError(TInt aError) + { + TInt err=aError; + // if iDevSoundIgnoresUnderflow is true, then KErrUnderflow should not be produced by DevSound when we are not stopping + __ASSERT_DEBUG(!iDevSoundIgnoresUnderflow || !(err==KErrUnderflow && iState!=EStopping), Panic(EAOSStoppingError)); + iState = EStopped; + iBuffer = NULL; + iShutDownTimer->Cancel(); + if (err == KErrNone || err == KErrUnderflow) + { + if (!iFifo->IsEmpty()) + { + // We live again - the Fifo still has some data. The sound device + // will have to be opened again though so there might be an + // audible click. + TRAP(err, StartPlayL()); + if (err == KErrNone) + { + return; + } + } + } + + EmptyFifo(err); + + // Note - KErrUnderflow will be reported even when all the buffers have + // successfully played + iCallback.MaoscPlayComplete(err); + } + +void CMMFMdaAudioOutputStream::EmptyFifo(TInt aError) + { + // Delete all buffers in the fifo and notify the observer + TMMFFifoItem* firstItem; + while((firstItem = iFifo->Get()) != NULL) + { + iFifo->RemoveFirstItem(); + iCallback.MaoscBufferCopied(aError, firstItem->GetData()); + delete firstItem; + } + } + +void CMMFMdaAudioOutputStream::SendEventToClient(const TMMFEvent& aEvent) + { + if (aEvent.iEventType == KMMFEventCategoryAudioResourceAvailable) + { + // Retrieve the number of samples played + // For the event type KMMFEventCategoryAudioResourceAvailable GetResourceNotificationData() returns + // a package buffer as TMMFTimeIntervalMicroSecondsPckg, but the contents should be + // converted to an integer and interpreted as the data returned is samples played, + // but not as a microsecond value. + TBuf8 notificationData; + if (KErrNone != iDevSound->GetResourceNotificationData(aEvent.iEventType, notificationData)) + { + notificationData.SetLength(0); + } + iAudioResourceNotificationCallBack->MarncResourceAvailable(aEvent.iEventType, notificationData); + } + } + +CMMFMdaAudioOutputStream::CActiveCallback::~CActiveCallback() + { + Cancel(); + } + +CMMFMdaAudioOutputStream::CActiveCallback::CActiveCallback(MMdaAudioOutputStreamCallback& aCallback) + : CActive(EPriorityStandard), iCallback(aCallback) + { + CActiveScheduler::Add(this); + } + +void CMMFMdaAudioOutputStream::CActiveCallback::RunL() + { + iCallback.MaoscOpenComplete(iStatus.Int()); + } + +void CMMFMdaAudioOutputStream::CActiveCallback::DoCancel() + { + } + +void CMMFMdaAudioOutputStream::CActiveCallback::Signal(const TInt aReason) + { + ASSERT(!IsActive()); + + // Signal ourselves to run with the given completion code + TRequestStatus* status = &iStatus; + User::RequestComplete(status, aReason); + SetActive(); + } + + +/** + * + * Do not support + * + */ +void CMMFMdaAudioOutputStream::BufferToBeEmptied(CMMFBuffer* /*aBuffer*/) + { + Panic(EBufferToBeEmptiedNotSupported); + } + +/** + * + * Do not support + * + */ +void CMMFMdaAudioOutputStream::RecordError(TInt /*aError*/) + { + Panic(ERecordErrorNotSupported); + } + +/** + * + * Do not support + * + */ +void CMMFMdaAudioOutputStream::ConvertError(TInt /*aError*/) + { + Panic(EConvertErrorNotSupported); + } + +/** + * + * Do not support + * + */ +void CMMFMdaAudioOutputStream::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/) + { + Panic(EDeviceMessageNotSupported); + } + +// CustomInferface - just pass on to DevSound. +TAny* CMMFMdaAudioOutputStream::CustomInterface(TUid aInterfaceId) + { + return iDevSound->CustomInterface(aInterfaceId); + } +