~ubuntu-branches/debian/jessie/sflphone/jessie

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2013-06-02 18:04:11 UTC
  • mfrom: (1.1.9)
  • Revision ID: package-import@ubuntu.com-20130602180411-3rcpy8c1zdlo8y0s
Tags: 1.2.2-1
* New upstream release
* changeset_rb68857a4b485b7d43f92714cd5792595ff895f82.diff - fix QTest
* pjproject ./configure --disable-sound --disable-video

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: symb_aps_dev.cpp 3809 2011-10-11 03:05:34Z nanang $ */
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
1929