1
/* $Id: systest.c 4087 2012-04-26 03:39:24Z ming $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
#define THIS_FILE "systest.c"
24
unsigned test_item_count;
25
test_item_t test_items[SYSTEST_MAX_TEST];
26
char doc_path[PATH_LENGTH] = {0};
27
char res_path[PATH_LENGTH] = {0};
28
char fpath[PATH_LENGTH];
30
#define USER_ERROR "User used said not okay"
32
static void systest_wizard(void);
33
static void systest_list_audio_devs(void);
34
static void systest_display_settings(void);
35
static void systest_play_tone(void);
36
static void systest_play_wav1(void);
37
static void systest_play_wav2(void);
38
static void systest_rec_audio(void);
39
static void systest_audio_test(void);
40
static void systest_latency_test(void);
41
static void systest_aec_test(void);
42
static void exit_app(void);
45
static gui_menu menu_exit = { "Exit", &exit_app };
47
static gui_menu menu_wizard = { "Run test wizard", &systest_wizard };
48
static gui_menu menu_playtn = { "Play Tone", &systest_play_tone };
49
static gui_menu menu_playwv1 = { "Play WAV File1", &systest_play_wav1 };
50
static gui_menu menu_playwv2 = { "Play WAV File2", &systest_play_wav2 };
51
static gui_menu menu_recaud = { "Record Audio", &systest_rec_audio };
52
static gui_menu menu_audtest = { "Device Test", &systest_audio_test };
53
static gui_menu menu_calclat = { "Latency Test", &systest_latency_test };
54
static gui_menu menu_sndaec = { "AEC/AES Test", &systest_aec_test };
56
static gui_menu menu_listdev = { "View Devices", &systest_list_audio_devs };
57
static gui_menu menu_getsets = { "View Settings", &systest_display_settings };
59
static gui_menu menu_tests = {
76
static gui_menu menu_options = {
85
static gui_menu root_menu = {
86
"Root", NULL, 2, {&menu_tests, &menu_options}
89
/*****************************************************************/
91
#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
92
PJ_INLINE(char *) add_path(const char *path, const char *fname)
94
strncpy(fpath, path, PATH_LENGTH);
95
strncat(fpath, fname, PATH_LENGTH);
99
# define add_path(path, fname) fname
102
static void exit_app(void)
104
systest_save_result(add_path(doc_path, RESULT_OUT_PATH));
109
#include <pjsua-lib/pjsua.h>
110
#include <pjmedia_audiodev.h>
112
typedef struct systest_t
115
pjsua_media_config media_cfg;
116
pjmedia_aud_dev_index rec_id;
117
pjmedia_aud_dev_index play_id;
120
static systest_t systest;
121
static char textbuf[600];
123
/* Device ID to test */
124
int systest_cap_dev_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
125
int systest_play_dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
127
static void systest_perror(const char *title, pj_status_t status)
129
char errmsg[PJ_ERR_MSG_SIZE];
130
char themsg[PJ_ERR_MSG_SIZE + 100];
132
if (status != PJ_SUCCESS)
133
pj_strerror(status, errmsg, sizeof(errmsg));
137
strcpy(themsg, title);
138
strncat(themsg, errmsg, sizeof(themsg)-1);
139
themsg[sizeof(themsg)-1] = '\0';
141
gui_msgbox("Error", themsg, WITH_OK);
144
test_item_t *systest_alloc_test_item(const char *title)
148
if (test_item_count == SYSTEST_MAX_TEST) {
149
gui_msgbox("Error", "You have done too many tests", WITH_OK);
153
ti = &test_items[test_item_count++];
154
pj_bzero(ti, sizeof(*ti));
155
pj_ansi_strcpy(ti->title, title);
160
/*****************************************************************************
161
* test: play simple ringback tone and hear it
163
static void systest_play_tone(void)
166
#define RINGBACK_FREQ1 440 /* 400 */
167
#define RINGBACK_FREQ2 480 /* 450 */
168
#define RINGBACK_ON 3000 /* 400 */
169
#define RINGBACK_OFF 4000 /* 200 */
170
#define RINGBACK_CNT 1 /* 2 */
171
#define RINGBACK_INTERVAL 4000 /* 2000 */
173
unsigned i, samples_per_frame;
174
pjmedia_tone_desc tone[RINGBACK_CNT];
175
pj_pool_t *pool = NULL;
176
pjmedia_port *ringback_port = NULL;
178
int ringback_slot = -1;
181
const char *title = "Audio Tone Playback Test";
184
ti = systest_alloc_test_item(title);
188
key = gui_msgbox(title,
189
"This test will play simple ringback tone to "
190
"the speaker. Please listen carefully for audio "
191
"impairments such as stutter. You may need "
192
"to let this test running for a while to "
193
"make sure that everything is okay. Press "
194
"OK to start, CANCEL to skip",
197
ti->skipped = PJ_TRUE;
201
PJ_LOG(3,(THIS_FILE, "Running %s", title));
203
pool = pjsua_pool_create("ringback", 512, 512);
204
samples_per_frame = systest.media_cfg.audio_frame_ptime *
205
systest.media_cfg.clock_rate *
206
systest.media_cfg.channel_count / 1000;
208
/* Ringback tone (call is ringing) */
209
name = pj_str("ringback");
210
status = pjmedia_tonegen_create2(pool, &name,
211
systest.media_cfg.clock_rate,
212
systest.media_cfg.channel_count,
214
16, PJMEDIA_TONEGEN_LOOP,
216
if (status != PJ_SUCCESS)
219
pj_bzero(&tone, sizeof(tone));
220
for (i=0; i<RINGBACK_CNT; ++i) {
221
tone[i].freq1 = RINGBACK_FREQ1;
222
tone[i].freq2 = RINGBACK_FREQ2;
223
tone[i].on_msec = RINGBACK_ON;
224
tone[i].off_msec = RINGBACK_OFF;
226
tone[RINGBACK_CNT-1].off_msec = RINGBACK_INTERVAL;
228
status = pjmedia_tonegen_play(ringback_port, RINGBACK_CNT, tone,
229
PJMEDIA_TONEGEN_LOOP);
230
if (status != PJ_SUCCESS)
233
status = pjsua_conf_add_port(pool, ringback_port, &ringback_slot);
234
if (status != PJ_SUCCESS)
237
status = pjsua_conf_connect(ringback_slot, 0);
238
if (status != PJ_SUCCESS)
241
key = gui_msgbox(title,
242
"Ringback tone should be playing now in the "
243
"speaker. Press OK to stop. ", WITH_OK);
248
if (ringback_slot != -1)
249
pjsua_conf_remove_port(ringback_slot);
251
pjmedia_port_destroy(ringback_port);
253
pj_pool_release(pool);
255
if (status != PJ_SUCCESS) {
256
systest_perror("Sorry we encounter error when initializing "
257
"the tone generator: ", status);
258
ti->success = PJ_FALSE;
259
pj_strerror(status, ti->reason, sizeof(ti->reason));
261
key = gui_msgbox(title, "Is the audio okay?", WITH_YESNO);
262
ti->success = (key == KEY_YES);
264
pj_ansi_strcpy(ti->reason, USER_ERROR);
269
/* Util: create file player, each time trying different paths until we get
272
static pj_status_t create_player(unsigned path_cnt, const char *paths[],
273
pjsua_player_id *p_id)
276
pj_status_t status = PJ_ENOTFOUND;
279
for (i=0; i<path_cnt; ++i) {
280
status = pjsua_player_create(pj_cstr(&name, paths[i]), 0, p_id);
281
if (status == PJ_SUCCESS)
287
/*****************************************************************************
288
* test: play WAV file
290
static void systest_play_wav(unsigned path_cnt, const char *paths[])
292
pjsua_player_id play_id = PJSUA_INVALID_ID;
295
const char *title = "WAV File Playback Test";
298
ti = systest_alloc_test_item(title);
302
pj_ansi_snprintf(textbuf, sizeof(textbuf),
303
"This test will play %s file to "
304
"the speaker. Please listen carefully for audio "
305
"impairments such as stutter. Let this test run "
306
"for a while to make sure that everything is okay."
307
" Press OK to start, CANCEL to skip",
310
key = gui_msgbox(title, textbuf,
313
ti->skipped = PJ_TRUE;
317
PJ_LOG(3,(THIS_FILE, "Running %s", title));
320
status = create_player(path_cnt, paths, &play_id);
321
if (status != PJ_SUCCESS)
324
status = pjsua_conf_connect(pjsua_player_get_conf_port(play_id), 0);
325
if (status != PJ_SUCCESS)
328
key = gui_msgbox(title,
329
"WAV file should be playing now in the "
330
"speaker. Press OK to stop. ", WITH_OK);
336
pjsua_player_destroy(play_id);
338
if (status != PJ_SUCCESS) {
339
systest_perror("Sorry we've encountered error", status);
340
ti->success = PJ_FALSE;
341
pj_strerror(status, ti->reason, sizeof(ti->reason));
343
key = gui_msgbox(title, "Is the audio okay?", WITH_YESNO);
344
ti->success = (key == KEY_YES);
346
pj_ansi_strcpy(ti->reason, USER_ERROR);
351
static void systest_play_wav1(void)
353
const char *paths[] = { add_path(res_path, WAV_PLAYBACK_PATH),
354
ALT_PATH1 WAV_PLAYBACK_PATH };
355
systest_play_wav(PJ_ARRAY_SIZE(paths), paths);
358
static void systest_play_wav2(void)
360
const char *paths[] = { add_path(res_path, WAV_TOCK8_PATH),
361
ALT_PATH1 WAV_TOCK8_PATH};
362
systest_play_wav(PJ_ARRAY_SIZE(paths), paths);
366
/*****************************************************************************
369
static void systest_rec_audio(void)
371
const pj_str_t filename = pj_str(add_path(doc_path, WAV_REC_OUT_PATH));
372
pj_pool_t *pool = NULL;
374
pjsua_recorder_id rec_id = PJSUA_INVALID_ID;
375
pjsua_player_id play_id = PJSUA_INVALID_ID;
376
pjsua_conf_port_id rec_slot = PJSUA_INVALID_ID;
377
pjsua_conf_port_id play_slot = PJSUA_INVALID_ID;
378
pj_status_t status = PJ_SUCCESS;
379
const char *title = "Audio Recording";
382
ti = systest_alloc_test_item(title);
386
key = gui_msgbox(title,
387
"This test will allow you to record audio "
388
"from the microphone, and playback the "
389
"audio to the speaker. Press OK to start recording, "
393
ti->skipped = PJ_TRUE;
397
PJ_LOG(3,(THIS_FILE, "Running %s", title));
399
pool = pjsua_pool_create("rectest", 512, 512);
401
status = pjsua_recorder_create(&filename, 0, NULL, -1, 0, &rec_id);
402
if (status != PJ_SUCCESS)
405
rec_slot = pjsua_recorder_get_conf_port(rec_id);
407
status = pjsua_conf_connect(0, rec_slot);
408
if (status != PJ_SUCCESS)
411
key = gui_msgbox(title,
412
"Recording is in progress now, please say "
413
"something in the microphone. Press OK "
414
"to stop recording", WITH_OK);
416
pjsua_conf_disconnect(0, rec_slot);
417
rec_slot = PJSUA_INVALID_ID;
418
pjsua_recorder_destroy(rec_id);
419
rec_id = PJSUA_INVALID_ID;
421
status = pjsua_player_create(&filename, 0, &play_id);
422
if (status != PJ_SUCCESS)
425
play_slot = pjsua_player_get_conf_port(play_id);
427
status = pjsua_conf_connect(play_slot, 0);
428
if (status != PJ_SUCCESS)
431
key = gui_msgbox(title,
432
"Recording has been stopped. "
433
"The recorded audio is being played now to "
434
"the speaker device, in a loop. Listen for "
435
"any audio impairments. Press OK to stop.",
439
if (rec_slot != PJSUA_INVALID_ID)
440
pjsua_conf_disconnect(0, rec_slot);
441
if (rec_id != PJSUA_INVALID_ID)
442
pjsua_recorder_destroy(rec_id);
443
if (play_slot != PJSUA_INVALID_ID)
444
pjsua_conf_disconnect(play_slot, 0);
445
if (play_id != PJSUA_INVALID_ID)
446
pjsua_player_destroy(play_id);
448
pj_pool_release(pool);
450
if (status != PJ_SUCCESS) {
451
systest_perror("Sorry we encountered an error: ", status);
452
ti->success = PJ_FALSE;
453
pj_strerror(status, ti->reason, sizeof(ti->reason));
455
key = gui_msgbox(title, "Is the audio okay?", WITH_YESNO);
456
ti->success = (key == KEY_YES);
458
pj_ansi_snprintf(textbuf, sizeof(textbuf),
459
"You will probably need to copy the recorded "
460
"WAV file %s to a desktop computer and analyze "
461
"it, to find out whether it's a recording "
462
"or playback problem.",
464
gui_msgbox(title, textbuf, WITH_OK);
465
pj_ansi_strcpy(ti->reason, USER_ERROR);
471
/****************************************************************************
472
* test: audio system test
474
static void systest_audio_test(void)
477
GOOD_MAX_INTERVAL = 5,
479
const pjmedia_dir dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
480
pjmedia_aud_param param;
481
pjmedia_aud_test_results result;
484
unsigned problem_count = 0;
485
const char *problems[16];
488
const char *title = "Audio Device Test";
491
ti = systest_alloc_test_item(title);
495
key = gui_msgbox(title,
496
"This will run an automated test for about "
497
"ten seconds or so, and display some "
498
"statistics about your sound device. "
499
"Please don't do anything until the test completes. "
500
"Press OK to start, or CANCEL to skip this test.",
503
ti->skipped = PJ_TRUE;
507
PJ_LOG(3,(THIS_FILE, "Running %s", title));
509
/* Disable sound device in pjsua first */
510
pjsua_set_no_snd_dev();
512
/* Setup parameters */
513
status = pjmedia_aud_dev_default_param(systest.play_id, ¶m);
514
if (status != PJ_SUCCESS) {
515
systest_perror("Sorry we had error in pjmedia_aud_dev_default_param()", status);
516
pjsua_set_snd_dev(systest.rec_id, systest.play_id);
517
ti->success = PJ_FALSE;
518
pj_strerror(status, ti->reason, sizeof(ti->reason));
519
ti->reason[sizeof(ti->reason)-1] = '\0';
524
param.rec_id = systest.rec_id;
525
param.play_id = systest.play_id;
526
param.clock_rate = systest.media_cfg.snd_clock_rate;
527
param.channel_count = systest.media_cfg.channel_count;
528
param.samples_per_frame = param.clock_rate * param.channel_count *
529
systest.media_cfg.audio_frame_ptime / 1000;
531
/* Latency settings */
532
param.flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
533
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY);
534
param.input_latency_ms = systest.media_cfg.snd_rec_latency;
535
param.output_latency_ms = systest.media_cfg.snd_play_latency;
538
status = pjmedia_aud_test(¶m, &result);
539
if (status != PJ_SUCCESS) {
540
systest_perror("Sorry we encountered error with the test", status);
541
pjsua_set_snd_dev(systest.rec_id, systest.play_id);
542
ti->success = PJ_FALSE;
543
pj_strerror(status, ti->reason, sizeof(ti->reason));
544
ti->reason[sizeof(ti->reason)-1] = '\0';
548
/* Restore pjsua sound device */
549
pjsua_set_snd_dev(systest.rec_id, systest.play_id);
551
/* Analyze the result! */
552
strcpy(textbuf, "Here are the audio statistics:\r\n");
553
textbufpos = strlen(textbuf);
555
if (result.rec.frame_cnt==0) {
556
problems[problem_count++] =
557
"No audio frames were captured from the microphone. "
558
"This means the audio device is not working properly.";
560
pj_ansi_snprintf(textbuf+textbufpos,
561
sizeof(textbuf)-textbufpos,
562
"Rec : interval (min/max/avg/dev)=\r\n"
563
" %u/%u/%u/%u (ms)\r\n"
565
result.rec.min_interval,
566
result.rec.max_interval,
567
result.rec.avg_interval,
568
result.rec.dev_interval,
569
result.rec.max_burst);
570
textbufpos = strlen(textbuf);
572
if (result.rec.max_burst > GOOD_MAX_INTERVAL) {
573
problems[problem_count++] =
574
"Recording max burst is quite high";
578
if (result.play.frame_cnt==0) {
579
problems[problem_count++] =
580
"No audio frames were played to the speaker. "
581
"This means the audio device is not working properly.";
583
pj_ansi_snprintf(textbuf+textbufpos,
584
sizeof(textbuf)-textbufpos,
585
"Play: interval (min/max/avg/dev)=\r\n"
586
" %u/%u/%u/%u (ms)\r\n"
588
result.play.min_interval,
589
result.play.max_interval,
590
result.play.avg_interval,
591
result.play.dev_interval,
592
result.play.max_burst);
593
textbufpos = strlen(textbuf);
595
if (result.play.max_burst > GOOD_MAX_INTERVAL) {
596
problems[problem_count++] =
597
"Playback max burst is quite high";
601
if (result.rec_drift_per_sec) {
602
const char *which = result.rec_drift_per_sec>=0 ? "faster" : "slower";
603
unsigned drift = result.rec_drift_per_sec>=0 ?
604
result.rec_drift_per_sec :
605
-result.rec_drift_per_sec;
607
pj_ansi_snprintf(drifttext, sizeof(drifttext),
608
"Clock drifts detected. Capture "
609
"is %d samples/sec %s "
610
"than the playback device",
612
problems[problem_count++] = drifttext;
615
if (problem_count == 0) {
616
pj_ansi_snprintf(textbuf+textbufpos,
617
sizeof(textbuf)-textbufpos,
618
"\r\nThe sound device seems to be okay!");
619
textbufpos = strlen(textbuf);
621
key = gui_msgbox("Audio Device Test", textbuf, WITH_OK);
625
pj_ansi_snprintf(textbuf+textbufpos,
626
sizeof(textbuf)-textbufpos,
627
"There could be %d problem(s) with the "
630
textbufpos = strlen(textbuf);
632
for (i=0; i<problem_count; ++i) {
633
pj_ansi_snprintf(textbuf+textbufpos,
634
sizeof(textbuf)-textbufpos,
635
" %d: %s\r\n", i+1, problems[i]);
636
textbufpos = strlen(textbuf);
639
key = gui_msgbox(title, textbuf, WITH_OK);
642
ti->success = PJ_TRUE;
643
pj_ansi_strncpy(ti->reason, textbuf, sizeof(ti->reason));
644
ti->reason[sizeof(ti->reason)-1] = '\0';
648
/****************************************************************************
651
static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav,
652
unsigned *lat_sum, unsigned *lat_cnt,
653
unsigned *lat_min, unsigned *lat_max)
657
unsigned i, clock_rate, samples_per_frame, read, len;
667
samples_per_frame = PJMEDIA_PIA_SPF(&wav->info);
668
clock_rate = PJMEDIA_PIA_SRATE(&wav->info);
669
frm.buf = pj_pool_alloc(pool, samples_per_frame * 2);
670
frm.size = samples_per_frame * 2;
671
len = pjmedia_wav_player_get_len(wav);
672
buf = pj_pool_alloc(pool, len + samples_per_frame);
674
/* Read the whole file */
676
while (read < len/2) {
677
status = pjmedia_port_get_frame(wav, &frm);
678
if (status != PJ_SUCCESS)
681
pjmedia_copy_samples(buf+read, (short*)frm.buf, samples_per_frame);
682
read += samples_per_frame;
685
if (read < 2 * clock_rate) {
686
systest_perror("The WAV file is too short", PJ_SUCCESS);
690
/* Zero the first 500ms to remove loud click noises
691
* (keypad press, etc.)
693
pjmedia_zero_samples(buf, clock_rate / 2);
695
/* Loop to calculate latency */
698
while (start_pos < len/2 - clock_rate) {
700
unsigned max_signal_pos = start_pos;
701
unsigned max_echo_pos = 0;
705
/* Get the largest signal in the next 0.7s */
706
for (i=start_pos; i<start_pos + clock_rate * 700 / 1000; ++i) {
707
if (abs(buf[i]) > max_signal) {
708
max_signal = abs(buf[i]);
713
/* Advance 10ms from max_signal_pos */
714
pos = max_signal_pos + 10 * clock_rate / 1000;
716
/* Get the largest signal in the next 800ms */
719
for (i=pos; i<pos+clock_rate * 8 / 10; ++i) {
720
if (abs(buf[i]) > max_signal) {
721
max_signal = abs(buf[i]);
726
lat = (max_echo_pos - max_signal_pos) * 1000 / clock_rate;
729
PJ_LOG(4,(THIS_FILE, "Signal at %dms, echo at %d ms, latency %d ms",
730
max_signal_pos * 1000 / clock_rate,
731
max_echo_pos * 1000 / clock_rate,
742
/* Advance next loop */
744
start_pos = max_signal_pos + clock_rate * 9 / 10;
747
start_pos += clock_rate;
755
static void systest_latency_test(void)
757
const char *ref_wav_paths[] = { add_path(res_path, WAV_TOCK8_PATH), ALT_PATH1 WAV_TOCK8_PATH };
758
pj_str_t rec_wav_file;
759
pjsua_player_id play_id = PJSUA_INVALID_ID;
760
pjsua_conf_port_id play_slot = PJSUA_INVALID_ID;
761
pjsua_recorder_id rec_id = PJSUA_INVALID_ID;
762
pjsua_conf_port_id rec_slot = PJSUA_INVALID_ID;
763
pj_pool_t *pool = NULL;
764
pjmedia_port *wav_port = NULL;
765
unsigned lat_sum=0, lat_cnt=0, lat_min=0, lat_max=0;
768
const char *title = "Audio Latency Test";
771
ti = systest_alloc_test_item(title);
775
key = gui_msgbox(title,
776
"This test will try to find the audio device's "
777
"latency. We will play a special WAV file to the "
778
"speaker for ten seconds, then at the end "
779
"calculate the latency. Please don't do anything "
780
"until the test is done.", WITH_OKCANCEL);
782
ti->skipped = PJ_TRUE;
785
key = gui_msgbox(title,
786
"For this test to work, we must be able to capture "
787
"the audio played in the speaker (the echo), and only"
788
" that audio (i.e. you must be in relatively quiet "
789
"place to run this test). "
790
"Press OK to start, or CANCEL to skip.",
793
ti->skipped = PJ_TRUE;
797
PJ_LOG(3,(THIS_FILE, "Running %s", title));
799
status = create_player(PJ_ARRAY_SIZE(ref_wav_paths), ref_wav_paths,
801
if (status != PJ_SUCCESS)
804
play_slot = pjsua_player_get_conf_port(play_id);
806
rec_wav_file = pj_str(add_path(doc_path, WAV_LATENCY_OUT_PATH));
807
status = pjsua_recorder_create(&rec_wav_file, 0, NULL, -1, 0, &rec_id);
808
if (status != PJ_SUCCESS)
811
rec_slot = pjsua_recorder_get_conf_port(rec_id);
814
//status = pjsua_conf_connect(0, 0);
815
status = pjsua_conf_connect(play_slot, 0);
816
status = pjsua_conf_connect(0, rec_slot);
817
status = pjsua_conf_connect(play_slot, rec_slot);
821
PJ_LOG(3,(THIS_FILE, "Please wait while test is running (~10 sec)"));
824
/* Done with the test */
825
//status = pjsua_conf_disconnect(0, 0);
826
status = pjsua_conf_disconnect(play_slot, rec_slot);
827
status = pjsua_conf_disconnect(0, rec_slot);
828
status = pjsua_conf_disconnect(play_slot, 0);
830
pjsua_recorder_destroy(rec_id);
831
rec_id = PJSUA_INVALID_ID;
833
pjsua_player_destroy(play_id);
834
play_id = PJSUA_INVALID_ID;
836
/* Confirm that echo is heard */
838
"Test is done. Now we need to confirm that we indeed "
839
"captured the echo. We will play the captured audio "
840
"and please confirm that you can hear the 'tock' echo.",
843
status = pjsua_player_create(&rec_wav_file, 0, &play_id);
844
if (status != PJ_SUCCESS)
847
play_slot = pjsua_player_get_conf_port(play_id);
849
status = pjsua_conf_connect(play_slot, 0);
850
if (status != PJ_SUCCESS)
853
key = gui_msgbox(title,
854
"The captured audio is being played back now. "
855
"Can you hear the 'tock' echo?",
858
pjsua_player_destroy(play_id);
859
play_id = PJSUA_INVALID_ID;
864
/* Now analyze the latency */
865
pool = pjsua_pool_create("latency", 512, 512);
867
status = pjmedia_wav_player_port_create(pool, rec_wav_file.ptr, 0, 0, 0, &wav_port);
868
if (status != PJ_SUCCESS)
871
status = calculate_latency(pool, wav_port, &lat_sum, &lat_cnt,
873
if (status != PJ_SUCCESS)
878
pjmedia_port_destroy(wav_port);
880
pj_pool_release(pool);
881
if (play_id != PJSUA_INVALID_ID)
882
pjsua_player_destroy(play_id);
883
if (rec_id != PJSUA_INVALID_ID)
884
pjsua_recorder_destroy(rec_id);
886
if (status != PJ_SUCCESS) {
887
systest_perror("Sorry we encountered an error: ", status);
888
ti->success = PJ_FALSE;
889
pj_strerror(status, ti->reason, sizeof(ti->reason));
890
} else if (key != KEY_YES) {
891
ti->success = PJ_FALSE;
893
pj_ansi_strcpy(ti->reason, USER_ERROR);
899
pj_ansi_snprintf(msg, sizeof(msg),
900
"The sound device latency:\r\n"
901
" Min=%u, Max=%u, Avg=%u\r\n",
902
lat_min, lat_max, lat_sum/lat_cnt);
903
msglen = strlen(msg);
905
if (lat_sum/lat_cnt > 500) {
906
pj_ansi_snprintf(msg+msglen, sizeof(msg)-msglen,
907
"The latency is huge!\r\n");
908
msglen = strlen(msg);
909
} else if (lat_sum/lat_cnt > 200) {
910
pj_ansi_snprintf(msg+msglen, sizeof(msg)-msglen,
911
"The latency is quite high\r\n");
912
msglen = strlen(msg);
915
key = gui_msgbox(title, msg, WITH_OK);
917
ti->success = PJ_TRUE;
918
pj_ansi_strncpy(ti->reason, msg, sizeof(ti->reason));
919
ti->reason[sizeof(ti->reason)-1] = '\0';
924
static void systest_aec_test(void)
926
const char *ref_wav_paths[] = { add_path(res_path, WAV_PLAYBACK_PATH),
927
ALT_PATH1 WAV_PLAYBACK_PATH };
928
pjsua_player_id player_id = PJSUA_INVALID_ID;
929
pjsua_recorder_id writer_id = PJSUA_INVALID_ID;
932
const char *title = "AEC/AES Test";
933
unsigned last_ec_tail = 0;
937
ti = systest_alloc_test_item(title);
941
key = gui_msgbox(title,
942
"This test will try to find whether the AEC/AES "
943
"works good on this system. Test will play a file "
944
"while recording from mic. The recording will be "
945
"played back later so you can check if echo is there. "
946
"Press OK to start.",
949
ti->skipped = PJ_TRUE;
953
/* Save current EC tail */
954
status = pjsua_get_ec_tail(&last_ec_tail);
955
if (status != PJ_SUCCESS)
958
/* Set EC tail setting to default */
959
status = pjsua_set_ec(PJSUA_DEFAULT_EC_TAIL_LEN, 0);
960
if (status != PJ_SUCCESS)
964
* Create player and recorder
966
status = create_player(PJ_ARRAY_SIZE(ref_wav_paths), ref_wav_paths,
968
if (status != PJ_SUCCESS) {
969
PJ_PERROR(1,(THIS_FILE, status, "Error opening WAV file %s",
974
status = pjsua_recorder_create(
975
pj_cstr(&tmp, add_path(doc_path, AEC_REC_PATH)), 0, 0, -1,
977
if (status != PJ_SUCCESS) {
978
PJ_PERROR(1,(THIS_FILE, status, "Error writing WAV file %s",
984
* Start playback and recording.
986
pjsua_conf_connect(pjsua_player_get_conf_port(player_id), 0);
987
pj_thread_sleep(100);
988
pjsua_conf_connect(0, pjsua_recorder_get_conf_port(writer_id));
990
/* Wait user signal */
991
gui_msgbox(title, "AEC/AES test is running. Press OK to stop this test.",
995
* Stop and close playback and recorder
997
pjsua_conf_disconnect(0, pjsua_recorder_get_conf_port(writer_id));
998
pjsua_conf_disconnect(pjsua_player_get_conf_port(player_id), 0);
999
pjsua_recorder_destroy(writer_id);
1000
pjsua_player_destroy(player_id);
1001
player_id = PJSUA_INVALID_ID;
1002
writer_id = PJSUA_INVALID_ID;
1007
status = pjsua_player_create(
1008
pj_cstr(&tmp, add_path(doc_path, AEC_REC_PATH)),
1010
if (status != PJ_SUCCESS) {
1011
PJ_PERROR(1,(THIS_FILE, status, "Error opening WAV file %s", AEC_REC_PATH));
1014
pjsua_conf_connect(pjsua_player_get_conf_port(player_id), 0);
1016
/* Wait user signal */
1017
gui_msgbox(title, "We are now playing the captured audio from the mic. "
1018
"Check if echo (of the audio played back previously) is "
1019
"present in the audio. The recording is stored in "
1020
AEC_REC_PATH " for offline analysis. "
1021
"Press OK to stop.",
1024
pjsua_conf_disconnect(pjsua_player_get_conf_port(player_id), 0);
1026
key = gui_msgbox(title,
1027
"Did you notice any echo in the recording?",
1032
if (player_id != PJSUA_INVALID_ID)
1033
pjsua_player_destroy(player_id);
1034
if (writer_id != PJSUA_INVALID_ID)
1035
pjsua_recorder_destroy(writer_id);
1037
/* Wait until sound device closed before restoring back EC tail setting */
1038
while (pjsua_snd_is_active())
1039
pj_thread_sleep(10);
1040
pjsua_set_ec(last_ec_tail, 0);
1043
if (status != PJ_SUCCESS) {
1044
systest_perror("Sorry we encountered an error: ", status);
1045
ti->success = PJ_FALSE;
1046
pj_strerror(status, ti->reason, sizeof(ti->reason));
1047
} else if (key == KEY_YES) {
1048
ti->success = PJ_FALSE;
1050
pj_ansi_strcpy(ti->reason, USER_ERROR);
1055
pj_ansi_snprintf(msg, sizeof(msg), "Test succeeded.\r\n");
1057
ti->success = PJ_TRUE;
1058
pj_ansi_strncpy(ti->reason, msg, sizeof(ti->reason));
1059
ti->reason[sizeof(ti->reason)-1] = '\0';
1064
/****************************************************************************
1067
static void systest_list_audio_devs()
1069
unsigned i, dev_count, len=0;
1073
const char *title = "Audio Device List";
1075
ti = systest_alloc_test_item(title);
1079
PJ_LOG(3,(THIS_FILE, "Running %s", title));
1081
dev_count = pjmedia_aud_dev_count();
1082
if (dev_count == 0) {
1083
key = gui_msgbox(title,
1084
"No audio devices are found", WITH_OK);
1085
ti->success = PJ_FALSE;
1086
pj_ansi_strcpy(ti->reason, "No device found");
1090
pj_ansi_snprintf(ti->reason+len, sizeof(ti->reason)-len,
1091
"Found %u devices\r\n", dev_count);
1092
len = strlen(ti->reason);
1094
for (i=0; i<dev_count; ++i) {
1095
pjmedia_aud_dev_info info;
1097
status = pjmedia_aud_dev_get_info(i, &info);
1098
if (status != PJ_SUCCESS) {
1099
systest_perror("Error retrieving device info: ", status);
1100
ti->success = PJ_FALSE;
1101
pj_strerror(status, ti->reason, sizeof(ti->reason));
1105
pj_ansi_snprintf(ti->reason+len, sizeof(ti->reason)-len,
1106
" %2d: %s [%s] (%d/%d)\r\n",
1107
i, info.driver, info.name,
1108
info.input_count, info.output_count);
1109
len = strlen(ti->reason);
1112
ti->reason[len] = '\0';
1113
key = gui_msgbox(title, ti->reason, WITH_OK);
1115
ti->success = PJ_TRUE;
1118
static void systest_display_settings(void)
1120
pjmedia_aud_dev_info di;
1124
const char *title = "Audio Settings";
1127
ti = systest_alloc_test_item(title);
1131
PJ_LOG(3,(THIS_FILE, "Running %s", title));
1133
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Version: %s\r\n",
1135
len = strlen(textbuf);
1137
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Test clock rate: %d\r\n",
1138
systest.media_cfg.clock_rate);
1139
len = strlen(textbuf);
1141
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Device clock rate: %d\r\n",
1142
systest.media_cfg.snd_clock_rate);
1143
len = strlen(textbuf);
1145
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Aud frame ptime: %d\r\n",
1146
systest.media_cfg.audio_frame_ptime);
1147
len = strlen(textbuf);
1149
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Channel count: %d\r\n",
1150
systest.media_cfg.channel_count);
1151
len = strlen(textbuf);
1153
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Audio switching: %s\r\n",
1154
(PJMEDIA_CONF_USE_SWITCH_BOARD ? "Switchboard" : "Conf bridge"));
1155
len = strlen(textbuf);
1157
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Snd buff count: %d\r\n",
1158
PJMEDIA_SOUND_BUFFER_COUNT);
1159
len = strlen(textbuf);
1161
/* Capture device */
1162
status = pjmedia_aud_dev_get_info(systest.rec_id, &di);
1163
if (status != PJ_SUCCESS) {
1164
systest_perror("Error querying device info", status);
1165
ti->success = PJ_FALSE;
1166
pj_strerror(status, ti->reason, sizeof(ti->reason));
1170
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len,
1171
"Rec dev : %d (%s) [%s]\r\n",
1175
len = strlen(textbuf);
1177
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len,
1178
"Rec buf : %d msec\r\n",
1179
systest.media_cfg.snd_rec_latency);
1180
len = strlen(textbuf);
1182
/* Playback device */
1183
status = pjmedia_aud_dev_get_info(systest.play_id, &di);
1184
if (status != PJ_SUCCESS) {
1185
systest_perror("Error querying device info", status);
1189
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len,
1190
"Play dev: %d (%s) [%s]\r\n",
1194
len = strlen(textbuf);
1196
pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len,
1197
"Play buf: %d msec\r\n",
1198
systest.media_cfg.snd_play_latency);
1199
len = strlen(textbuf);
1201
ti->success = PJ_TRUE;
1202
pj_ansi_strncpy(ti->reason, textbuf, sizeof(ti->reason));
1203
ti->reason[sizeof(ti->reason)-1] = '\0';
1204
key = gui_msgbox(title, textbuf, WITH_OK);
1208
/*****************************************************************/
1210
int systest_init(void)
1212
pjsua_logging_config log_cfg;
1213
pj_status_t status = PJ_SUCCESS;
1215
status = pjsua_create();
1216
if (status != PJ_SUCCESS) {
1217
systest_perror("Sorry we've had error in pjsua_create(): ", status);
1221
pjsua_logging_config_default(&log_cfg);
1222
log_cfg.log_filename = pj_str(add_path(doc_path, LOG_OUT_PATH));
1224
pjsua_config_default(&systest.ua_cfg);
1225
pjsua_media_config_default(&systest.media_cfg);
1226
systest.media_cfg.clock_rate = TEST_CLOCK_RATE;
1227
systest.media_cfg.snd_clock_rate = DEV_CLOCK_RATE;
1228
if (OVERRIDE_AUD_FRAME_PTIME)
1229
systest.media_cfg.audio_frame_ptime = OVERRIDE_AUD_FRAME_PTIME;
1230
systest.media_cfg.channel_count = CHANNEL_COUNT;
1231
systest.rec_id = REC_DEV_ID;
1232
systest.play_id = PLAY_DEV_ID;
1233
systest.media_cfg.ec_tail_len = 0;
1234
systest.media_cfg.snd_auto_close_time = 0;
1236
#if defined(OVERRIDE_AUDDEV_PLAY_LAT) && OVERRIDE_AUDDEV_PLAY_LAT!=0
1237
systest.media_cfg.snd_play_latency = OVERRIDE_AUDDEV_PLAY_LAT;
1240
#if defined(OVERRIDE_AUDDEV_REC_LAT) && OVERRIDE_AUDDEV_REC_LAT!=0
1241
systest.media_cfg.snd_rec_latency = OVERRIDE_AUDDEV_REC_LAT;
1244
status = pjsua_init(&systest.ua_cfg, &log_cfg, &systest.media_cfg);
1245
if (status != PJ_SUCCESS) {
1247
systest_perror("Sorry we've had error in pjsua_init(): ", status);
1251
status = pjsua_start();
1252
if (status != PJ_SUCCESS) {
1254
systest_perror("Sorry we've had error in pjsua_start(): ", status);
1258
status = gui_init(&root_menu);
1270
int systest_set_dev(int cap_dev, int play_dev)
1272
systest.rec_id = systest_cap_dev_id = cap_dev;
1273
systest.play_id = systest_play_dev_id = play_dev;
1274
return pjsua_set_snd_dev(cap_dev, play_dev);
1277
static void systest_wizard(void)
1279
PJ_LOG(3,(THIS_FILE, "Running test wizard"));
1280
systest_list_audio_devs();
1281
systest_display_settings();
1282
systest_play_tone();
1283
systest_play_wav1();
1284
systest_rec_audio();
1285
systest_audio_test();
1286
systest_latency_test();
1288
gui_msgbox("Test wizard", "Test wizard complete.", WITH_OK);
1292
int systest_run(void)
1294
gui_start(&root_menu);
1298
void systest_save_result(const char *filename)
1308
status = pj_file_open(NULL, filename, PJ_O_WRONLY | PJ_O_APPEND, &fd);
1309
if (status != PJ_SUCCESS) {
1310
pj_ansi_snprintf(textbuf, sizeof(textbuf),
1311
"Error opening file %s",
1313
systest_perror(textbuf, status);
1317
text = "\r\n\r\nPJSYSTEST Report\r\n";
1318
size = strlen(text);
1319
pj_file_write(fd, text, &size);
1322
pj_gettimeofday(&tv);
1323
if (pj_time_decode(&tv, &pt) == PJ_SUCCESS) {
1324
pj_ansi_snprintf(textbuf, sizeof(textbuf),
1325
"Time: %04d/%02d/%02d %02d:%02d:%02d\r\n",
1326
pt.year, pt.mon+1, pt.day,
1327
pt.hour, pt.min, pt.sec);
1328
size = strlen(textbuf);
1329
pj_file_write(fd, textbuf, &size);
1332
pj_ansi_snprintf(textbuf, sizeof(textbuf),
1333
"Tests invoked: %u\r\n"
1334
"-----------------------------------------------\r\n",
1336
size = strlen(textbuf);
1337
pj_file_write(fd, textbuf, &size);
1339
for (i=0; i<test_item_count; ++i) {
1340
test_item_t *ti = &test_items[i];
1341
pj_ansi_snprintf(textbuf, sizeof(textbuf),
1342
"\r\nTEST %d: %s %s\r\n",
1344
(ti->skipped? "Skipped" : (ti->success ? "Success" : "Failed")));
1345
size = strlen(textbuf);
1346
pj_file_write(fd, textbuf, &size);
1348
size = strlen(ti->reason);
1349
pj_file_write(fd, ti->reason, &size);
1352
pj_file_write(fd, "\r\n", &size);
1358
pj_ansi_snprintf(textbuf, sizeof(textbuf),
1359
"Test result successfully appended to file %s",
1361
gui_msgbox("Test result saved", textbuf, WITH_OK);
1364
void systest_deinit(void)