blob: f9437e55836d89c9a2e3e746e195169dd7cf6f49 [file] [log] [blame]
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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.
10 *
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.
15 *
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
19 */
20#include <pjmedia-audiodev/audiodev_imp.h>
Nanang Izzuddinda37ea32009-03-06 16:24:43 +000021#include <pjmedia-audiodev/errno.h>
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +000022#include <pjmedia/alaw_ulaw.h>
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +000023#include <pj/assert.h>
24#include <pj/log.h>
25#include <pj/math.h>
26#include <pj/os.h>
27#include <pj/string.h>
28
Nanang Izzuddinda37ea32009-03-06 16:24:43 +000029#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
30
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +000031/*
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
36 */
37#include <mda/common/audio.h>
38#include <mdaaudiooutputstream.h>
39#include <mdaaudioinputstream.h>
40
41
42#define THIS_FILE "symb_mda_dev.c"
43#define BITS_PER_SAMPLE 16
44#define BYTES_PER_SAMPLE (BITS_PER_SAMPLE/8)
45
46
47#if 1
48# define TRACE_(st) PJ_LOG(3, st)
49#else
50# define TRACE_(st)
51#endif
52
53
54/* MDA factory */
55struct mda_factory
56{
57 pjmedia_aud_dev_factory base;
58 pj_pool_t *pool;
59 pj_pool_factory *pf;
60 pjmedia_aud_dev_info dev_info;
61};
62
63/* Forward declaration of internal engine. */
64class CPjAudioInputEngine;
65class CPjAudioOutputEngine;
66
67/* MDA stream. */
68struct mda_stream
69{
70 // Base
71 pjmedia_aud_stream base; /**< Base class. */
72
73 // Pool
74 pj_pool_t *pool; /**< Memory pool. */
75
76 // Common settings.
Benny Prijono10454dc2009-02-21 14:21:59 +000077 pjmedia_aud_param param; /**< Stream param. */
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +000078
79 // Audio engine
80 CPjAudioInputEngine *in_engine; /**< Record engine. */
81 CPjAudioOutputEngine *out_engine; /**< Playback engine. */
82};
83
84
85/* Prototypes */
86static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
87static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
88static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
89static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
90 unsigned index,
91 pjmedia_aud_dev_info *info);
92static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
93 unsigned index,
Benny Prijono10454dc2009-02-21 14:21:59 +000094 pjmedia_aud_param *param);
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +000095static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
Benny Prijono10454dc2009-02-21 14:21:59 +000096 const pjmedia_aud_param *param,
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +000097 pjmedia_aud_rec_cb rec_cb,
98 pjmedia_aud_play_cb play_cb,
99 void *user_data,
100 pjmedia_aud_stream **p_aud_strm);
101
102static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
Benny Prijono10454dc2009-02-21 14:21:59 +0000103 pjmedia_aud_param *param);
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +0000104static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
105 pjmedia_aud_dev_cap cap,
106 void *value);
107static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
108 pjmedia_aud_dev_cap cap,
109 const void *value);
110static pj_status_t stream_start(pjmedia_aud_stream *strm);
111static pj_status_t stream_stop(pjmedia_aud_stream *strm);
112static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
113
114
115/* Operations */
116static pjmedia_aud_dev_factory_op factory_op =
117{
118 &factory_init,
119 &factory_destroy,
120 &factory_get_dev_count,
121 &factory_get_dev_info,
122 &factory_default_param,
123 &factory_create_stream
124};
125
126static pjmedia_aud_stream_op stream_op =
127{
128 &stream_get_param,
129 &stream_get_cap,
130 &stream_set_cap,
131 &stream_start,
132 &stream_stop,
133 &stream_destroy
134};
135
136
137/*
138 * Convert clock rate to Symbian's TMdaAudioDataSettings capability.
139 */
140static TInt get_clock_rate_cap(unsigned clock_rate)
141{
142 switch (clock_rate) {
143 case 8000: return TMdaAudioDataSettings::ESampleRate8000Hz;
144 case 11025: return TMdaAudioDataSettings::ESampleRate11025Hz;
145 case 12000: return TMdaAudioDataSettings::ESampleRate12000Hz;
146 case 16000: return TMdaAudioDataSettings::ESampleRate16000Hz;
147 case 22050: return TMdaAudioDataSettings::ESampleRate22050Hz;
148 case 24000: return TMdaAudioDataSettings::ESampleRate24000Hz;
149 case 32000: return TMdaAudioDataSettings::ESampleRate32000Hz;
150 case 44100: return TMdaAudioDataSettings::ESampleRate44100Hz;
151 case 48000: return TMdaAudioDataSettings::ESampleRate48000Hz;
152 case 64000: return TMdaAudioDataSettings::ESampleRate64000Hz;
153 case 96000: return TMdaAudioDataSettings::ESampleRate96000Hz;
154 default:
155 return 0;
156 }
157}
158
159/*
160 * Convert number of channels into Symbian's TMdaAudioDataSettings capability.
161 */
162static TInt get_channel_cap(unsigned channel_count)
163{
164 switch (channel_count) {
165 case 1: return TMdaAudioDataSettings::EChannelsMono;
166 case 2: return TMdaAudioDataSettings::EChannelsStereo;
167 default:
168 return 0;
169 }
170}
171
172/*
173 * Utility: print sound device error
174 */
175static void snd_perror(const char *title, TInt rc)
176{
177 PJ_LOG(1,(THIS_FILE, "%s: error code %d", title, rc));
178}
179
180//////////////////////////////////////////////////////////////////////////////
181//
182
183/*
184 * Implementation: Symbian Input Stream.
185 */
186class CPjAudioInputEngine : public CBase, MMdaAudioInputStreamCallback
187{
188public:
189 enum State
190 {
191 STATE_INACTIVE,
192 STATE_ACTIVE,
193 };
194
195 ~CPjAudioInputEngine();
196
197 static CPjAudioInputEngine *NewL(struct mda_stream *parent_strm,
198 pjmedia_aud_rec_cb rec_cb,
199 void *user_data);
200
201 static CPjAudioInputEngine *NewLC(struct mda_stream *parent_strm,
202 pjmedia_aud_rec_cb rec_cb,
203 void *user_data);
204
205 pj_status_t StartRecord();
206 void Stop();
207
208 pj_status_t SetGain(TInt gain) {
209 if (iInputStream_) {
210 iInputStream_->SetGain(gain);
211 return PJ_SUCCESS;
212 } else
213 return PJ_EINVALIDOP;
214 }
215
216 TInt GetGain() {
217 if (iInputStream_) {
218 return iInputStream_->Gain();
219 } else
220 return PJ_EINVALIDOP;
221 }
222
223 TInt GetMaxGain() {
224 if (iInputStream_) {
225 return iInputStream_->MaxGain();
226 } else
227 return PJ_EINVALIDOP;
228 }
229
230private:
231 State state_;
232 struct mda_stream *parentStrm_;
233 pjmedia_aud_rec_cb recCb_;
234 void *userData_;
235 CMdaAudioInputStream *iInputStream_;
236 HBufC8 *iStreamBuffer_;
237 TPtr8 iFramePtr_;
238 TInt lastError_;
239 pj_uint32_t timeStamp_;
240
241 // cache variable
242 // to avoid calculating frame length repeatedly
243 TInt frameLen_;
244
245 // sometimes recorded size != requested framesize, so let's
246 // provide a buffer to make sure the rec callback returning
247 // framesize as requested.
248 TUint8 *frameRecBuf_;
249 TInt frameRecBufLen_;
250
251 CPjAudioInputEngine(struct mda_stream *parent_strm,
252 pjmedia_aud_rec_cb rec_cb,
253 void *user_data);
254 void ConstructL();
255 TPtr8 & GetFrame();
256
257public:
258 virtual void MaiscOpenComplete(TInt aError);
259 virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer);
260 virtual void MaiscRecordComplete(TInt aError);
261
262};
263
264
265CPjAudioInputEngine::CPjAudioInputEngine(struct mda_stream *parent_strm,
266 pjmedia_aud_rec_cb rec_cb,
267 void *user_data)
268 : state_(STATE_INACTIVE), parentStrm_(parent_strm),
269 recCb_(rec_cb), userData_(user_data),
270 iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0),
271 lastError_(KErrNone), timeStamp_(0),
272 frameLen_(parent_strm->param.samples_per_frame *
273 parent_strm->param.channel_count *
274 BYTES_PER_SAMPLE),
275 frameRecBuf_(NULL), frameRecBufLen_(0)
276{
277}
278
279CPjAudioInputEngine::~CPjAudioInputEngine()
280{
281 Stop();
282
283 delete iStreamBuffer_;
284 iStreamBuffer_ = NULL;
285
286 delete [] frameRecBuf_;
287 frameRecBuf_ = NULL;
288 frameRecBufLen_ = 0;
289}
290
291void CPjAudioInputEngine::ConstructL()
292{
293 iStreamBuffer_ = HBufC8::NewL(frameLen_);
294 CleanupStack::PushL(iStreamBuffer_);
295
296 frameRecBuf_ = new TUint8[frameLen_*2];
297 CleanupStack::PushL(frameRecBuf_);
298}
299
300CPjAudioInputEngine *CPjAudioInputEngine::NewLC(struct mda_stream *parent,
301 pjmedia_aud_rec_cb rec_cb,
302 void *user_data)
303{
304 CPjAudioInputEngine* self = new (ELeave) CPjAudioInputEngine(parent,
305 rec_cb,
306 user_data);
307 CleanupStack::PushL(self);
308 self->ConstructL();
309 return self;
310}
311
312CPjAudioInputEngine *CPjAudioInputEngine::NewL(struct mda_stream *parent,
313 pjmedia_aud_rec_cb rec_cb,
314 void *user_data)
315{
316 CPjAudioInputEngine *self = NewLC(parent, rec_cb, user_data);
317 CleanupStack::Pop(self->frameRecBuf_);
318 CleanupStack::Pop(self->iStreamBuffer_);
319 CleanupStack::Pop(self);
320 return self;
321}
322
323
324pj_status_t CPjAudioInputEngine::StartRecord()
325{
326
327 // Ignore command if recording is in progress.
328 if (state_ == STATE_ACTIVE)
329 return PJ_SUCCESS;
330
331 // According to Nokia's AudioStream example, some 2nd Edition, FP2 devices
332 // (such as Nokia 6630) require the stream to be reconstructed each time
333 // before calling Open() - otherwise the callback never gets called.
334 // For uniform behavior, lets just delete/re-create the stream for all
335 // devices.
336
337 // Destroy existing stream.
338 if (iInputStream_) delete iInputStream_;
339 iInputStream_ = NULL;
340
341 // Create the stream.
342 TRAPD(err, iInputStream_ = CMdaAudioInputStream::NewL(*this));
343 if (err != KErrNone)
344 return PJ_RETURN_OS_ERROR(err);
345
346 // Initialize settings.
347 TMdaAudioDataSettings iStreamSettings;
348 iStreamSettings.iChannels =
349 get_channel_cap(parentStrm_->param.channel_count);
350 iStreamSettings.iSampleRate =
351 get_clock_rate_cap(parentStrm_->param.clock_rate);
352
353 pj_assert(iStreamSettings.iChannels != 0 &&
354 iStreamSettings.iSampleRate != 0);
355
356 PJ_LOG(4,(THIS_FILE, "Opening sound device for capture, "
357 "clock rate=%d, channel count=%d..",
358 parentStrm_->param.clock_rate,
359 parentStrm_->param.channel_count));
360
361 // Open stream.
362 lastError_ = KRequestPending;
363 iInputStream_->Open(&iStreamSettings);
364
365 // Success
366 PJ_LOG(4,(THIS_FILE, "Sound capture started."));
367 return PJ_SUCCESS;
368}
369
370
371void CPjAudioInputEngine::Stop()
372{
373 // If capture is in progress, stop it.
374 if (iInputStream_ && state_ == STATE_ACTIVE) {
375 lastError_ = KRequestPending;
376 iInputStream_->Stop();
377
378 // Wait until it's actually stopped
379 while (lastError_ == KRequestPending)
380 pj_symbianos_poll(-1, 100);
381 }
382
383 if (iInputStream_) {
384 delete iInputStream_;
385 iInputStream_ = NULL;
386 }
387
388 state_ = STATE_INACTIVE;
389}
390
391
392TPtr8 & CPjAudioInputEngine::GetFrame()
393{
394 //iStreamBuffer_->Des().FillZ(frameLen_);
395 iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), frameLen_, frameLen_);
396 return iFramePtr_;
397}
398
399void CPjAudioInputEngine::MaiscOpenComplete(TInt aError)
400{
401 lastError_ = aError;
402 if (aError != KErrNone) {
403 snd_perror("Error in MaiscOpenComplete()", aError);
404 return;
405 }
406
407 // set stream priority to normal and time sensitive
408 iInputStream_->SetPriority(EPriorityNormal,
409 EMdaPriorityPreferenceTime);
410
411 // Read the first frame.
412 TPtr8 & frm = GetFrame();
413 TRAPD(err2, iInputStream_->ReadL(frm));
414 if (err2) {
415 PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
416 }
417}
418
419void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
420 const TDesC8 &aBuffer)
421{
422 lastError_ = aError;
423 if (aError != KErrNone) {
424 snd_perror("Error in MaiscBufferCopied()", aError);
425 return;
426 }
427
428 if (frameRecBufLen_ || aBuffer.Size() < frameLen_) {
429 pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Size());
430 frameRecBufLen_ += aBuffer.Size();
431 }
432
433 if (frameRecBufLen_) {
434 while (frameRecBufLen_ >= frameLen_) {
435 pjmedia_frame f;
436
437 f.type = PJMEDIA_FRAME_TYPE_AUDIO;
438 f.buf = frameRecBuf_;
439 f.size = frameLen_;
440 f.timestamp.u32.lo = timeStamp_;
441 f.bit_info = 0;
442
443 // Call the callback.
444 recCb_(userData_, &f);
445 // Increment timestamp.
446 timeStamp_ += parentStrm_->param.samples_per_frame;
447
448 frameRecBufLen_ -= frameLen_;
449 pj_memmove(frameRecBuf_, frameRecBuf_+frameLen_, frameRecBufLen_);
450 }
451 } else {
452 pjmedia_frame f;
453
454 f.type = PJMEDIA_FRAME_TYPE_AUDIO;
455 f.buf = (void*)aBuffer.Ptr();
456 f.size = aBuffer.Size();
457 f.timestamp.u32.lo = timeStamp_;
458 f.bit_info = 0;
459
460 // Call the callback.
461 recCb_(userData_, &f);
462
463 // Increment timestamp.
464 timeStamp_ += parentStrm_->param.samples_per_frame;
465 }
466
467 // Record next frame
468 TPtr8 & frm = GetFrame();
469 TRAPD(err2, iInputStream_->ReadL(frm));
470 if (err2) {
471 PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
472 }
473}
474
475
476void CPjAudioInputEngine::MaiscRecordComplete(TInt aError)
477{
478 lastError_ = aError;
479 state_ = STATE_INACTIVE;
480 if (aError != KErrNone) {
481 snd_perror("Error in MaiscRecordComplete()", aError);
482 }
483}
484
485
486
487//////////////////////////////////////////////////////////////////////////////
488//
489
490/*
491 * Implementation: Symbian Output Stream.
492 */
493
494class CPjAudioOutputEngine : public CBase, MMdaAudioOutputStreamCallback
495{
496public:
497 enum State
498 {
499 STATE_INACTIVE,
500 STATE_ACTIVE,
501 };
502
503 ~CPjAudioOutputEngine();
504
505 static CPjAudioOutputEngine *NewL(struct mda_stream *parent_strm,
506 pjmedia_aud_play_cb play_cb,
507 void *user_data);
508
509 static CPjAudioOutputEngine *NewLC(struct mda_stream *parent_strm,
510 pjmedia_aud_play_cb rec_cb,
511 void *user_data);
512
513 pj_status_t StartPlay();
514 void Stop();
515
516 pj_status_t SetVolume(TInt vol) {
517 if (iOutputStream_) {
518 iOutputStream_->SetVolume(vol);
519 return PJ_SUCCESS;
520 } else
521 return PJ_EINVALIDOP;
522 }
523
524 TInt GetVolume() {
525 if (iOutputStream_) {
526 return iOutputStream_->Volume();
527 } else
528 return PJ_EINVALIDOP;
529 }
530
531 TInt GetMaxVolume() {
532 if (iOutputStream_) {
533 return iOutputStream_->MaxVolume();
534 } else
535 return PJ_EINVALIDOP;
536 }
537
538private:
539 State state_;
540 struct mda_stream *parentStrm_;
541 pjmedia_aud_play_cb playCb_;
542 void *userData_;
543 CMdaAudioOutputStream *iOutputStream_;
544 TUint8 *frameBuf_;
545 unsigned frameBufSize_;
546 TPtrC8 frame_;
547 TInt lastError_;
548 unsigned timestamp_;
549
550 CPjAudioOutputEngine(struct mda_stream *parent_strm,
551 pjmedia_aud_play_cb play_cb,
552 void *user_data);
553 void ConstructL();
554
555 virtual void MaoscOpenComplete(TInt aError);
556 virtual void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer);
557 virtual void MaoscPlayComplete(TInt aError);
558};
559
560
561CPjAudioOutputEngine::CPjAudioOutputEngine(struct mda_stream *parent_strm,
562 pjmedia_aud_play_cb play_cb,
563 void *user_data)
564: state_(STATE_INACTIVE), parentStrm_(parent_strm), playCb_(play_cb),
565 userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL),
566 lastError_(KErrNone), timestamp_(0)
567{
568}
569
570
571void CPjAudioOutputEngine::ConstructL()
572{
573 frameBufSize_ = parentStrm_->param.samples_per_frame *
574 parentStrm_->param.channel_count *
575 BYTES_PER_SAMPLE;
576 frameBuf_ = new TUint8[frameBufSize_];
577}
578
579CPjAudioOutputEngine::~CPjAudioOutputEngine()
580{
581 Stop();
582 delete [] frameBuf_;
583}
584
585CPjAudioOutputEngine *
586CPjAudioOutputEngine::NewLC(struct mda_stream *parent_strm,
587 pjmedia_aud_play_cb play_cb,
588 void *user_data)
589{
590 CPjAudioOutputEngine* self = new (ELeave) CPjAudioOutputEngine(parent_strm,
591 play_cb,
592 user_data);
593 CleanupStack::PushL(self);
594 self->ConstructL();
595 return self;
596}
597
598CPjAudioOutputEngine *
599CPjAudioOutputEngine::NewL(struct mda_stream *parent_strm,
600 pjmedia_aud_play_cb play_cb,
601 void *user_data)
602{
603 CPjAudioOutputEngine *self = NewLC(parent_strm, play_cb, user_data);
604 CleanupStack::Pop(self);
605 return self;
606}
607
608pj_status_t CPjAudioOutputEngine::StartPlay()
609{
610 // Ignore command if playing is in progress.
611 if (state_ == STATE_ACTIVE)
612 return PJ_SUCCESS;
613
614 // Destroy existing stream.
615 if (iOutputStream_) delete iOutputStream_;
616 iOutputStream_ = NULL;
617
618 // Create the stream
619 TRAPD(err, iOutputStream_ = CMdaAudioOutputStream::NewL(*this));
620 if (err != KErrNone)
621 return PJ_RETURN_OS_ERROR(err);
622
623 // Initialize settings.
624 TMdaAudioDataSettings iStreamSettings;
625 iStreamSettings.iChannels =
626 get_channel_cap(parentStrm_->param.channel_count);
627 iStreamSettings.iSampleRate =
628 get_clock_rate_cap(parentStrm_->param.clock_rate);
629
630 pj_assert(iStreamSettings.iChannels != 0 &&
631 iStreamSettings.iSampleRate != 0);
632
633 PJ_LOG(4,(THIS_FILE, "Opening sound device for playback, "
634 "clock rate=%d, channel count=%d..",
635 parentStrm_->param.clock_rate,
636 parentStrm_->param.channel_count));
637
638 // Open stream.
639 lastError_ = KRequestPending;
640 iOutputStream_->Open(&iStreamSettings);
641
642 // Success
643 PJ_LOG(4,(THIS_FILE, "Sound playback started"));
644 return PJ_SUCCESS;
645
646}
647
648void CPjAudioOutputEngine::Stop()
649{
650 // Stop stream if it's playing
651 if (iOutputStream_ && state_ != STATE_INACTIVE) {
652 lastError_ = KRequestPending;
653 iOutputStream_->Stop();
654
655 // Wait until it's actually stopped
656 while (lastError_ == KRequestPending)
657 pj_symbianos_poll(-1, 100);
658 }
659
660 if (iOutputStream_) {
661 delete iOutputStream_;
662 iOutputStream_ = NULL;
663 }
664
665 state_ = STATE_INACTIVE;
666}
667
668void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError)
669{
670 lastError_ = aError;
671
672 if (aError==KErrNone) {
673 // output stream opened succesfully, set status to Active
674 state_ = STATE_ACTIVE;
675
676 // set stream properties, 16bit 8KHz mono
677 TMdaAudioDataSettings iSettings;
678 iSettings.iChannels =
679 get_channel_cap(parentStrm_->param.channel_count);
680 iSettings.iSampleRate =
681 get_clock_rate_cap(parentStrm_->param.clock_rate);
682
683 iOutputStream_->SetAudioPropertiesL(iSettings.iSampleRate,
684 iSettings.iChannels);
685
686 // set volume to 1/2th of stream max volume
687 iOutputStream_->SetVolume(iOutputStream_->MaxVolume()/2);
688
689 // set stream priority to normal and time sensitive
690 iOutputStream_->SetPriority(EPriorityNormal,
691 EMdaPriorityPreferenceTime);
692
693 // Call callback to retrieve frame from upstream.
694 pjmedia_frame f;
695 pj_status_t status;
696
697 f.type = PJMEDIA_FRAME_TYPE_AUDIO;
698 f.buf = frameBuf_;
699 f.size = frameBufSize_;
700 f.timestamp.u32.lo = timestamp_;
701 f.bit_info = 0;
702
703 status = playCb_(this->userData_, &f);
704 if (status != PJ_SUCCESS) {
705 this->Stop();
706 return;
707 }
708
709 // Increment timestamp.
710 timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
711
712 // issue WriteL() to write the first audio data block,
713 // subsequent calls to WriteL() will be issued in
714 // MMdaAudioOutputStreamCallback::MaoscBufferCopied()
715 // until whole data buffer is written.
716 frame_.Set(frameBuf_, frameBufSize_);
717 iOutputStream_->WriteL(frame_);
718 } else {
719 snd_perror("Error in MaoscOpenComplete()", aError);
720 }
721}
722
723void CPjAudioOutputEngine::MaoscBufferCopied(TInt aError,
724 const TDesC8& aBuffer)
725{
726 PJ_UNUSED_ARG(aBuffer);
727
728 if (aError==KErrNone) {
729 // Buffer successfully written, feed another one.
730
731 // Call callback to retrieve frame from upstream.
732 pjmedia_frame f;
733 pj_status_t status;
734
735 f.type = PJMEDIA_FRAME_TYPE_AUDIO;
736 f.buf = frameBuf_;
737 f.size = frameBufSize_;
738 f.timestamp.u32.lo = timestamp_;
739 f.bit_info = 0;
740
741 status = playCb_(this->userData_, &f);
742 if (status != PJ_SUCCESS) {
743 this->Stop();
744 return;
745 }
746
747 // Increment timestamp.
748 timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
749
750 // Write to playback stream.
751 frame_.Set(frameBuf_, frameBufSize_);
752 iOutputStream_->WriteL(frame_);
753
754 } else if (aError==KErrAbort) {
755 // playing was aborted, due to call to CMdaAudioOutputStream::Stop()
756 state_ = STATE_INACTIVE;
757 } else {
758 // error writing data to output
759 lastError_ = aError;
760 state_ = STATE_INACTIVE;
761 snd_perror("Error in MaoscBufferCopied()", aError);
762 }
763}
764
765void CPjAudioOutputEngine::MaoscPlayComplete(TInt aError)
766{
767 lastError_ = aError;
768 state_ = STATE_INACTIVE;
769 if (aError != KErrNone) {
770 snd_perror("Error in MaoscPlayComplete()", aError);
771 }
772}
773
774/****************************************************************************
775 * Factory operations
776 */
Nanang Izzuddin6d17d502009-02-26 21:12:44 +0000777
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +0000778/*
Benny Prijono689f8a82009-02-24 08:23:36 +0000779 * C compatible declaration of MDA factory.
780 */
781PJ_BEGIN_DECL
782PJ_DECL(pjmedia_aud_dev_factory*) pjmedia_symb_mda_factory(pj_pool_factory *pf);
783PJ_END_DECL
784
785/*
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +0000786 * Init Symbian audio driver.
787 */
788pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf)
789{
790 struct mda_factory *f;
791 pj_pool_t *pool;
792
793 pool = pj_pool_create(pf, "symb_aud", 1000, 1000, NULL);
794 f = PJ_POOL_ZALLOC_T(pool, struct mda_factory);
795 f->pf = pf;
796 f->pool = pool;
797 f->base.op = &factory_op;
798
799 return &f->base;
800}
801
802/* API: init factory */
803static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
804{
805 struct mda_factory *af = (struct mda_factory*)f;
806
807 pj_ansi_strcpy(af->dev_info.name, "Symbian Audio");
808 af->dev_info.default_samples_per_sec = 8000;
809 af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
Nanang Izzuddina940b362009-02-23 13:53:30 +0000810 PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +0000811 af->dev_info.input_count = 1;
812 af->dev_info.output_count = 1;
813
814 PJ_LOG(4, (THIS_FILE, "Symb Mda initialized"));
815
816 return PJ_SUCCESS;
817}
818
819/* API: destroy factory */
820static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
821{
822 struct mda_factory *af = (struct mda_factory*)f;
823 pj_pool_t *pool = af->pool;
824
825 af->pool = NULL;
826 pj_pool_release(pool);
827
828 PJ_LOG(4, (THIS_FILE, "Symbian Mda destroyed"));
829
830 return PJ_SUCCESS;
831}
832
833/* API: get number of devices */
834static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
835{
836 PJ_UNUSED_ARG(f);
837 return 1;
838}
839
840/* API: get device info */
841static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
842 unsigned index,
843 pjmedia_aud_dev_info *info)
844{
845 struct mda_factory *af = (struct mda_factory*)f;
846
847 PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
848
849 pj_memcpy(info, &af->dev_info, sizeof(*info));
850
851 return PJ_SUCCESS;
852}
853
854/* API: create default device parameter */
855static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
856 unsigned index,
Benny Prijono10454dc2009-02-21 14:21:59 +0000857 pjmedia_aud_param *param)
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +0000858{
859 struct mda_factory *af = (struct mda_factory*)f;
860
861 PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
862
863 pj_bzero(param, sizeof(*param));
864 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
865 param->rec_id = index;
866 param->play_id = index;
867 param->clock_rate = af->dev_info.default_samples_per_sec;
868 param->channel_count = 1;
869 param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
870 param->bits_per_sample = BITS_PER_SAMPLE;
871 param->flags = af->dev_info.caps;
872
873 return PJ_SUCCESS;
874}
875
876
877/* API: create stream */
878static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
Benny Prijono10454dc2009-02-21 14:21:59 +0000879 const pjmedia_aud_param *param,
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +0000880 pjmedia_aud_rec_cb rec_cb,
881 pjmedia_aud_play_cb play_cb,
882 void *user_data,
883 pjmedia_aud_stream **p_aud_strm)
884{
885 struct mda_factory *mf = (struct mda_factory*)f;
886 pj_pool_t *pool;
887 struct mda_stream *strm;
888
889 /* Can only support 16bits per sample raw PCM format. */
890 PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
Nanang Izzuddin6d17d502009-02-26 21:12:44 +0000891 PJ_ASSERT_RETURN((param->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT)==0 ||
892 param->ext_fmt.id == PJMEDIA_FORMAT_L16,
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +0000893 PJ_ENOTSUP);
894
895 /* Create and Initialize stream descriptor */
896 pool = pj_pool_create(mf->pf, "symb_aud_dev", 1000, 1000, NULL);
897 PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
898
899 strm = PJ_POOL_ZALLOC_T(pool, struct mda_stream);
900 strm->pool = pool;
901 strm->param = *param;
902
903 // Create the output stream.
904 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
905 TRAPD(err, strm->out_engine = CPjAudioOutputEngine::NewL(strm, play_cb,
906 user_data));
907 if (err != KErrNone) {
908 pj_pool_release(pool);
909 return PJ_RETURN_OS_ERROR(err);
910 }
911 }
912
913 // Create the input stream.
914 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
915 TRAPD(err, strm->in_engine = CPjAudioInputEngine::NewL(strm, rec_cb,
916 user_data));
917 if (err != KErrNone) {
918 strm->in_engine = NULL;
919 delete strm->out_engine;
920 strm->out_engine = NULL;
921 pj_pool_release(pool);
922 return PJ_RETURN_OS_ERROR(err);
923 }
924 }
925
926 /* Done */
927 strm->base.op = &stream_op;
928 *p_aud_strm = &strm->base;
929
930 return PJ_SUCCESS;
931}
932
933/* API: Get stream info. */
934static pj_status_t stream_get_param(pjmedia_aud_stream *s,
Benny Prijono10454dc2009-02-21 14:21:59 +0000935 pjmedia_aud_param *pi)
Nanang Izzuddin5efbdf22009-02-20 14:59:55 +0000936{
937 struct mda_stream *strm = (struct mda_stream*)s;
938
939 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
940
941 pj_memcpy(pi, &strm->param, sizeof(*pi));
942
943 return PJ_SUCCESS;
944}
945
946/* API: get capability */
947static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
948 pjmedia_aud_dev_cap cap,
949 void *pval)
950{
951 struct mda_stream *strm = (struct mda_stream*)s;
952 pj_status_t status = PJ_ENOTSUP;
953
954 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
955
956 switch (cap) {
957 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
958 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
959 PJ_ASSERT_RETURN(strm->in_engine, PJ_EINVAL);
960
961 TInt max_gain = strm->in_engine->GetMaxGain();
962 TInt gain = strm->in_engine->GetGain();
963
964 if (max_gain > 0 && gain >= 0) {
965 *(unsigned*)pval = gain * 100 / max_gain;
966 status = PJ_SUCCESS;
967 } else {
968 status = PJMEDIA_EAUD_NOTREADY;
969 }
970 }
971 break;
972 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
973 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
974 PJ_ASSERT_RETURN(strm->out_engine, PJ_EINVAL);
975
976 TInt max_vol = strm->out_engine->GetMaxVolume();
977 TInt vol = strm->out_engine->GetVolume();
978
979 if (max_vol > 0 && vol >= 0) {
980 *(unsigned*)pval = vol * 100 / max_vol;
981 status = PJ_SUCCESS;
982 } else {
983 status = PJMEDIA_EAUD_NOTREADY;
984 }
985 }
986 break;
987 default:
988 break;
989 }
990
991 return status;
992}
993
994/* API: set capability */
995static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
996 pjmedia_aud_dev_cap cap,
997 const void *pval)
998{
999 struct mda_stream *strm = (struct mda_stream*)s;
1000 pj_status_t status = PJ_ENOTSUP;
1001
1002 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1003
1004 switch (cap) {
1005 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
1006 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1007 PJ_ASSERT_RETURN(strm->in_engine, PJ_EINVAL);
1008
1009 TInt max_gain = strm->in_engine->GetMaxGain();
1010 if (max_gain > 0) {
1011 TInt gain;
1012
1013 gain = *(unsigned*)pval * max_gain / 100;
1014 status = strm->in_engine->SetGain(gain);
1015 } else {
1016 status = PJMEDIA_EAUD_NOTREADY;
1017 }
1018 }
1019 break;
1020 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
1021 if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
1022 PJ_ASSERT_RETURN(strm->out_engine, PJ_EINVAL);
1023
1024 TInt max_vol = strm->out_engine->GetMaxVolume();
1025 if (max_vol > 0) {
1026 TInt vol;
1027
1028 vol = *(unsigned*)pval * max_vol / 100;
1029 status = strm->out_engine->SetVolume(vol);
1030 } else {
1031 status = PJMEDIA_EAUD_NOTREADY;
1032 }
1033 }
1034 break;
1035 default:
1036 break;
1037 }
1038
1039 return status;
1040}
1041
1042/* API: Start stream. */
1043static pj_status_t stream_start(pjmedia_aud_stream *strm)
1044{
1045 struct mda_stream *stream = (struct mda_stream*)strm;
1046
1047 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1048
1049 if (stream->out_engine) {
1050 pj_status_t status;
1051 status = stream->out_engine->StartPlay();
1052 if (status != PJ_SUCCESS)
1053 return status;
1054 }
1055
1056 if (stream->in_engine) {
1057 pj_status_t status;
1058 status = stream->in_engine->StartRecord();
1059 if (status != PJ_SUCCESS)
1060 return status;
1061 }
1062
1063 return PJ_SUCCESS;
1064}
1065
1066/* API: Stop stream. */
1067static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1068{
1069 struct mda_stream *stream = (struct mda_stream*)strm;
1070
1071 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1072
1073 if (stream->in_engine) {
1074 stream->in_engine->Stop();
1075 }
1076
1077 if (stream->out_engine) {
1078 stream->out_engine->Stop();
1079 }
1080
1081 return PJ_SUCCESS;
1082}
1083
1084
1085/* API: Destroy stream. */
1086static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1087{
1088 struct mda_stream *stream = (struct mda_stream*)strm;
1089
1090 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1091
1092 stream_stop(strm);
1093
1094 delete stream->in_engine;
1095 stream->in_engine = NULL;
1096
1097 delete stream->out_engine;
1098 stream->out_engine = NULL;
1099
1100 pj_pool_t *pool;
1101 pool = stream->pool;
1102 if (pool) {
1103 stream->pool = NULL;
1104 pj_pool_release(pool);
1105 }
1106
1107 return PJ_SUCCESS;
1108}
1109
Nanang Izzuddinda37ea32009-03-06 16:24:43 +00001110#endif /* PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA */