blob: 17a3e652012c406b846f2176e0242950d60bd136 [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 {
195 PJ_LOG(3,(THIS_FILE, " %-20s: max interval=%u, burst=%u",
196 "Recording result",
197 result.rec.max_interval,
198 result.rec.max_burst));
199 }
200 }
201
202 if (dir & PJMEDIA_DIR_PLAYBACK) {
203 if (result.play.frame_cnt==0) {
204 PJ_LOG(1,(THIS_FILE, "Error: no playback!"));
205 } else {
206 PJ_LOG(3,(THIS_FILE, " %-20s: max interval=%u, burst=%u",
207 "Playback result",
208 result.play.max_interval,
209 result.play.max_burst));
210 }
211 }
212
213 if (dir==PJMEDIA_DIR_CAPTURE_PLAYBACK) {
214 if (result.rec_drift_per_sec) {
215 PJ_LOG(3,(THIS_FILE, " No clock drift detected"));
216 } else {
217 const char *which = result.rec_drift_per_sec>=0 ? "faster" : "slower";
218 unsigned drift = result.rec_drift_per_sec>=0 ?
219 result.rec_drift_per_sec :
220 -result.rec_drift_per_sec;
221
222 PJ_LOG(3,(THIS_FILE, " Clock drifts detected. Capture device "
223 "is running %d samples per second %s "
224 "than the playback device",
225 drift, which));
226 }
227 }
228}
229
230
Benny Prijono555139d2009-02-19 12:08:19 +0000231static pj_status_t wav_rec_cb(void *user_data, pjmedia_frame *frame)
232{
233 return pjmedia_port_put_frame((pjmedia_port*)user_data, frame);
234}
235
236static void record(unsigned rec_index, const char *filename)
237{
238 pj_pool_t *pool = NULL;
239 pjmedia_port *wav = NULL;
Benny Prijono10454dc2009-02-21 14:21:59 +0000240 pjmedia_aud_param param;
Benny Prijono555139d2009-02-19 12:08:19 +0000241 pjmedia_aud_stream *strm = NULL;
242 char line[10];
243 pj_status_t status;
244
245 if (filename == NULL)
246 filename = WAV_FILE;
247
248 pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav",
249 1000, 1000, NULL);
250
251 status = pjmedia_wav_writer_port_create(pool, filename, 16000,
252 1, 320, 16, 0, 0, &wav);
253 if (status != PJ_SUCCESS) {
254 app_perror("Error creating WAV file", status);
255 goto on_return;
256 }
257
Benny Prijono10454dc2009-02-21 14:21:59 +0000258 status = pjmedia_aud_dev_default_param(rec_index, &param);
Benny Prijono555139d2009-02-19 12:08:19 +0000259 if (status != PJ_SUCCESS) {
260 app_perror("pjmedia_aud_dev_default_param()", status);
261 goto on_return;
262 }
263
264 param.dir = PJMEDIA_DIR_CAPTURE;
265 param.clock_rate = wav->info.clock_rate;
266 param.samples_per_frame = wav->info.samples_per_frame;
267 param.channel_count = wav->info.channel_count;
268 param.bits_per_sample = wav->info.bits_per_sample;
269
270 status = pjmedia_aud_stream_create(&param, &wav_rec_cb, NULL, wav,
271 &strm);
272 if (status != PJ_SUCCESS) {
273 app_perror("Error opening the sound device", status);
274 goto on_return;
275 }
276
277 status = pjmedia_aud_stream_start(strm);
278 if (status != PJ_SUCCESS) {
279 app_perror("Error starting the sound device", status);
280 goto on_return;
281 }
282
283 PJ_LOG(3,(THIS_FILE, "Recording started, press ENTER to stop"));
284 fgets(line, sizeof(line), stdin);
285
286on_return:
287 if (strm) {
288 pjmedia_aud_stream_stop(strm);
289 pjmedia_aud_stream_destroy(strm);
290 }
291 if (wav)
292 pjmedia_port_destroy(wav);
293 if (pool)
294 pj_pool_release(pool);
295}
296
297
298static pj_status_t wav_play_cb(void *user_data, pjmedia_frame *frame)
299{
300 return pjmedia_port_get_frame((pjmedia_port*)user_data, frame);
301}
302
303
304static void play_file(unsigned play_index, const char *filename)
305{
306 pj_pool_t *pool = NULL;
307 pjmedia_port *wav = NULL;
Benny Prijono10454dc2009-02-21 14:21:59 +0000308 pjmedia_aud_param param;
Benny Prijono555139d2009-02-19 12:08:19 +0000309 pjmedia_aud_stream *strm = NULL;
310 char line[10];
311 pj_status_t status;
312
313 if (filename == NULL)
314 filename = WAV_FILE;
315
316 pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav",
317 1000, 1000, NULL);
318
319 status = pjmedia_wav_player_port_create(pool, filename, 20, 0, 0, &wav);
320 if (status != PJ_SUCCESS) {
321 app_perror("Error opening WAV file", status);
322 goto on_return;
323 }
324
Benny Prijono10454dc2009-02-21 14:21:59 +0000325 status = pjmedia_aud_dev_default_param(play_index, &param);
Benny Prijono555139d2009-02-19 12:08:19 +0000326 if (status != PJ_SUCCESS) {
327 app_perror("pjmedia_aud_dev_default_param()", status);
328 goto on_return;
329 }
330
331 param.dir = PJMEDIA_DIR_PLAYBACK;
332 param.clock_rate = wav->info.clock_rate;
333 param.samples_per_frame = wav->info.samples_per_frame;
334 param.channel_count = wav->info.channel_count;
335 param.bits_per_sample = wav->info.bits_per_sample;
336
337 status = pjmedia_aud_stream_create(&param, NULL, &wav_play_cb, wav,
338 &strm);
339 if (status != PJ_SUCCESS) {
340 app_perror("Error opening the sound device", status);
341 goto on_return;
342 }
343
344 status = pjmedia_aud_stream_start(strm);
345 if (status != PJ_SUCCESS) {
346 app_perror("Error starting the sound device", status);
347 goto on_return;
348 }
349
350 PJ_LOG(3,(THIS_FILE, "Playback started, press ENTER to stop"));
351 fgets(line, sizeof(line), stdin);
352
353on_return:
354 if (strm) {
355 pjmedia_aud_stream_stop(strm);
356 pjmedia_aud_stream_destroy(strm);
357 }
358 if (wav)
359 pjmedia_port_destroy(wav);
360 if (pool)
361 pj_pool_release(pool);
362}
363
364
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000365static void print_menu(void)
366{
367 puts("");
368 puts("Audio demo menu:");
369 puts("-------------------------------");
370 puts(" l List devices");
371 puts(" i ID Show device info for device ID");
372 puts(" t RID PID CR PTIM [CH] Perform test on the device:");
373 puts(" RID: record device ID (-1 for no)");
374 puts(" PID: playback device ID (-1 for no)");
375 puts(" CR: clock rate");
376 puts(" PTIM: ptime in ms");
377 puts(" CH: # of channels");
Benny Prijono555139d2009-02-19 12:08:19 +0000378 puts(" r RID [FILE] Record capture device RID to WAV file");
379 puts(" p PID [FILE] Playback WAV file to device ID PID");
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000380 puts(" v Toggle log verbosity");
381 puts(" q Quit");
382 puts("");
383 printf("Enter selection: ");
384 fflush(stdout);
385}
386
387int main()
388{
389 pj_caching_pool cp;
390 pj_bool_t done = PJ_FALSE;
391 pj_status_t status;
392
393 /* Init pjlib */
394 status = pj_init();
395 PJ_ASSERT_RETURN(status==PJ_SUCCESS, 1);
396
397 pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_COLOR);
398
399 /* Must create a pool factory before we can allocate any memory. */
400 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
401
402 status = pjmedia_aud_subsys_init(&cp.factory);
403 if (status != PJ_SUCCESS) {
404 app_perror("pjmedia_aud_subsys_init()", status);
405 pj_caching_pool_destroy(&cp);
406 pj_shutdown();
407 return 1;
408 }
409
410 list_devices();
411
412 while (!done) {
413 char line[80];
414
415 print_menu();
416
417 if (fgets(line, sizeof(line), stdin)==NULL)
418 break;
419
420 switch (line[0]) {
421 case 'l':
422 list_devices();
423 break;
424
425 case 'i':
426 {
427 unsigned dev_index;
428 if (sscanf(line+2, "%u", &dev_index) != 1) {
429 puts("error: device ID required");
430 break;
431 }
432 show_dev_info(dev_index);
433 }
434 break;
435
436 case 't':
437 {
438 pjmedia_dir dir;
439 int rec_id, play_id;
440 unsigned clock_rate, ptime, chnum;
441 int cnt;
442
443 cnt = sscanf(line+2, "%d %d %u %u %u", &rec_id, &play_id,
444 &clock_rate, &ptime, &chnum);
445 if (cnt < 4) {
446 puts("error: not enough parameters");
447 break;
448 }
449 if (clock_rate < 8000 || clock_rate > 128000) {
450 puts("error: invalid clock rate");
451 break;
452 }
453 if (ptime < 10 || ptime > 500) {
454 puts("error: invalid ptime");
455 break;
456 }
457 if (cnt==5) {
458 if (chnum < 1 || chnum > 4) {
459 puts("error: invalid number of channels");
460 break;
461 }
462 } else {
463 chnum = 1;
464 }
465
466 if (rec_id >= 0 && rec_id < (int)dev_count) {
467 if (play_id >= 0 && play_id < (int)dev_count)
468 dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
469 else
470 dir = PJMEDIA_DIR_CAPTURE;
471 } else if (play_id >= 0 && play_id < (int)dev_count) {
472 dir = PJMEDIA_DIR_PLAYBACK;
473 } else {
474 puts("error: at least one valid device index required");
475 break;
476 }
477
478 test_device(dir, rec_id, play_id, clock_rate, ptime, chnum);
479
480 }
481 break;
482
Benny Prijono555139d2009-02-19 12:08:19 +0000483 case 'r':
484 /* record */
485 {
486 int index;
487 char filename[80];
488 int count;
489
490 count = sscanf(line+2, "%d %s", &index, filename);
491 if (count==1)
492 record(index, NULL);
493 else if (count==2)
494 record(index, filename);
495 else
496 puts("error: invalid command syntax");
497 }
498 break;
499
500 case 'p':
501 /* playback */
502 {
503 int index;
504 char filename[80];
505 int count;
506
507 count = sscanf(line+2, "%d %s", &index, filename);
508 if (count==1)
509 play_file(index, NULL);
510 else if (count==2)
511 play_file(index, filename);
512 else
513 puts("error: invalid command syntax");
514 }
515 break;
516
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000517 case 'v':
518 if (pj_log_get_level() <= 3) {
519 pj_log_set_level(5);
520 puts("Logging set to detail");
521 } else {
522 pj_log_set_level(3);
523 puts("Logging set to quiet");
524 }
525 break;
526
527 case 'q':
528 done = PJ_TRUE;
529 break;
530 }
531 }
532
533 pj_caching_pool_destroy(&cp);
534 pj_shutdown();
535 return 0;
536}
537
538