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