Blame bat/bat.c

Packit Service a9274b
/*
Packit Service a9274b
 * Copyright (C) 2013-2015 Intel Corporation
Packit Service a9274b
 *
Packit Service a9274b
 * This program is free software; you can redistribute it and/or modify
Packit Service a9274b
 * it under the terms of the GNU General Public License as published by
Packit Service a9274b
 * the Free Software Foundation; either version 2 of the License, or
Packit Service a9274b
 * (at your option) any later version.
Packit Service a9274b
 *
Packit Service a9274b
 * This program is distributed in the hope that it will be useful,
Packit Service a9274b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a9274b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a9274b
 * GNU General Public License for more details.
Packit Service a9274b
 *
Packit Service a9274b
 */
Packit Service a9274b
Packit Service a9274b
#include <unistd.h>
Packit Service a9274b
#include <stdio.h>
Packit Service a9274b
#include <stdlib.h>
Packit Service a9274b
#include <stdbool.h>
Packit Service a9274b
#include <string.h>
Packit Service a9274b
#include <errno.h>
Packit Service a9274b
#include <pthread.h>
Packit Service a9274b
#include <getopt.h>
Packit Service a9274b
#include <math.h>
Packit Service a9274b
#include <limits.h>
Packit Service a9274b
#include <locale.h>
Packit Service a9274b
#include <math.h>
Packit Service a9274b
Packit Service a9274b
#include "aconfig.h"
Packit Service a9274b
#include "gettext.h"
Packit Service a9274b
#include "version.h"
Packit Service a9274b
Packit Service a9274b
#include "common.h"
Packit Service a9274b
Packit Service a9274b
#ifdef HAVE_LIBTINYALSA
Packit Service a9274b
#include "tinyalsa.h"
Packit Service a9274b
#else
Packit Service a9274b
#include "alsa.h"
Packit Service a9274b
#endif
Packit Service a9274b
#include "convert.h"
Packit Service a9274b
#ifdef HAVE_LIBFFTW3F
Packit Service a9274b
#include "analyze.h"
Packit Service a9274b
#endif
Packit Service a9274b
#include "latencytest.h"
Packit Service a9274b
Packit Service a9274b
/* get snr threshold in dB */
Packit Service a9274b
static void get_snr_thd_db(struct bat *bat, char *thd)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
	float thd_db;
Packit Service a9274b
	char *ptrf;
Packit Service a9274b
Packit Service a9274b
	thd_db = strtof(thd, &ptrf);
Packit Service a9274b
	err = -errno;
Packit Service a9274b
	if (!snr_is_valid(thd_db)) {
Packit Service a9274b
		fprintf(bat->err, _("Invalid threshold '%s':%d\n"), thd, err);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
	bat->snr_thd_db = thd_db;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
/* get snr threshold in %, and convert to dB */
Packit Service a9274b
static void get_snr_thd_pc(struct bat *bat, char *thd)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
	float thd_pc;
Packit Service a9274b
	char *ptrf;
Packit Service a9274b
Packit Service a9274b
	thd_pc = strtof(thd, &ptrf);
Packit Service a9274b
	err = -errno;
Packit Service a9274b
	if (thd_pc <= 0.0 || thd_pc >= 100.0) {
Packit Service a9274b
		fprintf(bat->err, _("Invalid threshold '%s':%d\n"), thd, err);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
	bat->snr_thd_db = 20.0 * log10f(100.0 / thd_pc);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int get_duration(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
	float duration_f;
Packit Service a9274b
	long duration_i;
Packit Service a9274b
	char *ptrf, *ptri;
Packit Service a9274b
Packit Service a9274b
	duration_f = strtof(bat->narg, &ptrf);
Packit Service a9274b
	err = -errno;
Packit Service a9274b
	if (duration_f == HUGE_VALF || duration_f == -HUGE_VALF
Packit Service a9274b
			|| (duration_f == 0.0 && err != 0))
Packit Service a9274b
		goto err_exit;
Packit Service a9274b
Packit Service a9274b
	duration_i = strtol(bat->narg, &ptri, 10);
Packit Service a9274b
	if (duration_i == LONG_MAX || duration_i == LONG_MIN)
Packit Service a9274b
		goto err_exit;
Packit Service a9274b
Packit Service a9274b
	if (*ptrf == 's')
Packit Service a9274b
		bat->frames = duration_f * bat->rate;
Packit Service a9274b
	else if (*ptri == 0)
Packit Service a9274b
		bat->frames = duration_i;
Packit Service a9274b
	else
Packit Service a9274b
		bat->frames = -1;
Packit Service a9274b
Packit Service a9274b
	if (bat->frames <= 0 || bat->frames > MAX_FRAMES) {
Packit Service a9274b
		fprintf(bat->err, _("Invalid duration. Range: (0, %d(%fs))\n"),
Packit Service a9274b
				MAX_FRAMES, (float)MAX_FRAMES / bat->rate);
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
Packit Service a9274b
err_exit:
Packit Service a9274b
	fprintf(bat->err, _("Duration overflow/underflow: %d\n"), err);
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void get_sine_frequencies(struct bat *bat, char *freq)
Packit Service a9274b
{
Packit Service a9274b
	char *tmp1;
Packit Service a9274b
Packit Service a9274b
	tmp1 = strchr(freq, ':');
Packit Service a9274b
	if (tmp1 == NULL) {
Packit Service a9274b
		bat->target_freq[1] = bat->target_freq[0] = atof(optarg);
Packit Service a9274b
	} else {
Packit Service a9274b
		*tmp1 = '\0';
Packit Service a9274b
		bat->target_freq[0] = atof(optarg);
Packit Service a9274b
		bat->target_freq[1] = atof(tmp1 + 1);
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void get_format(struct bat *bat, char *optarg)
Packit Service a9274b
{
Packit Service a9274b
	if (strcasecmp(optarg, "cd") == 0) {
Packit Service a9274b
		bat->format = BAT_PCM_FORMAT_S16_LE;
Packit Service a9274b
		bat->rate = 44100;
Packit Service a9274b
		bat->channels = 2;
Packit Service a9274b
		bat->sample_size = 2;
Packit Service a9274b
	} else if (strcasecmp(optarg, "dat") == 0) {
Packit Service a9274b
		bat->format = BAT_PCM_FORMAT_S16_LE;
Packit Service a9274b
		bat->rate = 48000;
Packit Service a9274b
		bat->channels = 2;
Packit Service a9274b
		bat->sample_size = 2;
Packit Service a9274b
	} else if (strcasecmp(optarg, "U8") == 0) {
Packit Service a9274b
		bat->format = BAT_PCM_FORMAT_U8;
Packit Service a9274b
		bat->sample_size = 1;
Packit Service a9274b
	} else if (strcasecmp(optarg, "S16_LE") == 0) {
Packit Service a9274b
		bat->format = BAT_PCM_FORMAT_S16_LE;
Packit Service a9274b
		bat->sample_size = 2;
Packit Service a9274b
	} else if (strcasecmp(optarg, "S24_3LE") == 0) {
Packit Service a9274b
		bat->format = BAT_PCM_FORMAT_S24_3LE;
Packit Service a9274b
		bat->sample_size = 3;
Packit Service a9274b
	} else if (strcasecmp(optarg, "S32_LE") == 0) {
Packit Service a9274b
		bat->format = BAT_PCM_FORMAT_S32_LE;
Packit Service a9274b
		bat->sample_size = 4;
Packit Service a9274b
	} else {
Packit Service a9274b
		bat->format = BAT_PCM_FORMAT_UNKNOWN;
Packit Service a9274b
		fprintf(bat->err, _("wrong extended format '%s'\n"), optarg);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static inline int thread_wait_completion(struct bat *bat,
Packit Service a9274b
		pthread_t id, int **val)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	err = pthread_join(id, (void **) val);
Packit Service a9274b
	if (err)
Packit Service a9274b
		pthread_cancel(id);
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
/* loopback test where we play sine wave and capture the same sine wave */
Packit Service a9274b
static void test_loopback(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	pthread_t capture_id, playback_id;
Packit Service a9274b
	int err;
Packit Service a9274b
	int *thread_result_capture, *thread_result_playback;
Packit Service a9274b
Packit Service a9274b
	/* start playback */
Packit Service a9274b
	err = pthread_create(&playback_id, NULL,
Packit Service a9274b
			(void *) bat->playback.fct, bat);
Packit Service a9274b
	if (err != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Cannot create playback thread: %d\n"),
Packit Service a9274b
				err);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* TODO: use a pipe to signal stream start etc - i.e. to sync threads */
Packit Service a9274b
	/* Let some time for playing something before capturing */
Packit Service a9274b
	usleep(CAPTURE_DELAY * 1000);
Packit Service a9274b
Packit Service a9274b
	/* start capture */
Packit Service a9274b
	err = pthread_create(&capture_id, NULL, (void *) bat->capture.fct, bat);
Packit Service a9274b
	if (err != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Cannot create capture thread: %d\n"), err);
Packit Service a9274b
		pthread_cancel(playback_id);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* wait for playback to complete */
Packit Service a9274b
	err = thread_wait_completion(bat, playback_id, &thread_result_playback);
Packit Service a9274b
	if (err != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Cannot join playback thread: %d\n"), err);
Packit Service a9274b
		free(thread_result_playback);
Packit Service a9274b
		pthread_cancel(capture_id);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* check playback status */
Packit Service a9274b
	if (*thread_result_playback != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Exit playback thread fail: %d\n"),
Packit Service a9274b
				*thread_result_playback);
Packit Service a9274b
		pthread_cancel(capture_id);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	} else {
Packit Service a9274b
		fprintf(bat->log, _("Playback completed.\n"));
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* now stop and wait for capture to finish */
Packit Service a9274b
	pthread_cancel(capture_id);
Packit Service a9274b
	err = thread_wait_completion(bat, capture_id, &thread_result_capture);
Packit Service a9274b
	if (err != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Cannot join capture thread: %d\n"), err);
Packit Service a9274b
		free(thread_result_capture);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* check if capture thread is canceled or not */
Packit Service a9274b
	if (thread_result_capture == PTHREAD_CANCELED) {
Packit Service a9274b
		fprintf(bat->log, _("Capture canceled.\n"));
Packit Service a9274b
		return;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* check capture status */
Packit Service a9274b
	if (*thread_result_capture != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Exit capture thread fail: %d\n"),
Packit Service a9274b
				*thread_result_capture);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	} else {
Packit Service a9274b
		fprintf(bat->log, _("Capture completed.\n"));
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
/* single ended playback only test */
Packit Service a9274b
static void test_playback(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	pthread_t playback_id;
Packit Service a9274b
	int err;
Packit Service a9274b
	int *thread_result;
Packit Service a9274b
Packit Service a9274b
	/* start playback */
Packit Service a9274b
	err = pthread_create(&playback_id, NULL,
Packit Service a9274b
			(void *) bat->playback.fct, bat);
Packit Service a9274b
	if (err != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Cannot create playback thread: %d\n"),
Packit Service a9274b
				err);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* wait for playback to complete */
Packit Service a9274b
	err = thread_wait_completion(bat, playback_id, &thread_result);
Packit Service a9274b
	if (err != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Cannot join playback thread: %d\n"), err);
Packit Service a9274b
		free(thread_result);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* check playback status */
Packit Service a9274b
	if (*thread_result != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Exit playback thread fail: %d\n"),
Packit Service a9274b
				*thread_result);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	} else {
Packit Service a9274b
		fprintf(bat->log, _("Playback completed.\n"));
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
/* single ended capture only test */
Packit Service a9274b
static void test_capture(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	pthread_t capture_id;
Packit Service a9274b
	int err;
Packit Service a9274b
	int *thread_result;
Packit Service a9274b
Packit Service a9274b
	/* start capture */
Packit Service a9274b
	err = pthread_create(&capture_id, NULL, (void *) bat->capture.fct, bat);
Packit Service a9274b
	if (err != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Cannot create capture thread: %d\n"), err);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* TODO: stop capture */
Packit Service a9274b
Packit Service a9274b
	/* wait for capture to complete */
Packit Service a9274b
	err = thread_wait_completion(bat, capture_id, &thread_result);
Packit Service a9274b
	if (err != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Cannot join capture thread: %d\n"), err);
Packit Service a9274b
		free(thread_result);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* check playback status */
Packit Service a9274b
	if (*thread_result != 0) {
Packit Service a9274b
		fprintf(bat->err, _("Exit capture thread fail: %d\n"),
Packit Service a9274b
				*thread_result);
Packit Service a9274b
		exit(EXIT_FAILURE);
Packit Service a9274b
	} else {
Packit Service a9274b
		fprintf(bat->log, _("Capture completed.\n"));
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void usage(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	fprintf(bat->log,
Packit Service a9274b
_("Usage: alsabat [-options]...\n"
Packit Service a9274b
"\n"
Packit Service a9274b
"  -h, --help             this help\n"
Packit Service a9274b
"  -D                     pcm device for both playback and capture\n"
Packit Service a9274b
"  -P                     pcm device for playback\n"
Packit Service a9274b
"  -C                     pcm device for capture\n"
Packit Service a9274b
"  -f                     sample format\n"
Packit Service a9274b
"  -c                     number of channels\n"
Packit Service a9274b
"  -r                     sampling rate\n"
Packit Service a9274b
"  -n                     frames to playback or capture\n"
Packit Service a9274b
"  -k                     parameter for frequency detecting threshold\n"
Packit Service a9274b
"  -F                     target frequency\n"
Packit Service a9274b
"  -p                     total number of periods to play/capture\n"
Packit Service a9274b
"  -B                     buffer size in frames\n"
Packit Service a9274b
"  -E                     period size in frames\n"
Packit Service a9274b
"      --log=#            file that both stdout and strerr redirecting to\n"
Packit Service a9274b
"      --file=#           file for playback\n"
Packit Service a9274b
"      --saveplay=#       file that storing playback content, for debug\n"
Packit Service a9274b
"      --local            internal loop, set to bypass pcm hardware devices\n"
Packit Service a9274b
"      --standalone       standalone mode, to bypass analysis\n"
Packit Service a9274b
"      --roundtriplatency round trip latency mode\n"
Packit Service a9274b
"      --snr-db=#         noise detect threshold, in SNR(dB)\n"
Packit Service a9274b
"      --snr-pc=#         noise detect threshold, in noise percentage(%%)\n"
Packit Service a9274b
));
Packit Service a9274b
	fprintf(bat->log, _("Recognized sample formats are: "));
Packit Service a9274b
	fprintf(bat->log, _("U8 S16_LE S24_3LE S32_LE\n"));
Packit Service a9274b
	fprintf(bat->log, _("The available format shotcuts are:\n"));
Packit Service a9274b
	fprintf(bat->log, _("-f cd (16 bit little endian, 44100, stereo)\n"));
Packit Service a9274b
	fprintf(bat->log, _("-f dat (16 bit little endian, 48000, stereo)\n"));
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void set_defaults(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	memset(bat, 0, sizeof(struct bat));
Packit Service a9274b
Packit Service a9274b
	/* Set default values */
Packit Service a9274b
	bat->rate = 44100;
Packit Service a9274b
	bat->frame_size = 2;
Packit Service a9274b
	bat->sample_size = 2;
Packit Service a9274b
	bat->format = BAT_PCM_FORMAT_S16_LE;
Packit Service a9274b
	bat->convert_float_to_sample = convert_float_to_int16;
Packit Service a9274b
	bat->convert_sample_to_float = convert_int16_to_float;
Packit Service a9274b
	bat->frames = bat->rate * 2;
Packit Service a9274b
	bat->target_freq[0] = 997.0;
Packit Service a9274b
	bat->target_freq[1] = 997.0;
Packit Service a9274b
	bat->sigma_k = 3.0;
Packit Service a9274b
	bat->snr_thd_db = SNR_DB_INVALID;
Packit Service a9274b
	bat->playback.device = NULL;
Packit Service a9274b
	bat->capture.device = NULL;
Packit Service a9274b
	bat->buf = NULL;
Packit Service a9274b
	bat->local = false;
Packit Service a9274b
	bat->buffer_size = 0;
Packit Service a9274b
	bat->period_size = 0;
Packit Service a9274b
	bat->roundtriplatency = false;
Packit Service a9274b
#ifdef HAVE_LIBTINYALSA
Packit Service a9274b
	bat->channels = 2;
Packit Service a9274b
	bat->playback.fct = &playback_tinyalsa;
Packit Service a9274b
	bat->capture.fct = &record_tinyalsa;
Packit Service a9274b
#else
Packit Service a9274b
	bat->channels = 1;
Packit Service a9274b
	bat->playback.fct = &playback_alsa;
Packit Service a9274b
	bat->capture.fct = &record_alsa;
Packit Service a9274b
#endif
Packit Service a9274b
	bat->playback.mode = MODE_LOOPBACK;
Packit Service a9274b
	bat->capture.mode = MODE_LOOPBACK;
Packit Service a9274b
	bat->period_is_limited = false;
Packit Service a9274b
	bat->log = stdout;
Packit Service a9274b
	bat->err = stderr;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void parse_arguments(struct bat *bat, int argc, char *argv[])
Packit Service a9274b
{
Packit Service a9274b
	int c, option_index, err;
Packit Service a9274b
	static const char short_options[] = "D:P:C:f:n:F:c:r:s:k:p:B:E:lth";
Packit Service a9274b
	static const struct option long_options[] = {
Packit Service a9274b
		{"help",     0, 0, 'h'},
Packit Service a9274b
		{"log",      1, 0, OPT_LOG},
Packit Service a9274b
		{"file",     1, 0, OPT_READFILE},
Packit Service a9274b
		{"saveplay", 1, 0, OPT_SAVEPLAY},
Packit Service a9274b
		{"local",    0, 0, OPT_LOCAL},
Packit Service a9274b
		{"standalone", 0, 0, OPT_STANDALONE},
Packit Service a9274b
		{"roundtriplatency", 0, 0, OPT_ROUNDTRIPLATENCY},
Packit Service a9274b
		{"snr-db",   1, 0, OPT_SNRTHD_DB},
Packit Service a9274b
		{"snr-pc",   1, 0, OPT_SNRTHD_PC},
Packit Service a9274b
		{0, 0, 0, 0}
Packit Service a9274b
	};
Packit Service a9274b
Packit Service a9274b
	while ((c = getopt_long(argc, argv, short_options, long_options,
Packit Service a9274b
					&option_index)) != -1) {
Packit Service a9274b
		switch (c) {
Packit Service a9274b
		case OPT_LOG:
Packit Service a9274b
			bat->logarg = optarg;
Packit Service a9274b
			break;
Packit Service a9274b
		case OPT_READFILE:
Packit Service a9274b
			bat->playback.file = optarg;
Packit Service a9274b
			break;
Packit Service a9274b
		case OPT_SAVEPLAY:
Packit Service a9274b
			bat->debugplay = optarg;
Packit Service a9274b
			break;
Packit Service a9274b
		case OPT_LOCAL:
Packit Service a9274b
			bat->local = true;
Packit Service a9274b
			break;
Packit Service a9274b
		case OPT_STANDALONE:
Packit Service a9274b
			bat->standalone = true;
Packit Service a9274b
			break;
Packit Service a9274b
		case OPT_ROUNDTRIPLATENCY:
Packit Service a9274b
			bat->roundtriplatency = true;
Packit Service a9274b
			break;
Packit Service a9274b
		case OPT_SNRTHD_DB:
Packit Service a9274b
			get_snr_thd_db(bat, optarg);
Packit Service a9274b
			break;
Packit Service a9274b
		case OPT_SNRTHD_PC:
Packit Service a9274b
			get_snr_thd_pc(bat, optarg);
Packit Service a9274b
			break;
Packit Service a9274b
		case 'D':
Packit Service a9274b
			if (bat->playback.device == NULL)
Packit Service a9274b
				bat->playback.device = optarg;
Packit Service a9274b
			if (bat->capture.device == NULL)
Packit Service a9274b
				bat->capture.device = optarg;
Packit Service a9274b
			break;
Packit Service a9274b
		case 'P':
Packit Service a9274b
			if (bat->capture.mode == MODE_SINGLE)
Packit Service a9274b
				bat->capture.mode = MODE_LOOPBACK;
Packit Service a9274b
			else
Packit Service a9274b
				bat->playback.mode = MODE_SINGLE;
Packit Service a9274b
			bat->playback.device = optarg;
Packit Service a9274b
			break;
Packit Service a9274b
		case 'C':
Packit Service a9274b
			if (bat->playback.mode == MODE_SINGLE)
Packit Service a9274b
				bat->playback.mode = MODE_LOOPBACK;
Packit Service a9274b
			else
Packit Service a9274b
				bat->capture.mode = MODE_SINGLE;
Packit Service a9274b
			bat->capture.device = optarg;
Packit Service a9274b
			break;
Packit Service a9274b
		case 'n':
Packit Service a9274b
			bat->narg = optarg;
Packit Service a9274b
			break;
Packit Service a9274b
		case 'F':
Packit Service a9274b
			get_sine_frequencies(bat, optarg);
Packit Service a9274b
			break;
Packit Service a9274b
		case 'c':
Packit Service a9274b
			bat->channels = atoi(optarg);
Packit Service a9274b
			break;
Packit Service a9274b
		case 'r':
Packit Service a9274b
			bat->rate = atoi(optarg);
Packit Service a9274b
			break;
Packit Service a9274b
		case 'f':
Packit Service a9274b
			get_format(bat, optarg);
Packit Service a9274b
			break;
Packit Service a9274b
		case 'k':
Packit Service a9274b
			bat->sigma_k = atof(optarg);
Packit Service a9274b
			break;
Packit Service a9274b
		case 'p':
Packit Service a9274b
			bat->periods_total = atoi(optarg);
Packit Service a9274b
			bat->period_is_limited = true;
Packit Service a9274b
			break;
Packit Service a9274b
		case 'B':
Packit Service a9274b
			err = atoi(optarg);
Packit Service a9274b
			bat->buffer_size = err >= MIN_BUFFERSIZE
Packit Service a9274b
					&& err < MAX_BUFFERSIZE ? err : 0;
Packit Service a9274b
			break;
Packit Service a9274b
		case 'E':
Packit Service a9274b
			err = atoi(optarg);
Packit Service a9274b
			bat->period_size = err >= MIN_PERIODSIZE
Packit Service a9274b
					&& err < MAX_PERIODSIZE ? err : 0;
Packit Service a9274b
			break;
Packit Service a9274b
		case 'h':
Packit Service a9274b
		default:
Packit Service a9274b
			usage(bat);
Packit Service a9274b
			exit(EXIT_SUCCESS);
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int validate_options(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	int c;
Packit Service a9274b
	float freq_low, freq_high;
Packit Service a9274b
Packit Service a9274b
	/* check if we have an input file for local mode */
Packit Service a9274b
	if ((bat->local == true) && (bat->capture.file == NULL)) {
Packit Service a9274b
		fprintf(bat->err, _("no input file for local testing\n"));
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* check supported channels */
Packit Service a9274b
	if (bat->channels > MAX_CHANNELS || bat->channels < MIN_CHANNELS) {
Packit Service a9274b
		fprintf(bat->err, _("%d channels not supported\n"),
Packit Service a9274b
				bat->channels);
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* check single ended is in either playback or capture - not both */
Packit Service a9274b
	if ((bat->playback.mode == MODE_SINGLE)
Packit Service a9274b
			&& (bat->capture.mode == MODE_SINGLE)) {
Packit Service a9274b
		fprintf(bat->err, _("single ended mode is simplex\n"));
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* check sine wave frequency range */
Packit Service a9274b
	freq_low = DC_THRESHOLD;
Packit Service a9274b
	freq_high = bat->rate * RATE_FACTOR;
Packit Service a9274b
	for (c = 0; c < bat->channels; c++) {
Packit Service a9274b
		if (bat->target_freq[c] < freq_low
Packit Service a9274b
				|| bat->target_freq[c] > freq_high) {
Packit Service a9274b
			fprintf(bat->err, _("sine wave frequency out of"));
Packit Service a9274b
			fprintf(bat->err, _(" range: (%.1f, %.1f)\n"),
Packit Service a9274b
				freq_low, freq_high);
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int bat_init(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	int err = 0;
Packit Service a9274b
	int fd = 0;
Packit Service a9274b
	char name[] = TEMP_RECORD_FILE_NAME;
Packit Service a9274b
Packit Service a9274b
	/* Determine logging to a file or stdout and stderr */
Packit Service a9274b
	if (bat->logarg) {
Packit Service a9274b
		bat->log = NULL;
Packit Service a9274b
		bat->log = fopen(bat->logarg, "wb");
Packit Service a9274b
		err = -errno;
Packit Service a9274b
		if (bat->log == NULL) {
Packit Service a9274b
			fprintf(bat->err, _("Cannot open file: %s %d\n"),
Packit Service a9274b
					bat->logarg, err);
Packit Service a9274b
			return err;
Packit Service a9274b
		}
Packit Service a9274b
		bat->err = bat->log;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* Determine duration of playback and/or capture */
Packit Service a9274b
	if (bat->narg) {
Packit Service a9274b
		err = get_duration(bat);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* Set default playback and capture devices */
Packit Service a9274b
	if (bat->playback.device == NULL && bat->capture.device == NULL)
Packit Service a9274b
		bat->playback.device = bat->capture.device = DEFAULT_DEV_NAME;
Packit Service a9274b
Packit Service a9274b
	/* Determine capture file */
Packit Service a9274b
	if (bat->local) {
Packit Service a9274b
		bat->capture.file = bat->playback.file;
Packit Service a9274b
	} else {
Packit Service a9274b
		/* create temp file for sound record and analysis */
Packit Service a9274b
		fd = mkstemp(name);
Packit Service a9274b
		err = -errno;
Packit Service a9274b
		if (fd == -1) {
Packit Service a9274b
			fprintf(bat->err, _("Fail to create record file: %d\n"),
Packit Service a9274b
					err);
Packit Service a9274b
			return err;
Packit Service a9274b
		}
Packit Service a9274b
		/* store file name which is dynamically created */
Packit Service a9274b
		bat->capture.file = strdup(name);
Packit Service a9274b
		err = -errno;
Packit Service a9274b
		if (bat->capture.file == NULL)
Packit Service a9274b
			return err;
Packit Service a9274b
		/* close temp file */
Packit Service a9274b
		close(fd);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* Initial for playback */
Packit Service a9274b
	if (bat->playback.file == NULL) {
Packit Service a9274b
		/* No input file so we will generate our own sine wave */
Packit Service a9274b
		if (bat->frames) {
Packit Service a9274b
			if (bat->playback.mode == MODE_SINGLE) {
Packit Service a9274b
				/* Play nb of frames given by -n argument */
Packit Service a9274b
				bat->sinus_duration = bat->frames;
Packit Service a9274b
			} else {
Packit Service a9274b
				/* Play CAPTURE_DELAY msec +
Packit Service a9274b
				 * 150% of the nb of frames to be analyzed */
Packit Service a9274b
				bat->sinus_duration = bat->rate *
Packit Service a9274b
						CAPTURE_DELAY / 1000;
Packit Service a9274b
				bat->sinus_duration +=
Packit Service a9274b
						(bat->frames + bat->frames / 2);
Packit Service a9274b
			}
Packit Service a9274b
		} else {
Packit Service a9274b
			/* Special case where we want to generate a sine wave
Packit Service a9274b
			 * endlessly without capturing */
Packit Service a9274b
			bat->sinus_duration = 0;
Packit Service a9274b
			bat->playback.mode = MODE_SINGLE;
Packit Service a9274b
		}
Packit Service a9274b
	} else {
Packit Service a9274b
		bat->fp = fopen(bat->playback.file, "rb");
Packit Service a9274b
		err = -errno;
Packit Service a9274b
		if (bat->fp == NULL) {
Packit Service a9274b
			fprintf(bat->err, _("Cannot open file: %s %d\n"),
Packit Service a9274b
					bat->playback.file, err);
Packit Service a9274b
			return err;
Packit Service a9274b
		}
Packit Service a9274b
		err = read_wav_header(bat, bat->playback.file, bat->fp, false);
Packit Service a9274b
		fclose(bat->fp);
Packit Service a9274b
		if (err != 0)
Packit Service a9274b
			return err;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	bat->frame_size = bat->sample_size * bat->channels;
Packit Service a9274b
Packit Service a9274b
	/* Set conversion functions */
Packit Service a9274b
	switch (bat->sample_size) {
Packit Service a9274b
	case 1:
Packit Service a9274b
		bat->convert_float_to_sample = convert_float_to_uint8;
Packit Service a9274b
		bat->convert_sample_to_float = convert_uint8_to_float;
Packit Service a9274b
		break;
Packit Service a9274b
	case 2:
Packit Service a9274b
		bat->convert_float_to_sample = convert_float_to_int16;
Packit Service a9274b
		bat->convert_sample_to_float = convert_int16_to_float;
Packit Service a9274b
		break;
Packit Service a9274b
	case 3:
Packit Service a9274b
		bat->convert_float_to_sample = convert_float_to_int24;
Packit Service a9274b
		bat->convert_sample_to_float = convert_int24_to_float;
Packit Service a9274b
		break;
Packit Service a9274b
	case 4:
Packit Service a9274b
		bat->convert_float_to_sample = convert_float_to_int32;
Packit Service a9274b
		bat->convert_sample_to_float = convert_int32_to_float;
Packit Service a9274b
		break;
Packit Service a9274b
	default:
Packit Service a9274b
		fprintf(bat->err, _("Invalid PCM format: size=%d\n"),
Packit Service a9274b
				bat->sample_size);
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int main(int argc, char *argv[])
Packit Service a9274b
{
Packit Service a9274b
	struct bat bat;
Packit Service a9274b
	int err = 0;
Packit Service a9274b
Packit Service a9274b
	set_defaults(&bat;;
Packit Service a9274b
Packit Service a9274b
#ifdef ENABLE_NLS
Packit Service a9274b
	setlocale(LC_ALL, "");
Packit Service a9274b
	textdomain(PACKAGE);
Packit Service a9274b
#endif
Packit Service a9274b
Packit Service a9274b
	fprintf(bat.log, _("%s version %s\n\n"), PACKAGE_NAME, PACKAGE_VERSION);
Packit Service a9274b
Packit Service a9274b
	parse_arguments(&bat, argc, argv);
Packit Service a9274b
Packit Service a9274b
	err = bat_init(&bat;;
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		goto out;
Packit Service a9274b
Packit Service a9274b
	err = validate_options(&bat;;
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		goto out;
Packit Service a9274b
Packit Service a9274b
	/* round trip latency test thread */
Packit Service a9274b
	if (bat.roundtriplatency) {
Packit Service a9274b
		while (1) {
Packit Service a9274b
			fprintf(bat.log,
Packit Service a9274b
				_("\nStart round trip latency\n"));
Packit Service a9274b
			roundtrip_latency_init(&bat;;
Packit Service a9274b
			test_loopback(&bat;;
Packit Service a9274b
Packit Service a9274b
			if (bat.latency.xrun_error == false)
Packit Service a9274b
				break;
Packit Service a9274b
			else {
Packit Service a9274b
				/* Xrun error in playback or capture,
Packit Service a9274b
				increase period size and try again */
Packit Service a9274b
				bat.period_size += bat.rate / 1000;
Packit Service a9274b
				bat.buffer_size =
Packit Service a9274b
					bat.period_size * DIV_BUFFERSIZE;
Packit Service a9274b
Packit Service a9274b
				/* terminate the test if period_size is
Packit Service a9274b
				large enough */
Packit Service a9274b
				if (bat.period_size > bat.rate * 0.2)
Packit Service a9274b
					break;
Packit Service a9274b
			}
Packit Service a9274b
Packit Service a9274b
			/* Waiting 500ms and start the next round */
Packit Service a9274b
			usleep(CAPTURE_DELAY * 1000);
Packit Service a9274b
		}
Packit Service a9274b
		goto out;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* single line playback thread: playback only, no capture */
Packit Service a9274b
	if (bat.playback.mode == MODE_SINGLE) {
Packit Service a9274b
		test_playback(&bat;;
Packit Service a9274b
		goto out;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* single line capture thread: capture only, no playback */
Packit Service a9274b
	if (bat.capture.mode == MODE_SINGLE) {
Packit Service a9274b
		test_capture(&bat;;
Packit Service a9274b
		goto analyze;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* loopback thread: playback and capture in a loop */
Packit Service a9274b
	if (bat.local == false)
Packit Service a9274b
		test_loopback(&bat;;
Packit Service a9274b
Packit Service a9274b
analyze:
Packit Service a9274b
#ifdef HAVE_LIBFFTW3F
Packit Service a9274b
	if (!bat.standalone || snr_is_valid(bat.snr_thd_db))
Packit Service a9274b
		err = analyze_capture(&bat;;
Packit Service a9274b
#else
Packit Service a9274b
	fprintf(bat.log, _("No libfftw3 library. Exit without analysis.\n"));
Packit Service a9274b
#endif
Packit Service a9274b
out:
Packit Service a9274b
	fprintf(bat.log, _("\nReturn value is %d\n"), err);
Packit Service a9274b
Packit Service a9274b
	if (bat.logarg)
Packit Service a9274b
		fclose(bat.log);
Packit Service a9274b
	if (!bat.local)
Packit Service a9274b
		free(bat.capture.file);
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}