1
/* $Id: sound_port.c 4082 2012-04-24 13:09:14Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjmedia/sound_port.h>
21
#include <pjmedia/alaw_ulaw.h>
22
#include <pjmedia/delaybuf.h>
23
#include <pjmedia/echo.h>
24
#include <pjmedia/errno.h>
25
#include <pj/assert.h>
28
#include <pj/string.h> /* pj_memset() */
30
#define AEC_TAIL 128 /* default AEC length in ms */
31
#define AEC_SUSPEND_LIMIT 5 /* seconds of no activity */
33
#define THIS_FILE "sound_port.c"
35
//#define TEST_OVERFLOW_UNDERFLOW
37
struct pjmedia_snd_port
42
pjmedia_aud_param aud_param;
43
pjmedia_aud_stream *aud_stream;
47
pjmedia_clock_src cap_clocksrc,
51
unsigned channel_count;
52
unsigned samples_per_frame;
53
unsigned bits_per_sample;
55
unsigned prm_ec_options;
58
pjmedia_echo_state *ec_state;
61
pj_bool_t ec_suspended;
62
unsigned ec_suspend_count;
63
unsigned ec_suspend_limit;
67
* The callback called by sound player when it needs more samples to be
70
static pj_status_t play_cb(void *user_data, pjmedia_frame *frame)
72
pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
74
const unsigned required_size = frame->size;
77
pjmedia_clock_src_update(&snd_port->play_clocksrc, &frame->timestamp);
79
port = snd_port->port;
83
status = pjmedia_port_get_frame(port, frame);
84
if (status != PJ_SUCCESS)
87
if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO)
90
/* Must supply the required samples */
91
pj_assert(frame->size == required_size);
93
if (snd_port->ec_state) {
94
if (snd_port->ec_suspended) {
95
snd_port->ec_suspended = PJ_FALSE;
96
//pjmedia_echo_state_reset(snd_port->ec_state);
97
PJ_LOG(4,(THIS_FILE, "EC activated"));
99
snd_port->ec_suspend_count = 0;
100
pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
107
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
108
frame->size = required_size;
109
pj_bzero(frame->buf, frame->size);
111
if (snd_port->ec_state && !snd_port->ec_suspended) {
112
++snd_port->ec_suspend_count;
113
if (snd_port->ec_suspend_count > snd_port->ec_suspend_limit) {
114
snd_port->ec_suspended = PJ_TRUE;
115
PJ_LOG(4,(THIS_FILE, "EC suspended because of inactivity"));
117
if (snd_port->ec_state) {
118
/* To maintain correct delay in EC */
119
pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
128
* The callback called by sound recorder when it has finished capturing a
131
static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
133
pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
136
pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp);
138
port = snd_port->port;
143
if (snd_port->ec_state && !snd_port->ec_suspended) {
144
pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0);
147
pjmedia_port_put_frame(port, frame);
154
* The callback called by sound player when it needs more samples to be
155
* played. This version is for non-PCM data.
157
static pj_status_t play_cb_ext(void *user_data, pjmedia_frame *frame)
159
pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
160
pjmedia_port *port = snd_port->port;
163
frame->type = PJMEDIA_FRAME_TYPE_NONE;
167
pjmedia_port_get_frame(port, frame);
174
* The callback called by sound recorder when it has finished capturing a
175
* frame. This version is for non-PCM data.
177
static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame)
179
pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
182
port = snd_port->port;
186
pjmedia_port_put_frame(port, frame);
191
/* Initialize with default values (zero) */
192
PJ_DEF(void) pjmedia_snd_port_param_default(pjmedia_snd_port_param *prm)
194
pj_bzero(prm, sizeof(*prm));
198
* Start the sound stream.
199
* This may be called even when the sound stream has already been started.
201
static pj_status_t start_sound_device( pj_pool_t *pool,
202
pjmedia_snd_port *snd_port )
204
pjmedia_aud_rec_cb snd_rec_cb;
205
pjmedia_aud_play_cb snd_play_cb;
206
pjmedia_aud_param param_copy;
209
/* Check if sound has been started. */
210
if (snd_port->aud_stream != NULL)
213
PJ_ASSERT_RETURN(snd_port->dir == PJMEDIA_DIR_CAPTURE ||
214
snd_port->dir == PJMEDIA_DIR_PLAYBACK ||
215
snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
218
/* Get device caps */
219
if (snd_port->aud_param.dir & PJMEDIA_DIR_CAPTURE) {
220
pjmedia_aud_dev_info dev_info;
222
status = pjmedia_aud_dev_get_info(snd_port->aud_param.rec_id,
224
if (status != PJ_SUCCESS)
227
snd_port->aud_caps = dev_info.caps;
229
snd_port->aud_caps = 0;
232
/* Process EC settings */
233
pj_memcpy(¶m_copy, &snd_port->aud_param, sizeof(param_copy));
234
if (param_copy.flags & PJMEDIA_AUD_DEV_CAP_EC) {
236
if ((snd_port->prm_ec_options & PJMEDIA_ECHO_USE_SW_ECHO) == 0 &&
237
snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)
239
/* Device supports EC */
242
/* Application wants to use software EC or device
243
* doesn't support EC, remove EC settings from
246
param_copy.flags &= ~(PJMEDIA_AUD_DEV_CAP_EC |
247
PJMEDIA_AUD_DEV_CAP_EC_TAIL);
251
/* Use different callback if format is not PCM */
252
if (snd_port->aud_param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
253
snd_rec_cb = &rec_cb;
254
snd_play_cb = &play_cb;
256
snd_rec_cb = &rec_cb_ext;
257
snd_play_cb = &play_cb_ext;
260
/* Open the device */
261
status = pjmedia_aud_stream_create(¶m_copy,
265
&snd_port->aud_stream);
267
if (status != PJ_SUCCESS)
270
/* Inactivity limit before EC is suspended. */
271
snd_port->ec_suspend_limit = AEC_SUSPEND_LIMIT *
272
(snd_port->clock_rate /
273
snd_port->samples_per_frame);
275
/* Create software EC if parameter specifies EC and
276
* (app specifically requests software EC or device
277
* doesn't support EC). Only do this if the format is PCM!
279
if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC) &&
280
((snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)==0 ||
281
(snd_port->prm_ec_options & PJMEDIA_ECHO_USE_SW_ECHO) != 0) &&
282
param_copy.ext_fmt.id == PJMEDIA_FORMAT_PCM)
284
if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
285
snd_port->aud_param.flags |= PJMEDIA_AUD_DEV_CAP_EC_TAIL;
286
snd_port->aud_param.ec_tail_ms = AEC_TAIL;
287
PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms",
288
snd_port->aud_param.ec_tail_ms));
291
status = pjmedia_snd_port_set_ec(snd_port, pool,
292
snd_port->aud_param.ec_tail_ms,
293
snd_port->prm_ec_options);
294
if (status != PJ_SUCCESS) {
295
pjmedia_aud_stream_destroy(snd_port->aud_stream);
296
snd_port->aud_stream = NULL;
301
/* Start sound stream. */
302
if (!(snd_port->options & PJMEDIA_SND_PORT_NO_AUTO_START)) {
303
status = pjmedia_aud_stream_start(snd_port->aud_stream);
305
if (status != PJ_SUCCESS) {
306
pjmedia_aud_stream_destroy(snd_port->aud_stream);
307
snd_port->aud_stream = NULL;
316
* Stop the sound device.
317
* This may be called even when there's no sound device in the port.
319
static pj_status_t stop_sound_device( pjmedia_snd_port *snd_port )
321
/* Check if we have sound stream device. */
322
if (snd_port->aud_stream) {
323
pjmedia_aud_stream_stop(snd_port->aud_stream);
324
pjmedia_aud_stream_destroy(snd_port->aud_stream);
325
snd_port->aud_stream = NULL;
329
if (snd_port->ec_state) {
330
pjmedia_echo_destroy(snd_port->ec_state);
331
snd_port->ec_state = NULL;
339
* Create bidirectional port.
341
PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool,
345
unsigned channel_count,
346
unsigned samples_per_frame,
347
unsigned bits_per_sample,
349
pjmedia_snd_port **p_port)
351
pjmedia_snd_port_param param;
354
pjmedia_snd_port_param_default(¶m);
356
status = pjmedia_aud_dev_default_param(rec_id, ¶m.base);
357
if (status != PJ_SUCCESS)
360
param.base.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
361
param.base.rec_id = rec_id;
362
param.base.play_id = play_id;
363
param.base.clock_rate = clock_rate;
364
param.base.channel_count = channel_count;
365
param.base.samples_per_frame = samples_per_frame;
366
param.base.bits_per_sample = bits_per_sample;
367
param.options = options;
368
param.ec_options = 0;
370
return pjmedia_snd_port_create2(pool, ¶m, p_port);
374
* Create sound recorder AEC.
376
PJ_DEF(pj_status_t) pjmedia_snd_port_create_rec( pj_pool_t *pool,
379
unsigned channel_count,
380
unsigned samples_per_frame,
381
unsigned bits_per_sample,
383
pjmedia_snd_port **p_port)
385
pjmedia_snd_port_param param;
388
pjmedia_snd_port_param_default(¶m);
390
status = pjmedia_aud_dev_default_param(dev_id, ¶m.base);
391
if (status != PJ_SUCCESS)
394
param.base.dir = PJMEDIA_DIR_CAPTURE;
395
param.base.rec_id = dev_id;
396
param.base.clock_rate = clock_rate;
397
param.base.channel_count = channel_count;
398
param.base.samples_per_frame = samples_per_frame;
399
param.base.bits_per_sample = bits_per_sample;
400
param.options = options;
401
param.ec_options = 0;
403
return pjmedia_snd_port_create2(pool, ¶m, p_port);
408
* Create sound player port.
410
PJ_DEF(pj_status_t) pjmedia_snd_port_create_player( pj_pool_t *pool,
413
unsigned channel_count,
414
unsigned samples_per_frame,
415
unsigned bits_per_sample,
417
pjmedia_snd_port **p_port)
419
pjmedia_snd_port_param param;
422
pjmedia_snd_port_param_default(¶m);
424
status = pjmedia_aud_dev_default_param(dev_id, ¶m.base);
425
if (status != PJ_SUCCESS)
428
param.base.dir = PJMEDIA_DIR_PLAYBACK;
429
param.base.play_id = dev_id;
430
param.base.clock_rate = clock_rate;
431
param.base.channel_count = channel_count;
432
param.base.samples_per_frame = samples_per_frame;
433
param.base.bits_per_sample = bits_per_sample;
434
param.options = options;
435
param.ec_options = 0;
437
return pjmedia_snd_port_create2(pool, ¶m, p_port);
444
PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
445
const pjmedia_snd_port_param *prm,
446
pjmedia_snd_port **p_port)
448
pjmedia_snd_port *snd_port;
452
PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL);
454
snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
455
PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
457
snd_port->dir = prm->base.dir;
458
snd_port->rec_id = prm->base.rec_id;
459
snd_port->play_id = prm->base.play_id;
460
snd_port->clock_rate = prm->base.clock_rate;
461
snd_port->channel_count = prm->base.channel_count;
462
snd_port->samples_per_frame = prm->base.samples_per_frame;
463
snd_port->bits_per_sample = prm->base.bits_per_sample;
464
pj_memcpy(&snd_port->aud_param, &prm->base, sizeof(snd_port->aud_param));
465
snd_port->options = prm->options;
466
snd_port->prm_ec_options = prm->ec_options;
468
ptime_usec = prm->base.samples_per_frame * 1000 / prm->base.channel_count /
469
prm->base.clock_rate * 1000;
470
pjmedia_clock_src_init(&snd_port->cap_clocksrc, PJMEDIA_TYPE_AUDIO,
471
snd_port->clock_rate, ptime_usec);
472
pjmedia_clock_src_init(&snd_port->play_clocksrc, PJMEDIA_TYPE_AUDIO,
473
snd_port->clock_rate, ptime_usec);
475
/* Start sound device immediately.
476
* If there's no port connected, the sound callback will return
479
status = start_sound_device( pool, snd_port );
480
if (status != PJ_SUCCESS) {
481
pjmedia_snd_port_destroy(snd_port);
491
* Destroy port (also destroys the sound device).
493
PJ_DEF(pj_status_t) pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port)
495
PJ_ASSERT_RETURN(snd_port, PJ_EINVAL);
497
return stop_sound_device(snd_port);
502
* Retrieve the sound stream associated by this sound device port.
504
PJ_DEF(pjmedia_aud_stream*) pjmedia_snd_port_get_snd_stream(
505
pjmedia_snd_port *snd_port)
507
PJ_ASSERT_RETURN(snd_port, NULL);
508
return snd_port->aud_stream;
513
* Change EC settings.
515
PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port,
520
pjmedia_aud_param prm;
523
/* Sound must be opened in full-duplex mode */
524
PJ_ASSERT_RETURN(snd_port &&
525
snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
528
/* Determine whether we use device or software EC */
529
if ((snd_port->prm_ec_options & PJMEDIA_ECHO_USE_SW_ECHO) == 0 &&
530
snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)
532
/* We use device EC */
533
pj_bool_t ec_enabled;
535
/* Query EC status */
536
status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
537
PJMEDIA_AUD_DEV_CAP_EC,
539
if (status != PJ_SUCCESS)
543
/* Change EC setting */
546
/* Enable EC first */
547
pj_bool_t value = PJ_TRUE;
548
status = pjmedia_aud_stream_set_cap(snd_port->aud_stream,
549
PJMEDIA_AUD_DEV_CAP_EC,
551
if (status != PJ_SUCCESS)
555
if ((snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
556
/* Device does not support setting EC tail */
557
return PJMEDIA_EAUD_INVCAP;
560
return pjmedia_aud_stream_set_cap(snd_port->aud_stream,
561
PJMEDIA_AUD_DEV_CAP_EC_TAIL,
564
} else if (ec_enabled) {
566
pj_bool_t value = PJ_FALSE;
567
return pjmedia_aud_stream_set_cap(snd_port->aud_stream,
568
PJMEDIA_AUD_DEV_CAP_EC,
571
/* Request to disable EC but EC has been disabled */
577
/* We use software EC */
579
/* Check if there is change in parameters */
580
if (tail_ms==snd_port->ec_tail_len && options==snd_port->ec_options) {
581
PJ_LOG(5,(THIS_FILE, "pjmedia_snd_port_set_ec() ignored, no "
582
"change in settings"));
586
status = pjmedia_aud_stream_get_param(snd_port->aud_stream, &prm);
587
if (status != PJ_SUCCESS)
590
/* Audio stream must be in PCM format */
591
PJ_ASSERT_RETURN(prm.ext_fmt.id == PJMEDIA_FORMAT_PCM,
595
if (snd_port->ec_state) {
596
pjmedia_echo_destroy(snd_port->ec_state);
597
snd_port->ec_state = NULL;
603
//No need to add input latency in the latency calculation,
604
//since actual input latency should be zero.
605
//delay_ms = (si.rec_latency + si.play_latency) * 1000 /
606
// snd_port->clock_rate;
607
/* Set EC latency to 3/4 of output latency to reduce the
608
* possibility of missing/late reference frame.
610
delay_ms = prm.output_latency_ms * 3/4;
611
status = pjmedia_echo_create2(pool, snd_port->clock_rate,
612
snd_port->channel_count,
613
snd_port->samples_per_frame,
615
options, &snd_port->ec_state);
616
if (status != PJ_SUCCESS)
617
snd_port->ec_state = NULL;
619
snd_port->ec_suspended = PJ_FALSE;
621
PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the "
626
snd_port->ec_options = options;
627
snd_port->ec_tail_len = tail_ms;
634
/* Get AEC tail length */
635
PJ_DEF(pj_status_t) pjmedia_snd_port_get_ec_tail( pjmedia_snd_port *snd_port,
638
PJ_ASSERT_RETURN(snd_port && p_length, PJ_EINVAL);
640
/* Determine whether we use device or software EC */
641
if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) {
642
/* We use device EC */
643
pj_bool_t ec_enabled;
646
/* Query EC status */
647
status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
648
PJMEDIA_AUD_DEV_CAP_EC,
650
if (status != PJ_SUCCESS)
655
} else if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL) {
656
/* Get device EC tail */
657
status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
658
PJMEDIA_AUD_DEV_CAP_EC_TAIL,
660
if (status != PJ_SUCCESS)
663
/* Just use default */
664
*p_length = AEC_TAIL;
668
/* We use software EC */
669
*p_length = snd_port->ec_state ? snd_port->ec_tail_len : 0;
678
PJ_DEF(pjmedia_clock_src *)
679
pjmedia_snd_port_get_clock_src( pjmedia_snd_port *snd_port,
682
return (dir == PJMEDIA_DIR_CAPTURE? &snd_port->cap_clocksrc:
683
&snd_port->play_clocksrc);
690
PJ_DEF(pj_status_t) pjmedia_snd_port_connect( pjmedia_snd_port *snd_port,
693
pjmedia_audio_format_detail *afd;
695
PJ_ASSERT_RETURN(snd_port && port, PJ_EINVAL);
697
afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, PJ_TRUE);
699
/* Check that port has the same configuration as the sound device
702
if (afd->clock_rate != snd_port->clock_rate)
703
return PJMEDIA_ENCCLOCKRATE;
705
if (PJMEDIA_AFD_SPF(afd) != snd_port->samples_per_frame)
706
return PJMEDIA_ENCSAMPLESPFRAME;
708
if (afd->channel_count != snd_port->channel_count)
709
return PJMEDIA_ENCCHANNEL;
711
if (afd->bits_per_sample != snd_port->bits_per_sample)
712
return PJMEDIA_ENCBITS;
715
snd_port->port = port;
721
* Get the connected port.
723
PJ_DEF(pjmedia_port*) pjmedia_snd_port_get_port(pjmedia_snd_port *snd_port)
725
PJ_ASSERT_RETURN(snd_port, NULL);
726
return snd_port->port;
733
PJ_DEF(pj_status_t) pjmedia_snd_port_disconnect(pjmedia_snd_port *snd_port)
735
PJ_ASSERT_RETURN(snd_port, PJ_EINVAL);
737
snd_port->port = NULL;