1
/* $Id: symb_mda_dev.cpp 3841 2011-10-24 09:28:13Z ming $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjmedia-audiodev/audiodev_imp.h>
21
#include <pjmedia-audiodev/errno.h>
22
#include <pjmedia/alaw_ulaw.h>
23
#include <pj/assert.h>
27
#include <pj/string.h>
29
#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
32
* This file provides sound implementation for Symbian Audio Streaming
33
* device. Application using this sound abstraction must link with:
34
* - mediaclientaudiostream.lib, and
35
* - mediaclientaudioinputstream.lib
37
#include <mda/common/audio.h>
38
#include <mdaaudiooutputstream.h>
39
#include <mdaaudioinputstream.h>
42
#define THIS_FILE "symb_mda_dev.c"
43
#define BITS_PER_SAMPLE 16
44
#define BYTES_PER_SAMPLE (BITS_PER_SAMPLE/8)
48
# define TRACE_(st) PJ_LOG(3, st)
57
pjmedia_aud_dev_factory base;
60
pjmedia_aud_dev_info dev_info;
63
/* Forward declaration of internal engine. */
64
class CPjAudioInputEngine;
65
class CPjAudioOutputEngine;
71
pjmedia_aud_stream base; /**< Base class. */
74
pj_pool_t *pool; /**< Memory pool. */
77
pjmedia_aud_param param; /**< Stream param. */
80
CPjAudioInputEngine *in_engine; /**< Record engine. */
81
CPjAudioOutputEngine *out_engine; /**< Playback engine. */
86
static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
87
static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
88
static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
89
static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
90
static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
92
pjmedia_aud_dev_info *info);
93
static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
95
pjmedia_aud_param *param);
96
static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
97
const pjmedia_aud_param *param,
98
pjmedia_aud_rec_cb rec_cb,
99
pjmedia_aud_play_cb play_cb,
101
pjmedia_aud_stream **p_aud_strm);
103
static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
104
pjmedia_aud_param *param);
105
static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
106
pjmedia_aud_dev_cap cap,
108
static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
109
pjmedia_aud_dev_cap cap,
111
static pj_status_t stream_start(pjmedia_aud_stream *strm);
112
static pj_status_t stream_stop(pjmedia_aud_stream *strm);
113
static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
117
static pjmedia_aud_dev_factory_op factory_op =
121
&factory_get_dev_count,
122
&factory_get_dev_info,
123
&factory_default_param,
124
&factory_create_stream,
128
static pjmedia_aud_stream_op stream_op =
140
* Convert clock rate to Symbian's TMdaAudioDataSettings capability.
142
static TInt get_clock_rate_cap(unsigned clock_rate)
144
switch (clock_rate) {
145
case 8000: return TMdaAudioDataSettings::ESampleRate8000Hz;
146
case 11025: return TMdaAudioDataSettings::ESampleRate11025Hz;
147
case 12000: return TMdaAudioDataSettings::ESampleRate12000Hz;
148
case 16000: return TMdaAudioDataSettings::ESampleRate16000Hz;
149
case 22050: return TMdaAudioDataSettings::ESampleRate22050Hz;
150
case 24000: return TMdaAudioDataSettings::ESampleRate24000Hz;
151
case 32000: return TMdaAudioDataSettings::ESampleRate32000Hz;
152
case 44100: return TMdaAudioDataSettings::ESampleRate44100Hz;
153
case 48000: return TMdaAudioDataSettings::ESampleRate48000Hz;
154
case 64000: return TMdaAudioDataSettings::ESampleRate64000Hz;
155
case 96000: return TMdaAudioDataSettings::ESampleRate96000Hz;
162
* Convert number of channels into Symbian's TMdaAudioDataSettings capability.
164
static TInt get_channel_cap(unsigned channel_count)
166
switch (channel_count) {
167
case 1: return TMdaAudioDataSettings::EChannelsMono;
168
case 2: return TMdaAudioDataSettings::EChannelsStereo;
175
* Utility: print sound device error
177
static void snd_perror(const char *title, TInt rc)
179
PJ_LOG(1,(THIS_FILE, "%s: error code %d", title, rc));
182
//////////////////////////////////////////////////////////////////////////////
186
* Implementation: Symbian Input Stream.
188
class CPjAudioInputEngine : public CBase, MMdaAudioInputStreamCallback
197
~CPjAudioInputEngine();
199
static CPjAudioInputEngine *NewL(struct mda_stream *parent_strm,
200
pjmedia_aud_rec_cb rec_cb,
203
static CPjAudioInputEngine *NewLC(struct mda_stream *parent_strm,
204
pjmedia_aud_rec_cb rec_cb,
207
pj_status_t StartRecord();
210
pj_status_t SetGain(TInt gain) {
212
iInputStream_->SetGain(gain);
215
return PJ_EINVALIDOP;
220
return iInputStream_->Gain();
222
return PJ_EINVALIDOP;
227
return iInputStream_->MaxGain();
229
return PJ_EINVALIDOP;
234
struct mda_stream *parentStrm_;
235
pjmedia_aud_rec_cb recCb_;
237
CMdaAudioInputStream *iInputStream_;
238
HBufC8 *iStreamBuffer_;
241
pj_uint32_t timeStamp_;
242
CActiveSchedulerWait startAsw_;
245
// to avoid calculating frame length repeatedly
248
// sometimes recorded size != requested framesize, so let's
249
// provide a buffer to make sure the rec callback returning
250
// framesize as requested.
251
TUint8 *frameRecBuf_;
252
TInt frameRecBufLen_;
254
CPjAudioInputEngine(struct mda_stream *parent_strm,
255
pjmedia_aud_rec_cb rec_cb,
261
virtual void MaiscOpenComplete(TInt aError);
262
virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer);
263
virtual void MaiscRecordComplete(TInt aError);
268
CPjAudioInputEngine::CPjAudioInputEngine(struct mda_stream *parent_strm,
269
pjmedia_aud_rec_cb rec_cb,
271
: state_(STATE_INACTIVE), parentStrm_(parent_strm),
272
recCb_(rec_cb), userData_(user_data),
273
iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0),
274
lastError_(KErrNone), timeStamp_(0),
275
frameLen_(parent_strm->param.samples_per_frame *
277
frameRecBuf_(NULL), frameRecBufLen_(0)
281
CPjAudioInputEngine::~CPjAudioInputEngine()
285
delete iStreamBuffer_;
286
iStreamBuffer_ = NULL;
288
delete [] frameRecBuf_;
293
void CPjAudioInputEngine::ConstructL()
295
iStreamBuffer_ = HBufC8::NewL(frameLen_);
296
CleanupStack::PushL(iStreamBuffer_);
298
frameRecBuf_ = new TUint8[frameLen_*2];
299
CleanupStack::PushL(frameRecBuf_);
302
CPjAudioInputEngine *CPjAudioInputEngine::NewLC(struct mda_stream *parent,
303
pjmedia_aud_rec_cb rec_cb,
306
CPjAudioInputEngine* self = new (ELeave) CPjAudioInputEngine(parent,
309
CleanupStack::PushL(self);
314
CPjAudioInputEngine *CPjAudioInputEngine::NewL(struct mda_stream *parent,
315
pjmedia_aud_rec_cb rec_cb,
318
CPjAudioInputEngine *self = NewLC(parent, rec_cb, user_data);
319
CleanupStack::Pop(self->frameRecBuf_);
320
CleanupStack::Pop(self->iStreamBuffer_);
321
CleanupStack::Pop(self);
326
pj_status_t CPjAudioInputEngine::StartRecord()
329
// Ignore command if recording is in progress.
330
if (state_ == STATE_ACTIVE)
333
// According to Nokia's AudioStream example, some 2nd Edition, FP2 devices
334
// (such as Nokia 6630) require the stream to be reconstructed each time
335
// before calling Open() - otherwise the callback never gets called.
336
// For uniform behavior, lets just delete/re-create the stream for all
339
// Destroy existing stream.
340
if (iInputStream_) delete iInputStream_;
341
iInputStream_ = NULL;
343
// Create the stream.
344
TRAPD(err, iInputStream_ = CMdaAudioInputStream::NewL(*this));
346
return PJ_RETURN_OS_ERROR(err);
348
// Initialize settings.
349
TMdaAudioDataSettings iStreamSettings;
350
iStreamSettings.iChannels =
351
get_channel_cap(parentStrm_->param.channel_count);
352
iStreamSettings.iSampleRate =
353
get_clock_rate_cap(parentStrm_->param.clock_rate);
355
pj_assert(iStreamSettings.iChannels != 0 &&
356
iStreamSettings.iSampleRate != 0);
358
PJ_LOG(4,(THIS_FILE, "Opening sound device for capture, "
359
"clock rate=%d, channel count=%d..",
360
parentStrm_->param.clock_rate,
361
parentStrm_->param.channel_count));
364
lastError_ = KRequestPending;
365
iInputStream_->Open(&iStreamSettings);
367
#if defined(PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START) && \
368
PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START != 0
375
PJ_LOG(4,(THIS_FILE, "Sound capture started."));
380
void CPjAudioInputEngine::Stop()
382
// If capture is in progress, stop it.
383
if (iInputStream_ && state_ == STATE_ACTIVE) {
384
lastError_ = KRequestPending;
385
iInputStream_->Stop();
387
// Wait until it's actually stopped
388
while (lastError_ == KRequestPending)
389
pj_symbianos_poll(-1, 100);
393
delete iInputStream_;
394
iInputStream_ = NULL;
397
if (startAsw_.IsStarted()) {
398
startAsw_.AsyncStop();
401
state_ = STATE_INACTIVE;
405
TPtr8 & CPjAudioInputEngine::GetFrame()
407
//iStreamBuffer_->Des().FillZ(frameLen_);
408
iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), frameLen_, frameLen_);
412
void CPjAudioInputEngine::MaiscOpenComplete(TInt aError)
414
if (startAsw_.IsStarted()) {
415
startAsw_.AsyncStop();
419
if (aError != KErrNone) {
420
snd_perror("Error in MaiscOpenComplete()", aError);
424
/* Apply input volume setting if specified */
425
if (parentStrm_->param.flags &
426
PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING)
428
stream_set_cap(&parentStrm_->base,
429
PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
430
&parentStrm_->param.input_vol);
433
// set stream priority to normal and time sensitive
434
iInputStream_->SetPriority(EPriorityNormal,
435
EMdaPriorityPreferenceTime);
437
// Read the first frame.
438
TPtr8 & frm = GetFrame();
439
TRAPD(err2, iInputStream_->ReadL(frm));
441
PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
446
// input stream opened succesfully, set status to Active
447
state_ = STATE_ACTIVE;
450
void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
451
const TDesC8 &aBuffer)
454
if (aError != KErrNone) {
455
snd_perror("Error in MaiscBufferCopied()", aError);
459
if (frameRecBufLen_ || aBuffer.Length() < frameLen_) {
460
pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Length());
461
frameRecBufLen_ += aBuffer.Length();
464
if (frameRecBufLen_) {
465
while (frameRecBufLen_ >= frameLen_) {
468
f.type = PJMEDIA_FRAME_TYPE_AUDIO;
469
f.buf = frameRecBuf_;
471
f.timestamp.u32.lo = timeStamp_;
474
// Call the callback.
475
recCb_(userData_, &f);
476
// Increment timestamp.
477
timeStamp_ += parentStrm_->param.samples_per_frame;
479
frameRecBufLen_ -= frameLen_;
480
pj_memmove(frameRecBuf_, frameRecBuf_+frameLen_, frameRecBufLen_);
485
f.type = PJMEDIA_FRAME_TYPE_AUDIO;
486
f.buf = (void*)aBuffer.Ptr();
487
f.size = aBuffer.Length();
488
f.timestamp.u32.lo = timeStamp_;
491
// Call the callback.
492
recCb_(userData_, &f);
494
// Increment timestamp.
495
timeStamp_ += parentStrm_->param.samples_per_frame;
499
TPtr8 & frm = GetFrame();
500
TRAPD(err2, iInputStream_->ReadL(frm));
502
PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
507
void CPjAudioInputEngine::MaiscRecordComplete(TInt aError)
510
state_ = STATE_INACTIVE;
511
if (aError != KErrNone && aError != KErrCancel) {
512
snd_perror("Error in MaiscRecordComplete()", aError);
518
//////////////////////////////////////////////////////////////////////////////
522
* Implementation: Symbian Output Stream.
525
class CPjAudioOutputEngine : public CBase, MMdaAudioOutputStreamCallback
534
~CPjAudioOutputEngine();
536
static CPjAudioOutputEngine *NewL(struct mda_stream *parent_strm,
537
pjmedia_aud_play_cb play_cb,
540
static CPjAudioOutputEngine *NewLC(struct mda_stream *parent_strm,
541
pjmedia_aud_play_cb rec_cb,
544
pj_status_t StartPlay();
547
pj_status_t SetVolume(TInt vol) {
548
if (iOutputStream_) {
549
iOutputStream_->SetVolume(vol);
552
return PJ_EINVALIDOP;
556
if (iOutputStream_) {
557
return iOutputStream_->Volume();
559
return PJ_EINVALIDOP;
562
TInt GetMaxVolume() {
563
if (iOutputStream_) {
564
return iOutputStream_->MaxVolume();
566
return PJ_EINVALIDOP;
571
struct mda_stream *parentStrm_;
572
pjmedia_aud_play_cb playCb_;
574
CMdaAudioOutputStream *iOutputStream_;
576
unsigned frameBufSize_;
580
CActiveSchedulerWait startAsw_;
582
CPjAudioOutputEngine(struct mda_stream *parent_strm,
583
pjmedia_aud_play_cb play_cb,
587
virtual void MaoscOpenComplete(TInt aError);
588
virtual void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer);
589
virtual void MaoscPlayComplete(TInt aError);
593
CPjAudioOutputEngine::CPjAudioOutputEngine(struct mda_stream *parent_strm,
594
pjmedia_aud_play_cb play_cb,
596
: state_(STATE_INACTIVE), parentStrm_(parent_strm), playCb_(play_cb),
597
userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL),
598
lastError_(KErrNone), timestamp_(0)
603
void CPjAudioOutputEngine::ConstructL()
605
frameBufSize_ = parentStrm_->param.samples_per_frame *
607
frameBuf_ = new TUint8[frameBufSize_];
610
CPjAudioOutputEngine::~CPjAudioOutputEngine()
616
CPjAudioOutputEngine *
617
CPjAudioOutputEngine::NewLC(struct mda_stream *parent_strm,
618
pjmedia_aud_play_cb play_cb,
621
CPjAudioOutputEngine* self = new (ELeave) CPjAudioOutputEngine(parent_strm,
624
CleanupStack::PushL(self);
629
CPjAudioOutputEngine *
630
CPjAudioOutputEngine::NewL(struct mda_stream *parent_strm,
631
pjmedia_aud_play_cb play_cb,
634
CPjAudioOutputEngine *self = NewLC(parent_strm, play_cb, user_data);
635
CleanupStack::Pop(self);
639
pj_status_t CPjAudioOutputEngine::StartPlay()
641
// Ignore command if playing is in progress.
642
if (state_ == STATE_ACTIVE)
645
// Destroy existing stream.
646
if (iOutputStream_) delete iOutputStream_;
647
iOutputStream_ = NULL;
650
TRAPD(err, iOutputStream_ = CMdaAudioOutputStream::NewL(*this));
652
return PJ_RETURN_OS_ERROR(err);
654
// Initialize settings.
655
TMdaAudioDataSettings iStreamSettings;
656
iStreamSettings.iChannels =
657
get_channel_cap(parentStrm_->param.channel_count);
658
iStreamSettings.iSampleRate =
659
get_clock_rate_cap(parentStrm_->param.clock_rate);
661
pj_assert(iStreamSettings.iChannels != 0 &&
662
iStreamSettings.iSampleRate != 0);
664
PJ_LOG(4,(THIS_FILE, "Opening sound device for playback, "
665
"clock rate=%d, channel count=%d..",
666
parentStrm_->param.clock_rate,
667
parentStrm_->param.channel_count));
670
lastError_ = KRequestPending;
671
iOutputStream_->Open(&iStreamSettings);
673
#if defined(PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START) && \
674
PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START != 0
681
PJ_LOG(4,(THIS_FILE, "Sound playback started"));
686
void CPjAudioOutputEngine::Stop()
688
// Stop stream if it's playing
689
if (iOutputStream_ && state_ != STATE_INACTIVE) {
690
lastError_ = KRequestPending;
691
iOutputStream_->Stop();
693
// Wait until it's actually stopped
694
while (lastError_ == KRequestPending)
695
pj_symbianos_poll(-1, 100);
698
if (iOutputStream_) {
699
delete iOutputStream_;
700
iOutputStream_ = NULL;
703
if (startAsw_.IsStarted()) {
704
startAsw_.AsyncStop();
707
state_ = STATE_INACTIVE;
710
void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError)
712
if (startAsw_.IsStarted()) {
713
startAsw_.AsyncStop();
718
if (aError==KErrNone) {
719
// set stream properties, 16bit 8KHz mono
720
TMdaAudioDataSettings iSettings;
721
iSettings.iChannels =
722
get_channel_cap(parentStrm_->param.channel_count);
723
iSettings.iSampleRate =
724
get_clock_rate_cap(parentStrm_->param.clock_rate);
726
iOutputStream_->SetAudioPropertiesL(iSettings.iSampleRate,
727
iSettings.iChannels);
729
/* Apply output volume setting if specified */
730
if (parentStrm_->param.flags &
731
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING)
733
stream_set_cap(&parentStrm_->base,
734
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
735
&parentStrm_->param.output_vol);
737
// set volume to 1/2th of stream max volume
738
iOutputStream_->SetVolume(iOutputStream_->MaxVolume()/2);
741
// set stream priority to normal and time sensitive
742
iOutputStream_->SetPriority(EPriorityNormal,
743
EMdaPriorityPreferenceTime);
745
// Call callback to retrieve frame from upstream.
749
f.type = PJMEDIA_FRAME_TYPE_AUDIO;
751
f.size = frameBufSize_;
752
f.timestamp.u32.lo = timestamp_;
755
status = playCb_(this->userData_, &f);
756
if (status != PJ_SUCCESS) {
761
if (f.type != PJMEDIA_FRAME_TYPE_AUDIO)
762
pj_bzero(frameBuf_, frameBufSize_);
764
// Increment timestamp.
765
timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
767
// issue WriteL() to write the first audio data block,
768
// subsequent calls to WriteL() will be issued in
769
// MMdaAudioOutputStreamCallback::MaoscBufferCopied()
770
// until whole data buffer is written.
771
frame_.Set(frameBuf_, frameBufSize_);
772
iOutputStream_->WriteL(frame_);
774
// output stream opened succesfully, set status to Active
775
state_ = STATE_ACTIVE;
777
snd_perror("Error in MaoscOpenComplete()", aError);
781
void CPjAudioOutputEngine::MaoscBufferCopied(TInt aError,
782
const TDesC8& aBuffer)
784
PJ_UNUSED_ARG(aBuffer);
786
if (aError==KErrNone) {
787
// Buffer successfully written, feed another one.
789
// Call callback to retrieve frame from upstream.
793
f.type = PJMEDIA_FRAME_TYPE_AUDIO;
795
f.size = frameBufSize_;
796
f.timestamp.u32.lo = timestamp_;
799
status = playCb_(this->userData_, &f);
800
if (status != PJ_SUCCESS) {
805
if (f.type != PJMEDIA_FRAME_TYPE_AUDIO)
806
pj_bzero(frameBuf_, frameBufSize_);
808
// Increment timestamp.
809
timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
811
// Write to playback stream.
812
frame_.Set(frameBuf_, frameBufSize_);
813
iOutputStream_->WriteL(frame_);
815
} else if (aError==KErrAbort) {
816
// playing was aborted, due to call to CMdaAudioOutputStream::Stop()
817
state_ = STATE_INACTIVE;
819
// error writing data to output
821
state_ = STATE_INACTIVE;
822
snd_perror("Error in MaoscBufferCopied()", aError);
826
void CPjAudioOutputEngine::MaoscPlayComplete(TInt aError)
829
state_ = STATE_INACTIVE;
830
if (aError != KErrNone && aError != KErrCancel) {
831
snd_perror("Error in MaoscPlayComplete()", aError);
835
/****************************************************************************
840
* C compatible declaration of MDA factory.
843
PJ_DECL(pjmedia_aud_dev_factory*) pjmedia_symb_mda_factory(pj_pool_factory *pf);
847
* Init Symbian audio driver.
849
pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf)
851
struct mda_factory *f;
854
pool = pj_pool_create(pf, "symb_aud", 1000, 1000, NULL);
855
f = PJ_POOL_ZALLOC_T(pool, struct mda_factory);
858
f->base.op = &factory_op;
863
/* API: init factory */
864
static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
866
struct mda_factory *af = (struct mda_factory*)f;
868
pj_ansi_strcpy(af->dev_info.name, "Symbian Audio");
869
af->dev_info.default_samples_per_sec = 8000;
870
af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
871
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
872
af->dev_info.input_count = 1;
873
af->dev_info.output_count = 1;
875
PJ_LOG(4, (THIS_FILE, "Symb Mda initialized"));
880
/* API: destroy factory */
881
static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
883
struct mda_factory *af = (struct mda_factory*)f;
884
pj_pool_t *pool = af->pool;
887
pj_pool_release(pool);
889
PJ_LOG(4, (THIS_FILE, "Symbian Mda destroyed"));
894
/* API: refresh the device list */
895
static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
901
/* API: get number of devices */
902
static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
908
/* API: get device info */
909
static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
911
pjmedia_aud_dev_info *info)
913
struct mda_factory *af = (struct mda_factory*)f;
915
PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
917
pj_memcpy(info, &af->dev_info, sizeof(*info));
922
/* API: create default device parameter */
923
static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
925
pjmedia_aud_param *param)
927
struct mda_factory *af = (struct mda_factory*)f;
929
PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
931
pj_bzero(param, sizeof(*param));
932
param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
933
param->rec_id = index;
934
param->play_id = index;
935
param->clock_rate = af->dev_info.default_samples_per_sec;
936
param->channel_count = 1;
937
param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
938
param->bits_per_sample = BITS_PER_SAMPLE;
939
// Don't set the flags without specifying the flags value.
940
//param->flags = af->dev_info.caps;
946
/* API: create stream */
947
static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
948
const pjmedia_aud_param *param,
949
pjmedia_aud_rec_cb rec_cb,
950
pjmedia_aud_play_cb play_cb,
952
pjmedia_aud_stream **p_aud_strm)
954
struct mda_factory *mf = (struct mda_factory*)f;
956
struct mda_stream *strm;
958
/* Can only support 16bits per sample raw PCM format. */
959
PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
960
PJ_ASSERT_RETURN((param->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT)==0 ||
961
param->ext_fmt.id == PJMEDIA_FORMAT_L16,
964
/* It seems that MDA recorder only supports for mono channel. */
965
PJ_ASSERT_RETURN(param->channel_count == 1, PJ_EINVAL);
967
/* Create and Initialize stream descriptor */
968
pool = pj_pool_create(mf->pf, "symb_aud_dev", 1000, 1000, NULL);
969
PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
971
strm = PJ_POOL_ZALLOC_T(pool, struct mda_stream);
973
strm->param = *param;
975
// Create the output stream.
976
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
977
TRAPD(err, strm->out_engine = CPjAudioOutputEngine::NewL(strm, play_cb,
979
if (err != KErrNone) {
980
pj_pool_release(pool);
981
return PJ_RETURN_OS_ERROR(err);
985
// Create the input stream.
986
if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
987
TRAPD(err, strm->in_engine = CPjAudioInputEngine::NewL(strm, rec_cb,
989
if (err != KErrNone) {
990
strm->in_engine = NULL;
991
delete strm->out_engine;
992
strm->out_engine = NULL;
993
pj_pool_release(pool);
994
return PJ_RETURN_OS_ERROR(err);
999
strm->base.op = &stream_op;
1000
*p_aud_strm = &strm->base;
1005
/* API: Get stream info. */
1006
static pj_status_t stream_get_param(pjmedia_aud_stream *s,
1007
pjmedia_aud_param *pi)
1009
struct mda_stream *strm = (struct mda_stream*)s;
1011
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1013
pj_memcpy(pi, &strm->param, sizeof(*pi));
1015
/* Update the output volume setting */
1016
if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1017
&pi->output_vol) == PJ_SUCCESS)
1019
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1022
/* Update the input volume setting */
1023
if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
1024
&pi->input_vol) == PJ_SUCCESS)
1026
pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING;
1032
/* API: get capability */
1033
static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1034
pjmedia_aud_dev_cap cap,
1037
struct mda_stream *strm = (struct mda_stream*)s;
1038
pj_status_t status = PJ_ENOTSUP;
1040
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1043
case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1044
if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1045
PJ_ASSERT_RETURN(strm->in_engine, PJ_EINVAL);
1047
TInt max_gain = strm->in_engine->GetMaxGain();
1048
TInt gain = strm->in_engine->GetGain();
1050
if (max_gain > 0 && gain >= 0) {
1051
*(unsigned*)pval = gain * 100 / max_gain;
1052
status = PJ_SUCCESS;
1054
status = PJMEDIA_EAUD_NOTREADY;
1058
case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1059
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1060
PJ_ASSERT_RETURN(strm->out_engine, PJ_EINVAL);
1062
TInt max_vol = strm->out_engine->GetMaxVolume();
1063
TInt vol = strm->out_engine->GetVolume();
1065
if (max_vol > 0 && vol >= 0) {
1066
*(unsigned*)pval = vol * 100 / max_vol;
1067
status = PJ_SUCCESS;
1069
status = PJMEDIA_EAUD_NOTREADY;
1080
/* API: set capability */
1081
static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1082
pjmedia_aud_dev_cap cap,
1085
struct mda_stream *strm = (struct mda_stream*)s;
1086
pj_status_t status = PJ_ENOTSUP;
1088
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1091
case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1092
if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1093
PJ_ASSERT_RETURN(strm->in_engine, PJ_EINVAL);
1095
TInt max_gain = strm->in_engine->GetMaxGain();
1099
gain = *(unsigned*)pval * max_gain / 100;
1100
status = strm->in_engine->SetGain(gain);
1102
status = PJMEDIA_EAUD_NOTREADY;
1106
case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1107
if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
1108
PJ_ASSERT_RETURN(strm->out_engine, PJ_EINVAL);
1110
TInt max_vol = strm->out_engine->GetMaxVolume();
1114
vol = *(unsigned*)pval * max_vol / 100;
1115
status = strm->out_engine->SetVolume(vol);
1117
status = PJMEDIA_EAUD_NOTREADY;
1128
/* API: Start stream. */
1129
static pj_status_t stream_start(pjmedia_aud_stream *strm)
1131
struct mda_stream *stream = (struct mda_stream*)strm;
1133
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1135
if (stream->out_engine) {
1137
status = stream->out_engine->StartPlay();
1138
if (status != PJ_SUCCESS)
1142
if (stream->in_engine) {
1144
status = stream->in_engine->StartRecord();
1145
if (status != PJ_SUCCESS)
1152
/* API: Stop stream. */
1153
static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1155
struct mda_stream *stream = (struct mda_stream*)strm;
1157
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1159
if (stream->in_engine) {
1160
stream->in_engine->Stop();
1163
if (stream->out_engine) {
1164
stream->out_engine->Stop();
1171
/* API: Destroy stream. */
1172
static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1174
struct mda_stream *stream = (struct mda_stream*)strm;
1176
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1180
delete stream->in_engine;
1181
stream->in_engine = NULL;
1183
delete stream->out_engine;
1184
stream->out_engine = NULL;
1187
pool = stream->pool;
1189
stream->pool = NULL;
1190
pj_pool_release(pool);
1196
#endif /* PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA */