1
/* $Id: auddemo.c 3664 2011-07-19 03:42:28Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjmedia-audiodev/audiodev.h>
21
#include <pjmedia-audiodev/audiotest.h>
24
#include <pjlib-util.h>
26
#define THIS_FILE "auddemo.c"
27
#define MAX_DEVICES 64
28
#define WAV_FILE "auddemo.wav"
31
static unsigned dev_count;
32
static unsigned playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
33
static unsigned capture_lat = PJMEDIA_SND_DEFAULT_REC_LATENCY;
35
static void app_perror(const char *title, pj_status_t status)
37
char errmsg[PJ_ERR_MSG_SIZE];
39
pj_strerror(status, errmsg, sizeof(errmsg));
40
printf( "%s: %s (err=%d)\n",
41
title, errmsg, status);
44
static void list_devices(void)
49
dev_count = pjmedia_aud_dev_count();
51
PJ_LOG(3,(THIS_FILE, "No devices found"));
55
PJ_LOG(3,(THIS_FILE, "Found %d devices:", dev_count));
57
for (i=0; i<dev_count; ++i) {
58
pjmedia_aud_dev_info info;
60
status = pjmedia_aud_dev_get_info(i, &info);
61
if (status != PJ_SUCCESS)
64
PJ_LOG(3,(THIS_FILE," %2d: %s [%s] (%d/%d)",
65
i, info.driver, info.name, info.input_count, info.output_count));
69
static const char *decode_caps(unsigned caps)
71
static char text[200];
76
for (i=0; i<31; ++i) {
77
if ((1 << i) & caps) {
79
capname = pjmedia_aud_dev_cap_name((pjmedia_aud_dev_cap)(1 << i),
81
strcat(text, capname);
89
static void show_dev_info(unsigned index)
92
pjmedia_aud_dev_info info;
96
if (index >= dev_count) {
97
PJ_LOG(1,(THIS_FILE, "Error: invalid index %u", index));
101
status = pjmedia_aud_dev_get_info(index, &info);
102
if (status != PJ_SUCCESS) {
103
app_perror("pjmedia_aud_dev_get_info() error", status);
107
PJ_LOG(3, (THIS_FILE, "Device at index %u:", index));
108
PJ_LOG(3, (THIS_FILE, "-------------------------"));
110
PJ_LOG(3, (THIS_FILE, H": %u (0x%x)", "ID", index, index));
111
PJ_LOG(3, (THIS_FILE, H": %s", "Name", info.name));
112
PJ_LOG(3, (THIS_FILE, H": %s", "Driver", info.driver));
113
PJ_LOG(3, (THIS_FILE, H": %u", "Input channels", info.input_count));
114
PJ_LOG(3, (THIS_FILE, H": %u", "Output channels", info.output_count));
115
PJ_LOG(3, (THIS_FILE, H": %s", "Capabilities", decode_caps(info.caps)));
118
if (info.caps & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) {
121
for (i=0; i<info.ext_fmt_cnt; ++i) {
124
switch (info.ext_fmt[i].id) {
125
case PJMEDIA_FORMAT_L16:
126
strcat(formats, "L16/");
128
case PJMEDIA_FORMAT_PCMA:
129
strcat(formats, "PCMA/");
131
case PJMEDIA_FORMAT_PCMU:
132
strcat(formats, "PCMU/");
134
case PJMEDIA_FORMAT_AMR:
135
strcat(formats, "AMR/");
137
case PJMEDIA_FORMAT_G729:
138
strcat(formats, "G729/");
140
case PJMEDIA_FORMAT_ILBC:
141
strcat(formats, "ILBC/");
144
strcat(formats, "unknown/");
147
sprintf(bitrate, "%u", info.ext_fmt[i].det.aud.avg_bps);
148
strcat(formats, bitrate);
149
strcat(formats, " ");
152
PJ_LOG(3, (THIS_FILE, H": %s", "Extended formats", formats));
157
static void test_device(pjmedia_dir dir, unsigned rec_id, unsigned play_id,
158
unsigned clock_rate, unsigned ptime,
161
pjmedia_aud_param param;
162
pjmedia_aud_test_results result;
165
if (dir & PJMEDIA_DIR_CAPTURE) {
166
status = pjmedia_aud_dev_default_param(rec_id, ¶m);
168
status = pjmedia_aud_dev_default_param(play_id, ¶m);
171
if (status != PJ_SUCCESS) {
172
app_perror("pjmedia_aud_dev_default_param()", status);
177
param.rec_id = rec_id;
178
param.play_id = play_id;
179
param.clock_rate = clock_rate;
180
param.channel_count = chnum;
181
param.samples_per_frame = clock_rate * chnum * ptime / 1000;
183
/* Latency settings */
184
param.flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
185
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY);
186
param.input_latency_ms = capture_lat;
187
param.output_latency_ms = playback_lat;
189
PJ_LOG(3,(THIS_FILE, "Performing test.."));
191
status = pjmedia_aud_test(¶m, &result);
192
if (status != PJ_SUCCESS) {
193
app_perror("Test has completed with error", status);
197
PJ_LOG(3,(THIS_FILE, "Done. Result:"));
199
if (dir & PJMEDIA_DIR_CAPTURE) {
200
if (result.rec.frame_cnt==0) {
201
PJ_LOG(1,(THIS_FILE, "Error: no frames captured!"));
203
PJ_LOG(3,(THIS_FILE, " %-20s: interval (min/max/avg/dev)=%u/%u/%u/%u, burst=%u",
205
result.rec.min_interval,
206
result.rec.max_interval,
207
result.rec.avg_interval,
208
result.rec.dev_interval,
209
result.rec.max_burst));
213
if (dir & PJMEDIA_DIR_PLAYBACK) {
214
if (result.play.frame_cnt==0) {
215
PJ_LOG(1,(THIS_FILE, "Error: no playback!"));
217
PJ_LOG(3,(THIS_FILE, " %-20s: interval (min/max/avg/dev)=%u/%u/%u/%u, burst=%u",
219
result.play.min_interval,
220
result.play.max_interval,
221
result.play.avg_interval,
222
result.play.dev_interval,
223
result.play.max_burst));
227
if (dir==PJMEDIA_DIR_CAPTURE_PLAYBACK) {
228
if (result.rec_drift_per_sec == 0) {
229
PJ_LOG(3,(THIS_FILE, " No clock drift detected"));
231
const char *which = result.rec_drift_per_sec>=0 ? "faster" : "slower";
232
unsigned drift = result.rec_drift_per_sec>=0 ?
233
result.rec_drift_per_sec :
234
-result.rec_drift_per_sec;
236
PJ_LOG(3,(THIS_FILE, " Clock drifts detected. Capture device "
237
"is running %d samples per second %s "
238
"than the playback device",
245
static pj_status_t wav_rec_cb(void *user_data, pjmedia_frame *frame)
247
return pjmedia_port_put_frame((pjmedia_port*)user_data, frame);
250
static void record(unsigned rec_index, const char *filename)
252
pj_pool_t *pool = NULL;
253
pjmedia_port *wav = NULL;
254
pjmedia_aud_param param;
255
pjmedia_aud_stream *strm = NULL;
256
char line[10], *dummy;
259
if (filename == NULL)
262
pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav",
265
status = pjmedia_wav_writer_port_create(pool, filename, 16000,
266
1, 320, 16, 0, 0, &wav);
267
if (status != PJ_SUCCESS) {
268
app_perror("Error creating WAV file", status);
272
status = pjmedia_aud_dev_default_param(rec_index, ¶m);
273
if (status != PJ_SUCCESS) {
274
app_perror("pjmedia_aud_dev_default_param()", status);
278
param.dir = PJMEDIA_DIR_CAPTURE;
279
param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info);
280
param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info);
281
param.channel_count = PJMEDIA_PIA_CCNT(&wav->info);
282
param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info);
284
status = pjmedia_aud_stream_create(¶m, &wav_rec_cb, NULL, wav,
286
if (status != PJ_SUCCESS) {
287
app_perror("Error opening the sound device", status);
291
status = pjmedia_aud_stream_start(strm);
292
if (status != PJ_SUCCESS) {
293
app_perror("Error starting the sound device", status);
297
PJ_LOG(3,(THIS_FILE, "Recording started, press ENTER to stop"));
298
dummy = fgets(line, sizeof(line), stdin);
302
pjmedia_aud_stream_stop(strm);
303
pjmedia_aud_stream_destroy(strm);
306
pjmedia_port_destroy(wav);
308
pj_pool_release(pool);
312
static pj_status_t wav_play_cb(void *user_data, pjmedia_frame *frame)
314
return pjmedia_port_get_frame((pjmedia_port*)user_data, frame);
318
static void play_file(unsigned play_index, const char *filename)
320
pj_pool_t *pool = NULL;
321
pjmedia_port *wav = NULL;
322
pjmedia_aud_param param;
323
pjmedia_aud_stream *strm = NULL;
324
char line[10], *dummy;
327
if (filename == NULL)
330
pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav",
333
status = pjmedia_wav_player_port_create(pool, filename, 20, 0, 0, &wav);
334
if (status != PJ_SUCCESS) {
335
app_perror("Error opening WAV file", status);
339
status = pjmedia_aud_dev_default_param(play_index, ¶m);
340
if (status != PJ_SUCCESS) {
341
app_perror("pjmedia_aud_dev_default_param()", status);
345
param.dir = PJMEDIA_DIR_PLAYBACK;
346
param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info);
347
param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info);
348
param.channel_count = PJMEDIA_PIA_CCNT(&wav->info);
349
param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info);
351
status = pjmedia_aud_stream_create(¶m, NULL, &wav_play_cb, wav,
353
if (status != PJ_SUCCESS) {
354
app_perror("Error opening the sound device", status);
358
status = pjmedia_aud_stream_start(strm);
359
if (status != PJ_SUCCESS) {
360
app_perror("Error starting the sound device", status);
364
PJ_LOG(3,(THIS_FILE, "Playback started, press ENTER to stop"));
365
dummy = fgets(line, sizeof(line), stdin);
369
pjmedia_aud_stream_stop(strm);
370
pjmedia_aud_stream_destroy(strm);
373
pjmedia_port_destroy(wav);
375
pj_pool_release(pool);
379
static void print_menu(void)
382
puts("Audio demo menu:");
383
puts("-------------------------------");
384
puts(" l List devices");
385
puts(" R Refresh devices");
386
puts(" i ID Show device info for device ID");
387
puts(" t RID PID CR PTIM [CH] Perform test on the device:");
388
puts(" RID: record device ID (-1 for no)");
389
puts(" PID: playback device ID (-1 for no)");
390
puts(" CR: clock rate");
391
puts(" PTIM: ptime in ms");
392
puts(" CH: # of channels");
393
puts(" r RID [FILE] Record capture device RID to WAV file");
394
puts(" p PID [FILE] Playback WAV file to device ID PID");
395
puts(" d [RLAT PLAT] Get/set sound device latencies (in ms):");
396
puts(" Specify no param to get current latencies setting");
397
puts(" RLAT: record latency (-1 for default)");
398
puts(" PLAT: playback latency (-1 for default)");
399
puts(" v Toggle log verbosity");
402
printf("Enter selection: ");
409
pj_bool_t done = PJ_FALSE;
414
PJ_ASSERT_RETURN(status==PJ_SUCCESS, 1);
416
pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
418
/* Must create a pool factory before we can allocate any memory. */
419
pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
421
status = pjmedia_aud_subsys_init(&cp.factory);
422
if (status != PJ_SUCCESS) {
423
app_perror("pjmedia_aud_subsys_init()", status);
424
pj_caching_pool_destroy(&cp);
436
if (fgets(line, sizeof(line), stdin)==NULL)
445
pjmedia_aud_dev_refresh();
446
puts("Audio device list refreshed.");
452
if (sscanf(line+2, "%u", &dev_index) != 1) {
453
puts("error: device ID required");
456
show_dev_info(dev_index);
464
unsigned clock_rate, ptime, chnum;
467
cnt = sscanf(line+2, "%d %d %u %u %u", &rec_id, &play_id,
468
&clock_rate, &ptime, &chnum);
470
puts("error: not enough parameters");
473
if (clock_rate < 8000 || clock_rate > 128000) {
474
puts("error: invalid clock rate");
477
if (ptime < 10 || ptime > 500) {
478
puts("error: invalid ptime");
482
if (chnum < 1 || chnum > 4) {
483
puts("error: invalid number of channels");
490
if (rec_id >= 0 && rec_id < (int)dev_count) {
491
if (play_id >= 0 && play_id < (int)dev_count)
492
dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
494
dir = PJMEDIA_DIR_CAPTURE;
495
} else if (play_id >= 0 && play_id < (int)dev_count) {
496
dir = PJMEDIA_DIR_PLAYBACK;
498
puts("error: at least one valid device index required");
502
test_device(dir, rec_id, play_id, clock_rate, ptime, chnum);
514
count = sscanf(line+2, "%d %s", &index, filename);
518
record(index, filename);
520
puts("error: invalid command syntax");
531
count = sscanf(line+2, "%d %s", &index, filename);
533
play_file(index, NULL);
535
play_file(index, filename);
537
puts("error: invalid command syntax");
544
int rec_lat, play_lat;
546
if (sscanf(line+2, "%d %d", &rec_lat, &play_lat) == 2) {
547
capture_lat = (unsigned)
548
(rec_lat>=0? rec_lat:PJMEDIA_SND_DEFAULT_REC_LATENCY);
549
playback_lat = (unsigned)
550
(play_lat >= 0? play_lat : PJMEDIA_SND_DEFAULT_PLAY_LATENCY);
551
printf("Recording latency=%ums, playback latency=%ums",
552
capture_lat, playback_lat);
554
printf("Current latencies: record=%ums, playback=%ums",
555
capture_lat, playback_lat);
562
if (pj_log_get_level() <= 3) {
564
puts("Logging set to detail");
567
puts("Logging set to quiet");
577
pj_caching_pool_destroy(&cp);