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