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