~noskcaj/ubuntu/saucy/sflphone/merge-1.2.3-2

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp

  • Committer: Jackson Doak
  • Date: 2013-07-10 21:04:46 UTC
  • mfrom: (20.1.3 sid)
  • Revision ID: noskcaj@ubuntu.com-20130710210446-y8f587vza807icr9
Properly merged from upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: symb_aps_dev.cpp 3841 2011-10-24 09:28:13Z ming $ */
 
2
/*
 
3
 * Copyright (C) 2008-2011 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>
 
21
#include <pjmedia-audiodev/errno.h>
 
22
#include <pjmedia/alaw_ulaw.h>
 
23
#include <pjmedia/resample.h>
 
24
#include <pjmedia/stereo.h>
 
25
#include <pj/assert.h>
 
26
#include <pj/log.h>
 
27
#include <pj/math.h>
 
28
#include <pj/os.h>
 
29
#include <pj/string.h>
 
30
 
 
31
#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
 
32
 
 
33
#include <e32msgqueue.h>
 
34
#include <sounddevice.h>
 
35
#include <APSClientSession.h>
 
36
#include <pjmedia-codec/amr_helper.h>
 
37
 
 
38
/* Pack/unpack G.729 frame of S60 DSP codec, taken from:
 
39
 * http://wiki.forum.nokia.com/index.php/TSS000776_-_Payload_conversion_for_G.729_audio_format
 
40
 */
 
41
#include "s60_g729_bitstream.h"
 
42
 
 
43
 
 
44
#define THIS_FILE                       "symb_aps_dev.c"
 
45
#define BITS_PER_SAMPLE                 16
 
46
 
 
47
 
 
48
#if 1
 
49
#   define TRACE_(st) PJ_LOG(3, st)
 
50
#else
 
51
#   define TRACE_(st)
 
52
#endif
 
53
 
 
54
 
 
55
/* App UID to open global APS queues to communicate with the APS server. */
 
56
extern TPtrC APP_UID;
 
57
 
 
58
/* APS G.711 frame length */
 
59
static pj_uint8_t aps_g711_frame_len;
 
60
 
 
61
 
 
62
/* APS factory */
 
63
struct aps_factory
 
64
{
 
65
    pjmedia_aud_dev_factory      base;
 
66
    pj_pool_t                   *pool;
 
67
    pj_pool_factory             *pf;
 
68
    pjmedia_aud_dev_info         dev_info;
 
69
};
 
70
 
 
71
 
 
72
/* Forward declaration of CPjAudioEngine */
 
73
class CPjAudioEngine;
 
74
 
 
75
 
 
76
/* APS stream. */
 
77
struct aps_stream
 
78
{
 
79
    // Base
 
80
    pjmedia_aud_stream   base;                  /**< Base class.        */
 
81
 
 
82
    // Pool
 
83
    pj_pool_t           *pool;                  /**< Memory pool.       */
 
84
 
 
85
    // Common settings.
 
86
    pjmedia_aud_param    param;                 /**< Stream param.      */
 
87
    pjmedia_aud_rec_cb   rec_cb;                /**< Record callback.   */
 
88
    pjmedia_aud_play_cb  play_cb;               /**< Playback callback. */
 
89
    void                *user_data;             /**< Application data.  */
 
90
 
 
91
    // Audio engine
 
92
    CPjAudioEngine      *engine;                /**< Internal engine.   */
 
93
 
 
94
    pj_timestamp         ts_play;               /**< Playback timestamp.*/
 
95
    pj_timestamp         ts_rec;                /**< Record timestamp.  */
 
96
 
 
97
    pj_int16_t          *play_buf;              /**< Playback buffer.   */
 
98
    pj_uint16_t          play_buf_len;          /**< Playback buffer length. */
 
99
    pj_uint16_t          play_buf_start;        /**< Playback buffer start index. */
 
100
    pj_int16_t          *rec_buf;               /**< Record buffer.     */
 
101
    pj_uint16_t          rec_buf_len;           /**< Record buffer length. */
 
102
    void                *strm_data;             /**< Stream data.       */
 
103
 
 
104
    /* Resampling is needed, in case audio device is opened with clock rate
 
105
     * other than 8kHz (only for PCM format).
 
106
     */
 
107
    pjmedia_resample    *play_resample;         /**< Resampler for playback. */
 
108
    pjmedia_resample    *rec_resample;          /**< Resampler for recording */
 
109
    pj_uint16_t          resample_factor;       /**< Resample factor, requested
 
110
                                                     clock rate / 8000       */
 
111
 
 
112
    /* When stream is working in PCM format, where the samples may need to be
 
113
     * resampled from/to different clock rate and/or channel count, PCM buffer
 
114
     * is needed to perform such resampling operations.
 
115
     */
 
116
    pj_int16_t          *pcm_buf;               /**< PCM buffer.             */
 
117
};
 
118
 
 
119
 
 
120
/* Prototypes */
 
121
static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
 
122
static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
 
123
static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
 
124
static unsigned    factory_get_dev_count(pjmedia_aud_dev_factory *f);
 
125
static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
 
126
                                        unsigned index,
 
127
                                        pjmedia_aud_dev_info *info);
 
128
static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
 
129
                                         unsigned index,
 
130
                                         pjmedia_aud_param *param);
 
131
static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
 
132
                                         const pjmedia_aud_param *param,
 
133
                                         pjmedia_aud_rec_cb rec_cb,
 
134
                                         pjmedia_aud_play_cb play_cb,
 
135
                                         void *user_data,
 
136
                                         pjmedia_aud_stream **p_aud_strm);
 
137
 
 
138
static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
 
139
                                    pjmedia_aud_param *param);
 
140
static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
 
141
                                  pjmedia_aud_dev_cap cap,
 
142
                                  void *value);
 
143
static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
 
144
                                  pjmedia_aud_dev_cap cap,
 
145
                                  const void *value);
 
146
static pj_status_t stream_start(pjmedia_aud_stream *strm);
 
147
static pj_status_t stream_stop(pjmedia_aud_stream *strm);
 
148
static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
 
149
 
 
150
 
 
151
/* Operations */
 
152
static pjmedia_aud_dev_factory_op factory_op =
 
153
{
 
154
    &factory_init,
 
155
    &factory_destroy,
 
156
    &factory_get_dev_count,
 
157
    &factory_get_dev_info,
 
158
    &factory_default_param,
 
159
    &factory_create_stream,
 
160
    &factory_refresh
 
161
};
 
162
 
 
163
static pjmedia_aud_stream_op stream_op =
 
164
{
 
165
    &stream_get_param,
 
166
    &stream_get_cap,
 
167
    &stream_set_cap,
 
168
    &stream_start,
 
169
    &stream_stop,
 
170
    &stream_destroy
 
171
};
 
172
 
 
173
 
 
174
/****************************************************************************
 
175
 * Internal APS Engine
 
176
 */
 
177
 
 
178
/*
 
179
 * Utility: print sound device error
 
180
 */
 
181
static void snd_perror(const char *title, TInt rc)
 
182
{
 
183
    PJ_LOG(1,(THIS_FILE, "%s (error code=%d)", title, rc));
 
184
}
 
185
 
 
186
/*
 
187
 * Utility: wait for specified time.
 
188
 */
 
189
static void snd_wait(unsigned ms)
 
190
{
 
191
    TTime start, now;
 
192
 
 
193
    start.UniversalTime();
 
194
    do {
 
195
        pj_symbianos_poll(-1, ms);
 
196
        now.UniversalTime();
 
197
    } while (now.MicroSecondsFrom(start) < ms * 1000);
 
198
}
 
199
 
 
200
typedef void(*PjAudioCallback)(TAPSCommBuffer &buf, void *user_data);
 
201
 
 
202
/**
 
203
 * Abstract class for handler of callbacks from APS client.
 
204
 */
 
205
class MQueueHandlerObserver
 
206
{
 
207
public:
 
208
    MQueueHandlerObserver(PjAudioCallback RecCb_, PjAudioCallback PlayCb_,
 
209
                          void *UserData_)
 
210
    : RecCb(RecCb_), PlayCb(PlayCb_), UserData(UserData_)
 
211
    {}
 
212
 
 
213
    virtual void InputStreamInitialized(const TInt aStatus) = 0;
 
214
    virtual void OutputStreamInitialized(const TInt aStatus) = 0;
 
215
    virtual void NotifyError(const TInt aError) = 0;
 
216
 
 
217
public:
 
218
    PjAudioCallback RecCb;
 
219
    PjAudioCallback PlayCb;
 
220
    void *UserData;
 
221
};
 
222
 
 
223
/**
 
224
 * Handler for communication and data queue.
 
225
 */
 
226
class CQueueHandler : public CActive
 
227
{
 
228
public:
 
229
    // Types of queue handler
 
230
    enum TQueueHandlerType {
 
231
        ERecordCommQueue,
 
232
        EPlayCommQueue,
 
233
        ERecordQueue,
 
234
        EPlayQueue
 
235
    };
 
236
 
 
237
    // The order corresponds to the APS Server state, do not change!
 
238
    enum TState {
 
239
        EAPSPlayerInitialize        = 1,
 
240
        EAPSRecorderInitialize      = 2,
 
241
        EAPSPlayData                = 3,
 
242
        EAPSRecordData              = 4,
 
243
        EAPSPlayerInitComplete      = 5,
 
244
        EAPSRecorderInitComplete    = 6
 
245
    };
 
246
 
 
247
    static CQueueHandler* NewL(MQueueHandlerObserver* aObserver,
 
248
                               RMsgQueue<TAPSCommBuffer>* aQ,
 
249
                               RMsgQueue<TAPSCommBuffer>* aWriteQ,
 
250
                               TQueueHandlerType aType)
 
251
    {
 
252
        CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aWriteQ,
 
253
                                                         aType);
 
254
        CleanupStack::PushL(self);
 
255
        self->ConstructL();
 
256
        CleanupStack::Pop(self);
 
257
        return self;
 
258
    }
 
259
 
 
260
    // Destructor
 
261
    ~CQueueHandler() { Cancel(); }
 
262
 
 
263
    // Start listening queue event
 
264
    void Start() {
 
265
        iQ->NotifyDataAvailable(iStatus);
 
266
        SetActive();
 
267
    }
 
268
 
 
269
private:
 
270
    // Constructor
 
271
    CQueueHandler(MQueueHandlerObserver* aObserver,
 
272
                  RMsgQueue<TAPSCommBuffer>* aQ,
 
273
                  RMsgQueue<TAPSCommBuffer>* aWriteQ,
 
274
                  TQueueHandlerType aType)
 
275
        : CActive(CActive::EPriorityHigh),
 
276
          iQ(aQ), iWriteQ(aWriteQ), iObserver(aObserver), iType(aType)
 
277
    {
 
278
        CActiveScheduler::Add(this);
 
279
 
 
280
        // use lower priority for comm queues
 
281
        if ((iType == ERecordCommQueue) || (iType == EPlayCommQueue))
 
282
            SetPriority(CActive::EPriorityStandard);
 
283
    }
 
284
 
 
285
    // Second phase constructor
 
286
    void ConstructL() {}
 
287
 
 
288
    // Inherited from CActive
 
289
    void DoCancel() { iQ->CancelDataAvailable(); }
 
290
 
 
291
    void RunL() {
 
292
        if (iStatus != KErrNone) {
 
293
            iObserver->NotifyError(iStatus.Int());
 
294
            return;
 
295
        }
 
296
 
 
297
        TAPSCommBuffer buffer;
 
298
        TInt ret = iQ->Receive(buffer);
 
299
 
 
300
        if (ret != KErrNone) {
 
301
            iObserver->NotifyError(ret);
 
302
            return;
 
303
        }
 
304
 
 
305
        switch (iType) {
 
306
        case ERecordQueue:
 
307
            if (buffer.iCommand == EAPSRecordData) {
 
308
                iObserver->RecCb(buffer, iObserver->UserData);
 
309
            } else {
 
310
                iObserver->NotifyError(buffer.iStatus);
 
311
            }
 
312
            break;
 
313
 
 
314
        // Callbacks from the APS main thread
 
315
        case EPlayCommQueue:
 
316
            switch (buffer.iCommand) {
 
317
                case EAPSPlayData:
 
318
                    if (buffer.iStatus == KErrUnderflow) {
 
319
                        iObserver->PlayCb(buffer, iObserver->UserData);
 
320
                        iWriteQ->Send(buffer);
 
321
                    }
 
322
                    break;
 
323
                case EAPSPlayerInitialize:
 
324
                    iObserver->NotifyError(buffer.iStatus);
 
325
                    break;
 
326
                case EAPSPlayerInitComplete:
 
327
                    iObserver->OutputStreamInitialized(buffer.iStatus);
 
328
                    break;
 
329
                case EAPSRecorderInitComplete:
 
330
                    iObserver->InputStreamInitialized(buffer.iStatus);
 
331
                    break;
 
332
                default:
 
333
                    iObserver->NotifyError(buffer.iStatus);
 
334
                    break;
 
335
            }
 
336
            break;
 
337
 
 
338
        // Callbacks from the APS recorder thread
 
339
        case ERecordCommQueue:
 
340
            switch (buffer.iCommand) {
 
341
                // The APS recorder thread will only report errors
 
342
                // through this handler. All other callbacks will be
 
343
                // sent from the APS main thread through EPlayCommQueue
 
344
                case EAPSRecorderInitialize:
 
345
                case EAPSRecordData:
 
346
                default:
 
347
                    iObserver->NotifyError(buffer.iStatus);
 
348
                    break;
 
349
            }
 
350
            break;
 
351
 
 
352
        default:
 
353
            break;
 
354
        }
 
355
 
 
356
        // issue next request
 
357
        iQ->NotifyDataAvailable(iStatus);
 
358
        SetActive();
 
359
    }
 
360
 
 
361
    TInt RunError(TInt) {
 
362
        return 0;
 
363
    }
 
364
 
 
365
    // Data
 
366
    RMsgQueue<TAPSCommBuffer>   *iQ;   // (not owned)
 
367
    RMsgQueue<TAPSCommBuffer>   *iWriteQ;   // (not owned)
 
368
    MQueueHandlerObserver       *iObserver; // (not owned)
 
369
    TQueueHandlerType            iType;
 
370
};
 
371
 
 
372
/*
 
373
 * Audio setting for CPjAudioEngine.
 
374
 */
 
375
class CPjAudioSetting
 
376
{
 
377
public:
 
378
    TFourCC              fourcc;
 
379
    TAPSCodecMode        mode;
 
380
    TBool                plc;
 
381
    TBool                vad;
 
382
    TBool                cng;
 
383
    TBool                loudspk;
 
384
};
 
385
 
 
386
/*
 
387
 * Implementation: Symbian Input & Output Stream.
 
388
 */
 
389
class CPjAudioEngine : public CBase, MQueueHandlerObserver
 
390
{
 
391
public:
 
392
    enum State
 
393
    {
 
394
        STATE_NULL,
 
395
        STATE_INITIALIZING,
 
396
        STATE_READY,
 
397
        STATE_STREAMING,
 
398
        STATE_PENDING_STOP
 
399
    };
 
400
 
 
401
    ~CPjAudioEngine();
 
402
 
 
403
    static CPjAudioEngine *NewL(struct aps_stream *parent_strm,
 
404
                                PjAudioCallback rec_cb,
 
405
                                PjAudioCallback play_cb,
 
406
                                void *user_data,
 
407
                                const CPjAudioSetting &setting);
 
408
 
 
409
    TInt StartL();
 
410
    void Stop();
 
411
 
 
412
    TInt ActivateSpeaker(TBool active);
 
413
 
 
414
    TInt SetVolume(TInt vol) { return iSession.SetVolume(vol); }
 
415
    TInt GetVolume() { return iSession.Volume(); }
 
416
    TInt GetMaxVolume() { return iSession.MaxVolume(); }
 
417
 
 
418
    TInt SetGain(TInt gain) { return iSession.SetGain(gain); }
 
419
    TInt GetGain() { return iSession.Gain(); }
 
420
    TInt GetMaxGain() { return iSession.MaxGain(); }
 
421
 
 
422
private:
 
423
    CPjAudioEngine(struct aps_stream *parent_strm,
 
424
                   PjAudioCallback rec_cb,
 
425
                   PjAudioCallback play_cb,
 
426
                   void *user_data,
 
427
                   const CPjAudioSetting &setting);
 
428
    void ConstructL();
 
429
 
 
430
    TInt InitPlayL();
 
431
    TInt InitRecL();
 
432
    TInt StartStreamL();
 
433
    void Deinit();
 
434
 
 
435
    // Inherited from MQueueHandlerObserver
 
436
    virtual void InputStreamInitialized(const TInt aStatus);
 
437
    virtual void OutputStreamInitialized(const TInt aStatus);
 
438
    virtual void NotifyError(const TInt aError);
 
439
 
 
440
    TBool                        session_opened;
 
441
    State                        state_;
 
442
    struct aps_stream           *parentStrm_;
 
443
    CPjAudioSetting              setting_;
 
444
 
 
445
    RAPSSession                  iSession;
 
446
    TAPSInitSettings             iPlaySettings;
 
447
    TAPSInitSettings             iRecSettings;
 
448
 
 
449
    RMsgQueue<TAPSCommBuffer>    iReadQ;
 
450
    RMsgQueue<TAPSCommBuffer>    iReadCommQ;
 
451
    TBool                        readq_opened;
 
452
    RMsgQueue<TAPSCommBuffer>    iWriteQ;
 
453
    RMsgQueue<TAPSCommBuffer>    iWriteCommQ;
 
454
    TBool                        writeq_opened;
 
455
 
 
456
    CQueueHandler               *iPlayCommHandler;
 
457
    CQueueHandler               *iRecCommHandler;
 
458
    CQueueHandler               *iRecHandler;
 
459
};
 
460
 
 
461
 
 
462
CPjAudioEngine* CPjAudioEngine::NewL(struct aps_stream *parent_strm,
 
463
                                     PjAudioCallback rec_cb,
 
464
                                     PjAudioCallback play_cb,
 
465
                                     void *user_data,
 
466
                                     const CPjAudioSetting &setting)
 
467
{
 
468
    CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm,
 
469
                                                       rec_cb, play_cb,
 
470
                                                       user_data,
 
471
                                                       setting);
 
472
    CleanupStack::PushL(self);
 
473
    self->ConstructL();
 
474
    CleanupStack::Pop(self);
 
475
    return self;
 
476
}
 
477
 
 
478
CPjAudioEngine::CPjAudioEngine(struct aps_stream *parent_strm,
 
479
                               PjAudioCallback rec_cb,
 
480
                               PjAudioCallback play_cb,
 
481
                               void *user_data,
 
482
                               const CPjAudioSetting &setting)
 
483
      : MQueueHandlerObserver(rec_cb, play_cb, user_data),
 
484
        session_opened(EFalse),
 
485
        state_(STATE_NULL),
 
486
        parentStrm_(parent_strm),
 
487
        setting_(setting),
 
488
        readq_opened(EFalse),
 
489
        writeq_opened(EFalse),
 
490
        iPlayCommHandler(0),
 
491
        iRecCommHandler(0),
 
492
        iRecHandler(0)
 
493
{
 
494
}
 
495
 
 
496
CPjAudioEngine::~CPjAudioEngine()
 
497
{
 
498
    Deinit();
 
499
 
 
500
    TRACE_((THIS_FILE, "Sound device destroyed"));
 
501
}
 
502
 
 
503
TInt CPjAudioEngine::InitPlayL()
 
504
{
 
505
    TInt err = iSession.InitializePlayer(iPlaySettings);
 
506
    if (err != KErrNone) {
 
507
        Deinit();
 
508
        snd_perror("Failed to initialize player", err);
 
509
        return err;
 
510
    }
 
511
 
 
512
    // Open message queues for the output stream
 
513
    TBuf<128> buf2 = iPlaySettings.iGlobal;
 
514
    buf2.Append(_L("PlayQueue"));
 
515
    TBuf<128> buf3 = iPlaySettings.iGlobal;
 
516
    buf3.Append(_L("PlayCommQueue"));
 
517
 
 
518
    while (iWriteQ.OpenGlobal(buf2))
 
519
        User::After(10);
 
520
    while (iWriteCommQ.OpenGlobal(buf3))
 
521
        User::After(10);
 
522
 
 
523
    writeq_opened = ETrue;
 
524
 
 
525
    // Construct message queue handler
 
526
    iPlayCommHandler = CQueueHandler::NewL(this, &iWriteCommQ, &iWriteQ,
 
527
                                           CQueueHandler::EPlayCommQueue);
 
528
 
 
529
    // Start observing APS callbacks on output stream message queue
 
530
    iPlayCommHandler->Start();
 
531
 
 
532
    return 0;
 
533
}
 
534
 
 
535
TInt CPjAudioEngine::InitRecL()
 
536
{
 
537
    // Initialize input stream device
 
538
    TInt err = iSession.InitializeRecorder(iRecSettings);
 
539
    if (err != KErrNone && err != KErrAlreadyExists) {
 
540
        Deinit();
 
541
        snd_perror("Failed to initialize recorder", err);
 
542
        return err;
 
543
    }
 
544
 
 
545
    TBuf<128> buf1 = iRecSettings.iGlobal;
 
546
    buf1.Append(_L("RecordQueue"));
 
547
    TBuf<128> buf4 = iRecSettings.iGlobal;
 
548
    buf4.Append(_L("RecordCommQueue"));
 
549
 
 
550
    // Must wait for APS thread to finish creating message queues
 
551
    // before we can open and use them.
 
552
    while (iReadQ.OpenGlobal(buf1))
 
553
        User::After(10);
 
554
    while (iReadCommQ.OpenGlobal(buf4))
 
555
        User::After(10);
 
556
 
 
557
    readq_opened = ETrue;
 
558
 
 
559
    // Construct message queue handlers
 
560
    iRecHandler = CQueueHandler::NewL(this, &iReadQ, NULL,
 
561
                                      CQueueHandler::ERecordQueue);
 
562
    iRecCommHandler = CQueueHandler::NewL(this, &iReadCommQ, NULL,
 
563
                                          CQueueHandler::ERecordCommQueue);
 
564
 
 
565
    // Start observing APS callbacks from on input stream message queue
 
566
    iRecHandler->Start();
 
567
    iRecCommHandler->Start();
 
568
 
 
569
    return 0;
 
570
}
 
571
 
 
572
TInt CPjAudioEngine::StartL()
 
573
{
 
574
    if (state_ == STATE_READY)
 
575
        return StartStreamL();
 
576
 
 
577
    PJ_ASSERT_RETURN(state_ == STATE_NULL, PJMEDIA_EAUD_INVOP);
 
578
 
 
579
    if (!session_opened) {
 
580
        TInt err = iSession.Connect();
 
581
        if (err != KErrNone)
 
582
            return err;
 
583
        session_opened = ETrue;
 
584
    }
 
585
 
 
586
    // Even if only capturer are opened, playback thread of APS Server need
 
587
    // to be run(?). Since some messages will be delivered via play comm queue.
 
588
    state_ = STATE_INITIALIZING;
 
589
 
 
590
    return InitPlayL();
 
591
}
 
592
 
 
593
void CPjAudioEngine::Stop()
 
594
{
 
595
    if (state_ == STATE_STREAMING) {
 
596
        iSession.Stop();
 
597
        state_ = STATE_READY;
 
598
        TRACE_((THIS_FILE, "Sound device stopped"));
 
599
    } else if (state_ == STATE_INITIALIZING) {
 
600
        // Initialization is on progress, so let's set the state to
 
601
        // STATE_PENDING_STOP to prevent it starting the stream.
 
602
        state_ = STATE_PENDING_STOP;
 
603
 
 
604
        // Then wait until initialization done.
 
605
        while (state_ != STATE_READY && state_ != STATE_NULL)
 
606
            pj_symbianos_poll(-1, 100);
 
607
    }
 
608
}
 
609
 
 
610
void CPjAudioEngine::ConstructL()
 
611
{
 
612
    // Recorder settings
 
613
    iRecSettings.iFourCC                = setting_.fourcc;
 
614
    iRecSettings.iGlobal                = APP_UID;
 
615
    iRecSettings.iPriority              = TMdaPriority(100);
 
616
    iRecSettings.iPreference            = TMdaPriorityPreference(0x05210001);
 
617
    iRecSettings.iSettings.iChannels    = EMMFMono;
 
618
    iRecSettings.iSettings.iSampleRate  = EMMFSampleRate8000Hz;
 
619
 
 
620
    // Player settings
 
621
    iPlaySettings.iFourCC               = setting_.fourcc;
 
622
    iPlaySettings.iGlobal               = APP_UID;
 
623
    iPlaySettings.iPriority             = TMdaPriority(100);
 
624
    iPlaySettings.iPreference           = TMdaPriorityPreference(0x05220001);
 
625
    iPlaySettings.iSettings.iChannels   = EMMFMono;
 
626
    iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
 
627
    iPlaySettings.iSettings.iVolume     = 0;
 
628
 
 
629
    User::LeaveIfError(iSession.Connect());
 
630
    session_opened = ETrue;
 
631
}
 
632
 
 
633
TInt CPjAudioEngine::StartStreamL()
 
634
{
 
635
    pj_assert(state_==STATE_READY || state_==STATE_INITIALIZING);
 
636
 
 
637
    iSession.SetCng(setting_.cng);
 
638
    iSession.SetVadMode(setting_.vad);
 
639
    iSession.SetPlc(setting_.plc);
 
640
    iSession.SetEncoderMode(setting_.mode);
 
641
    iSession.SetDecoderMode(setting_.mode);
 
642
    iSession.ActivateLoudspeaker(setting_.loudspk);
 
643
 
 
644
    // Not only capture
 
645
    if (parentStrm_->param.dir != PJMEDIA_DIR_CAPTURE) {
 
646
        iSession.Write();
 
647
        TRACE_((THIS_FILE, "Player started"));
 
648
    }
 
649
 
 
650
    // Not only playback
 
651
    if (parentStrm_->param.dir != PJMEDIA_DIR_PLAYBACK) {
 
652
        iSession.Read();
 
653
        TRACE_((THIS_FILE, "Recorder started"));
 
654
    }
 
655
 
 
656
    state_ = STATE_STREAMING;
 
657
 
 
658
    return 0;
 
659
}
 
660
 
 
661
void CPjAudioEngine::Deinit()
 
662
{
 
663
    Stop();
 
664
 
 
665
    delete iRecHandler;
 
666
    delete iPlayCommHandler;
 
667
    delete iRecCommHandler;
 
668
 
 
669
    if (session_opened) {
 
670
        enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
 
671
 
 
672
        // On some devices, immediate closing after stopping may cause
 
673
        // APS server panic KERN-EXEC 0, so let's wait for sometime before
 
674
        // closing the client session.
 
675
        snd_wait(APS_CLOSE_WAIT_TIME);
 
676
 
 
677
        iSession.Close();
 
678
        session_opened = EFalse;
 
679
    }
 
680
 
 
681
    if (readq_opened) {
 
682
        iReadQ.Close();
 
683
        iReadCommQ.Close();
 
684
        readq_opened = EFalse;
 
685
    }
 
686
 
 
687
    if (writeq_opened) {
 
688
        iWriteQ.Close();
 
689
        iWriteCommQ.Close();
 
690
        writeq_opened = EFalse;
 
691
    }
 
692
 
 
693
    state_ = STATE_NULL;
 
694
}
 
695
 
 
696
void CPjAudioEngine::InputStreamInitialized(const TInt aStatus)
 
697
{
 
698
    TRACE_((THIS_FILE, "Recorder initialized, err=%d", aStatus));
 
699
 
 
700
    if (aStatus == KErrNone) {
 
701
        // Don't start the stream since Stop() has been requested.
 
702
        if (state_ != STATE_PENDING_STOP) {
 
703
            StartStreamL();
 
704
        } else {
 
705
            state_ = STATE_READY;
 
706
        }
 
707
    } else {
 
708
        Deinit();
 
709
    }
 
710
}
 
711
 
 
712
void CPjAudioEngine::OutputStreamInitialized(const TInt aStatus)
 
713
{
 
714
    TRACE_((THIS_FILE, "Player initialized, err=%d", aStatus));
 
715
 
 
716
    if (aStatus == KErrNone) {
 
717
        if (parentStrm_->param.dir == PJMEDIA_DIR_PLAYBACK) {
 
718
            // Don't start the stream since Stop() has been requested.
 
719
            if (state_ != STATE_PENDING_STOP) {
 
720
                StartStreamL();
 
721
            } else {
 
722
                state_ = STATE_READY;
 
723
            }
 
724
        } else
 
725
            InitRecL();
 
726
    } else {
 
727
        Deinit();
 
728
    }
 
729
}
 
730
 
 
731
void CPjAudioEngine::NotifyError(const TInt aError)
 
732
{
 
733
    Deinit();
 
734
    snd_perror("Error from CQueueHandler", aError);
 
735
}
 
736
 
 
737
TInt CPjAudioEngine::ActivateSpeaker(TBool active)
 
738
{
 
739
    if (state_ == STATE_READY || state_ == STATE_STREAMING) {
 
740
        iSession.ActivateLoudspeaker(active);
 
741
        TRACE_((THIS_FILE, "Loudspeaker turned %s", (active? "on":"off")));
 
742
        return KErrNone;
 
743
    }
 
744
    return KErrNotReady;
 
745
}
 
746
 
 
747
/****************************************************************************
 
748
 * Internal APS callbacks for PCM format
 
749
 */
 
750
 
 
751
static void RecCbPcm(TAPSCommBuffer &buf, void *user_data)
 
752
{
 
753
    struct aps_stream *strm = (struct aps_stream*) user_data;
 
754
 
 
755
    /* Buffer has to contain normal speech. */
 
756
    pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
 
757
 
 
758
    /* Detect the recorder G.711 frame size, player frame size will follow
 
759
     * this recorder frame size.
 
760
     */
 
761
    if (aps_g711_frame_len == 0) {
 
762
        aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
 
763
        TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
 
764
                aps_g711_frame_len));
 
765
    }
 
766
 
 
767
    /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.
 
768
     * Whenever rec_buf is full, call parent stream callback.
 
769
     */
 
770
    unsigned samples_processed = 0;
 
771
 
 
772
    while (samples_processed < aps_g711_frame_len) {
 
773
        unsigned samples_to_process;
 
774
        unsigned samples_req;
 
775
 
 
776
        samples_to_process = aps_g711_frame_len - samples_processed;
 
777
        samples_req = (strm->param.samples_per_frame /
 
778
                       strm->param.channel_count /
 
779
                       strm->resample_factor) -
 
780
                      strm->rec_buf_len;
 
781
        if (samples_to_process > samples_req)
 
782
            samples_to_process = samples_req;
 
783
 
 
784
        pjmedia_ulaw_decode(&strm->rec_buf[strm->rec_buf_len],
 
785
                            buf.iBuffer.Ptr() + 2 + samples_processed,
 
786
                            samples_to_process);
 
787
 
 
788
        strm->rec_buf_len += samples_to_process;
 
789
        samples_processed += samples_to_process;
 
790
 
 
791
        /* Buffer is full, time to call parent callback */
 
792
        if (strm->rec_buf_len == strm->param.samples_per_frame /
 
793
                                 strm->param.channel_count /
 
794
                                 strm->resample_factor)
 
795
        {
 
796
            pjmedia_frame f;
 
797
 
 
798
            /* Need to resample clock rate? */
 
799
            if (strm->rec_resample) {
 
800
                unsigned resampled = 0;
 
801
 
 
802
                while (resampled < strm->rec_buf_len) {
 
803
                    pjmedia_resample_run(strm->rec_resample,
 
804
                                &strm->rec_buf[resampled],
 
805
                                strm->pcm_buf +
 
806
                                resampled * strm->resample_factor);
 
807
                    resampled += 80;
 
808
                }
 
809
                f.buf = strm->pcm_buf;
 
810
            } else {
 
811
                f.buf = strm->rec_buf;
 
812
            }
 
813
 
 
814
            /* Need to convert channel count? */
 
815
            if (strm->param.channel_count != 1) {
 
816
                pjmedia_convert_channel_1ton((pj_int16_t*)f.buf,
 
817
                                             (pj_int16_t*)f.buf,
 
818
                                             strm->param.channel_count,
 
819
                                             strm->param.samples_per_frame /
 
820
                                             strm->param.channel_count,
 
821
                                             0);
 
822
            }
 
823
 
 
824
            /* Call parent callback */
 
825
            f.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
826
            f.size = strm->param.samples_per_frame << 1;
 
827
            strm->rec_cb(strm->user_data, &f);
 
828
            strm->rec_buf_len = 0;
 
829
        }
 
830
    }
 
831
}
 
832
 
 
833
static void PlayCbPcm(TAPSCommBuffer &buf, void *user_data)
 
834
{
 
835
    struct aps_stream *strm = (struct aps_stream*) user_data;
 
836
    unsigned g711_frame_len = aps_g711_frame_len;
 
837
 
 
838
    /* Init buffer attributes and header. */
 
839
    buf.iCommand = CQueueHandler::EAPSPlayData;
 
840
    buf.iStatus = 0;
 
841
    buf.iBuffer.Zero();
 
842
    buf.iBuffer.Append(1);
 
843
    buf.iBuffer.Append(0);
 
844
 
 
845
    /* Assume frame size is 10ms if frame size hasn't been known. */
 
846
    if (g711_frame_len == 0)
 
847
        g711_frame_len = 80;
 
848
 
 
849
    /* Call parent stream callback to get PCM samples to play,
 
850
     * encode the PCM samples into G.711 and put it into APS buffer.
 
851
     */
 
852
    unsigned samples_processed = 0;
 
853
 
 
854
    while (samples_processed < g711_frame_len) {
 
855
        /* Need more samples to play, time to call parent callback */
 
856
        if (strm->play_buf_len == 0) {
 
857
            pjmedia_frame f;
 
858
            unsigned samples_got;
 
859
 
 
860
            f.size = strm->param.samples_per_frame << 1;
 
861
            if (strm->play_resample || strm->param.channel_count != 1)
 
862
                f.buf = strm->pcm_buf;
 
863
            else
 
864
                f.buf = strm->play_buf;
 
865
 
 
866
            /* Call parent callback */
 
867
            strm->play_cb(strm->user_data, &f);
 
868
            if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
 
869
                pjmedia_zero_samples((pj_int16_t*)f.buf,
 
870
                                     strm->param.samples_per_frame);
 
871
            }
 
872
 
 
873
            samples_got = strm->param.samples_per_frame /
 
874
                          strm->param.channel_count /
 
875
                          strm->resample_factor;
 
876
 
 
877
            /* Need to convert channel count? */
 
878
            if (strm->param.channel_count != 1) {
 
879
                pjmedia_convert_channel_nto1((pj_int16_t*)f.buf,
 
880
                                             (pj_int16_t*)f.buf,
 
881
                                             strm->param.channel_count,
 
882
                                             strm->param.samples_per_frame,
 
883
                                             PJ_FALSE,
 
884
                                             0);
 
885
            }
 
886
 
 
887
            /* Need to resample clock rate? */
 
888
            if (strm->play_resample) {
 
889
                unsigned resampled = 0;
 
890
 
 
891
                while (resampled < samples_got)
 
892
                {
 
893
                    pjmedia_resample_run(strm->play_resample,
 
894
                                strm->pcm_buf +
 
895
                                resampled * strm->resample_factor,
 
896
                                &strm->play_buf[resampled]);
 
897
                    resampled += 80;
 
898
                }
 
899
            }
 
900
 
 
901
            strm->play_buf_len = samples_got;
 
902
            strm->play_buf_start = 0;
 
903
        }
 
904
 
 
905
        unsigned tmp;
 
906
 
 
907
        tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - samples_processed);
 
908
        pjmedia_ulaw_encode((pj_uint8_t*)&strm->play_buf[strm->play_buf_start],
 
909
                            &strm->play_buf[strm->play_buf_start],
 
910
                            tmp);
 
911
        buf.iBuffer.Append((TUint8*)&strm->play_buf[strm->play_buf_start], tmp);
 
912
        samples_processed += tmp;
 
913
        strm->play_buf_len -= tmp;
 
914
        strm->play_buf_start += tmp;
 
915
    }
 
916
}
 
917
 
 
918
/****************************************************************************
 
919
 * Internal APS callbacks for non-PCM format
 
920
 */
 
921
 
 
922
static void RecCb(TAPSCommBuffer &buf, void *user_data)
 
923
{
 
924
    struct aps_stream *strm = (struct aps_stream*) user_data;
 
925
    pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->rec_buf;
 
926
 
 
927
    switch(strm->param.ext_fmt.id) {
 
928
    case PJMEDIA_FORMAT_AMR:
 
929
        {
 
930
            const pj_uint8_t *p = (const pj_uint8_t*)buf.iBuffer.Ptr() + 1;
 
931
            unsigned len = buf.iBuffer.Length() - 1;
 
932
 
 
933
            pjmedia_frame_ext_append_subframe(frame, p, len << 3, 160);
 
934
            if (frame->samples_cnt == strm->param.samples_per_frame) {
 
935
                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
 
936
                strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
 
937
                frame->samples_cnt = 0;
 
938
                frame->subframe_cnt = 0;
 
939
            }
 
940
        }
 
941
        break;
 
942
 
 
943
    case PJMEDIA_FORMAT_G729:
 
944
        {
 
945
            /* Check if we got a normal or SID frame. */
 
946
            if (buf.iBuffer[0] != 0 || buf.iBuffer[1] != 0) {
 
947
                enum { NORMAL_LEN = 22, SID_LEN = 8 };
 
948
                TBitStream *bitstream = (TBitStream*)strm->strm_data;
 
949
                unsigned src_len = buf.iBuffer.Length()- 2;
 
950
 
 
951
                pj_assert(src_len == NORMAL_LEN || src_len == SID_LEN);
 
952
 
 
953
                const TDesC8& p = bitstream->CompressG729Frame(
 
954
                                            buf.iBuffer.Right(src_len),
 
955
                                            src_len == SID_LEN);
 
956
 
 
957
                pjmedia_frame_ext_append_subframe(frame, p.Ptr(),
 
958
                                                  p.Length() << 3, 80);
 
959
            } else { /* We got null frame. */
 
960
                pjmedia_frame_ext_append_subframe(frame, NULL, 0, 80);
 
961
            }
 
962
 
 
963
            if (frame->samples_cnt == strm->param.samples_per_frame) {
 
964
                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
 
965
                strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
 
966
                frame->samples_cnt = 0;
 
967
                frame->subframe_cnt = 0;
 
968
            }
 
969
        }
 
970
        break;
 
971
 
 
972
    case PJMEDIA_FORMAT_ILBC:
 
973
        {
 
974
            unsigned samples_got;
 
975
 
 
976
            samples_got = strm->param.ext_fmt.bitrate == 15200? 160 : 240;
 
977
 
 
978
            /* Check if we got a normal frame. */
 
979
            if (buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0) {
 
980
                const pj_uint8_t *p = (const pj_uint8_t*)buf.iBuffer.Ptr() + 2;
 
981
                unsigned len = buf.iBuffer.Length() - 2;
 
982
 
 
983
                pjmedia_frame_ext_append_subframe(frame, p, len << 3,
 
984
                                                  samples_got);
 
985
            } else { /* We got null frame. */
 
986
                pjmedia_frame_ext_append_subframe(frame, NULL, 0, samples_got);
 
987
            }
 
988
 
 
989
            if (frame->samples_cnt == strm->param.samples_per_frame) {
 
990
                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
 
991
                strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
 
992
                frame->samples_cnt = 0;
 
993
                frame->subframe_cnt = 0;
 
994
            }
 
995
        }
 
996
        break;
 
997
 
 
998
    case PJMEDIA_FORMAT_PCMU:
 
999
    case PJMEDIA_FORMAT_PCMA:
 
1000
        {
 
1001
            unsigned samples_processed = 0;
 
1002
 
 
1003
            /* Make sure it is normal frame. */
 
1004
            pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
 
1005
 
 
1006
            /* Detect the recorder G.711 frame size, player frame size will
 
1007
             * follow this recorder frame size.
 
1008
             */
 
1009
            if (aps_g711_frame_len == 0) {
 
1010
                aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
 
1011
                TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
 
1012
                        aps_g711_frame_len));
 
1013
            }
 
1014
 
 
1015
            /* Convert APS buffer format into pjmedia_frame_ext. Whenever
 
1016
             * samples count in the frame is equal to stream's samples per
 
1017
             * frame, call parent stream callback.
 
1018
             */
 
1019
            while (samples_processed < aps_g711_frame_len) {
 
1020
                unsigned tmp;
 
1021
                const pj_uint8_t *pb = (const pj_uint8_t*)buf.iBuffer.Ptr() +
 
1022
                                       2 + samples_processed;
 
1023
 
 
1024
                tmp = PJ_MIN(strm->param.samples_per_frame - frame->samples_cnt,
 
1025
                             aps_g711_frame_len - samples_processed);
 
1026
 
 
1027
                pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp);
 
1028
                samples_processed += tmp;
 
1029
 
 
1030
                if (frame->samples_cnt == strm->param.samples_per_frame) {
 
1031
                    frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
 
1032
                    strm->rec_cb(strm->user_data, (pjmedia_frame*)frame);
 
1033
                    frame->samples_cnt = 0;
 
1034
                    frame->subframe_cnt = 0;
 
1035
                }
 
1036
            }
 
1037
        }
 
1038
        break;
 
1039
 
 
1040
    default:
 
1041
        break;
 
1042
    }
 
1043
}
 
1044
 
 
1045
static void PlayCb(TAPSCommBuffer &buf, void *user_data)
 
1046
{
 
1047
    struct aps_stream *strm = (struct aps_stream*) user_data;
 
1048
    pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->play_buf;
 
1049
 
 
1050
    /* Init buffer attributes and header. */
 
1051
    buf.iCommand = CQueueHandler::EAPSPlayData;
 
1052
    buf.iStatus = 0;
 
1053
    buf.iBuffer.Zero();
 
1054
 
 
1055
    switch(strm->param.ext_fmt.id) {
 
1056
    case PJMEDIA_FORMAT_AMR:
 
1057
        {
 
1058
            if (frame->samples_cnt == 0) {
 
1059
                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
 
1060
                strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
 
1061
                pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
 
1062
                          frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
 
1063
            }
 
1064
 
 
1065
            if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 
1066
                pjmedia_frame_ext_subframe *sf;
 
1067
                unsigned samples_cnt;
 
1068
 
 
1069
                sf = pjmedia_frame_ext_get_subframe(frame, 0);
 
1070
                samples_cnt = frame->samples_cnt / frame->subframe_cnt;
 
1071
 
 
1072
                if (sf->data && sf->bitlen) {
 
1073
                    /* AMR header for APS is one byte, the format (may be!):
 
1074
                     * 0xxxxy00, where xxxx:frame type, y:not sure.
 
1075
                     */
 
1076
                    unsigned len = (sf->bitlen+7)>>3;
 
1077
                    enum {SID_FT = 8 };
 
1078
                    pj_uint8_t amr_header = 4, ft = SID_FT;
 
1079
 
 
1080
                    if (len >= pjmedia_codec_amrnb_framelen[0])
 
1081
                        ft = pjmedia_codec_amr_get_mode2(PJ_TRUE, len);
 
1082
 
 
1083
                    amr_header |= ft << 3;
 
1084
                    buf.iBuffer.Append(amr_header);
 
1085
 
 
1086
                    buf.iBuffer.Append((TUint8*)sf->data, len);
 
1087
                } else {
 
1088
                    enum {NO_DATA_FT = 15 };
 
1089
                    pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
 
1090
 
 
1091
                    buf.iBuffer.Append(amr_header);
 
1092
                }
 
1093
 
 
1094
                pjmedia_frame_ext_pop_subframes(frame, 1);
 
1095
 
 
1096
            } else { /* PJMEDIA_FRAME_TYPE_NONE */
 
1097
                enum {NO_DATA_FT = 15 };
 
1098
                pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
 
1099
 
 
1100
                buf.iBuffer.Append(amr_header);
 
1101
 
 
1102
                frame->samples_cnt = 0;
 
1103
                frame->subframe_cnt = 0;
 
1104
            }
 
1105
        }
 
1106
        break;
 
1107
 
 
1108
    case PJMEDIA_FORMAT_G729:
 
1109
        {
 
1110
            if (frame->samples_cnt == 0) {
 
1111
                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
 
1112
                strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
 
1113
                pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
 
1114
                          frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
 
1115
            }
 
1116
 
 
1117
            if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 
1118
                pjmedia_frame_ext_subframe *sf;
 
1119
                unsigned samples_cnt;
 
1120
 
 
1121
                sf = pjmedia_frame_ext_get_subframe(frame, 0);
 
1122
                samples_cnt = frame->samples_cnt / frame->subframe_cnt;
 
1123
 
 
1124
                if (sf->data && sf->bitlen) {
 
1125
                    enum { NORMAL_LEN = 10, SID_LEN = 2 };
 
1126
                    pj_bool_t sid_frame = ((sf->bitlen >> 3) == SID_LEN);
 
1127
                    TBitStream *bitstream = (TBitStream*)strm->strm_data;
 
1128
                    const TPtrC8 src(sf->data, sf->bitlen>>3);
 
1129
                    const TDesC8 &dst = bitstream->ExpandG729Frame(src,
 
1130
                                                                   sid_frame);
 
1131
                    if (sid_frame) {
 
1132
                        buf.iBuffer.Append(2);
 
1133
                        buf.iBuffer.Append(0);
 
1134
                    } else {
 
1135
                        buf.iBuffer.Append(1);
 
1136
                        buf.iBuffer.Append(0);
 
1137
                    }
 
1138
                    buf.iBuffer.Append(dst);
 
1139
                } else {
 
1140
                    buf.iBuffer.Append(2);
 
1141
                    buf.iBuffer.Append(0);
 
1142
                    buf.iBuffer.AppendFill(0, 22);
 
1143
                }
 
1144
 
 
1145
                pjmedia_frame_ext_pop_subframes(frame, 1);
 
1146
 
 
1147
            } else { /* PJMEDIA_FRAME_TYPE_NONE */
 
1148
                buf.iBuffer.Append(2);
 
1149
                buf.iBuffer.Append(0);
 
1150
                buf.iBuffer.AppendFill(0, 22);
 
1151
 
 
1152
                frame->samples_cnt = 0;
 
1153
                frame->subframe_cnt = 0;
 
1154
            }
 
1155
        }
 
1156
        break;
 
1157
 
 
1158
    case PJMEDIA_FORMAT_ILBC:
 
1159
        {
 
1160
            if (frame->samples_cnt == 0) {
 
1161
                frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
 
1162
                strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
 
1163
                pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
 
1164
                          frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
 
1165
            }
 
1166
 
 
1167
            if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 
1168
                pjmedia_frame_ext_subframe *sf;
 
1169
                unsigned samples_cnt;
 
1170
 
 
1171
                sf = pjmedia_frame_ext_get_subframe(frame, 0);
 
1172
                samples_cnt = frame->samples_cnt / frame->subframe_cnt;
 
1173
 
 
1174
                pj_assert((strm->param.ext_fmt.bitrate == 15200 &&
 
1175
                           samples_cnt == 160) ||
 
1176
                          (strm->param.ext_fmt.bitrate != 15200 &&
 
1177
                           samples_cnt == 240));
 
1178
 
 
1179
                if (sf->data && sf->bitlen) {
 
1180
                    buf.iBuffer.Append(1);
 
1181
                    buf.iBuffer.Append(0);
 
1182
                    buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
 
1183
                } else {
 
1184
                    buf.iBuffer.Append(0);
 
1185
                    buf.iBuffer.Append(0);
 
1186
                }
 
1187
 
 
1188
                pjmedia_frame_ext_pop_subframes(frame, 1);
 
1189
 
 
1190
            } else { /* PJMEDIA_FRAME_TYPE_NONE */
 
1191
                buf.iBuffer.Append(0);
 
1192
                buf.iBuffer.Append(0);
 
1193
 
 
1194
                frame->samples_cnt = 0;
 
1195
                frame->subframe_cnt = 0;
 
1196
            }
 
1197
        }
 
1198
        break;
 
1199
 
 
1200
    case PJMEDIA_FORMAT_PCMU:
 
1201
    case PJMEDIA_FORMAT_PCMA:
 
1202
        {
 
1203
            unsigned samples_ready = 0;
 
1204
            unsigned samples_req = aps_g711_frame_len;
 
1205
 
 
1206
            /* Assume frame size is 10ms if frame size hasn't been known. */
 
1207
            if (samples_req == 0)
 
1208
                samples_req = 80;
 
1209
 
 
1210
            buf.iBuffer.Append(1);
 
1211
            buf.iBuffer.Append(0);
 
1212
 
 
1213
            /* Call parent stream callback to get samples to play. */
 
1214
            while (samples_ready < samples_req) {
 
1215
                if (frame->samples_cnt == 0) {
 
1216
                    frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
 
1217
                    strm->play_cb(strm->user_data, (pjmedia_frame*)frame);
 
1218
                    pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED ||
 
1219
                              frame->base.type==PJMEDIA_FRAME_TYPE_NONE);
 
1220
                }
 
1221
 
 
1222
                if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 
1223
                    pjmedia_frame_ext_subframe *sf;
 
1224
                    unsigned samples_cnt;
 
1225
 
 
1226
                    sf = pjmedia_frame_ext_get_subframe(frame, 0);
 
1227
                    samples_cnt = frame->samples_cnt / frame->subframe_cnt;
 
1228
                    if (sf->data && sf->bitlen) {
 
1229
                        buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
 
1230
                    } else {
 
1231
                        pj_uint8_t silc;
 
1232
                        silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
 
1233
                                pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
 
1234
                        buf.iBuffer.AppendFill(silc, samples_cnt);
 
1235
                    }
 
1236
                    samples_ready += samples_cnt;
 
1237
 
 
1238
                    pjmedia_frame_ext_pop_subframes(frame, 1);
 
1239
 
 
1240
                } else { /* PJMEDIA_FRAME_TYPE_NONE */
 
1241
                    pj_uint8_t silc;
 
1242
 
 
1243
                    silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)?
 
1244
                            pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0);
 
1245
                    buf.iBuffer.AppendFill(silc, samples_req - samples_ready);
 
1246
 
 
1247
                    samples_ready = samples_req;
 
1248
                    frame->samples_cnt = 0;
 
1249
                    frame->subframe_cnt = 0;
 
1250
                }
 
1251
            }
 
1252
        }
 
1253
        break;
 
1254
 
 
1255
    default:
 
1256
        break;
 
1257
    }
 
1258
}
 
1259
 
 
1260
 
 
1261
/****************************************************************************
 
1262
 * Factory operations
 
1263
 */
 
1264
 
 
1265
/*
 
1266
 * C compatible declaration of APS factory.
 
1267
 */
 
1268
PJ_BEGIN_DECL
 
1269
PJ_DECL(pjmedia_aud_dev_factory*) pjmedia_aps_factory(pj_pool_factory *pf);
 
1270
PJ_END_DECL
 
1271
 
 
1272
/*
 
1273
 * Init APS audio driver.
 
1274
 */
 
1275
PJ_DEF(pjmedia_aud_dev_factory*) pjmedia_aps_factory(pj_pool_factory *pf)
 
1276
{
 
1277
    struct aps_factory *f;
 
1278
    pj_pool_t *pool;
 
1279
 
 
1280
    pool = pj_pool_create(pf, "APS", 1000, 1000, NULL);
 
1281
    f = PJ_POOL_ZALLOC_T(pool, struct aps_factory);
 
1282
    f->pf = pf;
 
1283
    f->pool = pool;
 
1284
    f->base.op = &factory_op;
 
1285
 
 
1286
    return &f->base;
 
1287
}
 
1288
 
 
1289
/* API: init factory */
 
1290
static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
 
1291
{
 
1292
    struct aps_factory *af = (struct aps_factory*)f;
 
1293
 
 
1294
    pj_ansi_strcpy(af->dev_info.name, "S60 APS");
 
1295
    af->dev_info.default_samples_per_sec = 8000;
 
1296
    af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_EXT_FORMAT |
 
1297
                        //PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
 
1298
                        PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
 
1299
                        PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
 
1300
                        PJMEDIA_AUD_DEV_CAP_VAD |
 
1301
                        PJMEDIA_AUD_DEV_CAP_CNG;
 
1302
    af->dev_info.routes = PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
 
1303
                          PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
 
1304
    af->dev_info.input_count = 1;
 
1305
    af->dev_info.output_count = 1;
 
1306
 
 
1307
    /* Enumerate codecs by trying to initialize each codec and examining
 
1308
     * the error code. Consider the following:
 
1309
     * - not possible to reinitialize the same APS session with
 
1310
     *   different settings,
 
1311
     * - closing APS session and trying to immediately reconnect may fail,
 
1312
     *   clients should wait ~5s before attempting to reconnect.
 
1313
     */
 
1314
 
 
1315
    unsigned i, fmt_cnt = 0;
 
1316
    pj_bool_t g711_supported = PJ_FALSE;
 
1317
 
 
1318
    /* Do not change the order! */
 
1319
    TFourCC fourcc[] = {
 
1320
        TFourCC(KMCPFourCCIdAMRNB),
 
1321
        TFourCC(KMCPFourCCIdG711),
 
1322
        TFourCC(KMCPFourCCIdG729),
 
1323
        TFourCC(KMCPFourCCIdILBC)
 
1324
    };
 
1325
 
 
1326
    for (i = 0; i < PJ_ARRAY_SIZE(fourcc); ++i) {
 
1327
        pj_bool_t supported = PJ_FALSE;
 
1328
        unsigned retry_cnt = 0;
 
1329
        enum { MAX_RETRY = 3 };
 
1330
 
 
1331
#if (PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC == 0)
 
1332
        /* Codec detection is disabled */
 
1333
        supported = PJ_TRUE;
 
1334
#elif (PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC == 1)
 
1335
        /* Minimal codec detection, AMR-NB and G.711 only */
 
1336
        if (i > 1) {
 
1337
            /* If G.711 has been checked, skip G.729 and iLBC checks */
 
1338
            retry_cnt = MAX_RETRY;
 
1339
            supported = g711_supported;
 
1340
        }
 
1341
#endif
 
1342
 
 
1343
        while (!supported && ++retry_cnt <= MAX_RETRY) {
 
1344
            RAPSSession iSession;
 
1345
            TAPSInitSettings iPlaySettings;
 
1346
            TAPSInitSettings iRecSettings;
 
1347
            TInt err;
 
1348
 
 
1349
            // Recorder settings
 
1350
            iRecSettings.iGlobal                = APP_UID;
 
1351
            iRecSettings.iPriority              = TMdaPriority(100);
 
1352
            iRecSettings.iPreference            = TMdaPriorityPreference(0x05210001);
 
1353
            iRecSettings.iSettings.iChannels    = EMMFMono;
 
1354
            iRecSettings.iSettings.iSampleRate  = EMMFSampleRate8000Hz;
 
1355
 
 
1356
            // Player settings
 
1357
            iPlaySettings.iGlobal               = APP_UID;
 
1358
            iPlaySettings.iPriority             = TMdaPriority(100);
 
1359
            iPlaySettings.iPreference           = TMdaPriorityPreference(0x05220001);
 
1360
            iPlaySettings.iSettings.iChannels   = EMMFMono;
 
1361
            iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
 
1362
 
 
1363
            iRecSettings.iFourCC = iPlaySettings.iFourCC = fourcc[i];
 
1364
 
 
1365
            err = iSession.Connect();
 
1366
            if (err == KErrNone)
 
1367
                err = iSession.InitializePlayer(iPlaySettings);
 
1368
            if (err == KErrNone)
 
1369
                err = iSession.InitializeRecorder(iRecSettings);
 
1370
 
 
1371
            // On some devices, immediate closing causes APS Server panic,
 
1372
            // e.g: N95, so let's just wait for some time before closing.
 
1373
            enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
 
1374
            snd_wait(APS_CLOSE_WAIT_TIME);
 
1375
 
 
1376
            iSession.Close();
 
1377
 
 
1378
            if (err == KErrNone) {
 
1379
                /* All fine, stop retyring */
 
1380
                supported = PJ_TRUE;
 
1381
            }  else if (err == KErrAlreadyExists && retry_cnt < MAX_RETRY) {
 
1382
                /* Seems that the previous session is still arround,
 
1383
                 * let's wait before retrying.
 
1384
                 */
 
1385
                enum { RETRY_WAIT_TIME = 3000 }; /* in msecs */
 
1386
                snd_wait(RETRY_WAIT_TIME);
 
1387
            } else {
 
1388
                /* Seems that this format is not supported */
 
1389
                retry_cnt = MAX_RETRY;
 
1390
            }
 
1391
        }
 
1392
 
 
1393
        if (supported) {
 
1394
            switch(i) {
 
1395
            case 0: /* AMRNB */
 
1396
                af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_AMR;
 
1397
                af->dev_info.ext_fmt[fmt_cnt].bitrate = 7400;
 
1398
                af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
 
1399
                ++fmt_cnt;
 
1400
                break;
 
1401
            case 1: /* G.711 */
 
1402
                af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMU;
 
1403
                af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000;
 
1404
                af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
 
1405
                ++fmt_cnt;
 
1406
                af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMA;
 
1407
                af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000;
 
1408
                af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
 
1409
                ++fmt_cnt;
 
1410
                g711_supported = PJ_TRUE;
 
1411
                break;
 
1412
            case 2: /* G.729 */
 
1413
                af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_G729;
 
1414
                af->dev_info.ext_fmt[fmt_cnt].bitrate = 8000;
 
1415
                af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
 
1416
                ++fmt_cnt;
 
1417
                break;
 
1418
            case 3: /* iLBC */
 
1419
                af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_ILBC;
 
1420
                af->dev_info.ext_fmt[fmt_cnt].bitrate = 13333;
 
1421
                af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
 
1422
                ++fmt_cnt;
 
1423
                break;
 
1424
            }
 
1425
        }
 
1426
    }
 
1427
 
 
1428
    af->dev_info.ext_fmt_cnt = fmt_cnt;
 
1429
 
 
1430
    PJ_LOG(4, (THIS_FILE, "APS initialized"));
 
1431
 
 
1432
    return PJ_SUCCESS;
 
1433
}
 
1434
 
 
1435
/* API: destroy factory */
 
1436
static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
 
1437
{
 
1438
    struct aps_factory *af = (struct aps_factory*)f;
 
1439
    pj_pool_t *pool = af->pool;
 
1440
 
 
1441
    af->pool = NULL;
 
1442
    pj_pool_release(pool);
 
1443
 
 
1444
    PJ_LOG(4, (THIS_FILE, "APS destroyed"));
 
1445
 
 
1446
    return PJ_SUCCESS;
 
1447
}
 
1448
 
 
1449
/* API: refresh the device list */
 
1450
static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
 
1451
{
 
1452
    PJ_UNUSED_ARG(f);
 
1453
    return PJ_ENOTSUP;
 
1454
}
 
1455
 
 
1456
/* API: get number of devices */
 
1457
static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
 
1458
{
 
1459
    PJ_UNUSED_ARG(f);
 
1460
    return 1;
 
1461
}
 
1462
 
 
1463
/* API: get device info */
 
1464
static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
 
1465
                                        unsigned index,
 
1466
                                        pjmedia_aud_dev_info *info)
 
1467
{
 
1468
    struct aps_factory *af = (struct aps_factory*)f;
 
1469
 
 
1470
    PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
 
1471
 
 
1472
    pj_memcpy(info, &af->dev_info, sizeof(*info));
 
1473
 
 
1474
    return PJ_SUCCESS;
 
1475
}
 
1476
 
 
1477
/* API: create default device parameter */
 
1478
static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
 
1479
                                         unsigned index,
 
1480
                                         pjmedia_aud_param *param)
 
1481
{
 
1482
    struct aps_factory *af = (struct aps_factory*)f;
 
1483
 
 
1484
    PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
 
1485
 
 
1486
    pj_bzero(param, sizeof(*param));
 
1487
    param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
 
1488
    param->rec_id = index;
 
1489
    param->play_id = index;
 
1490
    param->clock_rate = af->dev_info.default_samples_per_sec;
 
1491
    param->channel_count = 1;
 
1492
    param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
 
1493
    param->bits_per_sample = BITS_PER_SAMPLE;
 
1494
    param->flags = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
 
1495
    param->output_route = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
 
1496
 
 
1497
    return PJ_SUCCESS;
 
1498
}
 
1499
 
 
1500
 
 
1501
/* API: create stream */
 
1502
static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
 
1503
                                         const pjmedia_aud_param *param,
 
1504
                                         pjmedia_aud_rec_cb rec_cb,
 
1505
                                         pjmedia_aud_play_cb play_cb,
 
1506
                                         void *user_data,
 
1507
                                         pjmedia_aud_stream **p_aud_strm)
 
1508
{
 
1509
    struct aps_factory *af = (struct aps_factory*)f;
 
1510
    pj_pool_t *pool;
 
1511
    struct aps_stream *strm;
 
1512
 
 
1513
    CPjAudioSetting aps_setting;
 
1514
    PjAudioCallback aps_rec_cb;
 
1515
    PjAudioCallback aps_play_cb;
 
1516
 
 
1517
    /* Can only support 16bits per sample */
 
1518
    PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
 
1519
 
 
1520
    /* Supported clock rates:
 
1521
     * - for non-PCM format: 8kHz
 
1522
     * - for PCM format: 8kHz and 16kHz
 
1523
     */
 
1524
    PJ_ASSERT_RETURN(param->clock_rate == 8000 ||
 
1525
                     (param->clock_rate == 16000 &&
 
1526
                      param->ext_fmt.id == PJMEDIA_FORMAT_L16),
 
1527
                     PJ_EINVAL);
 
1528
 
 
1529
    /* Supported channels number:
 
1530
     * - for non-PCM format: mono
 
1531
     * - for PCM format: mono and stereo
 
1532
     */
 
1533
    PJ_ASSERT_RETURN(param->channel_count == 1 ||
 
1534
                     (param->channel_count == 2 &&
 
1535
                      param->ext_fmt.id == PJMEDIA_FORMAT_L16),
 
1536
                     PJ_EINVAL);
 
1537
 
 
1538
    /* Create and Initialize stream descriptor */
 
1539
    pool = pj_pool_create(af->pf, "aps-dev", 1000, 1000, NULL);
 
1540
    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
 
1541
 
 
1542
    strm = PJ_POOL_ZALLOC_T(pool, struct aps_stream);
 
1543
    strm->pool = pool;
 
1544
    strm->param = *param;
 
1545
 
 
1546
    if (strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT == 0)
 
1547
        strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16;
 
1548
 
 
1549
    /* Set audio engine fourcc. */
 
1550
    switch(strm->param.ext_fmt.id) {
 
1551
    case PJMEDIA_FORMAT_L16:
 
1552
    case PJMEDIA_FORMAT_PCMU:
 
1553
    case PJMEDIA_FORMAT_PCMA:
 
1554
        aps_setting.fourcc = TFourCC(KMCPFourCCIdG711);
 
1555
        break;
 
1556
    case PJMEDIA_FORMAT_AMR:
 
1557
        aps_setting.fourcc = TFourCC(KMCPFourCCIdAMRNB);
 
1558
        break;
 
1559
    case PJMEDIA_FORMAT_G729:
 
1560
        aps_setting.fourcc = TFourCC(KMCPFourCCIdG729);
 
1561
        break;
 
1562
    case PJMEDIA_FORMAT_ILBC:
 
1563
        aps_setting.fourcc = TFourCC(KMCPFourCCIdILBC);
 
1564
        break;
 
1565
    default:
 
1566
        aps_setting.fourcc = 0;
 
1567
        break;
 
1568
    }
 
1569
 
 
1570
    /* Set audio engine mode. */
 
1571
    if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR)
 
1572
    {
 
1573
        aps_setting.mode = (TAPSCodecMode)strm->param.ext_fmt.bitrate;
 
1574
    }
 
1575
    else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
 
1576
             strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
 
1577
            (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC  &&
 
1578
             strm->param.ext_fmt.bitrate != 15200))
 
1579
    {
 
1580
        aps_setting.mode = EULawOr30ms;
 
1581
    }
 
1582
    else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
 
1583
            (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC &&
 
1584
             strm->param.ext_fmt.bitrate == 15200))
 
1585
    {
 
1586
        aps_setting.mode = EALawOr20ms;
 
1587
    }
 
1588
 
 
1589
    /* Disable VAD on L16, G711, and also G729 (G729's VAD potentially
 
1590
     * causes noise?).
 
1591
     */
 
1592
    if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
 
1593
        strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
 
1594
        strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
 
1595
        strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729)
 
1596
    {
 
1597
        aps_setting.vad = EFalse;
 
1598
    } else {
 
1599
        aps_setting.vad = strm->param.ext_fmt.vad;
 
1600
    }
 
1601
 
 
1602
    /* Set other audio engine attributes. */
 
1603
    aps_setting.plc = strm->param.plc_enabled;
 
1604
    aps_setting.cng = aps_setting.vad;
 
1605
    aps_setting.loudspk =
 
1606
                strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
 
1607
 
 
1608
    /* Set audio engine callbacks. */
 
1609
    if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
 
1610
        aps_play_cb = &PlayCbPcm;
 
1611
        aps_rec_cb  = &RecCbPcm;
 
1612
    } else {
 
1613
        aps_play_cb = &PlayCb;
 
1614
        aps_rec_cb  = &RecCb;
 
1615
    }
 
1616
 
 
1617
    strm->rec_cb = rec_cb;
 
1618
    strm->play_cb = play_cb;
 
1619
    strm->user_data = user_data;
 
1620
    strm->resample_factor = strm->param.clock_rate / 8000;
 
1621
 
 
1622
    /* play_buf size is samples per frame scaled in to 8kHz mono. */
 
1623
    strm->play_buf = (pj_int16_t*)pj_pool_zalloc(
 
1624
                                        pool,
 
1625
                                        (strm->param.samples_per_frame /
 
1626
                                        strm->resample_factor /
 
1627
                                        strm->param.channel_count) << 1);
 
1628
    strm->play_buf_len = 0;
 
1629
    strm->play_buf_start = 0;
 
1630
 
 
1631
    /* rec_buf size is samples per frame scaled in to 8kHz mono. */
 
1632
    strm->rec_buf  = (pj_int16_t*)pj_pool_zalloc(
 
1633
                                        pool,
 
1634
                                        (strm->param.samples_per_frame /
 
1635
                                        strm->resample_factor /
 
1636
                                        strm->param.channel_count) << 1);
 
1637
    strm->rec_buf_len = 0;
 
1638
 
 
1639
    if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
 
1640
        TBitStream *g729_bitstream = new TBitStream;
 
1641
 
 
1642
        PJ_ASSERT_RETURN(g729_bitstream, PJ_ENOMEM);
 
1643
        strm->strm_data = (void*)g729_bitstream;
 
1644
    }
 
1645
 
 
1646
    /* Init resampler when format is PCM and clock rate is not 8kHz */
 
1647
    if (strm->param.clock_rate != 8000 &&
 
1648
        strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16)
 
1649
    {
 
1650
        pj_status_t status;
 
1651
 
 
1652
        if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
 
1653
            /* Create resample for recorder */
 
1654
            status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
 
1655
                                              8000,
 
1656
                                              strm->param.clock_rate,
 
1657
                                              80,
 
1658
                                              &strm->rec_resample);
 
1659
            if (status != PJ_SUCCESS)
 
1660
                return status;
 
1661
        }
 
1662
 
 
1663
        if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
 
1664
            /* Create resample for player */
 
1665
            status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
 
1666
                                              strm->param.clock_rate,
 
1667
                                              8000,
 
1668
                                              80 * strm->resample_factor,
 
1669
                                              &strm->play_resample);
 
1670
            if (status != PJ_SUCCESS)
 
1671
                return status;
 
1672
        }
 
1673
    }
 
1674
 
 
1675
    /* Create PCM buffer, when the clock rate is not 8kHz or not mono */
 
1676
    if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 &&
 
1677
        (strm->resample_factor > 1 || strm->param.channel_count != 1))
 
1678
    {
 
1679
        strm->pcm_buf = (pj_int16_t*)pj_pool_zalloc(pool,
 
1680
                                        strm->param.samples_per_frame << 1);
 
1681
    }
 
1682
 
 
1683
 
 
1684
    /* Create the audio engine. */
 
1685
    TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
 
1686
                                                   aps_rec_cb, aps_play_cb,
 
1687
                                                   strm, aps_setting));
 
1688
    if (err != KErrNone) {
 
1689
        pj_pool_release(pool);
 
1690
        return PJ_RETURN_OS_ERROR(err);
 
1691
    }
 
1692
 
 
1693
    /* Apply output volume setting if specified */
 
1694
    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
 
1695
        stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
 
1696
                       &param->output_vol);
 
1697
    }
 
1698
 
 
1699
    /* Done */
 
1700
    strm->base.op = &stream_op;
 
1701
    *p_aud_strm = &strm->base;
 
1702
 
 
1703
    return PJ_SUCCESS;
 
1704
}
 
1705
 
 
1706
/* API: Get stream info. */
 
1707
static pj_status_t stream_get_param(pjmedia_aud_stream *s,
 
1708
                                    pjmedia_aud_param *pi)
 
1709
{
 
1710
    struct aps_stream *strm = (struct aps_stream*)s;
 
1711
 
 
1712
    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
 
1713
 
 
1714
    pj_memcpy(pi, &strm->param, sizeof(*pi));
 
1715
 
 
1716
    /* Update the output volume setting */
 
1717
    if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
 
1718
                       &pi->output_vol) == PJ_SUCCESS)
 
1719
    {
 
1720
        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
 
1721
    }
 
1722
 
 
1723
    return PJ_SUCCESS;
 
1724
}
 
1725
 
 
1726
/* API: get capability */
 
1727
static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
 
1728
                                  pjmedia_aud_dev_cap cap,
 
1729
                                  void *pval)
 
1730
{
 
1731
    struct aps_stream *strm = (struct aps_stream*)s;
 
1732
    pj_status_t status = PJ_ENOTSUP;
 
1733
 
 
1734
    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
 
1735
 
 
1736
    switch (cap) {
 
1737
    case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
 
1738
        if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
 
1739
            *(pjmedia_aud_dev_route*)pval = strm->param.output_route;
 
1740
            status = PJ_SUCCESS;
 
1741
        }
 
1742
        break;
 
1743
 
 
1744
    /* There is a case that GetMaxGain() stucks, e.g: in N95. */
 
1745
    /*
 
1746
    case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
 
1747
        if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
 
1748
            PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
 
1749
 
 
1750
            TInt max_gain = strm->engine->GetMaxGain();
 
1751
            TInt gain = strm->engine->GetGain();
 
1752
 
 
1753
            if (max_gain > 0 && gain >= 0) {
 
1754
                *(unsigned*)pval = gain * 100 / max_gain;
 
1755
                status = PJ_SUCCESS;
 
1756
            } else {
 
1757
                status = PJMEDIA_EAUD_NOTREADY;
 
1758
            }
 
1759
        }
 
1760
        break;
 
1761
    */
 
1762
 
 
1763
    case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
 
1764
        if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
 
1765
            PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
 
1766
 
 
1767
            TInt max_vol = strm->engine->GetMaxVolume();
 
1768
            TInt vol = strm->engine->GetVolume();
 
1769
 
 
1770
            if (max_vol > 0 && vol >= 0) {
 
1771
                *(unsigned*)pval = vol * 100 / max_vol;
 
1772
                status = PJ_SUCCESS;
 
1773
            } else {
 
1774
                status = PJMEDIA_EAUD_NOTREADY;
 
1775
            }
 
1776
        }
 
1777
        break;
 
1778
    default:
 
1779
        break;
 
1780
    }
 
1781
 
 
1782
    return status;
 
1783
}
 
1784
 
 
1785
/* API: set capability */
 
1786
static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
 
1787
                                  pjmedia_aud_dev_cap cap,
 
1788
                                  const void *pval)
 
1789
{
 
1790
    struct aps_stream *strm = (struct aps_stream*)s;
 
1791
    pj_status_t status = PJ_ENOTSUP;
 
1792
 
 
1793
    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
 
1794
 
 
1795
    switch (cap) {
 
1796
    case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
 
1797
        if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
 
1798
            pjmedia_aud_dev_route r = *(const pjmedia_aud_dev_route*)pval;
 
1799
            TInt err;
 
1800
 
 
1801
            PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
 
1802
 
 
1803
            switch (r) {
 
1804
            case PJMEDIA_AUD_DEV_ROUTE_DEFAULT:
 
1805
            case PJMEDIA_AUD_DEV_ROUTE_EARPIECE:
 
1806
                err = strm->engine->ActivateSpeaker(EFalse);
 
1807
                status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
 
1808
                break;
 
1809
            case PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER:
 
1810
                err = strm->engine->ActivateSpeaker(ETrue);
 
1811
                status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
 
1812
                break;
 
1813
            default:
 
1814
                status = PJ_EINVAL;
 
1815
                break;
 
1816
            }
 
1817
            if (status == PJ_SUCCESS)
 
1818
                strm->param.output_route = r;
 
1819
        }
 
1820
        break;
 
1821
 
 
1822
    /* There is a case that GetMaxGain() stucks, e.g: in N95. */
 
1823
    /*
 
1824
    case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
 
1825
        if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
 
1826
            PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
 
1827
 
 
1828
            TInt max_gain = strm->engine->GetMaxGain();
 
1829
            if (max_gain > 0) {
 
1830
                TInt gain, err;
 
1831
 
 
1832
                gain = *(unsigned*)pval * max_gain / 100;
 
1833
                err = strm->engine->SetGain(gain);
 
1834
                status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
 
1835
            } else {
 
1836
                status = PJMEDIA_EAUD_NOTREADY;
 
1837
            }
 
1838
            if (status == PJ_SUCCESS)
 
1839
                strm->param.input_vol = *(unsigned*)pval;
 
1840
        }
 
1841
        break;
 
1842
    */
 
1843
 
 
1844
    case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
 
1845
        if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
 
1846
            PJ_ASSERT_RETURN(strm->engine, PJ_EINVAL);
 
1847
 
 
1848
            TInt max_vol = strm->engine->GetMaxVolume();
 
1849
            if (max_vol > 0) {
 
1850
                TInt vol, err;
 
1851
 
 
1852
                vol = *(unsigned*)pval * max_vol / 100;
 
1853
                err = strm->engine->SetVolume(vol);
 
1854
                status = (err==KErrNone)? PJ_SUCCESS:PJ_RETURN_OS_ERROR(err);
 
1855
            } else {
 
1856
                status = PJMEDIA_EAUD_NOTREADY;
 
1857
            }
 
1858
            if (status == PJ_SUCCESS)
 
1859
                strm->param.output_vol = *(unsigned*)pval;
 
1860
        }
 
1861
        break;
 
1862
    default:
 
1863
        break;
 
1864
    }
 
1865
 
 
1866
    return status;
 
1867
}
 
1868
 
 
1869
/* API: Start stream. */
 
1870
static pj_status_t stream_start(pjmedia_aud_stream *strm)
 
1871
{
 
1872
    struct aps_stream *stream = (struct aps_stream*)strm;
 
1873
 
 
1874
    PJ_ASSERT_RETURN(stream, PJ_EINVAL);
 
1875
 
 
1876
    if (stream->engine) {
 
1877
        TInt err = stream->engine->StartL();
 
1878
        if (err != KErrNone)
 
1879
            return PJ_RETURN_OS_ERROR(err);
 
1880
    }
 
1881
 
 
1882
    return PJ_SUCCESS;
 
1883
}
 
1884
 
 
1885
/* API: Stop stream. */
 
1886
static pj_status_t stream_stop(pjmedia_aud_stream *strm)
 
1887
{
 
1888
    struct aps_stream *stream = (struct aps_stream*)strm;
 
1889
 
 
1890
    PJ_ASSERT_RETURN(stream, PJ_EINVAL);
 
1891
 
 
1892
    if (stream->engine) {
 
1893
        stream->engine->Stop();
 
1894
    }
 
1895
 
 
1896
    return PJ_SUCCESS;
 
1897
}
 
1898
 
 
1899
 
 
1900
/* API: Destroy stream. */
 
1901
static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
 
1902
{
 
1903
    struct aps_stream *stream = (struct aps_stream*)strm;
 
1904
 
 
1905
    PJ_ASSERT_RETURN(stream, PJ_EINVAL);
 
1906
 
 
1907
    stream_stop(strm);
 
1908
 
 
1909
    delete stream->engine;
 
1910
    stream->engine = NULL;
 
1911
 
 
1912
    if (stream->param.ext_fmt.id == PJMEDIA_FORMAT_G729) {
 
1913
        TBitStream *g729_bitstream = (TBitStream*)stream->strm_data;
 
1914
        stream->strm_data = NULL;
 
1915
        delete g729_bitstream;
 
1916
    }
 
1917
 
 
1918
    pj_pool_t *pool;
 
1919
    pool = stream->pool;
 
1920
    if (pool) {
 
1921
        stream->pool = NULL;
 
1922
        pj_pool_release(pool);
 
1923
    }
 
1924
 
 
1925
    return PJ_SUCCESS;
 
1926
}
 
1927
 
 
1928
#endif // PJMEDIA_AUDIO_DEV_HAS_SYMB_APS