tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

bat.c (18683B)


      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 */
     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 */
     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 
     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 
    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 
    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 
    161 static inline int thread_wait_completion(struct bat *bat,
    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 */
    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 */
    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 */
    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 
    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 "      --local            internal loop, set to bypass pcm hardware devices\n"
    335 "      --standalone       standalone mode, to bypass analysis\n"
    336 "      --roundtriplatency round trip latency mode\n"
    337 "      --snr-db=#         noise detect threshold, in SNR(dB)\n"
    338 "      --snr-pc=#         noise detect threshold, in noise percentage(%%)\n"
    339 ));
    340 	fprintf(bat->log, _("Recognized sample formats are: "));
    341 	fprintf(bat->log, _("U8 S16_LE S24_3LE S32_LE\n"));
    342 	fprintf(bat->log, _("The available format shotcuts are:\n"));
    343 	fprintf(bat->log, _("-f cd (16 bit little endian, 44100, stereo)\n"));
    344 	fprintf(bat->log, _("-f dat (16 bit little endian, 48000, stereo)\n"));
    345 }
    346 
    347 static void set_defaults(struct bat *bat)
    348 {
    349 	memset(bat, 0, sizeof(struct bat));
    350 
    351 	/* Set default values */
    352 	bat->rate = 44100;
    353 	bat->frame_size = 2;
    354 	bat->sample_size = 2;
    355 	bat->format = BAT_PCM_FORMAT_S16_LE;
    356 	bat->convert_float_to_sample = convert_float_to_int16;
    357 	bat->convert_sample_to_float = convert_int16_to_float;
    358 	bat->frames = bat->rate * 2;
    359 	bat->target_freq[0] = 997.0;
    360 	bat->target_freq[1] = 997.0;
    361 	bat->sigma_k = 3.0;
    362 	bat->snr_thd_db = SNR_DB_INVALID;
    363 	bat->playback.device = NULL;
    364 	bat->capture.device = NULL;
    365 	bat->buf = NULL;
    366 	bat->local = false;
    367 	bat->buffer_size = 0;
    368 	bat->period_size = 0;
    369 	bat->roundtriplatency = false;
    370 #ifdef HAVE_LIBTINYALSA
    371 	bat->channels = 2;
    372 	bat->playback.fct = &playback_tinyalsa;
    373 	bat->capture.fct = &record_tinyalsa;
    374 #else
    375 	bat->channels = 1;
    376 	bat->playback.fct = &playback_alsa;
    377 	bat->capture.fct = &record_alsa;
    378 #endif
    379 	bat->playback.mode = MODE_LOOPBACK;
    380 	bat->capture.mode = MODE_LOOPBACK;
    381 	bat->period_is_limited = false;
    382 	bat->log = stdout;
    383 	bat->err = stderr;
    384 }
    385 
    386 static void parse_arguments(struct bat *bat, int argc, char *argv[])
    387 {
    388 	int c, option_index, err;
    389 	static const char short_options[] = "D:P:C:f:n:F:c:r:s:k:p:B:E:lth";
    390 	static const struct option long_options[] = {
    391 		{"help",     0, 0, 'h'},
    392 		{"log",      1, 0, OPT_LOG},
    393 		{"file",     1, 0, OPT_READFILE},
    394 		{"saveplay", 1, 0, OPT_SAVEPLAY},
    395 		{"local",    0, 0, OPT_LOCAL},
    396 		{"standalone", 0, 0, OPT_STANDALONE},
    397 		{"roundtriplatency", 0, 0, OPT_ROUNDTRIPLATENCY},
    398 		{"snr-db",   1, 0, OPT_SNRTHD_DB},
    399 		{"snr-pc",   1, 0, OPT_SNRTHD_PC},
    400 		{0, 0, 0, 0}
    401 	};
    402 
    403 	while ((c = getopt_long(argc, argv, short_options, long_options,
    404 					&option_index)) != -1) {
    405 		switch (c) {
    406 		case OPT_LOG:
    407 			bat->logarg = optarg;
    408 			break;
    409 		case OPT_READFILE:
    410 			bat->playback.file = optarg;
    411 			break;
    412 		case OPT_SAVEPLAY:
    413 			bat->debugplay = optarg;
    414 			break;
    415 		case OPT_LOCAL:
    416 			bat->local = true;
    417 			break;
    418 		case OPT_STANDALONE:
    419 			bat->standalone = true;
    420 			break;
    421 		case OPT_ROUNDTRIPLATENCY:
    422 			bat->roundtriplatency = true;
    423 			break;
    424 		case OPT_SNRTHD_DB:
    425 			get_snr_thd_db(bat, optarg);
    426 			break;
    427 		case OPT_SNRTHD_PC:
    428 			get_snr_thd_pc(bat, optarg);
    429 			break;
    430 		case 'D':
    431 			if (bat->playback.device == NULL)
    432 				bat->playback.device = optarg;
    433 			if (bat->capture.device == NULL)
    434 				bat->capture.device = optarg;
    435 			break;
    436 		case 'P':
    437 			if (bat->capture.mode == MODE_SINGLE)
    438 				bat->capture.mode = MODE_LOOPBACK;
    439 			else
    440 				bat->playback.mode = MODE_SINGLE;
    441 			bat->playback.device = optarg;
    442 			break;
    443 		case 'C':
    444 			if (bat->playback.mode == MODE_SINGLE)
    445 				bat->playback.mode = MODE_LOOPBACK;
    446 			else
    447 				bat->capture.mode = MODE_SINGLE;
    448 			bat->capture.device = optarg;
    449 			break;
    450 		case 'n':
    451 			bat->narg = optarg;
    452 			break;
    453 		case 'F':
    454 			get_sine_frequencies(bat, optarg);
    455 			break;
    456 		case 'c':
    457 			bat->channels = atoi(optarg);
    458 			break;
    459 		case 'r':
    460 			bat->rate = atoi(optarg);
    461 			break;
    462 		case 'f':
    463 			get_format(bat, optarg);
    464 			break;
    465 		case 'k':
    466 			bat->sigma_k = atof(optarg);
    467 			break;
    468 		case 'p':
    469 			bat->periods_total = atoi(optarg);
    470 			bat->period_is_limited = true;
    471 			break;
    472 		case 'B':
    473 			err = atoi(optarg);
    474 			bat->buffer_size = err >= MIN_BUFFERSIZE
    475 					&& err < MAX_BUFFERSIZE ? err : 0;
    476 			break;
    477 		case 'E':
    478 			err = atoi(optarg);
    479 			bat->period_size = err >= MIN_PERIODSIZE
    480 					&& err < MAX_PERIODSIZE ? err : 0;
    481 			break;
    482 		case 'h':
    483 		default:
    484 			usage(bat);
    485 			exit(EXIT_SUCCESS);
    486 		}
    487 	}
    488 }
    489 
    490 static int validate_options(struct bat *bat)
    491 {
    492 	int c;
    493 	float freq_low, freq_high;
    494 
    495 	/* check if we have an input file for local mode */
    496 	if ((bat->local == true) && (bat->capture.file == NULL)) {
    497 		fprintf(bat->err, _("no input file for local testing\n"));
    498 		return -EINVAL;
    499 	}
    500 
    501 	/* check supported channels */
    502 	if (bat->channels > MAX_CHANNELS || bat->channels < MIN_CHANNELS) {
    503 		fprintf(bat->err, _("%d channels not supported\n"),
    504 				bat->channels);
    505 		return -EINVAL;
    506 	}
    507 
    508 	/* check single ended is in either playback or capture - not both */
    509 	if ((bat->playback.mode == MODE_SINGLE)
    510 			&& (bat->capture.mode == MODE_SINGLE)) {
    511 		fprintf(bat->err, _("single ended mode is simplex\n"));
    512 		return -EINVAL;
    513 	}
    514 
    515 	/* check sine wave frequency range */
    516 	freq_low = DC_THRESHOLD;
    517 	freq_high = bat->rate * RATE_FACTOR;
    518 	for (c = 0; c < bat->channels; c++) {
    519 		if (bat->target_freq[c] < freq_low
    520 				|| bat->target_freq[c] > freq_high) {
    521 			fprintf(bat->err, _("sine wave frequency out of"));
    522 			fprintf(bat->err, _(" range: (%.1f, %.1f)\n"),
    523 				freq_low, freq_high);
    524 			return -EINVAL;
    525 		}
    526 	}
    527 
    528 	return 0;
    529 }
    530 
    531 static int bat_init(struct bat *bat)
    532 {
    533 	int err = 0;
    534 	int fd = 0;
    535 	char name[] = TEMP_RECORD_FILE_NAME;
    536 
    537 	/* Determine logging to a file or stdout and stderr */
    538 	if (bat->logarg) {
    539 		bat->log = NULL;
    540 		bat->log = fopen(bat->logarg, "wb");
    541 		err = -errno;
    542 		if (bat->log == NULL) {
    543 			fprintf(bat->err, _("Cannot open file: %s %d\n"),
    544 					bat->logarg, err);
    545 			return err;
    546 		}
    547 		bat->err = bat->log;
    548 	}
    549 
    550 	/* Determine duration of playback and/or capture */
    551 	if (bat->narg) {
    552 		err = get_duration(bat);
    553 		if (err < 0)
    554 			return err;
    555 	}
    556 
    557 	/* Set default playback and capture devices */
    558 	if (bat->playback.device == NULL && bat->capture.device == NULL)
    559 		bat->playback.device = bat->capture.device = DEFAULT_DEV_NAME;
    560 
    561 	/* Determine capture file */
    562 	if (bat->local) {
    563 		bat->capture.file = bat->playback.file;
    564 	} else {
    565 		/* create temp file for sound record and analysis */
    566 		fd = mkstemp(name);
    567 		err = -errno;
    568 		if (fd == -1) {
    569 			fprintf(bat->err, _("Fail to create record file: %d\n"),
    570 					err);
    571 			return err;
    572 		}
    573 		/* store file name which is dynamically created */
    574 		bat->capture.file = strdup(name);
    575 		err = -errno;
    576 		if (bat->capture.file == NULL)
    577 			return err;
    578 		/* close temp file */
    579 		close(fd);
    580 	}
    581 
    582 	/* Initial for playback */
    583 	if (bat->playback.file == NULL) {
    584 		/* No input file so we will generate our own sine wave */
    585 		if (bat->frames) {
    586 			if (bat->playback.mode == MODE_SINGLE) {
    587 				/* Play nb of frames given by -n argument */
    588 				bat->sinus_duration = bat->frames;
    589 			} else {
    590 				/* Play CAPTURE_DELAY msec +
    591 				 * 150% of the nb of frames to be analyzed */
    592 				bat->sinus_duration = bat->rate *
    593 						CAPTURE_DELAY / 1000;
    594 				bat->sinus_duration +=
    595 						(bat->frames + bat->frames / 2);
    596 			}
    597 		} else {
    598 			/* Special case where we want to generate a sine wave
    599 			 * endlessly without capturing */
    600 			bat->sinus_duration = 0;
    601 			bat->playback.mode = MODE_SINGLE;
    602 		}
    603 	} else {
    604 		bat->fp = fopen(bat->playback.file, "rb");
    605 		err = -errno;
    606 		if (bat->fp == NULL) {
    607 			fprintf(bat->err, _("Cannot open file: %s %d\n"),
    608 					bat->playback.file, err);
    609 			return err;
    610 		}
    611 		err = read_wav_header(bat, bat->playback.file, bat->fp, false);
    612 		fclose(bat->fp);
    613 		if (err != 0)
    614 			return err;
    615 	}
    616 
    617 	bat->frame_size = bat->sample_size * bat->channels;
    618 
    619 	/* Set conversion functions */
    620 	switch (bat->sample_size) {
    621 	case 1:
    622 		bat->convert_float_to_sample = convert_float_to_uint8;
    623 		bat->convert_sample_to_float = convert_uint8_to_float;
    624 		break;
    625 	case 2:
    626 		bat->convert_float_to_sample = convert_float_to_int16;
    627 		bat->convert_sample_to_float = convert_int16_to_float;
    628 		break;
    629 	case 3:
    630 		bat->convert_float_to_sample = convert_float_to_int24;
    631 		bat->convert_sample_to_float = convert_int24_to_float;
    632 		break;
    633 	case 4:
    634 		bat->convert_float_to_sample = convert_float_to_int32;
    635 		bat->convert_sample_to_float = convert_int32_to_float;
    636 		break;
    637 	default:
    638 		fprintf(bat->err, _("Invalid PCM format: size=%d\n"),
    639 				bat->sample_size);
    640 		return -EINVAL;
    641 	}
    642 
    643 	return err;
    644 }
    645 
    646 int main(int argc, char *argv[])
    647 {
    648 	struct bat bat;
    649 	int err = 0;
    650 
    651 	set_defaults(&bat);
    652 
    653 #ifdef ENABLE_NLS
    654 	setlocale(LC_ALL, "");
    655 	textdomain(PACKAGE);
    656 #endif
    657 
    658 	fprintf(bat.log, _("%s version %s\n\n"), PACKAGE_NAME, PACKAGE_VERSION);
    659 
    660 	parse_arguments(&bat, argc, argv);
    661 
    662 	err = bat_init(&bat);
    663 	if (err < 0)
    664 		goto out;
    665 
    666 	err = validate_options(&bat);
    667 	if (err < 0)
    668 		goto out;
    669 
    670 	/* round trip latency test thread */
    671 	if (bat.roundtriplatency) {
    672 		while (1) {
    673 			fprintf(bat.log,
    674 				_("\nStart round trip latency\n"));
    675 			roundtrip_latency_init(&bat);
    676 			test_loopback(&bat);
    677 
    678 			if (bat.latency.xrun_error == false)
    679 				break;
    680 			else {
    681 				/* Xrun error in playback or capture,
    682 				increase period size and try again */
    683 				bat.period_size += bat.rate / 1000;
    684 				bat.buffer_size =
    685 					bat.period_size * DIV_BUFFERSIZE;
    686 
    687 				/* terminate the test if period_size is
    688 				large enough */
    689 				if (bat.period_size > bat.rate * 0.2)
    690 					break;
    691 			}
    692 
    693 			/* Waiting 500ms and start the next round */
    694 			usleep(CAPTURE_DELAY * 1000);
    695 		}
    696 		goto out;
    697 	}
    698 
    699 	/* single line playback thread: playback only, no capture */
    700 	if (bat.playback.mode == MODE_SINGLE) {
    701 		test_playback(&bat);
    702 		goto out;
    703 	}
    704 
    705 	/* single line capture thread: capture only, no playback */
    706 	if (bat.capture.mode == MODE_SINGLE) {
    707 		test_capture(&bat);
    708 		goto analyze;
    709 	}
    710 
    711 	/* loopback thread: playback and capture in a loop */
    712 	if (bat.local == false)
    713 		test_loopback(&bat);
    714 
    715 analyze:
    716 #ifdef HAVE_LIBFFTW3F
    717 	if (!bat.standalone || snr_is_valid(bat.snr_thd_db))
    718 		err = analyze_capture(&bat);
    719 #else
    720 	fprintf(bat.log, _("No libfftw3 library. Exit without analysis.\n"));
    721 #endif
    722 out:
    723 	fprintf(bat.log, _("\nReturn value is %d\n"), err);
    724 
    725 	if (bat.logarg)
    726 		fclose(bat.log);
    727 	if (!bat.local)
    728 		free(bat.capture.file);
    729 
    730 	return err;
    731 }