blob: e805f1769c2f295fe54417eae02deef0ca6cf7bd [file] [log] [blame]
Benny Prijonoe3ebd552009-02-18 20:14:15 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia-audiodev/audiodev.h>
21#include <pjmedia-audiodev/audiotest.h>
22#include <pjmedia.h>
23#include <pjlib.h>
24#include <pjlib-util.h>
25
26#define THIS_FILE "auddemo.c"
27#define MAX_DEVICES 64
Benny Prijono555139d2009-02-19 12:08:19 +000028#define WAV_FILE "auddemo.wav"
Benny Prijonoe3ebd552009-02-18 20:14:15 +000029
30
31static unsigned dev_count;
Benny Prijonoe3ebd552009-02-18 20:14:15 +000032
33static void app_perror(const char *title, pj_status_t status)
34{
35 char errmsg[PJ_ERR_MSG_SIZE];
36
37 pj_strerror(status, errmsg, sizeof(errmsg));
38 printf( "%s: %s (err=%d)\n",
39 title, errmsg, status);
40}
41
42static void list_devices(void)
43{
44 unsigned i;
45 pj_status_t status;
46
47 dev_count = pjmedia_aud_dev_count();
48 if (dev_count == 0) {
49 PJ_LOG(3,(THIS_FILE, "No devices found"));
50 return;
51 }
52
53 PJ_LOG(3,(THIS_FILE, "Found %d devices:", dev_count));
54
Benny Prijonoe3ebd552009-02-18 20:14:15 +000055 for (i=0; i<dev_count; ++i) {
56 pjmedia_aud_dev_info info;
57
Benny Prijono10454dc2009-02-21 14:21:59 +000058 status = pjmedia_aud_dev_get_info(i, &info);
Benny Prijonoe3ebd552009-02-18 20:14:15 +000059 if (status != PJ_SUCCESS)
60 continue;
61
62 PJ_LOG(3,(THIS_FILE," %2d: %s [%s] (%d/%d)",
63 i, info.driver, info.name, info.input_count, info.output_count));
64 }
65}
66
67static const char *decode_caps(unsigned caps)
68{
69 static char text[200];
Benny Prijono10454dc2009-02-21 14:21:59 +000070 unsigned i;
Benny Prijonoe3ebd552009-02-18 20:14:15 +000071
72 text[0] = '\0';
73
Benny Prijono10454dc2009-02-21 14:21:59 +000074 for (i=0; i<31; ++i) {
75 if ((1 << i) & caps) {
76 const char *capname;
77 capname = pjmedia_aud_dev_cap_name((pjmedia_aud_dev_cap)(1 << i),
78 NULL);
79 strcat(text, capname);
80 strcat(text, " ");
81 }
Benny Prijonoe3ebd552009-02-18 20:14:15 +000082 }
83
84 return text;
85}
86
87static void show_dev_info(unsigned index)
88{
89#define H "%-20s"
90 pjmedia_aud_dev_info info;
91 char formats[200];
92 pj_status_t status;
93
94 if (index >= dev_count) {
95 PJ_LOG(1,(THIS_FILE, "Error: invalid index %u", index));
96 return;
97 }
98
Benny Prijono10454dc2009-02-21 14:21:59 +000099 status = pjmedia_aud_dev_get_info(index, &info);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000100 if (status != PJ_SUCCESS) {
101 app_perror("pjmedia_aud_dev_get_info() error", status);
102 return;
103 }
104
105 PJ_LOG(3, (THIS_FILE, "Device at index %u:", index));
106 PJ_LOG(3, (THIS_FILE, "-------------------------"));
107
Benny Prijono10454dc2009-02-21 14:21:59 +0000108 PJ_LOG(3, (THIS_FILE, H": %u (0x%x)", "ID", index, index));
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000109 PJ_LOG(3, (THIS_FILE, H": %s", "Name", info.name));
110 PJ_LOG(3, (THIS_FILE, H": %s", "Driver", info.driver));
111 PJ_LOG(3, (THIS_FILE, H": %u", "Input channels", info.input_count));
112 PJ_LOG(3, (THIS_FILE, H": %u", "Output channels", info.output_count));
113 PJ_LOG(3, (THIS_FILE, H": %s", "Capabilities", decode_caps(info.caps)));
114
115 formats[0] = '\0';
116 if (info.caps & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) {
117 unsigned i;
118
119 for (i=0; i<info.ext_fmt_cnt; ++i) {
120 char bitrate[32];
121
Benny Prijono555139d2009-02-19 12:08:19 +0000122 switch (info.ext_fmt[i].id) {
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000123 case PJMEDIA_FORMAT_L16:
124 strcat(formats, "L16/");
125 break;
126 case PJMEDIA_FORMAT_PCMA:
127 strcat(formats, "PCMA/");
128 break;
129 case PJMEDIA_FORMAT_PCMU:
130 strcat(formats, "PCMU/");
131 break;
132 case PJMEDIA_FORMAT_AMR:
133 strcat(formats, "AMR/");
134 break;
135 case PJMEDIA_FORMAT_G729:
136 strcat(formats, "G729/");
137 break;
138 case PJMEDIA_FORMAT_ILBC:
139 strcat(formats, "ILBC/");
140 break;
141 default:
142 strcat(formats, "unknown/");
143 break;
144 }
145 sprintf(bitrate, "%u", info.ext_fmt[i].bitrate);
146 strcat(formats, bitrate);
147 strcat(formats, " ");
148 }
149 }
150 PJ_LOG(3, (THIS_FILE, H": %s", "Extended formats", formats));
151
152#undef H
153}
154
155static void test_device(pjmedia_dir dir, unsigned rec_id, unsigned play_id,
156 unsigned clock_rate, unsigned ptime,
157 unsigned chnum)
158{
Benny Prijono10454dc2009-02-21 14:21:59 +0000159 pjmedia_aud_param param;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000160 pjmedia_aud_test_results result;
161 pj_status_t status;
162
163 if (dir & PJMEDIA_DIR_CAPTURE) {
Benny Prijono10454dc2009-02-21 14:21:59 +0000164 status = pjmedia_aud_dev_default_param(rec_id, &param);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000165 } else {
Benny Prijono10454dc2009-02-21 14:21:59 +0000166 status = pjmedia_aud_dev_default_param(play_id, &param);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000167 }
168
169 if (status != PJ_SUCCESS) {
170 app_perror("pjmedia_aud_dev_default_param()", status);
171 return;
172 }
173
174 param.dir = dir;
Benny Prijono10454dc2009-02-21 14:21:59 +0000175 param.rec_id = rec_id;
176 param.play_id = play_id;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000177 param.clock_rate = clock_rate;
178 param.channel_count = chnum;
179 param.samples_per_frame = clock_rate * chnum * ptime / 1000;
180
181 PJ_LOG(3,(THIS_FILE, "Performing test.."));
182
183 status = pjmedia_aud_test(&param, &result);
184 if (status != PJ_SUCCESS) {
185 app_perror("Test has completed with error", status);
186 return;
187 }
188
189 PJ_LOG(3,(THIS_FILE, "Done. Result:"));
190
191 if (dir & PJMEDIA_DIR_CAPTURE) {
192 if (result.rec.frame_cnt==0) {
193 PJ_LOG(1,(THIS_FILE, "Error: no frames captured!"));
194 } else {
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000195 PJ_LOG(3,(THIS_FILE, " %-20s: interval (min/max/avg/dev)=%u/%u/%u/%u, burst=%u",
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000196 "Recording result",
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000197 result.rec.min_interval,
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000198 result.rec.max_interval,
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000199 result.rec.avg_interval,
200 result.rec.dev_interval,
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000201 result.rec.max_burst));
202 }
203 }
204
205 if (dir & PJMEDIA_DIR_PLAYBACK) {
206 if (result.play.frame_cnt==0) {
207 PJ_LOG(1,(THIS_FILE, "Error: no playback!"));
208 } else {
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000209 PJ_LOG(3,(THIS_FILE, " %-20s: interval (min/max/avg/dev)=%u/%u/%u/%u, burst=%u",
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000210 "Playback result",
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000211 result.play.min_interval,
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000212 result.play.max_interval,
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000213 result.play.avg_interval,
214 result.play.dev_interval,
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000215 result.play.max_burst));
216 }
217 }
218
219 if (dir==PJMEDIA_DIR_CAPTURE_PLAYBACK) {
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000220 if (result.rec_drift_per_sec == 0) {
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000221 PJ_LOG(3,(THIS_FILE, " No clock drift detected"));
222 } else {
223 const char *which = result.rec_drift_per_sec>=0 ? "faster" : "slower";
224 unsigned drift = result.rec_drift_per_sec>=0 ?
225 result.rec_drift_per_sec :
226 -result.rec_drift_per_sec;
227
228 PJ_LOG(3,(THIS_FILE, " Clock drifts detected. Capture device "
229 "is running %d samples per second %s "
230 "than the playback device",
231 drift, which));
232 }
233 }
234}
235
236
Benny Prijono555139d2009-02-19 12:08:19 +0000237static pj_status_t wav_rec_cb(void *user_data, pjmedia_frame *frame)
238{
239 return pjmedia_port_put_frame((pjmedia_port*)user_data, frame);
240}
241
242static void record(unsigned rec_index, const char *filename)
243{
244 pj_pool_t *pool = NULL;
245 pjmedia_port *wav = NULL;
Benny Prijono10454dc2009-02-21 14:21:59 +0000246 pjmedia_aud_param param;
Benny Prijono555139d2009-02-19 12:08:19 +0000247 pjmedia_aud_stream *strm = NULL;
248 char line[10];
249 pj_status_t status;
250
251 if (filename == NULL)
252 filename = WAV_FILE;
253
254 pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav",
255 1000, 1000, NULL);
256
257 status = pjmedia_wav_writer_port_create(pool, filename, 16000,
258 1, 320, 16, 0, 0, &wav);
259 if (status != PJ_SUCCESS) {
260 app_perror("Error creating WAV file", status);
261 goto on_return;
262 }
263
Benny Prijono10454dc2009-02-21 14:21:59 +0000264 status = pjmedia_aud_dev_default_param(rec_index, &param);
Benny Prijono555139d2009-02-19 12:08:19 +0000265 if (status != PJ_SUCCESS) {
266 app_perror("pjmedia_aud_dev_default_param()", status);
267 goto on_return;
268 }
269
270 param.dir = PJMEDIA_DIR_CAPTURE;
271 param.clock_rate = wav->info.clock_rate;
272 param.samples_per_frame = wav->info.samples_per_frame;
273 param.channel_count = wav->info.channel_count;
274 param.bits_per_sample = wav->info.bits_per_sample;
275
276 status = pjmedia_aud_stream_create(&param, &wav_rec_cb, NULL, wav,
277 &strm);
278 if (status != PJ_SUCCESS) {
279 app_perror("Error opening the sound device", status);
280 goto on_return;
281 }
282
283 status = pjmedia_aud_stream_start(strm);
284 if (status != PJ_SUCCESS) {
285 app_perror("Error starting the sound device", status);
286 goto on_return;
287 }
288
289 PJ_LOG(3,(THIS_FILE, "Recording started, press ENTER to stop"));
290 fgets(line, sizeof(line), stdin);
291
292on_return:
293 if (strm) {
294 pjmedia_aud_stream_stop(strm);
295 pjmedia_aud_stream_destroy(strm);
296 }
297 if (wav)
298 pjmedia_port_destroy(wav);
299 if (pool)
300 pj_pool_release(pool);
301}
302
303
304static pj_status_t wav_play_cb(void *user_data, pjmedia_frame *frame)
305{
306 return pjmedia_port_get_frame((pjmedia_port*)user_data, frame);
307}
308
309
310static void play_file(unsigned play_index, const char *filename)
311{
312 pj_pool_t *pool = NULL;
313 pjmedia_port *wav = NULL;
Benny Prijono10454dc2009-02-21 14:21:59 +0000314 pjmedia_aud_param param;
Benny Prijono555139d2009-02-19 12:08:19 +0000315 pjmedia_aud_stream *strm = NULL;
316 char line[10];
317 pj_status_t status;
318
319 if (filename == NULL)
320 filename = WAV_FILE;
321
322 pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav",
323 1000, 1000, NULL);
324
325 status = pjmedia_wav_player_port_create(pool, filename, 20, 0, 0, &wav);
326 if (status != PJ_SUCCESS) {
327 app_perror("Error opening WAV file", status);
328 goto on_return;
329 }
330
Benny Prijono10454dc2009-02-21 14:21:59 +0000331 status = pjmedia_aud_dev_default_param(play_index, &param);
Benny Prijono555139d2009-02-19 12:08:19 +0000332 if (status != PJ_SUCCESS) {
333 app_perror("pjmedia_aud_dev_default_param()", status);
334 goto on_return;
335 }
336
337 param.dir = PJMEDIA_DIR_PLAYBACK;
338 param.clock_rate = wav->info.clock_rate;
339 param.samples_per_frame = wav->info.samples_per_frame;
340 param.channel_count = wav->info.channel_count;
341 param.bits_per_sample = wav->info.bits_per_sample;
342
343 status = pjmedia_aud_stream_create(&param, NULL, &wav_play_cb, wav,
344 &strm);
345 if (status != PJ_SUCCESS) {
346 app_perror("Error opening the sound device", status);
347 goto on_return;
348 }
349
350 status = pjmedia_aud_stream_start(strm);
351 if (status != PJ_SUCCESS) {
352 app_perror("Error starting the sound device", status);
353 goto on_return;
354 }
355
356 PJ_LOG(3,(THIS_FILE, "Playback started, press ENTER to stop"));
357 fgets(line, sizeof(line), stdin);
358
359on_return:
360 if (strm) {
361 pjmedia_aud_stream_stop(strm);
362 pjmedia_aud_stream_destroy(strm);
363 }
364 if (wav)
365 pjmedia_port_destroy(wav);
366 if (pool)
367 pj_pool_release(pool);
368}
369
370
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000371static void print_menu(void)
372{
373 puts("");
374 puts("Audio demo menu:");
375 puts("-------------------------------");
376 puts(" l List devices");
377 puts(" i ID Show device info for device ID");
378 puts(" t RID PID CR PTIM [CH] Perform test on the device:");
379 puts(" RID: record device ID (-1 for no)");
380 puts(" PID: playback device ID (-1 for no)");
381 puts(" CR: clock rate");
382 puts(" PTIM: ptime in ms");
383 puts(" CH: # of channels");
Benny Prijono555139d2009-02-19 12:08:19 +0000384 puts(" r RID [FILE] Record capture device RID to WAV file");
385 puts(" p PID [FILE] Playback WAV file to device ID PID");
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000386 puts(" v Toggle log verbosity");
387 puts(" q Quit");
388 puts("");
389 printf("Enter selection: ");
390 fflush(stdout);
391}
392
393int main()
394{
395 pj_caching_pool cp;
396 pj_bool_t done = PJ_FALSE;
397 pj_status_t status;
398
399 /* Init pjlib */
400 status = pj_init();
401 PJ_ASSERT_RETURN(status==PJ_SUCCESS, 1);
402
403 pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_COLOR);
404
405 /* Must create a pool factory before we can allocate any memory. */
406 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
407
408 status = pjmedia_aud_subsys_init(&cp.factory);
409 if (status != PJ_SUCCESS) {
410 app_perror("pjmedia_aud_subsys_init()", status);
411 pj_caching_pool_destroy(&cp);
412 pj_shutdown();
413 return 1;
414 }
415
416 list_devices();
417
418 while (!done) {
419 char line[80];
420
421 print_menu();
422
423 if (fgets(line, sizeof(line), stdin)==NULL)
424 break;
425
426 switch (line[0]) {
427 case 'l':
428 list_devices();
429 break;
430
431 case 'i':
432 {
433 unsigned dev_index;
434 if (sscanf(line+2, "%u", &dev_index) != 1) {
435 puts("error: device ID required");
436 break;
437 }
438 show_dev_info(dev_index);
439 }
440 break;
441
442 case 't':
443 {
444 pjmedia_dir dir;
445 int rec_id, play_id;
446 unsigned clock_rate, ptime, chnum;
447 int cnt;
448
449 cnt = sscanf(line+2, "%d %d %u %u %u", &rec_id, &play_id,
450 &clock_rate, &ptime, &chnum);
451 if (cnt < 4) {
452 puts("error: not enough parameters");
453 break;
454 }
455 if (clock_rate < 8000 || clock_rate > 128000) {
456 puts("error: invalid clock rate");
457 break;
458 }
459 if (ptime < 10 || ptime > 500) {
460 puts("error: invalid ptime");
461 break;
462 }
463 if (cnt==5) {
464 if (chnum < 1 || chnum > 4) {
465 puts("error: invalid number of channels");
466 break;
467 }
468 } else {
469 chnum = 1;
470 }
471
472 if (rec_id >= 0 && rec_id < (int)dev_count) {
473 if (play_id >= 0 && play_id < (int)dev_count)
474 dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
475 else
476 dir = PJMEDIA_DIR_CAPTURE;
477 } else if (play_id >= 0 && play_id < (int)dev_count) {
478 dir = PJMEDIA_DIR_PLAYBACK;
479 } else {
480 puts("error: at least one valid device index required");
481 break;
482 }
483
484 test_device(dir, rec_id, play_id, clock_rate, ptime, chnum);
485
486 }
487 break;
488
Benny Prijono555139d2009-02-19 12:08:19 +0000489 case 'r':
490 /* record */
491 {
492 int index;
493 char filename[80];
494 int count;
495
496 count = sscanf(line+2, "%d %s", &index, filename);
497 if (count==1)
498 record(index, NULL);
499 else if (count==2)
500 record(index, filename);
501 else
502 puts("error: invalid command syntax");
503 }
504 break;
505
506 case 'p':
507 /* playback */
508 {
509 int index;
510 char filename[80];
511 int count;
512
513 count = sscanf(line+2, "%d %s", &index, filename);
514 if (count==1)
515 play_file(index, NULL);
516 else if (count==2)
517 play_file(index, filename);
518 else
519 puts("error: invalid command syntax");
520 }
521 break;
522
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000523 case 'v':
524 if (pj_log_get_level() <= 3) {
525 pj_log_set_level(5);
526 puts("Logging set to detail");
527 } else {
528 pj_log_set_level(3);
529 puts("Logging set to quiet");
530 }
531 break;
532
533 case 'q':
534 done = PJ_TRUE;
535 break;
536 }
537 }
538
539 pj_caching_pool_destroy(&cp);
540 pj_shutdown();
541 return 0;
542}
543
544