Update contrib.
1 // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
16 #include "MmfAudioOutput.h"
17 #include <ecom/implementationproxy.h>
18 #include <mmf/server/mmfformat.h>
19 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
20 #include <mmf/common/mmfhelper.h>
22 #include <mmf/plugin/mmfaudioiointerfaceuids.hrh>
24 void Panic(TInt aPanicCode)
26 _LIT(KMMFAudioOutputPanicCategory, "MMFAudioOutput");
27 User::Panic(KMMFAudioOutputPanicCategory, aPanicCode);
31 Allocates and constructs a new audio output sink.
33 Static standard SymbianOS 2 phase constuction method.
35 @return A pointer to the new sink.
37 MDataSink* CMMFAudioOutput::NewSinkL()
39 CMMFAudioOutput* self = new (ELeave) CMMFAudioOutput ;
40 CleanupStack::PushL(self);
43 return STATIC_CAST( MDataSink*, self ) ;
47 Standard SymbianOS ConstructL.
49 Used to initialise member varibles with device specific behaviour.
51 void CMMFAudioOutput::ConstructL()
53 iInitializeState = KErrNone;
54 iDataTypeCode = KMMFFourCCCodePCM16;
55 iNeedsSWConversion = EFalse;
56 iSourceSampleRate = 0;
57 iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait;
61 Overridable constuction specific to this datasource.
63 The default implementation does nothing.
66 The initialisation data.
68 void CMMFAudioOutput::ConstructSinkL( const TDesC8& /*aInitData*/ )
76 Gets audio from hardware device abstracted MMFDevsound (not used).
79 The data to write out to a Hardware Device.
81 The MDataSource consuming the data contained in aBuffer
83 void CMMFAudioOutput::HWEmptyBufferL(CMMFBuffer* /*aBuffer*/, MDataSource* /*aSupplier*/)
88 Sends audio to MMFDevsound.
91 The data to write out.
93 The search criteria for the supplier.
95 The type of data supplied - currently ignored.
97 void CMMFAudioOutput::EmptyBufferL(CMMFBuffer* aBuffer, MDataSource* aSupplier, TMediaId /*aMediaId*/)
99 iSupplier = aSupplier;
102 Panic(EMMFAudioOutputDevSoundNotLoaded);
104 if (aSupplier == NULL)
105 User::Leave(KErrArgument);
107 // In order to avoid changes at the datapath data transfer state machine
108 // EmptyBufferL is still being called even
109 // if the first time there is no buffer to send
110 if (!iCanSendBuffers)
113 iCanSendBuffers = ETrue;
117 if (iNeedsSWConversion)
118 {//need to perform channel & sample rate conversion before writing to clip
119 CMMFDataBuffer* audio;
121 //need to make sure aBuffer is not null before it is used
124 User::Leave(KErrArgument);
127 //buffer check is placed here before possible use of the buffer
128 if (!CMMFBuffer::IsSupportedDataBuffer(aBuffer->Type()))
130 User::Leave(KErrNotSupported);
133 iConvertBuffer = CMMFDataBuffer::NewL(((CMMFDataBuffer*)aBuffer)->Data().Length());
134 iChannelAndSampleRateConverter->Convert(*(CMMFDataBuffer*)aBuffer, *iConvertBuffer);
135 audio = iConvertBuffer;
137 //copy our converted data back into the real buffer to return to datapath
138 TDes8& dest = STATIC_CAST( CMMFDataBuffer*, aBuffer )->Data() ;
140 dest.Copy(audio->Data());
143 if (iState != EDevSoundReady && iState != EPaused) // If we're paused we still feed a buffer to DevSound
144 User::Leave(KErrNotReady);
146 iMMFDevSound->PlayData();
148 #if defined(__AUDIO_PROFILING)
149 RDebug::ProfileEnd(0);
150 User::Leave(KErrEof);
151 #endif // defined(__AUDIO_PROFILING)
154 // The following will never have been allocated unless
155 // software conversion was required, and due to certain DevSound
156 // implementations, this requirement can change dynamically.
157 delete iConvertBuffer;
158 iConvertBuffer = NULL;
164 Negotiates with the source to set, for example, the sample rate and number of channels.
166 Called if the sink's setup depends on source.
169 The data source with which to negotiate.
171 void CMMFAudioOutput::NegotiateL(MDataSource& aSource)
173 if (aSource.DataSourceType() == KUidMmfFormatDecode)
174 {//source is a clip so for now set sink settings to match source
175 iSourceSampleRate = ((CMMFFormatDecode&)aSource).SampleRate();
176 iSourceChannels = ((CMMFFormatDecode&)aSource).NumChannels();
177 iSourceFourCC.Set(aSource.SourceDataTypeCode(TMediaId(KUidMediaTypeAudio)));
179 ((CMMFFormatDecode&)aSource).SuggestSourceBufferSize(KAudioOutputDefaultFrameSize);
182 // Query DevSound capabilities and Try to use DevSound sample rate and
183 // mono/stereo capability
185 Panic(EMMFAudioOutputDevSoundNotLoaded);
187 TMMFState prioritySettingsState = iPrioritySettings.iState; //should be EMMFStatePlaying
188 //to use the GetSupportedInputDatatypes but we'll save it just in case it's not
189 iPrioritySettings.iState = EMMFStatePlaying; //if playing does not support any output data types
190 RArray<TFourCC> supportedDataTypes;
191 //note Input data types becuase if we are playing audio ie audio output
192 //the data is sent as an input to DevSound
193 TRAPD(err, iMMFDevSound->GetSupportedInputDataTypesL(supportedDataTypes, iPrioritySettings));
194 iPrioritySettings.iState = prioritySettingsState;
197 if (supportedDataTypes.Find(iSourceFourCC) == KErrNotFound)
198 {//the source fourCC code could not be found in the list of
199 //data types supported by the Devsound therefor default to pcm16
200 iDataTypeCode = KMMFFourCCCodePCM16;
204 //the DevSound does support the same datatype as the source
205 //so set the fourcc to that of the source
206 iDataTypeCode = iSourceFourCC;
209 supportedDataTypes.Close();
210 if (err == KErrNotSupported)
211 {//if the Devsound does not support the GetSupportedOutputDataTypesL method
212 //then assume that the DevSound is pcm16 only
213 iDataTypeCode = KMMFFourCCCodePCM16;
215 else if (err != KErrNone) //we had a real leave error from GetSupportedOuputDataTypesL
220 // Prevent defect when SinkPrimeL is called before NegotiateL()
221 // since characterization is ambiguous
222 if(iState == EDevSoundReady)
226 //INC40817 do the initialize here as capabilities may depend on datatype
227 //note this implies client of audiooutput must call negotiate
228 iMMFDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying);
230 // In some implementations InitializeComplete is sent
231 // in context, so check before starting activeSchedulerWait.
232 if (iState != EDevSoundReady)
234 iInitializeState = KRequestPending;
235 iActiveSchedulerWait->Start();
238 User::LeaveIfError(iInitializeState);
240 // Reset the following flag in case DevSound's capabilities have
241 // changed since we were last here: INC037165
242 iNeedsSWConversion = EFalse;
243 TMMFCapabilities devSoundCaps;
244 devSoundCaps = iMMFDevSound->Capabilities();
246 iDevSoundConfig.iEncoding = EMMFSoundEncoding16BitPCM;
247 // 1 = Monophonic and 2 == Stereo
248 if (((iSourceChannels == 1) && (devSoundCaps.iChannels & EMMFMono)) ||
249 ((iSourceChannels == 2) && (devSoundCaps.iChannels & EMMFStereo)))
250 iDevSoundConfig.iChannels = iSourceChannels;
251 else //default or SW conversion, e.g. stereo on mono support
253 iDevSoundConfig.iChannels = EMMFMono;
254 iNeedsSWConversion = ETrue;
255 iSWConvertChannels = 1;
256 iSWConvertSampleRate = iSourceSampleRate;
259 // Check for std sample rates.
260 if ((iSourceSampleRate == 96000) && (devSoundCaps.iRate & EMMFSampleRate96000Hz))
261 iDevSoundConfig.iRate = EMMFSampleRate96000Hz;
262 else if ((iSourceSampleRate == 88200) && (devSoundCaps.iRate & EMMFSampleRate88200Hz))
263 iDevSoundConfig.iRate = EMMFSampleRate88200Hz;
264 else if ((iSourceSampleRate == 64000) && (devSoundCaps.iRate & EMMFSampleRate64000Hz))
265 iDevSoundConfig.iRate = EMMFSampleRate64000Hz;
266 else if ((iSourceSampleRate == 48000) && (devSoundCaps.iRate & EMMFSampleRate48000Hz))
267 iDevSoundConfig.iRate = EMMFSampleRate48000Hz;
268 else if ((iSourceSampleRate == 44100) && (devSoundCaps.iRate & EMMFSampleRate44100Hz))
269 iDevSoundConfig.iRate = EMMFSampleRate44100Hz;
270 else if ((iSourceSampleRate == 32000) && (devSoundCaps.iRate & EMMFSampleRate32000Hz))
271 iDevSoundConfig.iRate = EMMFSampleRate32000Hz;
272 else if ((iSourceSampleRate == 24000) && (devSoundCaps.iRate & EMMFSampleRate24000Hz))
273 iDevSoundConfig.iRate = EMMFSampleRate24000Hz;
274 else if ((iSourceSampleRate == 22050) && (devSoundCaps.iRate & EMMFSampleRate22050Hz))
275 iDevSoundConfig.iRate = EMMFSampleRate22050Hz;
276 else if ((iSourceSampleRate == 16000) && (devSoundCaps.iRate & EMMFSampleRate16000Hz))
277 iDevSoundConfig.iRate = EMMFSampleRate16000Hz;
278 else if ((iSourceSampleRate == 12000) && (devSoundCaps.iRate & EMMFSampleRate12000Hz))
279 iDevSoundConfig.iRate = EMMFSampleRate12000Hz;
280 else if ((iSourceSampleRate == 11025) && (devSoundCaps.iRate & EMMFSampleRate11025Hz))
281 iDevSoundConfig.iRate = EMMFSampleRate11025Hz;
282 else if ((iSourceSampleRate == 8000) && (devSoundCaps.iRate & EMMFSampleRate8000Hz))
283 iDevSoundConfig.iRate = EMMFSampleRate8000Hz;
284 else // non standard sample rate
286 iNeedsSWConversion = ETrue;
287 // we need to choose to the closest, and smaller standard sample rate
288 // and eventually convert the audio samples to this standard sample rate
289 //NB: this list must be in ascending order
290 const TInt KNumSampleRates = 12;
291 static const TUint supportedSR[KNumSampleRates][2] = {{8000, EMMFSampleRate8000Hz},
292 {11025, EMMFSampleRate11025Hz},
293 {12000, EMMFSampleRate12000Hz},
294 {16000, EMMFSampleRate16000Hz},
295 {22050, EMMFSampleRate22050Hz},
296 {24000, EMMFSampleRate24000Hz},
297 {32000, EMMFSampleRate32000Hz},
298 {44100, EMMFSampleRate44100Hz},
299 {48000, EMMFSampleRate48000Hz},
300 {64000, EMMFSampleRate64000Hz},
301 {88200, EMMFSampleRate88200Hz},
302 {96000, EMMFSampleRate96000Hz}};
304 //Only support down sampling
305 if (iSourceSampleRate < supportedSR[0][0])
306 User::Leave(KErrNotSupported);
309 TInt sampleRateIndex = KNumSampleRates;
311 //find the source sampleRateIndex
312 for (sampleRateIndex--; sampleRateIndex > -1; sampleRateIndex--)
314 if(iSourceSampleRate >= supportedSR[sampleRateIndex][0])
320 //find the highest sink sample rate below the source rate
321 for (; sampleRateIndex > -1; sampleRateIndex--)
323 if(devSoundCaps.iRate & supportedSR[sampleRateIndex][1])
325 iSWConvertSampleRate = supportedSR[sampleRateIndex][0];
326 iDevSoundConfig.iRate = supportedSR[sampleRateIndex][1];
331 //if a suitable sink sample rate is not available
332 if (sampleRateIndex < 0)
333 User::Leave(KErrNotSupported);
335 // set the channels as well
336 iSWConvertChannels = iDevSoundConfig.iChannels;
337 } // else // non standard sample rate
339 if (iNeedsSWConversion)
340 {//can only software convert if datatype is pcm16
341 //note cannot set non standard sample rates on DevSound API
342 //as the API does not allow this
343 //we need to reinitialize the devsound with pcm16 in this case
344 iDataTypeCode = KMMFFourCCCodePCM16;
346 iMMFDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying);
348 // In some implementations InitializeComplete is called
349 // in context, so check before starting activeSchedulerWait.
350 if (iState != EDevSoundReady)
352 iInitializeState = KRequestPending;
353 iActiveSchedulerWait->Start();
356 User::LeaveIfError(iInitializeState);
361 Sets the sink's priority settings.
363 @param aPrioritySettings
364 The sink's priority settings. Takes enumerations to determine audio playback priority.
365 Higher numbers mean high priority (can interrupt lower priorities).
367 void CMMFAudioOutput::SetSinkPrioritySettings(const TMMFPrioritySettings& aPrioritySettings)
369 iPrioritySettings = aPrioritySettings;
371 Panic(EMMFAudioOutputDevSoundNotLoaded);
373 iMMFDevSound->SetPrioritySettings(iPrioritySettings);
377 Gets the sink's data type code.
379 Used by datapath MDataSource / MDataSink for codec matching.
382 The Media ID. Optional parameter to specifiy specific stream when datasource contains more
383 than one stream of data.
385 @return The 4CC of the data expected by this sink.
387 TFourCC CMMFAudioOutput::SinkDataTypeCode(TMediaId /*aMediaId*/)
389 return iDataTypeCode;
393 Sets the sink's data type code.
396 The 4CC of the data to be supplied to this sink.
398 The Media ID. Optional parameter to specifiy specific stream when datasource contains more
399 than one stream of data.
401 @return An error code indicating if the function call was successful. KErrNone on success, otherwise
402 another of the system-wide error codes.
404 TInt CMMFAudioOutput::SetSinkDataTypeCode(TFourCC aSinkFourCC, TMediaId /*aMediaId*/)
405 {//will check with devsound to see if aSinkFourCC is supported
406 //when this is added to devsound
407 iDataTypeCode = aSinkFourCC;
414 This is a virtual function that each derived class must implement, but may be left blank for default
417 Called by CMMFDataPath::PrimeL().
419 void CMMFAudioOutput::SinkPrimeL()
421 if(iPlayStarted != EFalse)
425 iPlayStarted = EFalse;
426 iCanSendBuffers = EFalse;
431 User::Leave(KErrNotReady);
433 iState = EDevSoundReady;
440 This is a virtual function that each derived class must implement, but may be left blank for default
443 void CMMFAudioOutput::SinkPauseL()
446 Panic(EMMFAudioOutputDevSoundNotLoaded);
448 iMMFDevSound->Pause();
449 iPlayStarted = EFalse;
454 Starts playing the sink.
456 This is a virtual function that each derived class must implement, but may be left blank for default
459 void CMMFAudioOutput::SinkPlayL()
461 if (iState == EPaused)
463 TBool isResumeSupported = iMMFDevSound->IsResumeSupported();
464 // CMMFAudioOutput is not supposed to be paused
465 // if Resume is not supported by DevSound
466 if(!isResumeSupported)
468 User::Leave(KErrNotSupported);
471 User::LeaveIfError(iMMFDevSound->Resume());
472 iPlayStarted = ETrue;
474 else if (iPlayStarted == EFalse)
478 // This is a one-shot to "prime" MMFDevSound as first buffer uninitialised
479 iMMFDevSound->PlayInitL();
480 iPlayStarted = ETrue;
482 if ((iNeedsSWConversion)&&(iSourceChannels>0))
483 {//can only do SW convert - therefore need to do a conversion
484 //currently only pcm16 is supported so return with an error if format not pcm16
485 if (!iChannelAndSampleRateConverterFactory)
487 iChannelAndSampleRateConverterFactory
488 = new(ELeave)CMMFChannelAndSampleRateConverterFactory;
489 iChannelAndSampleRateConverter =
490 iChannelAndSampleRateConverterFactory->CreateConverterL( iSourceSampleRate, iSourceChannels,
491 iSWConvertSampleRate, iSWConvertChannels);
493 //need to create an intermediate buffer in which to place the converted data
495 iState = EDevSoundReady;
501 This is a virtual function that each derived class must implement, but may be left blank for default
504 void CMMFAudioOutput::SinkStopL()
506 if (iState == EDevSoundReady)
507 {//not waiting on a buffer being played so stop devsound now
513 Panic(EMMFAudioOutputDevSoundNotLoaded);
517 iPlayStarted = EFalse;
518 iMMFDevSound->Stop();
522 else if (iState == EPaused) //DEF46250 need to handle pause separately as we should always stop regardless of the state of iFirstBufferSent
524 iPlayStarted = EFalse;
525 iMMFDevSound->Stop();
528 iCanSendBuffers = EFalse;
532 Returns the playback state (EStopped, EPlaying, EPaused etc) of this sink
534 TInt CMMFAudioOutput::State()
540 Logs on the sink's thread.
542 Thread specific initialization procedure for this device. Runs automatically on thread construction.
547 @return An error code indicating if the function call was successful. KErrNone on success, otherwise
548 another of the system-wide error codes.
550 TInt CMMFAudioOutput::SinkThreadLogon(MAsyncEventHandler& aEventHandler)
552 iEventHandler = &aEventHandler;
554 if (!iDevSoundLoaded)
560 Logs off the sink thread.
562 Thread specific destruction procedure for this device. Runs automatically on thread destruction.
564 void CMMFAudioOutput::SinkThreadLogoff()
568 iMMFDevSound->Stop();
572 iDevSoundLoaded = EFalse;
577 Called by MDataSource to pass back a full buffer to the sink.
579 Should never be called by a sink, as sinks empty buffers, not fill them.
584 void CMMFAudioOutput::BufferFilledL(CMMFBuffer* /*aBuffer*/)
586 Panic(EMMFAudioOutputPanicBufferFilledLNotSupported);
590 Tests whether a sink buffer can be created.
592 The default implementation returns true.
594 @return A boolean indicating if the sink buffer can be created. ETrue if it can, otherwise EFalse.
596 TBool CMMFAudioOutput::CanCreateSinkBuffer()
602 Creates a sink buffer.
604 Intended for asynchronous usage (buffers supplied by Devsound device)
609 A boolean indicating if MDataSink owns the buffer. ETrue if does, otherwise EFalse.
611 @return A sink buffer.
613 CMMFBuffer* CMMFAudioOutput::CreateSinkBufferL(TMediaId /*aMediaId*/, TBool &aReference)
615 //iDevSoundBuffer = CMMFDataBuffer::NewL(KAudioOutputDefaultFrameSize);
616 iDevSoundBuffer = NULL; //DevSound supplies this buffer in first callback
618 if ( iNeedsSWConversion )
619 return iConvertBuffer;
621 return iDevSoundBuffer;
625 Standard SymbianOS destructor.
627 CMMFAudioOutput::~CMMFAudioOutput()
629 // The following will never have been allocated unless
630 // software conversion was required, and due to certain DevSound
631 // implementations, this requirement can change dynamically.
632 delete iChannelAndSampleRateConverterFactory;
633 delete iConvertBuffer;
637 iMMFDevSound->Stop();
640 delete iActiveSchedulerWait;
643 void CMMFAudioOutput::ConfigDevSoundL()
645 iMMFDevSound->SetConfigL(iDevSoundConfig);
652 This method should not be used - it is provided to maintain SC with v7.0s.
655 The 4CC of the data supplied by this source.
657 void CMMFAudioOutput::SetDataTypeL(TFourCC aAudioType)
659 if (aAudioType != KMMFFourCCCodePCM16)
661 User::Leave(KErrNotSupported);
669 This method should not be used - it is provided to maintain SC with v7.0s.
671 @return The 4CC of the data supplied by this source.
673 TFourCC CMMFAudioOutput::DataType() const
675 return KMMFFourCCCodePCM16;
679 Queries about DevSound resume support
681 @return ETrue if DevSound does support resume, EFalse otherwise
683 TBool CMMFAudioOutput::IsResumeSupported()
685 TBool isSupported = EFalse;
688 isSupported = iMMFDevSound->IsResumeSupported();
695 Loads audio device drivers and initialise this device.
697 void CMMFAudioOutput::LoadL()
699 iPlayStarted = EFalse;
700 if (iState != EDevSoundReady)
703 iMMFDevSound = CMMFDevSound::NewL();
705 //This is done to maintain compatibility with the media server
706 iMMFDevSound->SetVolume(iMMFDevSound->MaxVolume() - 1);
708 //note cannot perform further initlaisation here untill the datatype is known
710 iDevSoundLoaded = ETrue;
714 DeviceMessage MMFDevSoundObserver
716 void CMMFAudioOutput::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/)
722 ToneFinished MMFDevSoundObserver called when a tone has finished or interrupted
724 Should never get called.
726 void CMMFAudioOutput::ToneFinished(TInt /*aError*/)
728 //we should never get a tone error in MMFAudioOutput!
729 __ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicToneFinishedNotSupported));
734 RecordError MMFDevSoundObserver called when recording has halted.
736 Should never get called.
738 void CMMFAudioOutput::RecordError(TInt /*aError*/)
740 //we should never get a recording error in MMFAudioOutput!
741 __ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicRecordErrorNotSupported));
745 InitializeComplete MMFDevSoundObserver called when devsound initialisation completed.
747 void CMMFAudioOutput::InitializeComplete(TInt aError)
750 if (aError == KErrNone)
752 iState = EDevSoundReady;
755 if(iInitializeState == KRequestPending)
757 iInitializeState = aError;
758 iActiveSchedulerWait->AsyncStop();
763 BufferToBeEmptied MMFDevSoundObserver - should never get called.
765 void CMMFAudioOutput::BufferToBeEmptied(CMMFBuffer* /*aBuffer*/)
767 __ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicRecordErrorNotSupported));
771 BufferToBeFilled MMFDevSoundObserver.
772 Called when buffer used up.
774 void CMMFAudioOutput::BufferToBeFilled(CMMFBuffer* aBuffer)
778 TRAP(err, iSupplier->BufferEmptiedL(aBuffer));
780 //This error needs handling properly
781 __ASSERT_DEBUG(!err, Panic(err));
785 PlayError MMFDevSoundObserver.
787 Called when stopped due to error or EOF.
789 void CMMFAudioOutput::PlayError(TInt aError)
791 iMMFDevsoundError = aError;
794 TMMFEvent event(KMMFEventCategoryPlaybackComplete, aError);
795 SendEventToClient(event);
797 //stop stack overflow / looping problem - AD
798 if (aError == KErrCancel)
801 // NB KErrInUse, KErrDied OR KErrAccessDenied may be returned by the policy server
802 // to indicate that the sound device is in use by another higher priority client.
803 if (aError == KErrInUse || aError == KErrDied || aError == KErrAccessDenied)
807 {//probably have stopped audio output and have got an underflow from last buffer
808 iMMFDevSound->Stop();
809 iPlayStarted = EFalse;
810 iCanSendBuffers = EFalse;
816 ConvertError MMFDevSoundObserver.
818 Should never get called.
820 void CMMFAudioOutput::ConvertError(TInt /*aError*/)
826 Returns the number of bytes played.
828 @return The number of bytes played. If 16-bit divide this number returned by 2 to get word length.
830 TInt CMMFAudioOutput::BytesPlayed()
833 Panic(EMMFAudioOutputDevSoundNotLoaded);
834 return iMMFDevSound->SamplesPlayed();
838 Returns the sound device.
840 Accessor function exposing public CMMFDevsound methods.
842 @return A reference to a CMMFDevSound objector.
844 CMMFDevSound& CMMFAudioOutput::SoundDevice()
847 Panic(EMMFAudioOutputDevSoundNotLoaded);
848 return *iMMFDevSound;
851 void CMMFAudioOutput::SendEventToClient(const TMMFEvent& aEvent)
853 iEventHandler->SendEventToClient(aEvent);
855 // __________________________________________________________________________
856 // Exported proxy for instantiation method resolution
857 // Define the interface UIDs
861 const TImplementationProxy ImplementationTable[] =
863 IMPLEMENTATION_PROXY_ENTRY(KMmfUidAudioOutputInterface, CMMFAudioOutput::NewSinkL)
866 EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
868 aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
870 return ImplementationTable;