⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 symbian_sound.cpp

📁 一个开源的sip源代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/* $Id: symbian_sound.cpp 1242 2007-05-02 11:29:37Z bennylp $ */
/* 
 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include <pjmedia/sound.h>
#include <pjmedia/errno.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/os.h>


/*
 * This file provides sound implementation for Symbian Audio Streaming
 * device. Application using this sound abstraction must link with:
 *  - mediaclientaudiostream.lib, and
 *  - mediaclientaudioinputstream.lib 
 */
#include <mda/common/audio.h>
#include <MdaAudioOutputStream.h>
#include <MdaAudioInputStream.h>



//////////////////////////////////////////////////////////////////////////////
//

#define THIS_FILE	    "symbian_sound.cpp"
#define BYTES_PER_SAMPLE    2
#define POOL_NAME	    "SymbianSound"
#define POOL_SIZE	    512
#define POOL_INC	    512

static pjmedia_snd_dev_info symbian_snd_dev_info = 
{
    "Symbian Sound Device",
    1,
    1,
    8000
};

class CPjAudioInputEngine;
class CPjAudioOutputEngine;

/* 
 * PJMEDIA Sound Stream instance 
 */
struct pjmedia_snd_stream
{
    // Pool
    pj_pool_t			*pool;

    // Common settings.
    unsigned			 clock_rate;
    unsigned			 channel_count;
    unsigned			 samples_per_frame;

    // Input stream
    CPjAudioInputEngine		*inEngine;

    // Output stream
    CPjAudioOutputEngine	*outEngine;
};

static pj_pool_factory *snd_pool_factory;


/*
 * Convert clock rate to Symbian's TMdaAudioDataSettings capability.
 */
static TInt get_clock_rate_cap(unsigned clock_rate)
{
    switch (clock_rate) {
    case 8000:  return TMdaAudioDataSettings::ESampleRate8000Hz;
    case 11025: return TMdaAudioDataSettings::ESampleRate11025Hz;
    case 12000: return TMdaAudioDataSettings::ESampleRate12000Hz;
    case 16000: return TMdaAudioDataSettings::ESampleRate16000Hz;
    case 22050: return TMdaAudioDataSettings::ESampleRate22050Hz;
    case 24000: return TMdaAudioDataSettings::ESampleRate24000Hz;
    case 32000: return TMdaAudioDataSettings::ESampleRate32000Hz;
    case 44100: return TMdaAudioDataSettings::ESampleRate44100Hz;
    case 48000: return TMdaAudioDataSettings::ESampleRate48000Hz;
    case 64000: return TMdaAudioDataSettings::ESampleRate64000Hz;
    case 96000: return TMdaAudioDataSettings::ESampleRate96000Hz;
    default:
	return 0;
    }
}


/*
 * Convert number of channels into Symbian's TMdaAudioDataSettings capability.
 */
static TInt get_channel_cap(unsigned channel_count)
{
    switch (channel_count) {
    case 1: return TMdaAudioDataSettings::EChannelsMono;
    case 2: return TMdaAudioDataSettings::EChannelsStereo;
    default:
	return 0;
    }
}


//////////////////////////////////////////////////////////////////////////////
//

/*
 * Implementation: Symbian Input Stream.
 */
class CPjAudioInputEngine : public MMdaAudioInputStreamCallback
{
public:
    enum State
    {
	STATE_INACTIVE,
	STATE_ACTIVE,
    };

    ~CPjAudioInputEngine();

    static CPjAudioInputEngine *NewL(pjmedia_snd_stream *parent_strm,
				     pjmedia_snd_rec_cb rec_cb,
				     void *user_data);

    static CPjAudioInputEngine *NewLC(pjmedia_snd_stream *parent_strm,
				      pjmedia_snd_rec_cb rec_cb,
				      void *user_data);

    pj_status_t StartRecord();
    void Stop();

public:
    State		     state_;
    pjmedia_snd_stream	    *parentStrm_;
    pjmedia_snd_rec_cb	     recCb_;
    void		    *userData_;
    CMdaAudioInputStream    *iInputStream_;
    HBufC8		    *iStreamBuffer_;
    TPtr8		     iFramePtr_;
    TInt		     lastError_;
    pj_uint32_t		     timeStamp_;

    CPjAudioInputEngine(pjmedia_snd_stream *parent_strm,
			pjmedia_snd_rec_cb rec_cb,
			void *user_data);
    void ConstructL();
    
public:
    virtual void MaiscOpenComplete(TInt aError);
    virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer);
    virtual void MaiscRecordComplete(TInt aError);

};


CPjAudioInputEngine::CPjAudioInputEngine(pjmedia_snd_stream *parent_strm,
					 pjmedia_snd_rec_cb rec_cb,
					 void *user_data)
    : state_(STATE_INACTIVE), parentStrm_(parent_strm), recCb_(rec_cb), 
      iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(NULL, 0),
      userData_(user_data), lastError_(KErrNone), timeStamp_(0)
{
}

CPjAudioInputEngine::~CPjAudioInputEngine()
{
    Stop();
    delete iStreamBuffer_;
}

void CPjAudioInputEngine::ConstructL()
{
    iStreamBuffer_ = HBufC8::NewMaxL(parentStrm_->samples_per_frame *
				     parentStrm_->channel_count * 
				     BYTES_PER_SAMPLE);
}

CPjAudioInputEngine *CPjAudioInputEngine::NewLC(pjmedia_snd_stream *parent,
					        pjmedia_snd_rec_cb rec_cb,
					        void *user_data)
{
    CPjAudioInputEngine* self = new (ELeave) CPjAudioInputEngine(parent,
								 rec_cb, 
								 user_data);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
}

CPjAudioInputEngine *CPjAudioInputEngine::NewL(pjmedia_snd_stream *parent,
					       pjmedia_snd_rec_cb rec_cb,
					       void *user_data)
{
    CPjAudioInputEngine *self = NewLC(parent, rec_cb, user_data);
    CleanupStack::Pop(self);
    return self;
}


pj_status_t CPjAudioInputEngine::StartRecord()
{

    // Ignore command if recording is in progress.
    if (state_ == STATE_ACTIVE)
	return PJ_SUCCESS;

    // According to Nokia's AudioStream example, some 2nd Edition, FP2 devices
    // (such as Nokia 6630) require the stream to be reconstructed each time 
    // before calling Open() - otherwise the callback never gets called.
    // For uniform behavior, lets just delete/re-create the stream for all
    // devices.

    // Destroy existing stream.
    if (iInputStream_) delete iInputStream_;
    iInputStream_ = NULL;

    // Create the stream.
    TRAPD(err, iInputStream_ = CMdaAudioInputStream::NewL(*this));
    if (err != KErrNone)
	return PJ_RETURN_OS_ERROR(err);

    // Initialize settings.
    TMdaAudioDataSettings iStreamSettings;
    iStreamSettings.iChannels = get_channel_cap(parentStrm_->channel_count);
    iStreamSettings.iSampleRate = get_clock_rate_cap(parentStrm_->clock_rate);

    pj_assert(iStreamSettings.iChannels != 0 && 
	      iStreamSettings.iSampleRate != 0);

    // Create timeout timer to wait for Open to complete
    RTimer timer;
    TRequestStatus reqStatus;
    TInt rc;
    
    rc = timer.CreateLocal();
    if (rc != KErrNone) {
    	delete iInputStream_;
	iInputStream_ = NULL;
	return PJ_RETURN_OS_ERROR(rc);
    }

    PJ_LOG(4,(THIS_FILE, "Opening sound device for capture, "
    		         "clock rate=%d, channel count=%d..",
    		         parentStrm_->clock_rate, 
    		         parentStrm_->channel_count));
    
    // Open stream.
    lastError_ = KRequestPending;
    iInputStream_->Open(&iStreamSettings);

    // Wait until callback is called.
    if (lastError_ == KRequestPending) {
	timer.After(reqStatus, 5 * 1000 * 1000);

	do {
	    User::WaitForAnyRequest();
	} while (lastError_==KRequestPending && reqStatus==KRequestPending);
	
	if (reqStatus==KRequestPending)
	    timer.Cancel();
    }

    // Close timer
    timer.Close();
    
    // Handle timeout
    if (lastError_ == KRequestPending) {
    	iInputStream_->Stop();
    	delete iInputStream_;
	iInputStream_ = NULL;
	return PJ_ETIMEDOUT;
    }
    else if (lastError_ != KErrNone) {
    	// Handle failure.
	delete iInputStream_;
	iInputStream_ = NULL;
	return PJ_RETURN_OS_ERROR(lastError_);
    }

    // Feed the first frame.
    iFramePtr_ = iStreamBuffer_->Des();
    iInputStream_->ReadL(iFramePtr_);

    // Success
    PJ_LOG(4,(THIS_FILE, "Sound capture started."));
    return PJ_SUCCESS;
}


void CPjAudioInputEngine::Stop()
{
    // If capture is in progress, stop it.
    if (iInputStream_ && state_ == STATE_ACTIVE) {
    	lastError_ = KRequestPending;
    	iInputStream_->Stop();

	// Wait until it's actually stopped
    	while (lastError_ == KRequestPending)
	    pj_thread_sleep(100);
    }

    if (iInputStream_) {
	delete iInputStream_;
	iInputStream_ = NULL;
    }
    
    state_ = STATE_INACTIVE;
}


void CPjAudioInputEngine::MaiscOpenComplete(TInt aError)
{
    lastError_ = aError;
}

void CPjAudioInputEngine::MaiscBufferCopied(TInt aError, 
					    const TDesC8 &aBuffer)
{
    lastError_ = aError;
    if (aError != KErrNone)
	return;

    // Call the callback.
    recCb_(userData_, timeStamp_, (void*)aBuffer.Ptr(), aBuffer.Size());

    // Increment timestamp.
    timeStamp_ += (aBuffer.Size() * BYTES_PER_SAMPLE);

    // Record next frame
    iFramePtr_ = iStreamBuffer_->Des();
    iInputStream_->ReadL(iFramePtr_);
}


void CPjAudioInputEngine::MaiscRecordComplete(TInt aError)
{
    lastError_ = aError;
    state_ = STATE_INACTIVE;
}



//////////////////////////////////////////////////////////////////////////////
//

/*
 * Implementation: Symbian Output Stream.
 */

class CPjAudioOutputEngine : public MMdaAudioOutputStreamCallback
{
public:
    enum State
    {
	STATE_INACTIVE,
	STATE_ACTIVE,
    };

    ~CPjAudioOutputEngine();

    static CPjAudioOutputEngine *NewL(pjmedia_snd_stream *parent_strm,
				      pjmedia_snd_play_cb play_cb,
				      void *user_data);

    static CPjAudioOutputEngine *NewLC(pjmedia_snd_stream *parent_strm,
				       pjmedia_snd_play_cb rec_cb,
				       void *user_data);

    pj_status_t StartPlay();
    void Stop();

public:
    State		     state_;
    pjmedia_snd_stream	    *parentStrm_;
    pjmedia_snd_play_cb	     playCb_;
    void		    *userData_;
    CMdaAudioOutputStream   *iOutputStream_;
    TUint8		    *frameBuf_;
    unsigned		     frameBufSize_;
    TInt		     lastError_;
    unsigned		     timestamp_;

    CPjAudioOutputEngine(pjmedia_snd_stream *parent_strm,
			 pjmedia_snd_play_cb play_cb,
			 void *user_data);
    void ConstructL();

    virtual void MaoscOpenComplete(TInt aError);
    virtual void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer);
    virtual void MaoscPlayComplete(TInt aError);
};


CPjAudioOutputEngine::CPjAudioOutputEngine(pjmedia_snd_stream *parent_strm,
					   pjmedia_snd_play_cb play_cb,
					   void *user_data) 
: state_(STATE_INACTIVE), parentStrm_(parent_strm), playCb_(play_cb), 
  userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL),
  lastError_(KErrNone), timestamp_(0)
{
}


void CPjAudioOutputEngine::ConstructL()
{
    frameBufSize_ = parentStrm_->samples_per_frame *
			   parentStrm_->channel_count * 
			   BYTES_PER_SAMPLE;
    frameBuf_ = new TUint8[frameBufSize_];
}

CPjAudioOutputEngine::~CPjAudioOutputEngine()
{
    Stop();
    delete [] frameBuf_;	
}

CPjAudioOutputEngine *
CPjAudioOutputEngine::NewLC(pjmedia_snd_stream *parent_strm,
			    pjmedia_snd_play_cb rec_cb,
			    void *user_data)
{
    CPjAudioOutputEngine* self = new (ELeave) CPjAudioOutputEngine(parent_strm,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -