www.pudn.com > Full-Duplex_Audio_Example.rar > fullduplexengine.cpp
/*
* =============================================================================
* Name : FullDuplexEngine.cpp
* Part of : FullDuplexEx
* Description : Implementation for engine class - runs the audio output stream
interacts with UI and is also responsible for creating the
input stream thread
* Version :
*
* Copyright © 2007 Nokia. All rights reserved.
* This material, including documentation and any related
* computer programs, is protected by copyright controlled by
* Nokia. All rights are reserved. Copying, including
* reproducing, storing, adapting or translating, any
* or all of this material requires the prior written consent of
* Nokia. This material also contains confidential
* information which may not be disclosed to others without the
* prior written consent of Nokia.
* =============================================================================
*/
#include "FullDuplexEngine.h"
#include "InputStreamThread.h"
#include "FullDuplexExContainer.h"
#include "filelogger.h"
/******************************************************************************
* Method Name: CFullDuplexEngine::CFullDuplexEngine
* Description: Standard Constructor
******************************************************************************/
CFullDuplexEngine::CFullDuplexEngine(MNotifierCallback* aConsole)
: CFullDuplexEngineBase(),
iConsole(aConsole)
{
}
/******************************************************************************
* Method Name: CFullDuplexEngine::NewL
* Description: Part one of Symbian two phase construction
******************************************************************************/
CFullDuplexEngine* CFullDuplexEngine::NewL(MNotifierCallback* aConsole,
const TFourCC& aFourCC)
{
CFullDuplexEngine* self = new(ELeave) CFullDuplexEngine(aConsole);
CleanupStack::PushL(self);
self->ConstructL(aFourCC);
CleanupStack::Pop(); // self
return self;
}
/******************************************************************************
* Method Name: CFullDuplexEngine::ConstructL
* Description: Part two of Symbian two phase construction
******************************************************************************/
void CFullDuplexEngine::ConstructL(const TFourCC& aFourCC)
{
// create the input stream thread
#ifdef __SERIES60_3X__
User::LeaveIfError( iFDIThread.Create( _L("FDInputStrThread"),
CInputStreamThread::ThreadFunction,
KDefaultStackSize,
KMinHeapSize,
KMinHeapSize << 2,
&iShared));
#else
User::LeaveIfError( iFDIThread.Create( _L("FDInputStrThread"),
CInputStreamThread::ThreadFunction,
KDefaultStackSize, 0, &iShared));
#endif // __SERIES60_3X__
iFDIThread.SetPriority( EPriorityNormal );
// create the shared buffer for audio data
iShared.iBuffer = CMMFDescriptorBuffer::NewL(KBufferLength);
iShared.iBuffer->SetStatus(EAvailable);
iShared.iFourCC = aFourCC;
// create semaphores & mutex used for inter-thread signalling
User::LeaveIfError( iShared.iThreadSync.CreateLocal(0) );
User::LeaveIfError( iShared.iBufferFilled.CreateLocal(0));
User::LeaveIfError( iShared.iMutex.CreateLocal() );
iFDIThread.Resume();
iShared.iThreadSync.Wait(); // wait until the input stream is running
// create DevSound for audio output and initialize it
iMMFDevSound = CMMFDevSound::NewL();
Initialize();
}
/******************************************************************************
* Method Name: CFullDuplexEngine::~CFullDuplexEngine
* Description: Standard Destructor
******************************************************************************/
CFullDuplexEngine::~CFullDuplexEngine()
{
Stop();
// send terminate command to input stream and wait for its signal
SendCmd(ECmdTerminateThread);
iShared.iThreadSync.Wait();
iShared.iThreadSync.Close();
iShared.iBufferFilled.Close();
iShared.iMutex.Close();
delete iMMFDevSound;
delete iShared.iBuffer;
iFDIThread.Close();
}
/******************************************************************************
* Method Name: CFullDuplexEngine::InitializeL()
******************************************************************************/
TInt CFullDuplexEngine::Initialize()
{
TInt initError;
// delete and reconstruct devSound object if needed
if(iStreamStatus == ERestartNeeded)
{
delete iMMFDevSound;
TRAP(initError, iMMFDevSound = CMMFDevSound::NewL());
if(initError)
return initError;
iShared.iBuffer->SetStatus(EAvailable);
}
TRAP(initError,
iMMFDevSound->InitializeL(*this, iShared.iFourCC, EMMFStatePlaying));
// init the input stream thread
if (!initError)
{
SendCmd(ECmdInitialize);
User::After(0); // let the other thread to run
}
else
{
iStreamStatus = ENotReady;
if( initError == KErrNotFound )
{
iConsole->Print(_L("Error: Codec not found."));
return KErrNotFound;
}
FormatPrint(_L("Leave during Init (%d)."), initError);
}
return initError;
}
/******************************************************************************
* Method Name: CFullDuplexEngine::StartL()
******************************************************************************/
void CFullDuplexEngine::StartL()
{
if(iStreamStatus != EReady)
return;
// start input stream
SendCmd(ECmdStartInputStream);
// adjust volume to half of the max value
iMMFDevSound->SetVolume(iMMFDevSound->MaxVolume() >> 1);
iMMFDevSound->PlayInitL();
iStreamStatus = EPlaying;
}
/******************************************************************************
* Method Name: CFullDuplexEngine::Stop()
******************************************************************************/
void CFullDuplexEngine::Stop()
{
if(iStreamStatus != EPlaying)
return;
// stop output stream
iMMFDevSound->Stop();
// send stop command input stream, wait until it signals about stopping
SendCmd(ECmdStopInputStream);
iShared.iThreadSync.Wait();
// reset the buffer status to EAvailable
iShared.iMutex.Wait();
iShared.iBuffer->Data().Zero();
iShared.iBuffer->SetStatus(EAvailable);
iShared.iMutex.Signal();
// we don't seem to be getting any PlayError callback after stream
// has stopped...set status to EReady so playing can be restarted
iStreamStatus = EReady;
iConsole->Print(_L("Stopped."));
}
/******************************************************************************
* Method Name: CFullDuplexEngine::SendCmd( TUserCommand aCmd )
* Description: Send user-defined commands to input stream thread by raising
* a thread exception
******************************************************************************/
void CFullDuplexEngine::SendCmd(TUserCommand aCmd)
{
__LOGSTR_TOFILE1("SendCmd: %d", aCmd);
iShared.iMutex.Wait();
iShared.iCmd = aCmd;
iShared.iMutex.Signal();
TRequestStatus* status = iShared.iStatusPtr;
if( status->Int() == KRequestPending )
{
iFDIThread.RequestComplete(status,KErrNone);
}
}
/******************************************************************************
******************************** CALLBACKS ************************************
*******************************************************************************/
/******************************************************************************
* Method Name: CFullDuplexEngine::InitializeComplete
* Description: CMMFDevSound callback when Initialization is complete
******************************************************************************/
void CFullDuplexEngine::InitializeComplete(TInt aError)
{
if(aError == KErrNone)
{
iPrioritySettings.iPref = (TMdaPriorityPreference)KAudioPrefOutput;
iPrioritySettings.iPriority = KAudioPriority;
iMMFDevSound->SetPrioritySettings(iPrioritySettings);
// set sample rate and channels
TMMFCapabilities conf;
conf = iMMFDevSound->Config();
conf.iRate = EMMFSampleRate8000Hz;
conf.iChannels = EMMFMono;
TRAPD(configErr, iMMFDevSound->SetConfigL(conf));
if(configErr)
{
FormatPrint(_L("Config failed (%d)."), configErr);
return;
}
iStreamStatus = EReady;
}
else
{
FormatPrint(_L("Init failed (%d)."), aError);
return;
}
iConsole->Print(_L("Init complete."));
}
/******************************************************************************
* Method Name: CFullDuplexEngine::BufferToBeFilled
* Description: CMMFDevSound callback, requesting to fill next playback buffer
******************************************************************************/
void CFullDuplexEngine::BufferToBeFilled(CMMFBuffer* aBuffer)
{
CMMFDataBuffer* buffer = static_cast(aBuffer);
TInt requestSize = buffer->RequestSize();
// wait until input thread is running and has filled the buffer
while(iShared.iBuffer->Data().Length() < requestSize)
{
iShared.iBufferFilled.Wait();
}
// wait for access to shared buffer
iShared.iMutex.Wait();
// move a request number of bytes to MMF-side buffer
buffer->Data().Copy(iShared.iBuffer->Data().LeftTPtr(requestSize));
iShared.iBuffer->Data().Delete(0, requestSize);
iShared.iMutex.Signal();
// update progress info on console, and display current buffer length
iConsole->UpdateProgress(iShared.iBuffer->Data().Length());
// continue playback
iMMFDevSound->PlayData();
}
/******************************************************************************
* Method Name: CFullDuplexEngine::PlayError
* Description: CMMFDevSound callback when playback error occurs
******************************************************************************/
void CFullDuplexEngine::PlayError(TInt aError)
{
switch(aError)
{
case KErrNone:
case KErrAbort:
case KErrCancel:
case KErrUnderflow:
{
iStreamStatus = EReady;
FormatPrint(_L("Stopped (%d)"), aError);
break;
}
case KErrDied: // incoming call has reserved the devSound
{ // -> need to reinit
iStreamStatus = ERestartNeeded;
FormatPrint(_L("Stopped (%d)"), aError);
break;
}
default:
FormatPrint(_L("PlayError: %d"), aError);
iStreamStatus = ENotReady;
break;
}
}
/******************************************************************************
* Method Name: CFullDuplexEngine::FormatPrint
* Description: Helper function for printing formatted messages in console
******************************************************************************/
void CFullDuplexEngine::FormatPrint(const TDesC& aMsg, TInt aValue)
{
TBuf<64> message;
message.Format(aMsg, aValue);
iConsole->Print(message);
}
// End of File