1 /*
2  * Copyright (C) 2013-2015 Intel Corporation
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  */
15 
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdbool.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <pthread.h>
23 #include <getopt.h>
24 #include <math.h>
25 #include <limits.h>
26 #include <locale.h>
27 #include <math.h>
28 
29 #include "aconfig.h"
30 #include "gettext.h"
31 #include "version.h"
32 
33 #include "common.h"
34 
35 #ifdef HAVE_LIBTINYALSA
36 #include "tinyalsa.h"
37 #else
38 #include "alsa.h"
39 #endif
40 #include "convert.h"
41 #ifdef HAVE_LIBFFTW3F
42 #include "analyze.h"
43 #endif
44 #include "latencytest.h"
45 
46 /* get snr threshold in dB */
get_snr_thd_db(struct bat *bat, char *thd)47 static void get_snr_thd_db(struct bat *bat, char *thd)
48 {
49 	int err;
50 	float thd_db;
51 	char *ptrf;
52 
53 	thd_db = strtof(thd, &ptrf);
54 	err = -errno;
55 	if (!snr_is_valid(thd_db)) {
56 		fprintf(bat->err, _("Invalid threshold '%s':%d\n"), thd, err);
57 		exit(EXIT_FAILURE);
58 	}
59 	bat->snr_thd_db = thd_db;
60 }
61 
62 /* get snr threshold in %, and convert to dB */
get_snr_thd_pc(struct bat *bat, char *thd)63 static void get_snr_thd_pc(struct bat *bat, char *thd)
64 {
65 	int err;
66 	float thd_pc;
67 	char *ptrf;
68 
69 	thd_pc = strtof(thd, &ptrf);
70 	err = -errno;
71 	if (thd_pc <= 0.0 || thd_pc >= 100.0) {
72 		fprintf(bat->err, _("Invalid threshold '%s':%d\n"), thd, err);
73 		exit(EXIT_FAILURE);
74 	}
75 	bat->snr_thd_db = 20.0 * log10f(100.0 / thd_pc);
76 }
77 
get_duration(struct bat *bat)78 static int get_duration(struct bat *bat)
79 {
80 	int err;
81 	float duration_f;
82 	long duration_i;
83 	char *ptrf, *ptri;
84 
85 	duration_f = strtof(bat->narg, &ptrf);
86 	err = -errno;
87 	if (duration_f == HUGE_VALF || duration_f == -HUGE_VALF
88 			|| (duration_f == 0.0 && err != 0))
89 		goto err_exit;
90 
91 	duration_i = strtol(bat->narg, &ptri, 10);
92 	if (duration_i == LONG_MAX || duration_i == LONG_MIN)
93 		goto err_exit;
94 
95 	if (*ptrf == 's')
96 		bat->frames = duration_f * bat->rate;
97 	else if (*ptri == 0)
98 		bat->frames = duration_i;
99 	else
100 		bat->frames = -1;
101 
102 	if (bat->frames <= 0 || bat->frames > MAX_FRAMES) {
103 		fprintf(bat->err, _("Invalid duration. Range: (0, %d(%fs))\n"),
104 				MAX_FRAMES, (float)MAX_FRAMES / bat->rate);
105 		return -EINVAL;
106 	}
107 
108 	return 0;
109 
110 err_exit:
111 	fprintf(bat->err, _("Duration overflow/underflow: %d\n"), err);
112 
113 	return err;
114 }
115 
get_sine_frequencies(struct bat *bat, char *freq)116 static void get_sine_frequencies(struct bat *bat, char *freq)
117 {
118 	char *tmp1;
119 
120 	tmp1 = strchr(freq, ':');
121 	if (tmp1 == NULL) {
122 		bat->target_freq[1] = bat->target_freq[0] = atof(optarg);
123 	} else {
124 		*tmp1 = '\0';
125 		bat->target_freq[0] = atof(optarg);
126 		bat->target_freq[1] = atof(tmp1 + 1);
127 	}
128 }
129 
get_format(struct bat *bat, char *optarg)130 static void get_format(struct bat *bat, char *optarg)
131 {
132 	if (strcasecmp(optarg, "cd") == 0) {
133 		bat->format = BAT_PCM_FORMAT_S16_LE;
134 		bat->rate = 44100;
135 		bat->channels = 2;
136 		bat->sample_size = 2;
137 	} else if (strcasecmp(optarg, "dat") == 0) {
138 		bat->format = BAT_PCM_FORMAT_S16_LE;
139 		bat->rate = 48000;
140 		bat->channels = 2;
141 		bat->sample_size = 2;
142 	} else if (strcasecmp(optarg, "U8") == 0) {
143 		bat->format = BAT_PCM_FORMAT_U8;
144 		bat->sample_size = 1;
145 	} else if (strcasecmp(optarg, "S16_LE") == 0) {
146 		bat->format = BAT_PCM_FORMAT_S16_LE;
147 		bat->sample_size = 2;
148 	} else if (strcasecmp(optarg, "S24_3LE") == 0) {
149 		bat->format = BAT_PCM_FORMAT_S24_3LE;
150 		bat->sample_size = 3;
151 	} else if (strcasecmp(optarg, "S32_LE") == 0) {
152 		bat->format = BAT_PCM_FORMAT_S32_LE;
153 		bat->sample_size = 4;
154 	} else {
155 		bat->format = BAT_PCM_FORMAT_UNKNOWN;
156 		fprintf(bat->err, _("wrong extended format '%s'\n"), optarg);
157 		exit(EXIT_FAILURE);
158 	}
159 }
160 
thread_wait_completion(struct bat *bat ATTRIBUTE_UNUSED, pthread_t id, int **val)161 static inline int thread_wait_completion(struct bat *bat ATTRIBUTE_UNUSED,
162 					 pthread_t id, int **val)
163 {
164 	int err;
165 
166 	err = pthread_join(id, (void **) val);
167 	if (err)
168 		pthread_cancel(id);
169 
170 	return err;
171 }
172 
173 /* loopback test where we play sine wave and capture the same sine wave */
test_loopback(struct bat *bat)174 static void test_loopback(struct bat *bat)
175 {
176 	pthread_t capture_id, playback_id;
177 	int err;
178 	int *thread_result_capture, *thread_result_playback;
179 
180 	/* start playback */
181 	err = pthread_create(&playback_id, NULL,
182 			(void *) bat->playback.fct, bat);
183 	if (err != 0) {
184 		fprintf(bat->err, _("Cannot create playback thread: %d\n"),
185 				err);
186 		exit(EXIT_FAILURE);
187 	}
188 
189 	/* TODO: use a pipe to signal stream start etc - i.e. to sync threads */
190 	/* Let some time for playing something before capturing */
191 	usleep(CAPTURE_DELAY * 1000);
192 
193 	/* start capture */
194 	err = pthread_create(&capture_id, NULL, (void *) bat->capture.fct, bat);
195 	if (err != 0) {
196 		fprintf(bat->err, _("Cannot create capture thread: %d\n"), err);
197 		pthread_cancel(playback_id);
198 		exit(EXIT_FAILURE);
199 	}
200 
201 	/* wait for playback to complete */
202 	err = thread_wait_completion(bat, playback_id, &thread_result_playback);
203 	if (err != 0) {
204 		fprintf(bat->err, _("Cannot join playback thread: %d\n"), err);
205 		free(thread_result_playback);
206 		pthread_cancel(capture_id);
207 		exit(EXIT_FAILURE);
208 	}
209 
210 	/* check playback status */
211 	if (*thread_result_playback != 0) {
212 		fprintf(bat->err, _("Exit playback thread fail: %d\n"),
213 				*thread_result_playback);
214 		pthread_cancel(capture_id);
215 		exit(EXIT_FAILURE);
216 	} else {
217 		fprintf(bat->log, _("Playback completed.\n"));
218 	}
219 
220 	/* now stop and wait for capture to finish */
221 	pthread_cancel(capture_id);
222 	err = thread_wait_completion(bat, capture_id, &thread_result_capture);
223 	if (err != 0) {
224 		fprintf(bat->err, _("Cannot join capture thread: %d\n"), err);
225 		free(thread_result_capture);
226 		exit(EXIT_FAILURE);
227 	}
228 
229 	/* check if capture thread is canceled or not */
230 	if (thread_result_capture == PTHREAD_CANCELED) {
231 		fprintf(bat->log, _("Capture canceled.\n"));
232 		return;
233 	}
234 
235 	/* check capture status */
236 	if (*thread_result_capture != 0) {
237 		fprintf(bat->err, _("Exit capture thread fail: %d\n"),
238 				*thread_result_capture);
239 		exit(EXIT_FAILURE);
240 	} else {
241 		fprintf(bat->log, _("Capture completed.\n"));
242 	}
243 }
244 
245 /* single ended playback only test */
test_playback(struct bat *bat)246 static void test_playback(struct bat *bat)
247 {
248 	pthread_t playback_id;
249 	int err;
250 	int *thread_result;
251 
252 	/* start playback */
253 	err = pthread_create(&playback_id, NULL,
254 			(void *) bat->playback.fct, bat);
255 	if (err != 0) {
256 		fprintf(bat->err, _("Cannot create playback thread: %d\n"),
257 				err);
258 		exit(EXIT_FAILURE);
259 	}
260 
261 	/* wait for playback to complete */
262 	err = thread_wait_completion(bat, playback_id, &thread_result);
263 	if (err != 0) {
264 		fprintf(bat->err, _("Cannot join playback thread: %d\n"), err);
265 		free(thread_result);
266 		exit(EXIT_FAILURE);
267 	}
268 
269 	/* check playback status */
270 	if (*thread_result != 0) {
271 		fprintf(bat->err, _("Exit playback thread fail: %d\n"),
272 				*thread_result);
273 		exit(EXIT_FAILURE);
274 	} else {
275 		fprintf(bat->log, _("Playback completed.\n"));
276 	}
277 }
278 
279 /* single ended capture only test */
test_capture(struct bat *bat)280 static void test_capture(struct bat *bat)
281 {
282 	pthread_t capture_id;
283 	int err;
284 	int *thread_result;
285 
286 	/* start capture */
287 	err = pthread_create(&capture_id, NULL, (void *) bat->capture.fct, bat);
288 	if (err != 0) {
289 		fprintf(bat->err, _("Cannot create capture thread: %d\n"), err);
290 		exit(EXIT_FAILURE);
291 	}
292 
293 	/* TODO: stop capture */
294 
295 	/* wait for capture to complete */
296 	err = thread_wait_completion(bat, capture_id, &thread_result);
297 	if (err != 0) {
298 		fprintf(bat->err, _("Cannot join capture thread: %d\n"), err);
299 		free(thread_result);
300 		exit(EXIT_FAILURE);
301 	}
302 
303 	/* check playback status */
304 	if (*thread_result != 0) {
305 		fprintf(bat->err, _("Exit capture thread fail: %d\n"),
306 				*thread_result);
307 		exit(EXIT_FAILURE);
308 	} else {
309 		fprintf(bat->log, _("Capture completed.\n"));
310 	}
311 }
312 
usage(struct bat *bat)313 static void usage(struct bat *bat)
314 {
315 	fprintf(bat->log,
316 _("Usage: alsabat [-options]...\n"
317 "\n"
318 "  -h, --help             this help\n"
319 "  -D                     pcm device for both playback and capture\n"
320 "  -P                     pcm device for playback\n"
321 "  -C                     pcm device for capture\n"
322 "  -f                     sample format\n"
323 "  -c                     number of channels\n"
324 "  -r                     sampling rate\n"
325 "  -n                     frames to playback or capture\n"
326 "  -k                     parameter for frequency detecting threshold\n"
327 "  -F                     target frequency\n"
328 "  -p                     total number of periods to play/capture\n"
329 "  -B                     buffer size in frames\n"
330 "  -E                     period size in frames\n"
331 "      --log=#            file that both stdout and strerr redirecting to\n"
332 "      --file=#           file for playback\n"
333 "      --saveplay=#       file that storing playback content, for debug\n"
334 "      --readcapture=#    file with previously captured content.  File data\n"
335 "                         is used for analysis instead of capturing it.\n"
336 "      --local            internal loop, set to bypass pcm hardware devices\n"
337 "      --standalone       standalone mode, to bypass analysis\n"
338 "      --roundtriplatency round trip latency mode\n"
339 "      --snr-db=#         noise detect threshold, in SNR(dB)\n"
340 "      --snr-pc=#         noise detect threshold, in noise percentage(%%)\n"
341 ));
342 	fprintf(bat->log, _("Recognized sample formats are: "));
343 	fprintf(bat->log, _("U8 S16_LE S24_3LE S32_LE\n"));
344 	fprintf(bat->log, _("The available format shotcuts are:\n"));
345 	fprintf(bat->log, _("-f cd (16 bit little endian, 44100, stereo)\n"));
346 	fprintf(bat->log, _("-f dat (16 bit little endian, 48000, stereo)\n"));
347 }
348 
set_defaults(struct bat *bat)349 static void set_defaults(struct bat *bat)
350 {
351 	memset(bat, 0, sizeof(struct bat));
352 
353 	/* Set default values */
354 	bat->rate = 44100;
355 	bat->frame_size = 2;
356 	bat->sample_size = 2;
357 	bat->format = BAT_PCM_FORMAT_S16_LE;
358 	bat->convert_float_to_sample = convert_float_to_int16;
359 	bat->convert_sample_to_float = convert_int16_to_float;
360 	bat->frames = bat->rate * 2;
361 	bat->target_freq[0] = 997.0;
362 	bat->target_freq[1] = 997.0;
363 	bat->sigma_k = 3.0;
364 	bat->snr_thd_db = SNR_DB_INVALID;
365 	bat->playback.device = NULL;
366 	bat->capture.device = NULL;
367 	bat->buf = NULL;
368 	bat->local = false;
369 	bat->buffer_size = 0;
370 	bat->period_size = 0;
371 	bat->roundtriplatency = false;
372 #ifdef HAVE_LIBTINYALSA
373 	bat->channels = 2;
374 	bat->playback.fct = &playback_tinyalsa;
375 	bat->capture.fct = &record_tinyalsa;
376 #else
377 	bat->channels = 1;
378 	bat->playback.fct = &playback_alsa;
379 	bat->capture.fct = &record_alsa;
380 #endif
381 	bat->playback.mode = MODE_LOOPBACK;
382 	bat->capture.mode = MODE_LOOPBACK;
383 	bat->period_is_limited = false;
384 	bat->log = stdout;
385 	bat->err = stderr;
386 }
387 
parse_arguments(struct bat *bat, int argc, char *argv[])388 static void parse_arguments(struct bat *bat, int argc, char *argv[])
389 {
390 	int c, option_index, err;
391 	static const char short_options[] = "D:P:C:f:n:F:c:r:s:k:p:B:E:lth";
392 	static const struct option long_options[] = {
393 		{"help",     0, 0, 'h'},
394 		{"log",      1, 0, OPT_LOG},
395 		{"file",     1, 0, OPT_READFILE},
396 		{"saveplay", 1, 0, OPT_SAVEPLAY},
397 		{"local",    0, 0, OPT_LOCAL},
398 		{"standalone", 0, 0, OPT_STANDALONE},
399 		{"roundtriplatency", 0, 0, OPT_ROUNDTRIPLATENCY},
400 		{"snr-db",   1, 0, OPT_SNRTHD_DB},
401 		{"snr-pc",   1, 0, OPT_SNRTHD_PC},
402 		{"readcapture", 1, 0, OPT_READCAPTURE},
403 		{0, 0, 0, 0}
404 	};
405 
406 	while ((c = getopt_long(argc, argv, short_options, long_options,
407 					&option_index)) != -1) {
408 		switch (c) {
409 		case OPT_LOG:
410 			bat->logarg = optarg;
411 			break;
412 		case OPT_READFILE:
413 			bat->playback.file = optarg;
414 			break;
415 		case OPT_SAVEPLAY:
416 			bat->debugplay = optarg;
417 			break;
418 		case OPT_READCAPTURE:
419 			bat->capturefile = optarg;
420 			bat->capture.mode = MODE_ANALYZE_ONLY;
421 			bat->playback.mode = MODE_ANALYZE_ONLY;
422 			break;
423 		case OPT_LOCAL:
424 			bat->local = true;
425 			break;
426 		case OPT_STANDALONE:
427 			bat->standalone = true;
428 			break;
429 		case OPT_ROUNDTRIPLATENCY:
430 			bat->roundtriplatency = true;
431 			break;
432 		case OPT_SNRTHD_DB:
433 			get_snr_thd_db(bat, optarg);
434 			break;
435 		case OPT_SNRTHD_PC:
436 			get_snr_thd_pc(bat, optarg);
437 			break;
438 		case 'D':
439 			if (bat->playback.device == NULL)
440 				bat->playback.device = optarg;
441 			if (bat->capture.device == NULL)
442 				bat->capture.device = optarg;
443 			break;
444 		case 'P':
445 			if (bat->capture.mode == MODE_SINGLE)
446 				bat->capture.mode = MODE_LOOPBACK;
447 			else
448 				bat->playback.mode = MODE_SINGLE;
449 			bat->playback.device = optarg;
450 			break;
451 		case 'C':
452 			if (bat->playback.mode == MODE_SINGLE)
453 				bat->playback.mode = MODE_LOOPBACK;
454 			else
455 				bat->capture.mode = MODE_SINGLE;
456 			bat->capture.device = optarg;
457 			break;
458 		case 'n':
459 			bat->narg = optarg;
460 			break;
461 		case 'F':
462 			get_sine_frequencies(bat, optarg);
463 			break;
464 		case 'c':
465 			bat->channels = atoi(optarg);
466 			break;
467 		case 'r':
468 			bat->rate = atoi(optarg);
469 			break;
470 		case 'f':
471 			get_format(bat, optarg);
472 			break;
473 		case 'k':
474 			bat->sigma_k = atof(optarg);
475 			break;
476 		case 'p':
477 			bat->periods_total = atoi(optarg);
478 			bat->period_is_limited = true;
479 			break;
480 		case 'B':
481 			err = atoi(optarg);
482 			bat->buffer_size = err >= MIN_BUFFERSIZE
483 					&& err < MAX_BUFFERSIZE ? err : 0;
484 			break;
485 		case 'E':
486 			err = atoi(optarg);
487 			bat->period_size = err >= MIN_PERIODSIZE
488 					&& err < MAX_PERIODSIZE ? err : 0;
489 			break;
490 		case 'h':
491 		default:
492 			usage(bat);
493 			exit(EXIT_SUCCESS);
494 		}
495 	}
496 }
497 
validate_options(struct bat *bat)498 static int validate_options(struct bat *bat)
499 {
500 	int c;
501 	float freq_low, freq_high;
502 
503 	/* check if we have an input file for local mode */
504 	if ((bat->local == true) && (bat->capture.file == NULL)) {
505 		fprintf(bat->err, _("no input file for local testing\n"));
506 		return -EINVAL;
507 	}
508 
509 	/* check supported channels */
510 	if (bat->channels > MAX_CHANNELS || bat->channels < MIN_CHANNELS) {
511 		fprintf(bat->err, _("%d channels not supported\n"),
512 				bat->channels);
513 		return -EINVAL;
514 	}
515 
516 	/* check single ended is in either playback or capture - not both */
517 	if ((bat->playback.mode == MODE_SINGLE)
518 			&& (bat->capture.mode == MODE_SINGLE)) {
519 		fprintf(bat->err, _("single ended mode is simplex\n"));
520 		return -EINVAL;
521 	}
522 
523 	/* check sine wave frequency range */
524 	freq_low = DC_THRESHOLD;
525 	freq_high = bat->rate * RATE_FACTOR;
526 	for (c = 0; c < bat->channels; c++) {
527 		if (bat->target_freq[c] < freq_low
528 				|| bat->target_freq[c] > freq_high) {
529 			fprintf(bat->err, _("sine wave frequency out of"));
530 			fprintf(bat->err, _(" range: (%.1f, %.1f)\n"),
531 				freq_low, freq_high);
532 			return -EINVAL;
533 		}
534 	}
535 
536 	return 0;
537 }
538 
bat_init(struct bat *bat)539 static int bat_init(struct bat *bat)
540 {
541 	int err = 0;
542 	int fd = 0;
543 	char name[] = TEMP_RECORD_FILE_NAME;
544 
545 	/* Determine logging to a file or stdout and stderr */
546 	if (bat->logarg) {
547 		bat->log = NULL;
548 		bat->log = fopen(bat->logarg, "wb");
549 		if (bat->log == NULL) {
550 			err = -errno;
551 			fprintf(bat->err, _("Cannot open file: %s %d\n"),
552 					bat->logarg, err);
553 			return err;
554 		}
555 		bat->err = bat->log;
556 	}
557 
558 	/* Determine duration of playback and/or capture */
559 	if (bat->narg) {
560 		err = get_duration(bat);
561 		if (err < 0)
562 			return err;
563 	}
564 
565 	/* Set default playback and capture devices */
566 	if (bat->playback.device == NULL && bat->capture.device == NULL)
567 		bat->playback.device = bat->capture.device = DEFAULT_DEV_NAME;
568 
569 	/* Determine capture file */
570 	if (bat->local) {
571 		bat->capture.file = bat->playback.file;
572 	} else {
573 		/* create temp file for sound record and analysis */
574 		fd = mkstemp(name);
575 		if (fd == -1) {
576 			err = -errno;
577 			fprintf(bat->err, _("Fail to create record file: %d\n"),
578 					err);
579 			return err;
580 		}
581 		/* store file name which is dynamically created */
582 		bat->capture.file = strdup(name);
583 		if (bat->capture.file == NULL)
584 			return -ENOMEM;
585 		/* close temp file */
586 		close(fd);
587 	}
588 
589 	/* Initial for playback */
590 	if (bat->playback.file == NULL) {
591 		/* No input file so we will generate our own sine wave */
592 		if (bat->frames) {
593 			if (bat->playback.mode == MODE_SINGLE) {
594 				/* Play nb of frames given by -n argument */
595 				bat->sinus_duration = bat->frames;
596 			} else {
597 				/* Play CAPTURE_DELAY msec +
598 				 * 150% of the nb of frames to be analyzed */
599 				bat->sinus_duration = bat->rate *
600 						CAPTURE_DELAY / 1000;
601 				bat->sinus_duration +=
602 						(bat->frames + bat->frames / 2);
603 			}
604 		} else {
605 			/* Special case where we want to generate a sine wave
606 			 * endlessly without capturing */
607 			bat->sinus_duration = 0;
608 			bat->playback.mode = MODE_SINGLE;
609 		}
610 	} else {
611 		bat->fp = fopen(bat->playback.file, "rb");
612 		if (bat->fp == NULL) {
613 			err = -errno;
614 			fprintf(bat->err, _("Cannot open file: %s %d\n"),
615 					bat->playback.file, err);
616 			return err;
617 		}
618 		err = read_wav_header(bat, bat->playback.file, bat->fp, false);
619 		fclose(bat->fp);
620 		if (err != 0)
621 			return err;
622 	}
623 
624 	bat->frame_size = bat->sample_size * bat->channels;
625 
626 	/* Set conversion functions */
627 	switch (bat->sample_size) {
628 	case 1:
629 		bat->convert_float_to_sample = convert_float_to_uint8;
630 		bat->convert_sample_to_float = convert_uint8_to_float;
631 		break;
632 	case 2:
633 		bat->convert_float_to_sample = convert_float_to_int16;
634 		bat->convert_sample_to_float = convert_int16_to_float;
635 		break;
636 	case 3:
637 		bat->convert_float_to_sample = convert_float_to_int24;
638 		bat->convert_sample_to_float = convert_int24_to_float;
639 		break;
640 	case 4:
641 		bat->convert_float_to_sample = convert_float_to_int32;
642 		bat->convert_sample_to_float = convert_int32_to_float;
643 		break;
644 	default:
645 		fprintf(bat->err, _("Invalid PCM format: size=%d\n"),
646 				bat->sample_size);
647 		return -EINVAL;
648 	}
649 
650 	return err;
651 }
652 
main(int argc, char *argv[])653 int main(int argc, char *argv[])
654 {
655 	struct bat bat;
656 	int err = 0;
657 
658 	set_defaults(&bat);
659 
660 #ifdef ENABLE_NLS
661 	setlocale(LC_ALL, "");
662 	textdomain(PACKAGE);
663 #endif
664 
665 	fprintf(bat.log, _("%s version %s\n\n"), PACKAGE_NAME, PACKAGE_VERSION);
666 
667 	parse_arguments(&bat, argc, argv);
668 
669 	err = bat_init(&bat);
670 	if (err < 0)
671 		goto out;
672 
673 	err = validate_options(&bat);
674 	if (err < 0)
675 		goto out;
676 
677 	/* round trip latency test thread */
678 	if (bat.roundtriplatency) {
679 		while (1) {
680 			fprintf(bat.log,
681 				_("\nStart round trip latency\n"));
682 			roundtrip_latency_init(&bat);
683 			test_loopback(&bat);
684 
685 			if (bat.latency.xrun_error == false)
686 				break;
687 			else {
688 				/* Xrun error in playback or capture,
689 				increase period size and try again */
690 				bat.period_size += bat.rate / 1000;
691 				bat.buffer_size =
692 					bat.period_size * DIV_BUFFERSIZE;
693 
694 				/* terminate the test if period_size is
695 				large enough */
696 				if (bat.period_size > bat.rate * 0.2)
697 					break;
698 			}
699 
700 			/* Waiting 500ms and start the next round */
701 			usleep(CAPTURE_DELAY * 1000);
702 		}
703 		goto out;
704 	}
705 
706 	/* single line playback thread: playback only, no capture */
707 	if (bat.playback.mode == MODE_SINGLE) {
708 		test_playback(&bat);
709 		goto out;
710 	}
711 
712 	/* single line capture thread: capture only, no playback */
713 	if (bat.capture.mode == MODE_SINGLE) {
714 		test_capture(&bat);
715 		goto analyze;
716 	}
717 
718 	if (bat.capture.mode == MODE_ANALYZE_ONLY && bat.capturefile) {
719 		bat.capture.file = strdup(bat.capturefile);
720 		fprintf(bat.log,
721 			_("Using data from file %s for analysis\n"),
722 			bat.capture.file);
723 		fprintf(bat.log, _("Skipping playback and capture\n"));
724 		goto analyze;
725 	}
726 
727 	/* loopback thread: playback and capture in a loop */
728 	if (bat.local == false)
729 		test_loopback(&bat);
730 
731 analyze:
732 #ifdef HAVE_LIBFFTW3F
733 	if (!bat.standalone || snr_is_valid(bat.snr_thd_db))
734 		err = analyze_capture(&bat);
735 #else
736 	fprintf(bat.log, _("No libfftw3 library. Exit without analysis.\n"));
737 #endif
738 out:
739 	fprintf(bat.log, _("\nReturn value is %d\n"), err);
740 
741 	if (bat.logarg)
742 		fclose(bat.log);
743 	if (!bat.local)
744 		free(bat.capture.file);
745 
746 	return err;
747 }
748