Update contrib.
1 // Copyright (c) 2003-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 "tonedatapath.h"
19 CToneDataPath* CToneDataPath::NewL()
21 CToneDataPath* self = new(ELeave) CToneDataPath;
22 CleanupStack::PushL(self);
29 void CToneDataPath::ConstructL()
31 iAudioPlayer = new (ELeave) CToneDataPathPlayer(*this,CActive::EPriorityUserInput);
32 iSoundDeviceErrorReceiver = new (ELeave) CToneSoundDevPlayErrorReceiver(*this, CActive::EPriorityUserInput);
36 CToneDataPath::~CToneDataPath()
39 delete iSoundDeviceErrorReceiver;
46 if (!iCodec->IsNullCodec())
48 delete iSoundDeviceBuffer;
52 #ifdef __USE_MMF_TRANSFERBUFFERS__
53 delete iTransferWindow;
57 iTransferBuffer->Close();
58 delete iTransferBuffer;
62 #ifdef __USE_MMF_PTRBUFFERS__
63 delete iPtrBufferMemoryBlock;
68 TInt CToneDataPath::SetObserver(MMMFHwDeviceObserver& aObserver)
71 if (iHwDeviceObserver)
73 error = KErrAlreadyExists;
77 iHwDeviceObserver = &aObserver;
84 TInt CToneDataPath::AddCodec(CToneCodec& aCodec)
88 return KErrNotSupported; //doesn't support multiple codecs
95 // Allocate data buffer
96 iSourceBufferSize = iCodec->SourceBufferSize();
97 iSoundDevBufferSize = iCodec->SinkBufferSize();
99 if ((!iSourceBufferSize)||(!iSoundDevBufferSize))
101 err = KErrArgument; //codec plugin has not specified buffer size
106 #ifdef __USE_MMF_TRANSFERBUFFERS__
107 TRAP(err,iSourceBuffer = CreateTransferBufferL(iSourceBufferSize, static_cast<CMMFTransferBuffer*>(iSourceBuffer)));
109 #ifdef __USE_MMF_PTRBUFFERS__
110 TRAP(err,iSourceBuffer = CreatePtrBufferL(iSourceBufferSize));
112 TRAP(err,iSourceBuffer = CMMFDataBuffer::NewL(iSourceBufferSize));
118 if (iCodec->IsNullCodec())
119 {//use source buffer for sound device buffer
120 iSoundDeviceBuffer = NULL;
123 {//codec needs separate source and sound device buffers
124 TRAP(err,iSoundDeviceBuffer = CMMFDataBuffer::NewL(iSoundDevBufferSize));
130 TInt CToneDataPath::Start()
132 TInt startError = KErrNone;
135 {//check that a codec has been added
136 startError = KErrNotReady;
138 if ((!iSoundDevice.Handle())&&(!startError))
139 {//check that the sound drivers can be opened
140 startError = iSoundDevice.Open();
143 if (iState == EPaused)
144 {//we are paused so need to resume play
148 RDebug::Print(_L("CToneDataPath::Start-Resume"));
150 iAudioPlayer->ResumePlaying();
154 else if (!startError)
157 RDebug::Print(_L("CToneDataPath::Start-Normal"));
159 // get sample rate and channels from RMdaDevSound
160 RMdaDevSound::TCurrentSoundFormatBuf format;
161 iSoundDevice.GetPlayFormat(format);
162 iSampleRate = format().iRate;
163 iChannels = format().iChannels;
165 iNoMoreSourceData = EFalse;
166 iSourceBuffer->SetLastBuffer(EFalse);
168 iSoundDeviceErrorReceiver->Start();
169 TRAP(startError,FillSourceBufferL()); //get initial buffer of audio data
170 if (startError == KErrNone)
172 // Start the player objects
173 iAudioPlayer->Start();
176 {//failed to start up correctly go back to stopped state
178 iSoundDeviceErrorReceiver->Stop();
185 // *** Main Play Loop ***
187 void CToneDataPath::FillSourceBufferL()
188 {//asks observer to fill the source buffer
189 // Ask immediately for data from the observer
190 #ifdef __CYCLE_MMF_DATABUFFERS__
191 // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
192 // If the creation fails, we carry on regardless as the original buffer will not have been
193 // destroyed. Must do this as alloc fail tests will not run.
196 iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
198 #endif // __CYCLE_MMF_DATABUFFERS__
199 User::LeaveIfError(iHwDeviceObserver->FillThisHwBuffer(*iSourceBuffer));
204 void CToneDataPath::BufferFilledL(CMMFDataBuffer& aBuffer)
205 {//call back from observer to indicate buffer has been filled
207 if (iState == EStopped)
209 User::Leave(KErrNotReady);//ok if paused?
212 iSourceBuffer = &aBuffer;
213 iSourceBuffer->SetStatus(EFull);
215 RDebug::Print(_L("CToneDataPath::BufferFilledL"));
218 //need to check that the buffer size is not 0 - if so assume we've reached the end of the data
219 iBuffSize = iSourceBuffer->BufferSize();
221 {//no buffer - could be end of source or could be that the source has no data??
222 iNoMoreSourceData = ETrue;
224 RDebug::Print(_L("CToneDataPath::BufferFilledL-NoMoreSourceData"));
227 //even if the buffer size is 0 we still
228 //need to perform the following to get the sound device callback
229 FillSoundDeviceBufferL(); //get buffer in pcm16 format for sound device
231 iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers
235 void CToneDataPath::FillSoundDeviceBufferL()
236 {//use CToneCodec to fill the sound device buffer
238 CToneCodec::TCodecProcessResult codecProcessResult;
240 if (iCodec->IsNullCodec())
241 {//no codec so data can be sent direct to sink
242 iSoundDeviceBuffer = iSourceBuffer;
243 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
247 //pass buffer to codec for processing
248 codecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSoundDeviceBuffer);
250 if (iSourceBuffer->LastBuffer()) //if source is last buffer so is sound dev
252 iSoundDeviceBuffer->SetLastBuffer(ETrue);
254 if ((!iSoundDeviceBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded))
255 {//the codec has added data but not set the buffer length
256 iSoundDeviceBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded);
258 //only supports EProcessComplete
259 switch (codecProcessResult.iCodecProcessStatus)
261 case CToneCodec::TCodecProcessResult::EProcessComplete:
262 //finished procesing source data - all data in sink buffer
264 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
267 #ifdef SYMBIAN_VARIABLE_BITRATE_CODEC
268 case CToneCodec::TCodecProcessResult::EProcessIncomplete:
269 //finished procesing source data - all data in sink buffer
271 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
275 case CToneCodec::TCodecProcessResult::EDstNotFilled:
276 //could be the last buffer in which case dst might not get filled
278 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full
281 case CToneCodec::TCodecProcessResult::EEndOfData:
282 //no more data - send what we've got to the sink
283 //note we can't always rely on this - in many cases the codec will not know when
284 //it has reached the end of data.
286 iSoundDeviceBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get
287 iNoMoreSourceData = ETrue;
288 //doesn't matter if sink buffer is not full
292 //Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec
299 void CToneDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer)
300 {//call back from CToneDataPathPlayer when the sound device buffer has been emptied
301 if (&aBuffer != iSoundDeviceBuffer)
303 Panic(EToneBadBuffer);
306 if (!iNoMoreSourceData)
312 //*** End of Main Play Loop ***
315 void CToneDataPath::Stop()
317 iAudioPlayer->Cancel();
318 iSoundDeviceErrorReceiver->Cancel();
319 iSoundDevice.Close();
321 #ifdef __CYCLE_MMF_DATABUFFERS__
322 // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics
323 // If the creation fails, we carry on regardless as the original buffer will not have been
324 // destroyed. Must do this as alloc fail tests will not run.
327 iSourceBuffer = CycleAudioBuffer(iSourceBuffer);
329 #endif // __CYCLE_MMF_DATABUFFERS__
335 void CToneDataPath::Pause()
337 //since a pause can happen anyway in the datatransfer -need to set to a known
338 //state so that when play is resumed the behaviour is predictable
339 if (iSoundDevice.Handle())
341 iSoundDevice.PausePlayBuffer(); //needs new LDD
344 RDebug::Print(_L("Pause"));
348 {//an error must have occured
354 TInt CToneDataPath::EmptyBuffers()
356 TInt error = KErrNone;
357 if (iSoundDevice.Handle() == 0)
359 error = KErrNotReady;
363 iSoundDevice.FlushPlayBuffer();
369 RMdaDevSound& CToneDataPath::Device()
375 void CToneDataPath::SoundDeviceException(TInt aError)
379 if((aError == KErrUnderflow) && (!iNoMoreSourceData))
386 //this sends a request to the hw device observer
387 //to update the bytes played
388 //it is done here so that the sound driver can be closed prior to
389 //updating the policy and sending the error back
390 TUid uidUpdateBytesPlayed;
391 uidUpdateBytesPlayed.iUid = KToneHwDeviceObserverUpdateBytesPlayed;
394 ASSERT(iHwDeviceObserver);
395 iHwDeviceObserver->MsgFromHwDevice(uidUpdateBytesPlayed,dummy);
397 //this closes RMdaDevSound.
400 //inform devsound so it can update policy
401 iHwDeviceObserver->Stopped();
403 // Inform the observer of the exception condition
404 // We inform the hw device observer after the policy has been
405 // updated incase the observer relied on the error to assume
406 // the policy has been updated
407 iHwDeviceObserver->Error(aError);
409 RDebug::Print(_L("CToneDataPath::iHwDeviceObserver->Error(%d)"),aError);
413 Retrieves a custom interface to the device.
414 The reference CToneDataPath supports three custom interfaces,
415 MEmptyBuffersCustomInterface, MSetVbrFlagCustomInterface and MIgnoreUnderflowEventsCustomInterface
418 Interface UID, defined with the custom interface.
419 aInterface = KMmfUidEmptyBuffersCustomInterface for MEmptyBuffersCustomInterface,
420 KSetVbrFlagCustomInterfaceTypeUid for MSetVbrFlagCustomInterface
422 @return A pointer to the interface implementation, or NULL if the device can not
423 implement the interface requested. The return value must be cast to the
424 correct type by the user.
426 TAny* CToneDataPath::CustomInterface(TUid aInterface)
430 if (aInterface == KIgnoreUnderflowCustomInterfaceTypeUid)
432 MIgnoreUnderflowEventsCustomInterface* result = static_cast<MIgnoreUnderflowEventsCustomInterface*> (this);
433 ret = static_cast<TAny*>(result);
439 void CToneDataPath::IgnoreUnderflowEvents()
441 iIgnoreUnderflow = ETrue;
446 /************************************************************************
448 ************************************************************************/
450 CToneDataPathPlayer::CToneDataPathPlayer(CToneDataPath& aParent, TInt aPriority)
451 : CActive(aPriority), iParent(aParent)
453 CActiveScheduler::Add(this);
457 CToneDataPathPlayer::~CToneDataPathPlayer()
463 void CToneDataPathPlayer::Start()
469 void CToneDataPathPlayer::ResumePlaying()
471 if (iParent.Device().Handle())
473 //should be ok to call this even if we are active
474 iParent.Device().ResumePlaying();
475 iResumePlaying = ETrue;
478 RDebug::Print(_L("Playing Resumed"));
483 void CToneDataPathPlayer::PlayData(const CMMFDataBuffer& aData)
485 iDataFromSource = &aData;
489 RDebug::Print(_L("CToneDataPathPlayer::PlayData"));
491 iParent.Device().PlayData(iStatus,(static_cast<const CMMFDataBuffer*> (iDataFromSource))->Data());
497 void CToneDataPathPlayer::Stop()
501 iParent.Device().FlushPlayBuffer(); // Otherwise won't be flushed
504 iParent.SoundDeviceException(KErrCancel);
508 void CToneDataPathPlayer::RunL()
511 RDebug::Print(_L("CToneDataPathPlayer::RunL error[%d]"), iStatus.Int());
513 if (iStatus.Int()!=KErrNone)
515 iParent.SoundDeviceException(iStatus.Int());
519 iParent.BufferEmptiedL(static_cast<const CMMFDataBuffer&>(*iDataFromSource));
520 iResumePlaying = EFalse;
525 TInt CToneDataPathPlayer::RunError(TInt aError)
532 void CToneDataPathPlayer::DoCancel()
534 if (iParent.Device().Handle())
536 iParent.Device().CancelPlayData();
537 iParent.Device().FlushPlayBuffer();
542 void CToneDataPathPlayer::Error(TInt aError)
544 iParent.SoundDeviceException(aError);
549 /************************************************************************
550 * CToneSoundDevPlayErrorReceiver *
551 ************************************************************************/
553 CToneSoundDevPlayErrorReceiver::CToneSoundDevPlayErrorReceiver(CToneDataPath& aParent, TInt aPriority)
554 : CActive(aPriority), iParent(aParent)
556 CActiveScheduler::Add(this);
559 CToneSoundDevPlayErrorReceiver::~CToneSoundDevPlayErrorReceiver()
564 void CToneSoundDevPlayErrorReceiver::Start()
566 iParent.Device().NotifyPlayError(iStatus);
570 void CToneSoundDevPlayErrorReceiver::Stop()
575 void CToneSoundDevPlayErrorReceiver::RunL()
577 TInt reason = iStatus.Int();
580 // An error has been returned
582 RDebug::Print(_L("CToneSoundDevPlayErrorReceiver::RunL[%d]"), reason);
584 iParent.SoundDeviceException(reason);
587 void CToneSoundDevPlayErrorReceiver::DoCancel()
589 iParent.Device().CancelNotifyPlayError();
597 * Sets up a usable buffer for passing to MMF
599 * This method has been written such that it must allocate a new buffer before
600 * replacing the existing one. The purpose of this is to force creation of a
601 * new buffer. Simply deleting and then re-allocing may result in the same
602 * address being used.
604 * Only cycles if there is enough memory
607 #ifdef __CYCLE_MMF_DATABUFFERS__
608 CMMFDataBuffer* CToneDataPath::CycleAudioBuffer(CMMFDataBuffer* aBuffer)
610 CMMFDataBuffer* buffer = NULL;
611 TUint bufferSize = aBuffer->Data().MaxLength();
613 #ifdef __USE_MMF_TRANSFERBUFFERS__
614 TRAPD(err, buffer = CreateTransferBufferL(bufferSize, static_cast<CMMFTransferBuffer*>(aBuffer)));
616 TRAPD(err,buffer = CMMFDataBuffer::NewL(bufferSize));
624 {//there was a problem creating buffer eg OOM so use same buffer
634 * DoCleanupRHandleBase
636 * This method will initially Close the handle and then delete it.
639 #ifdef __USE_MMF_TRANSFERBUFFERS__
640 inline static void DoCleanupRHandleBase(TAny* aRHandleBase)
642 ASSERT(aRHandleBase);
643 RHandleBase* rHandleBase = static_cast<RHandleBase*> (aRHandleBase);
644 TRAPD(error, rHandleBase->Close());
648 CMMFTransferBuffer* CToneDataPath::CreateTransferBufferL(TUint aBufferSize, CMMFTransferBuffer* aOldBuffer)
650 CMMFTransferBuffer* buffer = NULL;
652 RTransferBuffer* transBuffer = new (ELeave) RTransferBuffer;
654 TCleanupItem bufferCleanupItem(DoCleanupRHandleBase, transBuffer); //closes and deletes.
655 CleanupStack::PushL(bufferCleanupItem);
657 RTransferWindow* transWindow = new (ELeave) RTransferWindow;
659 TCleanupItem windowCleanupItem(DoCleanupRHandleBase, transWindow); //closes and deletes.
660 CleanupStack::PushL(windowCleanupItem);
662 User::LeaveIfError(transBuffer->Create(aBufferSize));
663 User::LeaveIfError(transWindow->Create(aBufferSize));
664 User::LeaveIfError(transWindow->MapInBuffer(*transBuffer));
666 buffer = CMMFTransferBuffer::NewL(*transWindow);
668 delete aOldBuffer; //closes RTransferWindow
669 delete iTransferWindow;
673 iTransferBuffer->Close();
675 delete iTransferBuffer;
677 iTransferBuffer = transBuffer;
678 iTransferWindow = transWindow;
680 CleanupStack::Pop(transWindow);
681 CleanupStack::Pop(transBuffer);
688 #ifdef __USE_MMF_PTRBUFFERS__
689 CMMFPtrBuffer* CToneDataPath::CreatePtrBufferL(TUint aBufferSize)
691 CMMFPtrBuffer* buffer = NULL;
692 if (iPtrBufferMemoryBlock)
694 delete iPtrBufferMemoryBlock;//incase already exisits
696 iPtrBufferMemoryBlock = HBufC8::NewL(aBufferSize);
697 TPtr8 ptrMemoryBlock(iPtrBufferMemoryBlock->Des());
698 buffer = CMMFPtrBuffer::NewL(ptrMemoryBlock);
701 #endif // __USE_MMF_PTRBUFFERS__