diff -r 000000000000 -r b8ed18f6c07b mmlibs/mmfw/src/Plugin/AudioOutput/MmfAudioOutput.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmlibs/mmfw/src/Plugin/AudioOutput/MmfAudioOutput.cpp Thu Oct 07 22:34:12 2010 +0100 @@ -0,0 +1,872 @@ +// Copyright (c) 2005-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 "MmfAudioOutput.h" +#include +#include +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#endif +#include + +void Panic(TInt aPanicCode) + { + _LIT(KMMFAudioOutputPanicCategory, "MMFAudioOutput"); + User::Panic(KMMFAudioOutputPanicCategory, aPanicCode); + } + +/** +Allocates and constructs a new audio output sink. + +Static standard SymbianOS 2 phase constuction method. + +@return A pointer to the new sink. +*/ +MDataSink* CMMFAudioOutput::NewSinkL() + { + CMMFAudioOutput* self = new (ELeave) CMMFAudioOutput ; + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return STATIC_CAST( MDataSink*, self ) ; + } + +/** +Standard SymbianOS ConstructL. + +Used to initialise member varibles with device specific behaviour. +*/ +void CMMFAudioOutput::ConstructL() + { + iInitializeState = KErrNone; + iDataTypeCode = KMMFFourCCCodePCM16; + iNeedsSWConversion = EFalse; + iSourceSampleRate = 0; + iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait; + } + +/** +Overridable constuction specific to this datasource. + +The default implementation does nothing. + +@param aInitData + The initialisation data. +*/ +void CMMFAudioOutput::ConstructSinkL( const TDesC8& /*aInitData*/ ) + { + } + + +/** +@deprecated + +Gets audio from hardware device abstracted MMFDevsound (not used). + +@param aBuffer + The data to write out to a Hardware Device. +@param aSupplier + The MDataSource consuming the data contained in aBuffer +*/ +void CMMFAudioOutput::HWEmptyBufferL(CMMFBuffer* /*aBuffer*/, MDataSource* /*aSupplier*/) + { + } + +/** +Sends audio to MMFDevsound. + +@param aBuffer + The data to write out. +@param aSupplier + The search criteria for the supplier. +@param aMediaId + The type of data supplied - currently ignored. +*/ +void CMMFAudioOutput::EmptyBufferL(CMMFBuffer* aBuffer, MDataSource* aSupplier, TMediaId /*aMediaId*/) + { + iSupplier = aSupplier; + + if (!iMMFDevSound) + Panic(EMMFAudioOutputDevSoundNotLoaded); + + if (aSupplier == NULL) + User::Leave(KErrArgument); + + // In order to avoid changes at the datapath data transfer state machine + // EmptyBufferL is still being called even + // if the first time there is no buffer to send + if (!iCanSendBuffers) + { + + iCanSendBuffers = ETrue; + return; + } + + if (iNeedsSWConversion) + {//need to perform channel & sample rate conversion before writing to clip + CMMFDataBuffer* audio; + + //need to make sure aBuffer is not null before it is used + if (aBuffer == NULL) + { + User::Leave(KErrArgument); + } + + //buffer check is placed here before possible use of the buffer + if (!CMMFBuffer::IsSupportedDataBuffer(aBuffer->Type())) + { + User::Leave(KErrNotSupported); + } + + iConvertBuffer = CMMFDataBuffer::NewL(((CMMFDataBuffer*)aBuffer)->Data().Length()); + iChannelAndSampleRateConverter->Convert(*(CMMFDataBuffer*)aBuffer, *iConvertBuffer); + audio = iConvertBuffer; + + //copy our converted data back into the real buffer to return to datapath + TDes8& dest = STATIC_CAST( CMMFDataBuffer*, aBuffer )->Data() ; + dest.SetLength(0); + dest.Copy(audio->Data()); + } + + if (iState != EDevSoundReady && iState != EPaused) // If we're paused we still feed a buffer to DevSound + User::Leave(KErrNotReady); + + iMMFDevSound->PlayData(); + +#if defined(__AUDIO_PROFILING) + RDebug::ProfileEnd(0); + User::Leave(KErrEof); +#endif // defined(__AUDIO_PROFILING) + + + // The following will never have been allocated unless + // software conversion was required, and due to certain DevSound + // implementations, this requirement can change dynamically. + delete iConvertBuffer; + iConvertBuffer = NULL; + } + + + +/** +Negotiates with the source to set, for example, the sample rate and number of channels. + +Called if the sink's setup depends on source. + +@param aSource + The data source with which to negotiate. +*/ +void CMMFAudioOutput::NegotiateL(MDataSource& aSource) + { + if (aSource.DataSourceType() == KUidMmfFormatDecode) + {//source is a clip so for now set sink settings to match source + iSourceSampleRate = ((CMMFFormatDecode&)aSource).SampleRate(); + iSourceChannels = ((CMMFFormatDecode&)aSource).NumChannels(); + iSourceFourCC.Set(aSource.SourceDataTypeCode(TMediaId(KUidMediaTypeAudio))); + + ((CMMFFormatDecode&)aSource).SuggestSourceBufferSize(KAudioOutputDefaultFrameSize); + } + + // Query DevSound capabilities and Try to use DevSound sample rate and + // mono/stereo capability + if (!iMMFDevSound) + Panic(EMMFAudioOutputDevSoundNotLoaded); + + TMMFState prioritySettingsState = iPrioritySettings.iState; //should be EMMFStatePlaying + //to use the GetSupportedInputDatatypes but we'll save it just in case it's not + iPrioritySettings.iState = EMMFStatePlaying; //if playing does not support any output data types + RArray supportedDataTypes; + //note Input data types becuase if we are playing audio ie audio output + //the data is sent as an input to DevSound + TRAPD(err, iMMFDevSound->GetSupportedInputDataTypesL(supportedDataTypes, iPrioritySettings)); + iPrioritySettings.iState = prioritySettingsState; + if (err == KErrNone) + { + if (supportedDataTypes.Find(iSourceFourCC) == KErrNotFound) + {//the source fourCC code could not be found in the list of + //data types supported by the Devsound therefor default to pcm16 + iDataTypeCode = KMMFFourCCCodePCM16; + } + else + { + //the DevSound does support the same datatype as the source + //so set the fourcc to that of the source + iDataTypeCode = iSourceFourCC; + } + } + supportedDataTypes.Close(); + if (err == KErrNotSupported) + {//if the Devsound does not support the GetSupportedOutputDataTypesL method + //then assume that the DevSound is pcm16 only + iDataTypeCode = KMMFFourCCCodePCM16; + } + else if (err != KErrNone) //we had a real leave error from GetSupportedOuputDataTypesL + { + User::Leave(err); + } + + // Prevent defect when SinkPrimeL is called before NegotiateL() + // since characterization is ambiguous + if(iState == EDevSoundReady) + { + iState = EIdle; + } + //INC40817 do the initialize here as capabilities may depend on datatype + //note this implies client of audiooutput must call negotiate + iMMFDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying); + + // In some implementations InitializeComplete is sent + // in context, so check before starting activeSchedulerWait. + if (iState != EDevSoundReady) + { + iInitializeState = KRequestPending; + iActiveSchedulerWait->Start(); + } + + User::LeaveIfError(iInitializeState); + + // Reset the following flag in case DevSound's capabilities have + // changed since we were last here: INC037165 + iNeedsSWConversion = EFalse; + TMMFCapabilities devSoundCaps; + devSoundCaps = iMMFDevSound->Capabilities(); + // Default PCM16 + iDevSoundConfig.iEncoding = EMMFSoundEncoding16BitPCM; + // 1 = Monophonic and 2 == Stereo + if (((iSourceChannels == 1) && (devSoundCaps.iChannels & EMMFMono)) || + ((iSourceChannels == 2) && (devSoundCaps.iChannels & EMMFStereo))) + iDevSoundConfig.iChannels = iSourceChannels; + else //default or SW conversion, e.g. stereo on mono support + { + iDevSoundConfig.iChannels = EMMFMono; + iNeedsSWConversion = ETrue; + iSWConvertChannels = 1; + iSWConvertSampleRate = iSourceSampleRate; + } + + // Check for std sample rates. + if ((iSourceSampleRate == 96000) && (devSoundCaps.iRate & EMMFSampleRate96000Hz)) + iDevSoundConfig.iRate = EMMFSampleRate96000Hz; + else if ((iSourceSampleRate == 88200) && (devSoundCaps.iRate & EMMFSampleRate88200Hz)) + iDevSoundConfig.iRate = EMMFSampleRate88200Hz; + else if ((iSourceSampleRate == 64000) && (devSoundCaps.iRate & EMMFSampleRate64000Hz)) + iDevSoundConfig.iRate = EMMFSampleRate64000Hz; + else if ((iSourceSampleRate == 48000) && (devSoundCaps.iRate & EMMFSampleRate48000Hz)) + iDevSoundConfig.iRate = EMMFSampleRate48000Hz; + else if ((iSourceSampleRate == 44100) && (devSoundCaps.iRate & EMMFSampleRate44100Hz)) + iDevSoundConfig.iRate = EMMFSampleRate44100Hz; + else if ((iSourceSampleRate == 32000) && (devSoundCaps.iRate & EMMFSampleRate32000Hz)) + iDevSoundConfig.iRate = EMMFSampleRate32000Hz; + else if ((iSourceSampleRate == 24000) && (devSoundCaps.iRate & EMMFSampleRate24000Hz)) + iDevSoundConfig.iRate = EMMFSampleRate24000Hz; + else if ((iSourceSampleRate == 22050) && (devSoundCaps.iRate & EMMFSampleRate22050Hz)) + iDevSoundConfig.iRate = EMMFSampleRate22050Hz; + else if ((iSourceSampleRate == 16000) && (devSoundCaps.iRate & EMMFSampleRate16000Hz)) + iDevSoundConfig.iRate = EMMFSampleRate16000Hz; + else if ((iSourceSampleRate == 12000) && (devSoundCaps.iRate & EMMFSampleRate12000Hz)) + iDevSoundConfig.iRate = EMMFSampleRate12000Hz; + else if ((iSourceSampleRate == 11025) && (devSoundCaps.iRate & EMMFSampleRate11025Hz)) + iDevSoundConfig.iRate = EMMFSampleRate11025Hz; + else if ((iSourceSampleRate == 8000) && (devSoundCaps.iRate & EMMFSampleRate8000Hz)) + iDevSoundConfig.iRate = EMMFSampleRate8000Hz; + else // non standard sample rate + { + iNeedsSWConversion = ETrue; + // we need to choose to the closest, and smaller standard sample rate + // and eventually convert the audio samples to this standard sample rate + //NB: this list must be in ascending order + const TInt KNumSampleRates = 12; + static const TUint supportedSR[KNumSampleRates][2] = {{8000, EMMFSampleRate8000Hz}, + {11025, EMMFSampleRate11025Hz}, + {12000, EMMFSampleRate12000Hz}, + {16000, EMMFSampleRate16000Hz}, + {22050, EMMFSampleRate22050Hz}, + {24000, EMMFSampleRate24000Hz}, + {32000, EMMFSampleRate32000Hz}, + {44100, EMMFSampleRate44100Hz}, + {48000, EMMFSampleRate48000Hz}, + {64000, EMMFSampleRate64000Hz}, + {88200, EMMFSampleRate88200Hz}, + {96000, EMMFSampleRate96000Hz}}; + + //Only support down sampling + if (iSourceSampleRate < supportedSR[0][0]) + User::Leave(KErrNotSupported); + + + TInt sampleRateIndex = KNumSampleRates; + + //find the source sampleRateIndex + for (sampleRateIndex--; sampleRateIndex > -1; sampleRateIndex--) + { + if(iSourceSampleRate >= supportedSR[sampleRateIndex][0]) + { + break; + } + } + + //find the highest sink sample rate below the source rate + for (; sampleRateIndex > -1; sampleRateIndex--) + { + if(devSoundCaps.iRate & supportedSR[sampleRateIndex][1]) + { + iSWConvertSampleRate = supportedSR[sampleRateIndex][0]; + iDevSoundConfig.iRate = supportedSR[sampleRateIndex][1]; + break; + } + } + + //if a suitable sink sample rate is not available + if (sampleRateIndex < 0) + User::Leave(KErrNotSupported); + + // set the channels as well + iSWConvertChannels = iDevSoundConfig.iChannels; + } // else // non standard sample rate + + if (iNeedsSWConversion) + {//can only software convert if datatype is pcm16 + //note cannot set non standard sample rates on DevSound API + //as the API does not allow this + //we need to reinitialize the devsound with pcm16 in this case + iDataTypeCode = KMMFFourCCCodePCM16; + iState = EIdle; + iMMFDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying); + + // In some implementations InitializeComplete is called + // in context, so check before starting activeSchedulerWait. + if (iState != EDevSoundReady) + { + iInitializeState = KRequestPending; + iActiveSchedulerWait->Start(); + } + + User::LeaveIfError(iInitializeState); + } + } + +/** +Sets the sink's priority settings. + +@param aPrioritySettings + The sink's priority settings. Takes enumerations to determine audio playback priority. + Higher numbers mean high priority (can interrupt lower priorities). +*/ +void CMMFAudioOutput::SetSinkPrioritySettings(const TMMFPrioritySettings& aPrioritySettings) + { + iPrioritySettings = aPrioritySettings; + if (!iMMFDevSound) + Panic(EMMFAudioOutputDevSoundNotLoaded); + else + iMMFDevSound->SetPrioritySettings(iPrioritySettings); + } + +/** +Gets the sink's data type code. + +Used by datapath MDataSource / MDataSink for codec matching. + +@param aMediaId + The Media ID. Optional parameter to specifiy specific stream when datasource contains more + than one stream of data. + +@return The 4CC of the data expected by this sink. +*/ +TFourCC CMMFAudioOutput::SinkDataTypeCode(TMediaId /*aMediaId*/) + { + return iDataTypeCode; + } + +/** +Sets the sink's data type code. + +@param aSinkFourCC + The 4CC of the data to be supplied to this sink. +@param aMediaId + The Media ID. Optional parameter to specifiy specific stream when datasource contains more + than one stream of data. + +@return An error code indicating if the function call was successful. KErrNone on success, otherwise + another of the system-wide error codes. +*/ +TInt CMMFAudioOutput::SetSinkDataTypeCode(TFourCC aSinkFourCC, TMediaId /*aMediaId*/) + {//will check with devsound to see if aSinkFourCC is supported + //when this is added to devsound + iDataTypeCode = aSinkFourCC; + return KErrNone; + } + +/** +Primes the sink. + +This is a virtual function that each derived class must implement, but may be left blank for default +behaviour. + +Called by CMMFDataPath::PrimeL(). +*/ +void CMMFAudioOutput::SinkPrimeL() + { + if(iPlayStarted != EFalse) + { + return; + } + iPlayStarted = EFalse; + iCanSendBuffers = EFalse; + if (iState == EIdle) + { + if (!iMMFDevSound) + { + User::Leave(KErrNotReady); + } + iState = EDevSoundReady; + } + } + +/** +Pauses the sink. + +This is a virtual function that each derived class must implement, but may be left blank for default +behaviour. +*/ +void CMMFAudioOutput::SinkPauseL() + { + if (!iMMFDevSound) + Panic(EMMFAudioOutputDevSoundNotLoaded); + else + iMMFDevSound->Pause(); + iPlayStarted = EFalse; + iState = EPaused; + } + +/** +Starts playing the sink. + +This is a virtual function that each derived class must implement, but may be left blank for default +behaviour. +*/ +void CMMFAudioOutput::SinkPlayL() + { + if (iState == EPaused) + { + TBool isResumeSupported = iMMFDevSound->IsResumeSupported(); + // CMMFAudioOutput is not supposed to be paused + // if Resume is not supported by DevSound + if(!isResumeSupported) + { + User::Leave(KErrNotSupported); + } + + User::LeaveIfError(iMMFDevSound->Resume()); + iPlayStarted = ETrue; + } + else if (iPlayStarted == EFalse) + { + ConfigDevSoundL(); + + // This is a one-shot to "prime" MMFDevSound as first buffer uninitialised + iMMFDevSound->PlayInitL(); + iPlayStarted = ETrue; + } + if ((iNeedsSWConversion)&&(iSourceChannels>0)) + {//can only do SW convert - therefore need to do a conversion + //currently only pcm16 is supported so return with an error if format not pcm16 + if (!iChannelAndSampleRateConverterFactory) + { + iChannelAndSampleRateConverterFactory + = new(ELeave)CMMFChannelAndSampleRateConverterFactory; + iChannelAndSampleRateConverter = + iChannelAndSampleRateConverterFactory->CreateConverterL( iSourceSampleRate, iSourceChannels, + iSWConvertSampleRate, iSWConvertChannels); + } + //need to create an intermediate buffer in which to place the converted data + } + iState = EDevSoundReady; + } + +/** +Stops the sink. + +This is a virtual function that each derived class must implement, but may be left blank for default +behaviour. +*/ +void CMMFAudioOutput::SinkStopL() + { + if (iState == EDevSoundReady) + {//not waiting on a buffer being played so stop devsound now + iState = EIdle; + if (iPlayStarted) + { + if (!iMMFDevSound) + { + Panic(EMMFAudioOutputDevSoundNotLoaded); + } + else + { + iPlayStarted = EFalse; + iMMFDevSound->Stop(); + } + } + } + else if (iState == EPaused) //DEF46250 need to handle pause separately as we should always stop regardless of the state of iFirstBufferSent + { + iPlayStarted = EFalse; + iMMFDevSound->Stop(); + iState = EIdle; + } + iCanSendBuffers = EFalse; + } + +/** +Returns the playback state (EStopped, EPlaying, EPaused etc) of this sink +*/ +TInt CMMFAudioOutput::State() + { + return iState; + } + +/** +Logs on the sink's thread. + +Thread specific initialization procedure for this device. Runs automatically on thread construction. + +@param aEventHandler + The event handler. + +@return An error code indicating if the function call was successful. KErrNone on success, otherwise + another of the system-wide error codes. +*/ +TInt CMMFAudioOutput::SinkThreadLogon(MAsyncEventHandler& aEventHandler) + { + iEventHandler = &aEventHandler; + TInt err = KErrNone; + if (!iDevSoundLoaded) + TRAP(err, LoadL()); + return err; + } + +/** +Logs off the sink thread. + +Thread specific destruction procedure for this device. Runs automatically on thread destruction. +*/ +void CMMFAudioOutput::SinkThreadLogoff() + { + if(iMMFDevSound) + { + iMMFDevSound->Stop(); + delete iMMFDevSound; + iMMFDevSound = NULL; + } + iDevSoundLoaded = EFalse; + iState = EIdle; + } + +/** +Called by MDataSource to pass back a full buffer to the sink. + +Should never be called by a sink, as sinks empty buffers, not fill them. + +@param aBuffer + The filled buffer. +*/ +void CMMFAudioOutput::BufferFilledL(CMMFBuffer* /*aBuffer*/) + { + Panic(EMMFAudioOutputPanicBufferFilledLNotSupported); + } + +/** +Tests whether a sink buffer can be created. + +The default implementation returns true. + +@return A boolean indicating if the sink buffer can be created. ETrue if it can, otherwise EFalse. +*/ +TBool CMMFAudioOutput::CanCreateSinkBuffer() + { + return ETrue; + } + +/** +Creates a sink buffer. + +Intended for asynchronous usage (buffers supplied by Devsound device) + +@param aMediaId + The Media ID. +@param aReference + A boolean indicating if MDataSink owns the buffer. ETrue if does, otherwise EFalse. + +@return A sink buffer. +*/ +CMMFBuffer* CMMFAudioOutput::CreateSinkBufferL(TMediaId /*aMediaId*/, TBool &aReference) + { + //iDevSoundBuffer = CMMFDataBuffer::NewL(KAudioOutputDefaultFrameSize); + iDevSoundBuffer = NULL; //DevSound supplies this buffer in first callback + aReference = ETrue; + if ( iNeedsSWConversion ) + return iConvertBuffer; + else + return iDevSoundBuffer; + } + +/** +Standard SymbianOS destructor. +*/ +CMMFAudioOutput::~CMMFAudioOutput() + { + // The following will never have been allocated unless + // software conversion was required, and due to certain DevSound + // implementations, this requirement can change dynamically. + delete iChannelAndSampleRateConverterFactory; + delete iConvertBuffer; + + if (iMMFDevSound) + { + iMMFDevSound->Stop(); + delete iMMFDevSound; + } + delete iActiveSchedulerWait; + } + +void CMMFAudioOutput::ConfigDevSoundL() + { + iMMFDevSound->SetConfigL(iDevSoundConfig); + } + + +/** +@deprecated + +This method should not be used - it is provided to maintain SC with v7.0s. + +@param aAudioType + The 4CC of the data supplied by this source. +*/ +void CMMFAudioOutput::SetDataTypeL(TFourCC aAudioType) + { + if (aAudioType != KMMFFourCCCodePCM16) + { + User::Leave(KErrNotSupported); + } + } + + +/** +@deprecated + +This method should not be used - it is provided to maintain SC with v7.0s. + +@return The 4CC of the data supplied by this source. +*/ +TFourCC CMMFAudioOutput::DataType() const + { + return KMMFFourCCCodePCM16; + } + +/** +Queries about DevSound resume support + +@return ETrue if DevSound does support resume, EFalse otherwise +*/ +TBool CMMFAudioOutput::IsResumeSupported() + { + TBool isSupported = EFalse; + if (iMMFDevSound) + { + isSupported = iMMFDevSound->IsResumeSupported(); + } + return isSupported; + } + + +/** +Loads audio device drivers and initialise this device. +*/ +void CMMFAudioOutput::LoadL() + { + iPlayStarted = EFalse; + if (iState != EDevSoundReady) + iState = EIdle; + + iMMFDevSound = CMMFDevSound::NewL(); + + //This is done to maintain compatibility with the media server + iMMFDevSound->SetVolume(iMMFDevSound->MaxVolume() - 1); + + //note cannot perform further initlaisation here untill the datatype is known + + iDevSoundLoaded = ETrue; + } + +/** +DeviceMessage MMFDevSoundObserver +*/ +void CMMFAudioOutput::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/) + { + } + + +/** +ToneFinished MMFDevSoundObserver called when a tone has finished or interrupted + +Should never get called. +*/ +void CMMFAudioOutput::ToneFinished(TInt /*aError*/) + { + //we should never get a tone error in MMFAudioOutput! + __ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicToneFinishedNotSupported)); + } + + +/** +RecordError MMFDevSoundObserver called when recording has halted. + +Should never get called. +*/ +void CMMFAudioOutput::RecordError(TInt /*aError*/) + { + //we should never get a recording error in MMFAudioOutput! + __ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicRecordErrorNotSupported)); + } + +/** +InitializeComplete MMFDevSoundObserver called when devsound initialisation completed. +*/ +void CMMFAudioOutput::InitializeComplete(TInt aError) + { + + if (aError == KErrNone) + { + iState = EDevSoundReady; + } + + if(iInitializeState == KRequestPending) + { + iInitializeState = aError; + iActiveSchedulerWait->AsyncStop(); + } + } + +/** +BufferToBeEmptied MMFDevSoundObserver - should never get called. +*/ +void CMMFAudioOutput::BufferToBeEmptied(CMMFBuffer* /*aBuffer*/) + { + __ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicRecordErrorNotSupported)); + } + +/** +BufferToBeFilled MMFDevSoundObserver. +Called when buffer used up. +*/ +void CMMFAudioOutput::BufferToBeFilled(CMMFBuffer* aBuffer) + { + TInt err = KErrNone; + + TRAP(err, iSupplier->BufferEmptiedL(aBuffer)); + + //This error needs handling properly + __ASSERT_DEBUG(!err, Panic(err)); + } + +/** +PlayError MMFDevSoundObserver. + +Called when stopped due to error or EOF. +*/ +void CMMFAudioOutput::PlayError(TInt aError) + { + iMMFDevsoundError = aError; + + //send EOF to client + TMMFEvent event(KMMFEventCategoryPlaybackComplete, aError); + SendEventToClient(event); + + //stop stack overflow / looping problem - AD + if (aError == KErrCancel) + return; + + // NB KErrInUse, KErrDied OR KErrAccessDenied may be returned by the policy server + // to indicate that the sound device is in use by another higher priority client. + if (aError == KErrInUse || aError == KErrDied || aError == KErrAccessDenied) + return; + + if (iState == EIdle) + {//probably have stopped audio output and have got an underflow from last buffer + iMMFDevSound->Stop(); + iPlayStarted = EFalse; + iCanSendBuffers = EFalse; + } + } + + +/** +ConvertError MMFDevSoundObserver. + +Should never get called. +*/ +void CMMFAudioOutput::ConvertError(TInt /*aError*/) + { + } + + +/** +Returns the number of bytes played. + +@return The number of bytes played. If 16-bit divide this number returned by 2 to get word length. +*/ +TInt CMMFAudioOutput::BytesPlayed() + { + if (!iMMFDevSound) + Panic(EMMFAudioOutputDevSoundNotLoaded); + return iMMFDevSound->SamplesPlayed(); + } + +/** +Returns the sound device. + +Accessor function exposing public CMMFDevsound methods. + +@return A reference to a CMMFDevSound objector. +*/ +CMMFDevSound& CMMFAudioOutput::SoundDevice() + { + if (!iMMFDevSound) + Panic(EMMFAudioOutputDevSoundNotLoaded); + return *iMMFDevSound; + } + +void CMMFAudioOutput::SendEventToClient(const TMMFEvent& aEvent) + { + iEventHandler->SendEventToClient(aEvent); + } +// __________________________________________________________________________ +// Exported proxy for instantiation method resolution +// Define the interface UIDs + + + +const TImplementationProxy ImplementationTable[] = + { + IMPLEMENTATION_PROXY_ENTRY(KMmfUidAudioOutputInterface, CMMFAudioOutput::NewSinkL) + }; + +EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount) + { + aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy); + + return ImplementationTable; + } +