diff mbox

[03/10] alsabat: add tinyalsa support

Message ID b80d0a5a176bda9bef10d3611e3eb4dbf47231ef.1456907242.git.han.lu@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

han.lu@intel.com March 2, 2016, 8:53 a.m. UTC
From: "Lu, Han" <han.lu@intel.com>

Add support for alsabat to work on tinyalsa library based platforms
such as Android and some Embedded Linux devices.
Use option '-t' to select tinyalsa library instead of ALSA library.
If '-t' is used while tinyalsa library is not installed in system,
alsabat will print error message and quit.

Signed-off-by: Lu, Han <han.lu@intel.com>

Comments

Takashi Iwai March 11, 2016, 2:13 p.m. UTC | #1
On Wed, 02 Mar 2016 09:53:13 +0100,
han.lu@intel.com wrote:
> 
> From: "Lu, Han" <han.lu@intel.com>
> 
> Add support for alsabat to work on tinyalsa library based platforms
> such as Android and some Embedded Linux devices.
> Use option '-t' to select tinyalsa library instead of ALSA library.
> If '-t' is used while tinyalsa library is not installed in system,
> alsabat will print error message and quit.
> 
> Signed-off-by: Lu, Han <han.lu@intel.com>

Hrm, I don't think it makes so much sense to build a binary bound with
both libraries.  Usually it's mutual exclusive, the backend is
selected / detected via configure script.


thanks,

Takashi

> 
> diff --git a/bat/Makefile.am b/bat/Makefile.am
> index 5646e9a..6101681 100644
> --- a/bat/Makefile.am
> +++ b/bat/Makefile.am
> @@ -21,6 +21,11 @@ alsabat_SOURCES += analyze.c
>  noinst_HEADERS += analyze.h
>  endif
>  
> +if HAVE_LIBTINYALSA
> +alsabat_SOURCES += tinyalsa.c
> +noinst_HEADERS += tinyalsa.h
> +endif
> +
>  AM_CPPFLAGS = \
>  	      -Wall -I$(top_srcdir)/include
>  
> diff --git a/bat/alsabat.1 b/bat/alsabat.1
> index 5f41669..126232e 100644
> --- a/bat/alsabat.1
> +++ b/bat/alsabat.1
> @@ -106,6 +106,9 @@ Valid range is (DC_THRESHOLD, 40% * Sampling rate).
>  \fI\-p\fP
>  Total number of periods to play or capture.
>  .TP
> +\fI\-t\fP
> +If tinyalsa lib is installed, use tinyalsa lib instead of alsa lib.
> +.TP
>  \fI\-\-log=#\fP
>  Write stderr and stdout output to this log file.
>  .TP
> diff --git a/bat/bat.c b/bat/bat.c
> index 85ec5aa..8db16c0 100644
> --- a/bat/bat.c
> +++ b/bat/bat.c
> @@ -36,6 +36,9 @@
>  #ifdef HAVE_LIBFFTW3
>  #include "analyze.h"
>  #endif
> +#ifdef HAVE_LIBTINYALSA
> +#include "tinyalsa.h"
> +#endif
>  
>  static int get_duration(struct bat *bat)
>  {
> @@ -125,6 +128,35 @@ static void get_format(struct bat *bat, char *optarg)
>  	}
>  }
>  
> +static int get_tiny_format(struct bat *bat, char *alsa_device,
> +		unsigned int *tiny_card, unsigned int *tiny_device)
> +{
> +	char *tmp1, *tmp2, *tmp3;
> +
> +	if (alsa_device == NULL)
> +		goto fail;
> +
> +	tmp1 = strchr(alsa_device, ':');
> +	if (tmp1 == NULL)
> +		goto fail;
> +
> +	tmp3 = tmp1 + 1;
> +	tmp2 = strchr(tmp3, ',');
> +	if (tmp2 == NULL)
> +		goto fail;
> +
> +	tmp1 = tmp2 + 1;
> +	*tiny_device = atoi(tmp1);
> +	*tmp2 = '\0';
> +	*tiny_card = atoi(tmp3);
> +	*tmp2 = ',';
> +
> +	return 0;
> +fail:
> +	fprintf(bat->err, _("Invalid tiny format!\n"));
> +	return -EINVAL;
> +}
> +
>  static inline int thread_wait_completion(struct bat *bat,
>  		pthread_t id, int **val)
>  {
> @@ -287,6 +319,7 @@ _("Usage: alsabat [-options]...\n"
>  "  -k                     parameter for frequency detecting threshold\n"
>  "  -F                     target frequency\n"
>  "  -p                     total number of periods to play/capture\n"
> +"  -t                     use tinyalsa instead of alsa\n"
>  "      --log=#            file that both stdout and strerr redirecting to\n"
>  "      --file=#           file for playback\n"
>  "      --saveplay=#       file that storing playback content, for debug\n"
> @@ -330,6 +363,7 @@ static void set_defaults(struct bat *bat)
>  	bat->period_is_limited = false;
>  	bat->log = stdout;
>  	bat->err = stderr;
> +	bat->tinyalsa = false;
>  }
>  
>  static void parse_arguments(struct bat *bat, int argc, char *argv[])
> @@ -406,6 +440,16 @@ static void parse_arguments(struct bat *bat, int argc, char *argv[])
>  			bat->periods_total = atoi(optarg);
>  			bat->period_is_limited = true;
>  			break;
> +		case 't':
> +#ifdef HAVE_LIBTINYALSA
> +			bat->playback.fct = &playback_tinyalsa;
> +			bat->capture.fct = &record_tinyalsa;
> +			bat->tinyalsa = true;
> +#else
> +			fprintf(bat->err, _("tinyalsa lib is not installed\n"));
> +			exit(EXIT_FAILURE);
> +#endif
> +			break;
>  		case 'h':
>  		default:
>  			usage(bat);
> @@ -484,6 +528,24 @@ static int bat_init(struct bat *bat)
>  	if (bat->playback.device == NULL && bat->capture.device == NULL)
>  		bat->playback.device = bat->capture.device = DEFAULT_DEV_NAME;
>  
> +	/* Determine tiny device if needed */
> +	if (bat->tinyalsa == true) {
> +		if (bat->playback.mode != MODE_SINGLE) {
> +			err = get_tiny_format(bat, bat->capture.device,
> +					&bat->capture.card_tiny,
> +					&bat->capture.device_tiny);
> +			if (err < 0)
> +				return err;
> +		}
> +		if (bat->capture.mode != MODE_SINGLE) {
> +			err = get_tiny_format(bat, bat->playback.device,
> +					&bat->playback.card_tiny,
> +					&bat->playback.device_tiny);
> +			if (err < 0)
> +				return err;
> +		}
> +	}
> +
>  	/* Determine capture file */
>  	if (bat->local) {
>  		bat->capture.file = bat->playback.file;
> diff --git a/bat/common.c b/bat/common.c
> index 798b00b..bbf969e 100644
> --- a/bat/common.c
> +++ b/bat/common.c
> @@ -18,16 +18,39 @@
>  #include <stdlib.h>
>  #include <stdbool.h>
>  #include <errno.h>
> +#include <signal.h>
>  
>  #include "aconfig.h"
>  #include "gettext.h"
>  
>  #include "common.h"
>  #include "alsa.h"
> +#include "bat-signal.h"
>  
>  int retval_play;
>  int retval_record;
>  
> +int is_capturing = 1;
> +int is_playing = 1;
> +
> +/**
> + * Handling of Ctrl-C for capture
> + */
> +void sigint_handler(int sig)
> +{
> +	is_capturing = 0;
> +}
> +
> +/**
> + * Handling of Ctrl-C for playback
> + */
> +void stream_close(int sig)
> +{
> +	/* allow the stream to be closed gracefully */
> +	signal(sig, SIG_IGN);
> +	is_playing = 0;
> +}
> +
>  /* update chunk_fmt data to bat */
>  static int update_fmt_to_bat(struct bat *bat, struct chunk_fmt *fmt)
>  {
> @@ -196,3 +219,69 @@ int write_wav_header(FILE *fp, struct wav_container *wav, struct bat *bat)
>  
>  	return 0;
>  }
> +
> +/* update wav header when data size changed */
> +int update_wav_header(struct bat *bat, FILE *fp, int bytes)
> +{
> +	int err = 0;
> +	struct wav_container wav;
> +
> +	prepare_wav_info(&wav, bat);
> +	wav.chunk.length = bytes;
> +	wav.header.length = (wav.chunk.length) + sizeof(wav.chunk)
> +		+ sizeof(wav.format) + sizeof(wav.header) - 8;
> +	rewind(fp);
> +	err = write_wav_header(fp, &wav, bat);
> +
> +	return err;
> +}
> +
> +/*
> + * Generate buffer to be played either from input file or from generated data
> + * Return value
> + * <0 error
> + * 0 ok
> + * >0 break
> + */
> +int generate_input_data0(struct bat *bat, void *buffer, int bytes, int frames)
> +{
> +	int err;
> +	static int load;
> +
> +	if (bat->playback.file != NULL) {
> +		/* From input file */
> +		load = 0;
> +
> +		while (1) {
> +			err = fread(buffer + load, 1, bytes - load, bat->fp);
> +			if (0 == err) {
> +				if (feof(bat->fp)) {
> +					fprintf(bat->log,
> +							_("End of playing.\n"));
> +					return 1;
> +				}
> +			} else if (err < bytes - load) {
> +				if (ferror(bat->fp)) {
> +					fprintf(bat->err, _("Read file error"));
> +					fprintf(bat->err, _(": %d\n"), err);
> +					return -EIO;
> +				}
> +				load += err;
> +			} else {
> +				break;
> +			}
> +		}
> +	} else {
> +		/* Generate sine wave */
> +		if ((bat->sinus_duration) && (load > bat->sinus_duration))
> +			return 1;
> +
> +		err = generate_sine_wave(bat, frames, buffer);
> +		if (err != 0)
> +			return err;
> +
> +		load += frames;
> +	}
> +
> +	return 0;
> +}
> diff --git a/bat/common.h b/bat/common.h
> index 30e39fc..0d92a8d 100644
> --- a/bat/common.h
> +++ b/bat/common.h
> @@ -14,6 +14,9 @@
>   */
>  
>  #include <alsa/asoundlib.h>
> +#ifdef HAVE_LIBTINYALSA
> +#include <tinyalsa/asoundlib.h>
> +#endif
>  
>  #define TEMP_RECORD_FILE_NAME		"/tmp/bat.wav.XXXXXX"
>  #define DEFAULT_DEV_NAME		"default"
> @@ -119,6 +122,8 @@ enum _bat_op_mode {
>  
>  struct pcm {
>  	char *device;
> +	unsigned int card_tiny;
> +	unsigned int device_tiny;
>  	char *file;
>  	enum _bat_op_mode mode;
>  	void *(*fct)(struct bat *);
> @@ -171,6 +176,8 @@ struct bat {
>  	void *buf;			/* PCM Buffer */
>  
>  	bool local;			/* true for internal test */
> +
> +	bool tinyalsa;			/* true to use tinyalsa lib */
>  };
>  
>  struct analyze {
> @@ -180,6 +187,10 @@ struct analyze {
>  	double *mag;
>  };
>  
> +void sigint_handler(int);
> +void stream_close(int);
>  void prepare_wav_info(struct wav_container *, struct bat *);
>  int read_wav_header(struct bat *, char *, FILE *, bool);
>  int write_wav_header(FILE *, struct wav_container *, struct bat *);
> +int update_wav_header(struct bat *, FILE *, int);
> +int generate_input_data0(struct bat *, void *, int, int);
> diff --git a/bat/tinyalsa.c b/bat/tinyalsa.c
> new file mode 100644
> index 0000000..ab11247
> --- /dev/null
> +++ b/bat/tinyalsa.c
> @@ -0,0 +1,422 @@
> +/*
> + * Copyright (C) 2013-2015 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <signal.h>
> +#include <pthread.h>
> +#include <errno.h>
> +
> +#include <tinyalsa/asoundlib.h>
> +
> +#include "aconfig.h"
> +#include "gettext.h"
> +
> +#include "common.h"
> +#include "tinyalsa.h"
> +
> +struct format_map_table {
> +	int sample_bytes;
> +	enum pcm_format format;
> +};
> +
> +static struct format_map_table map_tables[] = {
> +	{ 2, PCM_FORMAT_S16_LE },
> +	{ 4, PCM_FORMAT_S32_LE },
> +	{}
> +};
> +
> +/**
> + * Called when thread is finished
> + */
> +static void close_handle(void *handle)
> +{
> +	struct pcm *pcm = handle;
> +
> +	if (NULL != pcm)
> +		pcm_close(pcm);
> +}
> +
> +static int format_convert(struct bat *bat, struct pcm_config *config)
> +{
> +	struct format_map_table *t = map_tables;
> +
> +	for (; t->sample_bytes; t++) {
> +		if (t->sample_bytes == bat->sample_size) {
> +			config->format = t->format;
> +			return 0;
> +		}
> +	}
> +
> +	fprintf(bat->err, _("Invalid format!\n"));
> +	return -EINVAL;
> +}
> +
> +static int init_config(struct bat *bat, struct pcm_config *config)
> +{
> +	config->channels = bat->channels;
> +	config->rate = bat->rate;
> +	config->period_size = 1024;
> +	config->period_count = 4;
> +	config->start_threshold = 0;
> +	config->stop_threshold = 0;
> +	config->silence_threshold = 0;
> +
> +	return format_convert(bat, config);
> +}
> +
> +/**
> + * Check that a parameter is inside bounds
> + */
> +static int check_param(struct bat *bat, struct pcm_params *params,
> +		unsigned int param, unsigned int value,
> +		char *param_name, char *param_unit)
> +{
> +	unsigned int min;
> +	unsigned int max;
> +	int ret = 0;
> +
> +	min = pcm_params_get_min(params, param);
> +	if (value < min) {
> +		fprintf(bat->err,
> +			_("%s is %u%s, device only supports >= %u%s!\n"),
> +			param_name, value, param_unit, min, param_unit);
> +		ret = -EINVAL;
> +	}
> +
> +	max = pcm_params_get_max(params, param);
> +	if (value > max) {
> +		fprintf(bat->err,
> +			_("%s is %u%s, device only supports <= %u%s!\n"),
> +			param_name, value, param_unit, max, param_unit);
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * Check all parameters
> + */
> +static int check_playback_params(struct bat *bat,
> +		struct pcm_config *config)
> +{
> +	struct pcm_params *params;
> +	unsigned int card = bat->playback.card_tiny;
> +	unsigned int device = bat->playback.device_tiny;
> +	int err = 0;
> +
> +	params = pcm_params_get(card, device, PCM_OUT);
> +	if (params == NULL) {
> +		fprintf(bat->err, _("Unable to open PCM device %u!\n"),
> +				device);
> +		return -EINVAL;
> +	}
> +
> +	err = check_param(bat, params, PCM_PARAM_RATE,
> +			config->rate, "Sample rate", "Hz");
> +	if (err < 0)
> +		goto exit;
> +	err = check_param(bat, params, PCM_PARAM_CHANNELS,
> +			config->channels, "Sample", " channels");
> +	if (err < 0)
> +		goto exit;
> +	err = check_param(bat, params, PCM_PARAM_SAMPLE_BITS,
> +			bat->sample_size * 8, "Bitrate", " bits");
> +	if (err < 0)
> +		goto exit;
> +	err = check_param(bat, params, PCM_PARAM_PERIOD_SIZE,
> +			config->period_size, "Period size", "Hz");
> +	if (err < 0)
> +		goto exit;
> +	err = check_param(bat, params, PCM_PARAM_PERIODS,
> +			config->period_count, "Period count", "Hz");
> +	if (err < 0)
> +		goto exit;
> +
> +exit:
> +	pcm_params_free(params);
> +
> +	return err;
> +}
> +
> +/**
> + * Play sample
> + */
> +static int play_sample(struct bat *bat, struct pcm *pcm,
> +		void *buffer, int bytes)
> +{
> +	int err = 0;
> +	FILE *fp = NULL;
> +	int frames = bytes / bat->frame_size;
> +	int bytes_total = 0;
> +
> +	if (bat->debugplay) {
> +		fp = fopen(bat->debugplay, "wb");
> +		if (fp == NULL) {
> +			fprintf(bat->err, _("Cannot open file for capture: "));
> +			fprintf(bat->err, _("%s %d\n"), bat->debugplay, -errno);
> +			return -errno;
> +		}
> +		/* leave space for file header */
> +		if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
> +			fclose(fp);
> +			return -errno;
> +		}
> +	}
> +
> +	do {
> +		err = generate_input_data0(bat, buffer, bytes, frames);
> +		if (err != 0)
> +			break;
> +
> +		if (bat->debugplay) {
> +			if (fwrite(buffer, 1, bytes, fp) != bytes) {
> +				update_wav_header(bat, fp, bytes_total);
> +				fclose(fp);
> +				return -EIO;
> +			}
> +			bytes_total += bytes;
> +		}
> +
> +		bat->periods_played++;
> +		if (bat->period_is_limited
> +				&& bat->periods_played >= bat->periods_total)
> +			break;
> +
> +		err = pcm_write(pcm, buffer, bytes);
> +		if (err != 0) {
> +			fprintf(bat->err, _("Write PCM device error: %d\n"),
> +					err);
> +			break;
> +		}
> +	} while (is_playing);
> +
> +	if (bat->debugplay) {
> +		err = update_wav_header(bat, fp, bytes_total);
> +		fclose(fp);
> +	}
> +	return err;
> +}
> +
> +/**
> + * Play
> + */
> +void *playback_tinyalsa(struct bat *bat)
> +{
> +	int err = 0;
> +	struct pcm_config config;
> +	struct pcm *pcm = NULL;
> +	void *buffer = NULL;
> +	int bufbytes;
> +	unsigned int card = bat->playback.card_tiny;
> +	unsigned int device = bat->playback.device_tiny;
> +
> +	fprintf(bat->log, _("Entering playback thread (tinyalsa).\n"));
> +
> +	retval_play = 0;
> +
> +	/* init config */
> +	err = init_config(bat, &config);
> +	if (err < 0) {
> +		retval_play = err;
> +		goto exit1;
> +	}
> +
> +	/* check param before open device */
> +	err = check_playback_params(bat, &config);
> +	if (err < 0) {
> +		retval_play = err;
> +		goto exit1;
> +	}
> +
> +	/* init device */
> +	pcm = pcm_open(card, device, PCM_OUT, &config);
> +	if (!pcm || !pcm_is_ready(pcm)) {
> +		fprintf(bat->err, _("Unable to open PCM device %u (%s)!\n"),
> +				device, pcm_get_error(pcm));
> +		retval_play = -EINVAL;
> +		goto exit1;
> +	}
> +
> +	/* init buffer */
> +	bufbytes = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
> +	buffer = malloc(bufbytes);
> +	if (!buffer) {
> +		retval_play = -ENOMEM;
> +		goto exit2;
> +	}
> +
> +	/* init playback source */
> +	if (bat->playback.file == NULL) {
> +		fprintf(bat->log, _("Playing generated audio sine wave"));
> +		bat->sinus_duration == 0 ?
> +			fprintf(bat->log, _(" endlessly\n")) :
> +			fprintf(bat->log, _("\n"));
> +	} else {
> +		fprintf(bat->log, _("Playing input audio file: %s\n"),
> +				bat->playback.file);
> +		bat->fp = fopen(bat->playback.file, "rb");
> +		if (bat->fp == NULL) {
> +			fprintf(bat->err, _("Cannot open file for playback: "));
> +			fprintf(bat->err, _("%s %d\n"),
> +					bat->playback.file, -errno);
> +			retval_play = -errno;
> +			goto exit3;
> +		}
> +		/* Skip header */
> +		err = read_wav_header(bat, bat->playback.file, bat->fp, true);
> +		if (err != 0) {
> +			retval_play = err;
> +			goto exit4;
> +		}
> +	}
> +
> +	/* catch ctrl-c to shutdown cleanly */
> +	signal(SIGINT, stream_close);
> +
> +	err = play_sample(bat, pcm, buffer, bufbytes);
> +	if (err < 0) {
> +		retval_play = err;
> +		goto exit4;
> +	}
> +
> +exit4:
> +	if (bat->playback.file)
> +		fclose(bat->fp);
> +exit3:
> +	free(buffer);
> +exit2:
> +	pcm_close(pcm);
> +exit1:
> +	pthread_exit(&retval_play);
> +}
> +
> +/**
> + * Capture sample
> + */
> +static int capture_sample(struct bat *bat, struct pcm *pcm,
> +		void *buffer, unsigned int bytes)
> +{
> +	int err = 0;
> +	FILE *fp = NULL;
> +	unsigned int bytes_read = 0;
> +	unsigned int bytes_count = bat->frames * bat->frame_size;
> +
> +	remove(bat->capture.file);
> +	fp = fopen(bat->capture.file, "wb");
> +	if (fp == NULL) {
> +		fprintf(bat->err, _("Cannot open file for capture: %s %d\n"),
> +				bat->capture.file, -errno);
> +		return -errno;
> +	}
> +	/* leave space for file header */
> +	if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
> +		fclose(fp);
> +		return -errno;
> +	}
> +
> +	while (bytes_read < bytes_count && is_capturing
> +			&& !pcm_read(pcm, buffer, bytes)) {
> +		if (fwrite(buffer, 1, bytes, fp) != bytes)
> +			break;
> +
> +		bytes_read += bytes;
> +
> +		bat->periods_played++;
> +
> +		if (bat->period_is_limited
> +				&& bat->periods_played >= bat->periods_total)
> +			break;
> +	}
> +
> +	err = update_wav_header(bat, fp, bytes_read);
> +
> +	fclose(fp);
> +	return err;
> +}
> +
> +/**
> + * Record
> + */
> +void *record_tinyalsa(struct bat *bat)
> +{
> +	int err = 0;
> +	struct pcm_config config;
> +	struct pcm *pcm;
> +	void *buffer;
> +	unsigned int bufbytes;
> +
> +	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
> +
> +	fprintf(bat->log, _("Entering capture thread (tinyalsa).\n"));
> +
> +	retval_record = 0;
> +
> +	/* init config */
> +	err = init_config(bat, &config);
> +	if (err < 0) {
> +		retval_record = err;
> +		goto exit1;
> +	}
> +
> +	/* init device */
> +	pcm = pcm_open(bat->capture.card_tiny, bat->capture.device_tiny,
> +			PCM_IN, &config);
> +	if (!pcm || !pcm_is_ready(pcm)) {
> +		fprintf(bat->err, _("Unable to open PCM device (%s)!\n"),
> +				pcm_get_error(pcm));
> +		retval_record = -EINVAL;
> +		goto exit1;
> +	}
> +
> +	/* init buffer */
> +	bufbytes = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
> +	buffer = malloc(bufbytes);
> +	if (!buffer) {
> +		retval_record = -ENOMEM;
> +		goto exit2;
> +	}
> +
> +	/* install signal handler and begin capturing Ctrl-C */
> +	signal(SIGINT, sigint_handler);
> +
> +	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
> +	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
> +	pthread_cleanup_push(close_handle, pcm);
> +	pthread_cleanup_push(free, buffer);
> +
> +	fprintf(bat->log, _("Recording ...\n"));
> +	err = capture_sample(bat, pcm, buffer, bufbytes);
> +	if (err != 0) {
> +		retval_record = err;
> +		goto exit3;
> +	}
> +
> +	/* Normally we will never reach this part of code (unless error in
> +	 * previous call) (before exit3) as this thread will be cancelled
> +	 *  by end of play thread. Except in single line mode. */
> +	pthread_cleanup_pop(0);
> +	pthread_cleanup_pop(0);
> +	pthread_exit(&retval_record);
> +
> +exit3:
> +	free(buffer);
> +exit2:
> +	pcm_close(pcm);
> +exit1:
> +	pthread_exit(&retval_record);
> +}
> diff --git a/bat/tinyalsa.h b/bat/tinyalsa.h
> new file mode 100644
> index 0000000..70e4749
> --- /dev/null
> +++ b/bat/tinyalsa.h
> @@ -0,0 +1,23 @@
> +/*
> + * Copyright (C) 2013-2015 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +extern int retval_play;
> +extern int retval_record;
> +
> +extern int is_capturing;
> +extern int is_playing;
> +
> +void *playback_tinyalsa(struct bat *);
> +void *record_tinyalsa(struct bat *);
> diff --git a/configure.ac b/configure.ac
> index f6f8103..87dd237 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -73,6 +73,9 @@ if test x$bat = xtrue; then
>    dnl Check for libfftw3
>    have_libfftw3="yes"
>    AC_CHECK_LIB([fftw3], [fftw_malloc], , [have_libfftw3="no"])
> +  dnl Check for libtinyalsa
> +  have_libtinyalsa="yes"
> +  AC_CHECK_LIB([tinyalsa], [pcm_open], , [have_libtinyalsa="no"])
>    AC_CHECK_LIB([m], [sqrtf], , [AC_MSG_ERROR([Error: Need sqrtf])])
>    AC_CHECK_LIB([pthread], [pthread_create], , [AC_MSG_ERROR([Error: need PTHREAD library])])
>    FFTW_CFLAGS="$CFLAGS"
> @@ -86,6 +89,7 @@ if test x$bat = xtrue; then
>  
>  fi
>  AM_CONDITIONAL(HAVE_LIBFFTW3, test "$have_libfftw3" = "yes")
> +AM_CONDITIONAL(HAVE_LIBTINYALSA, test "$have_libtinyalsa" = "yes")
>  
>  dnl Check for librt
>  LIBRT=""
> -- 
> 2.5.0
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>
han.lu@intel.com March 15, 2016, 3:50 a.m. UTC | #2
> -----Original Message-----
> From: Takashi Iwai [mailto:tiwai@suse.de]
> Sent: Friday, March 11, 2016 10:14 PM
> To: Lu, Han <han.lu@intel.com>
> Cc: liam.r.girdwood@linux.intel.com; Gautier, Bernard
> <bernard.gautier@intel.com>; Popescu, Edward C
> <edward.c.popescu@intel.com>; alsa-devel@alsa-project.org
> Subject: Re: [alsa-devel] [PATCH 03/10] alsabat: add tinyalsa support
> 
> On Wed, 02 Mar 2016 09:53:13 +0100,
> han.lu@intel.com wrote:
> >
> > From: "Lu, Han" <han.lu@intel.com>
> >
> > Add support for alsabat to work on tinyalsa library based platforms
> > such as Android and some Embedded Linux devices.
> > Use option '-t' to select tinyalsa library instead of ALSA library.
> > If '-t' is used while tinyalsa library is not installed in system,
> > alsabat will print error message and quit.
> >
> > Signed-off-by: Lu, Han <han.lu@intel.com>
> 
> Hrm, I don't think it makes so much sense to build a binary bound with both
> libraries.  Usually it's mutual exclusive, the backend is selected / detected via
> configure script.
> 
> 
> thanks,
> 
> Takashi
> 

[han] I agree. I'd like to submit the tinyalsa patches separately, since I need a
little more work to implement the mutual exclusive.

BR,
Han

> >
> > diff --git a/bat/Makefile.am b/bat/Makefile.am index 5646e9a..6101681
> > 100644
> > --- a/bat/Makefile.am
> > +++ b/bat/Makefile.am
> > @@ -21,6 +21,11 @@ alsabat_SOURCES += analyze.c  noinst_HEADERS +=
> > analyze.h  endif
> >
> > +if HAVE_LIBTINYALSA
> > +alsabat_SOURCES += tinyalsa.c
> > +noinst_HEADERS += tinyalsa.h
> > +endif
> > +
> >  AM_CPPFLAGS = \
> >  	      -Wall -I$(top_srcdir)/include
> >
> > diff --git a/bat/alsabat.1 b/bat/alsabat.1 index 5f41669..126232e
> > 100644
> > --- a/bat/alsabat.1
> > +++ b/bat/alsabat.1
> > @@ -106,6 +106,9 @@ Valid range is (DC_THRESHOLD, 40% * Sampling
> rate).
> >  \fI\-p\fP
> >  Total number of periods to play or capture.
> >  .TP
> > +\fI\-t\fP
> > +If tinyalsa lib is installed, use tinyalsa lib instead of alsa lib.
> > +.TP
> >  \fI\-\-log=#\fP
> >  Write stderr and stdout output to this log file.
> >  .TP
> > diff --git a/bat/bat.c b/bat/bat.c
> > index 85ec5aa..8db16c0 100644
> > --- a/bat/bat.c
> > +++ b/bat/bat.c
> > @@ -36,6 +36,9 @@
> >  #ifdef HAVE_LIBFFTW3
> >  #include "analyze.h"
> >  #endif
> > +#ifdef HAVE_LIBTINYALSA
> > +#include "tinyalsa.h"
> > +#endif
> >
> >  static int get_duration(struct bat *bat)  { @@ -125,6 +128,35 @@
> > static void get_format(struct bat *bat, char *optarg)
> >  	}
> >  }
> >
> > +static int get_tiny_format(struct bat *bat, char *alsa_device,
> > +		unsigned int *tiny_card, unsigned int *tiny_device) {
> > +	char *tmp1, *tmp2, *tmp3;
> > +
> > +	if (alsa_device == NULL)
> > +		goto fail;
> > +
> > +	tmp1 = strchr(alsa_device, ':');
> > +	if (tmp1 == NULL)
> > +		goto fail;
> > +
> > +	tmp3 = tmp1 + 1;
> > +	tmp2 = strchr(tmp3, ',');
> > +	if (tmp2 == NULL)
> > +		goto fail;
> > +
> > +	tmp1 = tmp2 + 1;
> > +	*tiny_device = atoi(tmp1);
> > +	*tmp2 = '\0';
> > +	*tiny_card = atoi(tmp3);
> > +	*tmp2 = ',';
> > +
> > +	return 0;
> > +fail:
> > +	fprintf(bat->err, _("Invalid tiny format!\n"));
> > +	return -EINVAL;
> > +}
> > +
> >  static inline int thread_wait_completion(struct bat *bat,
> >  		pthread_t id, int **val)
> >  {
> > @@ -287,6 +319,7 @@ _("Usage: alsabat [-options]...\n"
> >  "  -k                     parameter for frequency detecting threshold\n"
> >  "  -F                     target frequency\n"
> >  "  -p                     total number of periods to play/capture\n"
> > +"  -t                     use tinyalsa instead of alsa\n"
> >  "      --log=#            file that both stdout and strerr redirecting to\n"
> >  "      --file=#           file for playback\n"
> >  "      --saveplay=#       file that storing playback content, for debug\n"
> > @@ -330,6 +363,7 @@ static void set_defaults(struct bat *bat)
> >  	bat->period_is_limited = false;
> >  	bat->log = stdout;
> >  	bat->err = stderr;
> > +	bat->tinyalsa = false;
> >  }
> >
> >  static void parse_arguments(struct bat *bat, int argc, char *argv[])
> > @@ -406,6 +440,16 @@ static void parse_arguments(struct bat *bat, int
> argc, char *argv[])
> >  			bat->periods_total = atoi(optarg);
> >  			bat->period_is_limited = true;
> >  			break;
> > +		case 't':
> > +#ifdef HAVE_LIBTINYALSA
> > +			bat->playback.fct = &playback_tinyalsa;
> > +			bat->capture.fct = &record_tinyalsa;
> > +			bat->tinyalsa = true;
> > +#else
> > +			fprintf(bat->err, _("tinyalsa lib is not installed\n"));
> > +			exit(EXIT_FAILURE);
> > +#endif
> > +			break;
> >  		case 'h':
> >  		default:
> >  			usage(bat);
> > @@ -484,6 +528,24 @@ static int bat_init(struct bat *bat)
> >  	if (bat->playback.device == NULL && bat->capture.device == NULL)
> >  		bat->playback.device = bat->capture.device =
> DEFAULT_DEV_NAME;
> >
> > +	/* Determine tiny device if needed */
> > +	if (bat->tinyalsa == true) {
> > +		if (bat->playback.mode != MODE_SINGLE) {
> > +			err = get_tiny_format(bat, bat->capture.device,
> > +					&bat->capture.card_tiny,
> > +					&bat->capture.device_tiny);
> > +			if (err < 0)
> > +				return err;
> > +		}
> > +		if (bat->capture.mode != MODE_SINGLE) {
> > +			err = get_tiny_format(bat, bat->playback.device,
> > +					&bat->playback.card_tiny,
> > +					&bat->playback.device_tiny);
> > +			if (err < 0)
> > +				return err;
> > +		}
> > +	}
> > +
> >  	/* Determine capture file */
> >  	if (bat->local) {
> >  		bat->capture.file = bat->playback.file; diff --git
> a/bat/common.c
> > b/bat/common.c index 798b00b..bbf969e 100644
> > --- a/bat/common.c
> > +++ b/bat/common.c
> > @@ -18,16 +18,39 @@
> >  #include <stdlib.h>
> >  #include <stdbool.h>
> >  #include <errno.h>
> > +#include <signal.h>
> >
> >  #include "aconfig.h"
> >  #include "gettext.h"
> >
> >  #include "common.h"
> >  #include "alsa.h"
> > +#include "bat-signal.h"
> >
> >  int retval_play;
> >  int retval_record;
> >
> > +int is_capturing = 1;
> > +int is_playing = 1;
> > +
> > +/**
> > + * Handling of Ctrl-C for capture
> > + */
> > +void sigint_handler(int sig)
> > +{
> > +	is_capturing = 0;
> > +}
> > +
> > +/**
> > + * Handling of Ctrl-C for playback
> > + */
> > +void stream_close(int sig)
> > +{
> > +	/* allow the stream to be closed gracefully */
> > +	signal(sig, SIG_IGN);
> > +	is_playing = 0;
> > +}
> > +
> >  /* update chunk_fmt data to bat */
> >  static int update_fmt_to_bat(struct bat *bat, struct chunk_fmt *fmt)
> > { @@ -196,3 +219,69 @@ int write_wav_header(FILE *fp, struct
> > wav_container *wav, struct bat *bat)
> >
> >  	return 0;
> >  }
> > +
> > +/* update wav header when data size changed */ int
> > +update_wav_header(struct bat *bat, FILE *fp, int bytes) {
> > +	int err = 0;
> > +	struct wav_container wav;
> > +
> > +	prepare_wav_info(&wav, bat);
> > +	wav.chunk.length = bytes;
> > +	wav.header.length = (wav.chunk.length) + sizeof(wav.chunk)
> > +		+ sizeof(wav.format) + sizeof(wav.header) - 8;
> > +	rewind(fp);
> > +	err = write_wav_header(fp, &wav, bat);
> > +
> > +	return err;
> > +}
> > +
> > +/*
> > + * Generate buffer to be played either from input file or from
> > +generated data
> > + * Return value
> > + * <0 error
> > + * 0 ok
> > + * >0 break
> > + */
> > +int generate_input_data0(struct bat *bat, void *buffer, int bytes,
> > +int frames) {
> > +	int err;
> > +	static int load;
> > +
> > +	if (bat->playback.file != NULL) {
> > +		/* From input file */
> > +		load = 0;
> > +
> > +		while (1) {
> > +			err = fread(buffer + load, 1, bytes - load, bat->fp);
> > +			if (0 == err) {
> > +				if (feof(bat->fp)) {
> > +					fprintf(bat->log,
> > +							_("End of
> playing.\n"));
> > +					return 1;
> > +				}
> > +			} else if (err < bytes - load) {
> > +				if (ferror(bat->fp)) {
> > +					fprintf(bat->err, _("Read file error"));
> > +					fprintf(bat->err, _(": %d\n"), err);
> > +					return -EIO;
> > +				}
> > +				load += err;
> > +			} else {
> > +				break;
> > +			}
> > +		}
> > +	} else {
> > +		/* Generate sine wave */
> > +		if ((bat->sinus_duration) && (load > bat->sinus_duration))
> > +			return 1;
> > +
> > +		err = generate_sine_wave(bat, frames, buffer);
> > +		if (err != 0)
> > +			return err;
> > +
> > +		load += frames;
> > +	}
> > +
> > +	return 0;
> > +}
> > diff --git a/bat/common.h b/bat/common.h index 30e39fc..0d92a8d 100644
> > --- a/bat/common.h
> > +++ b/bat/common.h
> > @@ -14,6 +14,9 @@
> >   */
> >
> >  #include <alsa/asoundlib.h>
> > +#ifdef HAVE_LIBTINYALSA
> > +#include <tinyalsa/asoundlib.h>
> > +#endif
> >
> >  #define TEMP_RECORD_FILE_NAME		"/tmp/bat.wav.XXXXXX"
> >  #define DEFAULT_DEV_NAME		"default"
> > @@ -119,6 +122,8 @@ enum _bat_op_mode {
> >
> >  struct pcm {
> >  	char *device;
> > +	unsigned int card_tiny;
> > +	unsigned int device_tiny;
> >  	char *file;
> >  	enum _bat_op_mode mode;
> >  	void *(*fct)(struct bat *);
> > @@ -171,6 +176,8 @@ struct bat {
> >  	void *buf;			/* PCM Buffer */
> >
> >  	bool local;			/* true for internal test */
> > +
> > +	bool tinyalsa;			/* true to use tinyalsa lib */
> >  };
> >
> >  struct analyze {
> > @@ -180,6 +187,10 @@ struct analyze {
> >  	double *mag;
> >  };
> >
> > +void sigint_handler(int);
> > +void stream_close(int);
> >  void prepare_wav_info(struct wav_container *, struct bat *);  int
> > read_wav_header(struct bat *, char *, FILE *, bool);  int
> > write_wav_header(FILE *, struct wav_container *, struct bat *);
> > +int update_wav_header(struct bat *, FILE *, int); int
> > +generate_input_data0(struct bat *, void *, int, int);
> > diff --git a/bat/tinyalsa.c b/bat/tinyalsa.c new file mode 100644
> > index 0000000..ab11247
> > --- /dev/null
> > +++ b/bat/tinyalsa.c
> > @@ -0,0 +1,422 @@
> > +/*
> > + * Copyright (C) 2013-2015 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify
> > + * it under the terms of the GNU General Public License as published
> > +by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + */
> > +
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <stdbool.h>
> > +#include <signal.h>
> > +#include <pthread.h>
> > +#include <errno.h>
> > +
> > +#include <tinyalsa/asoundlib.h>
> > +
> > +#include "aconfig.h"
> > +#include "gettext.h"
> > +
> > +#include "common.h"
> > +#include "tinyalsa.h"
> > +
> > +struct format_map_table {
> > +	int sample_bytes;
> > +	enum pcm_format format;
> > +};
> > +
> > +static struct format_map_table map_tables[] = {
> > +	{ 2, PCM_FORMAT_S16_LE },
> > +	{ 4, PCM_FORMAT_S32_LE },
> > +	{}
> > +};
> > +
> > +/**
> > + * Called when thread is finished
> > + */
> > +static void close_handle(void *handle) {
> > +	struct pcm *pcm = handle;
> > +
> > +	if (NULL != pcm)
> > +		pcm_close(pcm);
> > +}
> > +
> > +static int format_convert(struct bat *bat, struct pcm_config *config)
> > +{
> > +	struct format_map_table *t = map_tables;
> > +
> > +	for (; t->sample_bytes; t++) {
> > +		if (t->sample_bytes == bat->sample_size) {
> > +			config->format = t->format;
> > +			return 0;
> > +		}
> > +	}
> > +
> > +	fprintf(bat->err, _("Invalid format!\n"));
> > +	return -EINVAL;
> > +}
> > +
> > +static int init_config(struct bat *bat, struct pcm_config *config) {
> > +	config->channels = bat->channels;
> > +	config->rate = bat->rate;
> > +	config->period_size = 1024;
> > +	config->period_count = 4;
> > +	config->start_threshold = 0;
> > +	config->stop_threshold = 0;
> > +	config->silence_threshold = 0;
> > +
> > +	return format_convert(bat, config);
> > +}
> > +
> > +/**
> > + * Check that a parameter is inside bounds  */ static int
> > +check_param(struct bat *bat, struct pcm_params *params,
> > +		unsigned int param, unsigned int value,
> > +		char *param_name, char *param_unit) {
> > +	unsigned int min;
> > +	unsigned int max;
> > +	int ret = 0;
> > +
> > +	min = pcm_params_get_min(params, param);
> > +	if (value < min) {
> > +		fprintf(bat->err,
> > +			_("%s is %u%s, device only supports >= %u%s!\n"),
> > +			param_name, value, param_unit, min, param_unit);
> > +		ret = -EINVAL;
> > +	}
> > +
> > +	max = pcm_params_get_max(params, param);
> > +	if (value > max) {
> > +		fprintf(bat->err,
> > +			_("%s is %u%s, device only supports <= %u%s!\n"),
> > +			param_name, value, param_unit, max, param_unit);
> > +		ret = -EINVAL;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * Check all parameters
> > + */
> > +static int check_playback_params(struct bat *bat,
> > +		struct pcm_config *config)
> > +{
> > +	struct pcm_params *params;
> > +	unsigned int card = bat->playback.card_tiny;
> > +	unsigned int device = bat->playback.device_tiny;
> > +	int err = 0;
> > +
> > +	params = pcm_params_get(card, device, PCM_OUT);
> > +	if (params == NULL) {
> > +		fprintf(bat->err, _("Unable to open PCM device %u!\n"),
> > +				device);
> > +		return -EINVAL;
> > +	}
> > +
> > +	err = check_param(bat, params, PCM_PARAM_RATE,
> > +			config->rate, "Sample rate", "Hz");
> > +	if (err < 0)
> > +		goto exit;
> > +	err = check_param(bat, params, PCM_PARAM_CHANNELS,
> > +			config->channels, "Sample", " channels");
> > +	if (err < 0)
> > +		goto exit;
> > +	err = check_param(bat, params, PCM_PARAM_SAMPLE_BITS,
> > +			bat->sample_size * 8, "Bitrate", " bits");
> > +	if (err < 0)
> > +		goto exit;
> > +	err = check_param(bat, params, PCM_PARAM_PERIOD_SIZE,
> > +			config->period_size, "Period size", "Hz");
> > +	if (err < 0)
> > +		goto exit;
> > +	err = check_param(bat, params, PCM_PARAM_PERIODS,
> > +			config->period_count, "Period count", "Hz");
> > +	if (err < 0)
> > +		goto exit;
> > +
> > +exit:
> > +	pcm_params_free(params);
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * Play sample
> > + */
> > +static int play_sample(struct bat *bat, struct pcm *pcm,
> > +		void *buffer, int bytes)
> > +{
> > +	int err = 0;
> > +	FILE *fp = NULL;
> > +	int frames = bytes / bat->frame_size;
> > +	int bytes_total = 0;
> > +
> > +	if (bat->debugplay) {
> > +		fp = fopen(bat->debugplay, "wb");
> > +		if (fp == NULL) {
> > +			fprintf(bat->err, _("Cannot open file for capture: "));
> > +			fprintf(bat->err, _("%s %d\n"), bat->debugplay, -
> errno);
> > +			return -errno;
> > +		}
> > +		/* leave space for file header */
> > +		if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
> > +			fclose(fp);
> > +			return -errno;
> > +		}
> > +	}
> > +
> > +	do {
> > +		err = generate_input_data0(bat, buffer, bytes, frames);
> > +		if (err != 0)
> > +			break;
> > +
> > +		if (bat->debugplay) {
> > +			if (fwrite(buffer, 1, bytes, fp) != bytes) {
> > +				update_wav_header(bat, fp, bytes_total);
> > +				fclose(fp);
> > +				return -EIO;
> > +			}
> > +			bytes_total += bytes;
> > +		}
> > +
> > +		bat->periods_played++;
> > +		if (bat->period_is_limited
> > +				&& bat->periods_played >= bat-
> >periods_total)
> > +			break;
> > +
> > +		err = pcm_write(pcm, buffer, bytes);
> > +		if (err != 0) {
> > +			fprintf(bat->err, _("Write PCM device error: %d\n"),
> > +					err);
> > +			break;
> > +		}
> > +	} while (is_playing);
> > +
> > +	if (bat->debugplay) {
> > +		err = update_wav_header(bat, fp, bytes_total);
> > +		fclose(fp);
> > +	}
> > +	return err;
> > +}
> > +
> > +/**
> > + * Play
> > + */
> > +void *playback_tinyalsa(struct bat *bat) {
> > +	int err = 0;
> > +	struct pcm_config config;
> > +	struct pcm *pcm = NULL;
> > +	void *buffer = NULL;
> > +	int bufbytes;
> > +	unsigned int card = bat->playback.card_tiny;
> > +	unsigned int device = bat->playback.device_tiny;
> > +
> > +	fprintf(bat->log, _("Entering playback thread (tinyalsa).\n"));
> > +
> > +	retval_play = 0;
> > +
> > +	/* init config */
> > +	err = init_config(bat, &config);
> > +	if (err < 0) {
> > +		retval_play = err;
> > +		goto exit1;
> > +	}
> > +
> > +	/* check param before open device */
> > +	err = check_playback_params(bat, &config);
> > +	if (err < 0) {
> > +		retval_play = err;
> > +		goto exit1;
> > +	}
> > +
> > +	/* init device */
> > +	pcm = pcm_open(card, device, PCM_OUT, &config);
> > +	if (!pcm || !pcm_is_ready(pcm)) {
> > +		fprintf(bat->err, _("Unable to open PCM device %u (%s)!\n"),
> > +				device, pcm_get_error(pcm));
> > +		retval_play = -EINVAL;
> > +		goto exit1;
> > +	}
> > +
> > +	/* init buffer */
> > +	bufbytes = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
> > +	buffer = malloc(bufbytes);
> > +	if (!buffer) {
> > +		retval_play = -ENOMEM;
> > +		goto exit2;
> > +	}
> > +
> > +	/* init playback source */
> > +	if (bat->playback.file == NULL) {
> > +		fprintf(bat->log, _("Playing generated audio sine wave"));
> > +		bat->sinus_duration == 0 ?
> > +			fprintf(bat->log, _(" endlessly\n")) :
> > +			fprintf(bat->log, _("\n"));
> > +	} else {
> > +		fprintf(bat->log, _("Playing input audio file: %s\n"),
> > +				bat->playback.file);
> > +		bat->fp = fopen(bat->playback.file, "rb");
> > +		if (bat->fp == NULL) {
> > +			fprintf(bat->err, _("Cannot open file for playback: "));
> > +			fprintf(bat->err, _("%s %d\n"),
> > +					bat->playback.file, -errno);
> > +			retval_play = -errno;
> > +			goto exit3;
> > +		}
> > +		/* Skip header */
> > +		err = read_wav_header(bat, bat->playback.file, bat->fp,
> true);
> > +		if (err != 0) {
> > +			retval_play = err;
> > +			goto exit4;
> > +		}
> > +	}
> > +
> > +	/* catch ctrl-c to shutdown cleanly */
> > +	signal(SIGINT, stream_close);
> > +
> > +	err = play_sample(bat, pcm, buffer, bufbytes);
> > +	if (err < 0) {
> > +		retval_play = err;
> > +		goto exit4;
> > +	}
> > +
> > +exit4:
> > +	if (bat->playback.file)
> > +		fclose(bat->fp);
> > +exit3:
> > +	free(buffer);
> > +exit2:
> > +	pcm_close(pcm);
> > +exit1:
> > +	pthread_exit(&retval_play);
> > +}
> > +
> > +/**
> > + * Capture sample
> > + */
> > +static int capture_sample(struct bat *bat, struct pcm *pcm,
> > +		void *buffer, unsigned int bytes)
> > +{
> > +	int err = 0;
> > +	FILE *fp = NULL;
> > +	unsigned int bytes_read = 0;
> > +	unsigned int bytes_count = bat->frames * bat->frame_size;
> > +
> > +	remove(bat->capture.file);
> > +	fp = fopen(bat->capture.file, "wb");
> > +	if (fp == NULL) {
> > +		fprintf(bat->err, _("Cannot open file for capture: %s %d\n"),
> > +				bat->capture.file, -errno);
> > +		return -errno;
> > +	}
> > +	/* leave space for file header */
> > +	if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
> > +		fclose(fp);
> > +		return -errno;
> > +	}
> > +
> > +	while (bytes_read < bytes_count && is_capturing
> > +			&& !pcm_read(pcm, buffer, bytes)) {
> > +		if (fwrite(buffer, 1, bytes, fp) != bytes)
> > +			break;
> > +
> > +		bytes_read += bytes;
> > +
> > +		bat->periods_played++;
> > +
> > +		if (bat->period_is_limited
> > +				&& bat->periods_played >= bat-
> >periods_total)
> > +			break;
> > +	}
> > +
> > +	err = update_wav_header(bat, fp, bytes_read);
> > +
> > +	fclose(fp);
> > +	return err;
> > +}
> > +
> > +/**
> > + * Record
> > + */
> > +void *record_tinyalsa(struct bat *bat) {
> > +	int err = 0;
> > +	struct pcm_config config;
> > +	struct pcm *pcm;
> > +	void *buffer;
> > +	unsigned int bufbytes;
> > +
> > +	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
> > +
> > +	fprintf(bat->log, _("Entering capture thread (tinyalsa).\n"));
> > +
> > +	retval_record = 0;
> > +
> > +	/* init config */
> > +	err = init_config(bat, &config);
> > +	if (err < 0) {
> > +		retval_record = err;
> > +		goto exit1;
> > +	}
> > +
> > +	/* init device */
> > +	pcm = pcm_open(bat->capture.card_tiny, bat->capture.device_tiny,
> > +			PCM_IN, &config);
> > +	if (!pcm || !pcm_is_ready(pcm)) {
> > +		fprintf(bat->err, _("Unable to open PCM device (%s)!\n"),
> > +				pcm_get_error(pcm));
> > +		retval_record = -EINVAL;
> > +		goto exit1;
> > +	}
> > +
> > +	/* init buffer */
> > +	bufbytes = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
> > +	buffer = malloc(bufbytes);
> > +	if (!buffer) {
> > +		retval_record = -ENOMEM;
> > +		goto exit2;
> > +	}
> > +
> > +	/* install signal handler and begin capturing Ctrl-C */
> > +	signal(SIGINT, sigint_handler);
> > +
> > +	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
> > +	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
> > +	pthread_cleanup_push(close_handle, pcm);
> > +	pthread_cleanup_push(free, buffer);
> > +
> > +	fprintf(bat->log, _("Recording ...\n"));
> > +	err = capture_sample(bat, pcm, buffer, bufbytes);
> > +	if (err != 0) {
> > +		retval_record = err;
> > +		goto exit3;
> > +	}
> > +
> > +	/* Normally we will never reach this part of code (unless error in
> > +	 * previous call) (before exit3) as this thread will be cancelled
> > +	 *  by end of play thread. Except in single line mode. */
> > +	pthread_cleanup_pop(0);
> > +	pthread_cleanup_pop(0);
> > +	pthread_exit(&retval_record);
> > +
> > +exit3:
> > +	free(buffer);
> > +exit2:
> > +	pcm_close(pcm);
> > +exit1:
> > +	pthread_exit(&retval_record);
> > +}
> > diff --git a/bat/tinyalsa.h b/bat/tinyalsa.h new file mode 100644
> > index 0000000..70e4749
> > --- /dev/null
> > +++ b/bat/tinyalsa.h
> > @@ -0,0 +1,23 @@
> > +/*
> > + * Copyright (C) 2013-2015 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify
> > + * it under the terms of the GNU General Public License as published
> > +by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + */
> > +
> > +extern int retval_play;
> > +extern int retval_record;
> > +
> > +extern int is_capturing;
> > +extern int is_playing;
> > +
> > +void *playback_tinyalsa(struct bat *); void *record_tinyalsa(struct
> > +bat *);
> > diff --git a/configure.ac b/configure.ac index f6f8103..87dd237 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -73,6 +73,9 @@ if test x$bat = xtrue; then
> >    dnl Check for libfftw3
> >    have_libfftw3="yes"
> >    AC_CHECK_LIB([fftw3], [fftw_malloc], , [have_libfftw3="no"])
> > +  dnl Check for libtinyalsa
> > +  have_libtinyalsa="yes"
> > +  AC_CHECK_LIB([tinyalsa], [pcm_open], , [have_libtinyalsa="no"])
> >    AC_CHECK_LIB([m], [sqrtf], , [AC_MSG_ERROR([Error: Need sqrtf])])
> >    AC_CHECK_LIB([pthread], [pthread_create], , [AC_MSG_ERROR([Error:
> need PTHREAD library])])
> >    FFTW_CFLAGS="$CFLAGS"
> > @@ -86,6 +89,7 @@ if test x$bat = xtrue; then
> >
> >  fi
> >  AM_CONDITIONAL(HAVE_LIBFFTW3, test "$have_libfftw3" = "yes")
> > +AM_CONDITIONAL(HAVE_LIBTINYALSA, test "$have_libtinyalsa" = "yes")
> >
> >  dnl Check for librt
> >  LIBRT=""
> > --
> > 2.5.0
> >
> > _______________________________________________
> > Alsa-devel mailing list
> > Alsa-devel@alsa-project.org
> > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> >
diff mbox

Patch

diff --git a/bat/Makefile.am b/bat/Makefile.am
index 5646e9a..6101681 100644
--- a/bat/Makefile.am
+++ b/bat/Makefile.am
@@ -21,6 +21,11 @@  alsabat_SOURCES += analyze.c
 noinst_HEADERS += analyze.h
 endif
 
+if HAVE_LIBTINYALSA
+alsabat_SOURCES += tinyalsa.c
+noinst_HEADERS += tinyalsa.h
+endif
+
 AM_CPPFLAGS = \
 	      -Wall -I$(top_srcdir)/include
 
diff --git a/bat/alsabat.1 b/bat/alsabat.1
index 5f41669..126232e 100644
--- a/bat/alsabat.1
+++ b/bat/alsabat.1
@@ -106,6 +106,9 @@  Valid range is (DC_THRESHOLD, 40% * Sampling rate).
 \fI\-p\fP
 Total number of periods to play or capture.
 .TP
+\fI\-t\fP
+If tinyalsa lib is installed, use tinyalsa lib instead of alsa lib.
+.TP
 \fI\-\-log=#\fP
 Write stderr and stdout output to this log file.
 .TP
diff --git a/bat/bat.c b/bat/bat.c
index 85ec5aa..8db16c0 100644
--- a/bat/bat.c
+++ b/bat/bat.c
@@ -36,6 +36,9 @@ 
 #ifdef HAVE_LIBFFTW3
 #include "analyze.h"
 #endif
+#ifdef HAVE_LIBTINYALSA
+#include "tinyalsa.h"
+#endif
 
 static int get_duration(struct bat *bat)
 {
@@ -125,6 +128,35 @@  static void get_format(struct bat *bat, char *optarg)
 	}
 }
 
+static int get_tiny_format(struct bat *bat, char *alsa_device,
+		unsigned int *tiny_card, unsigned int *tiny_device)
+{
+	char *tmp1, *tmp2, *tmp3;
+
+	if (alsa_device == NULL)
+		goto fail;
+
+	tmp1 = strchr(alsa_device, ':');
+	if (tmp1 == NULL)
+		goto fail;
+
+	tmp3 = tmp1 + 1;
+	tmp2 = strchr(tmp3, ',');
+	if (tmp2 == NULL)
+		goto fail;
+
+	tmp1 = tmp2 + 1;
+	*tiny_device = atoi(tmp1);
+	*tmp2 = '\0';
+	*tiny_card = atoi(tmp3);
+	*tmp2 = ',';
+
+	return 0;
+fail:
+	fprintf(bat->err, _("Invalid tiny format!\n"));
+	return -EINVAL;
+}
+
 static inline int thread_wait_completion(struct bat *bat,
 		pthread_t id, int **val)
 {
@@ -287,6 +319,7 @@  _("Usage: alsabat [-options]...\n"
 "  -k                     parameter for frequency detecting threshold\n"
 "  -F                     target frequency\n"
 "  -p                     total number of periods to play/capture\n"
+"  -t                     use tinyalsa instead of alsa\n"
 "      --log=#            file that both stdout and strerr redirecting to\n"
 "      --file=#           file for playback\n"
 "      --saveplay=#       file that storing playback content, for debug\n"
@@ -330,6 +363,7 @@  static void set_defaults(struct bat *bat)
 	bat->period_is_limited = false;
 	bat->log = stdout;
 	bat->err = stderr;
+	bat->tinyalsa = false;
 }
 
 static void parse_arguments(struct bat *bat, int argc, char *argv[])
@@ -406,6 +440,16 @@  static void parse_arguments(struct bat *bat, int argc, char *argv[])
 			bat->periods_total = atoi(optarg);
 			bat->period_is_limited = true;
 			break;
+		case 't':
+#ifdef HAVE_LIBTINYALSA
+			bat->playback.fct = &playback_tinyalsa;
+			bat->capture.fct = &record_tinyalsa;
+			bat->tinyalsa = true;
+#else
+			fprintf(bat->err, _("tinyalsa lib is not installed\n"));
+			exit(EXIT_FAILURE);
+#endif
+			break;
 		case 'h':
 		default:
 			usage(bat);
@@ -484,6 +528,24 @@  static int bat_init(struct bat *bat)
 	if (bat->playback.device == NULL && bat->capture.device == NULL)
 		bat->playback.device = bat->capture.device = DEFAULT_DEV_NAME;
 
+	/* Determine tiny device if needed */
+	if (bat->tinyalsa == true) {
+		if (bat->playback.mode != MODE_SINGLE) {
+			err = get_tiny_format(bat, bat->capture.device,
+					&bat->capture.card_tiny,
+					&bat->capture.device_tiny);
+			if (err < 0)
+				return err;
+		}
+		if (bat->capture.mode != MODE_SINGLE) {
+			err = get_tiny_format(bat, bat->playback.device,
+					&bat->playback.card_tiny,
+					&bat->playback.device_tiny);
+			if (err < 0)
+				return err;
+		}
+	}
+
 	/* Determine capture file */
 	if (bat->local) {
 		bat->capture.file = bat->playback.file;
diff --git a/bat/common.c b/bat/common.c
index 798b00b..bbf969e 100644
--- a/bat/common.c
+++ b/bat/common.c
@@ -18,16 +18,39 @@ 
 #include <stdlib.h>
 #include <stdbool.h>
 #include <errno.h>
+#include <signal.h>
 
 #include "aconfig.h"
 #include "gettext.h"
 
 #include "common.h"
 #include "alsa.h"
+#include "bat-signal.h"
 
 int retval_play;
 int retval_record;
 
+int is_capturing = 1;
+int is_playing = 1;
+
+/**
+ * Handling of Ctrl-C for capture
+ */
+void sigint_handler(int sig)
+{
+	is_capturing = 0;
+}
+
+/**
+ * Handling of Ctrl-C for playback
+ */
+void stream_close(int sig)
+{
+	/* allow the stream to be closed gracefully */
+	signal(sig, SIG_IGN);
+	is_playing = 0;
+}
+
 /* update chunk_fmt data to bat */
 static int update_fmt_to_bat(struct bat *bat, struct chunk_fmt *fmt)
 {
@@ -196,3 +219,69 @@  int write_wav_header(FILE *fp, struct wav_container *wav, struct bat *bat)
 
 	return 0;
 }
+
+/* update wav header when data size changed */
+int update_wav_header(struct bat *bat, FILE *fp, int bytes)
+{
+	int err = 0;
+	struct wav_container wav;
+
+	prepare_wav_info(&wav, bat);
+	wav.chunk.length = bytes;
+	wav.header.length = (wav.chunk.length) + sizeof(wav.chunk)
+		+ sizeof(wav.format) + sizeof(wav.header) - 8;
+	rewind(fp);
+	err = write_wav_header(fp, &wav, bat);
+
+	return err;
+}
+
+/*
+ * Generate buffer to be played either from input file or from generated data
+ * Return value
+ * <0 error
+ * 0 ok
+ * >0 break
+ */
+int generate_input_data0(struct bat *bat, void *buffer, int bytes, int frames)
+{
+	int err;
+	static int load;
+
+	if (bat->playback.file != NULL) {
+		/* From input file */
+		load = 0;
+
+		while (1) {
+			err = fread(buffer + load, 1, bytes - load, bat->fp);
+			if (0 == err) {
+				if (feof(bat->fp)) {
+					fprintf(bat->log,
+							_("End of playing.\n"));
+					return 1;
+				}
+			} else if (err < bytes - load) {
+				if (ferror(bat->fp)) {
+					fprintf(bat->err, _("Read file error"));
+					fprintf(bat->err, _(": %d\n"), err);
+					return -EIO;
+				}
+				load += err;
+			} else {
+				break;
+			}
+		}
+	} else {
+		/* Generate sine wave */
+		if ((bat->sinus_duration) && (load > bat->sinus_duration))
+			return 1;
+
+		err = generate_sine_wave(bat, frames, buffer);
+		if (err != 0)
+			return err;
+
+		load += frames;
+	}
+
+	return 0;
+}
diff --git a/bat/common.h b/bat/common.h
index 30e39fc..0d92a8d 100644
--- a/bat/common.h
+++ b/bat/common.h
@@ -14,6 +14,9 @@ 
  */
 
 #include <alsa/asoundlib.h>
+#ifdef HAVE_LIBTINYALSA
+#include <tinyalsa/asoundlib.h>
+#endif
 
 #define TEMP_RECORD_FILE_NAME		"/tmp/bat.wav.XXXXXX"
 #define DEFAULT_DEV_NAME		"default"
@@ -119,6 +122,8 @@  enum _bat_op_mode {
 
 struct pcm {
 	char *device;
+	unsigned int card_tiny;
+	unsigned int device_tiny;
 	char *file;
 	enum _bat_op_mode mode;
 	void *(*fct)(struct bat *);
@@ -171,6 +176,8 @@  struct bat {
 	void *buf;			/* PCM Buffer */
 
 	bool local;			/* true for internal test */
+
+	bool tinyalsa;			/* true to use tinyalsa lib */
 };
 
 struct analyze {
@@ -180,6 +187,10 @@  struct analyze {
 	double *mag;
 };
 
+void sigint_handler(int);
+void stream_close(int);
 void prepare_wav_info(struct wav_container *, struct bat *);
 int read_wav_header(struct bat *, char *, FILE *, bool);
 int write_wav_header(FILE *, struct wav_container *, struct bat *);
+int update_wav_header(struct bat *, FILE *, int);
+int generate_input_data0(struct bat *, void *, int, int);
diff --git a/bat/tinyalsa.c b/bat/tinyalsa.c
new file mode 100644
index 0000000..ab11247
--- /dev/null
+++ b/bat/tinyalsa.c
@@ -0,0 +1,422 @@ 
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#include "aconfig.h"
+#include "gettext.h"
+
+#include "common.h"
+#include "tinyalsa.h"
+
+struct format_map_table {
+	int sample_bytes;
+	enum pcm_format format;
+};
+
+static struct format_map_table map_tables[] = {
+	{ 2, PCM_FORMAT_S16_LE },
+	{ 4, PCM_FORMAT_S32_LE },
+	{}
+};
+
+/**
+ * Called when thread is finished
+ */
+static void close_handle(void *handle)
+{
+	struct pcm *pcm = handle;
+
+	if (NULL != pcm)
+		pcm_close(pcm);
+}
+
+static int format_convert(struct bat *bat, struct pcm_config *config)
+{
+	struct format_map_table *t = map_tables;
+
+	for (; t->sample_bytes; t++) {
+		if (t->sample_bytes == bat->sample_size) {
+			config->format = t->format;
+			return 0;
+		}
+	}
+
+	fprintf(bat->err, _("Invalid format!\n"));
+	return -EINVAL;
+}
+
+static int init_config(struct bat *bat, struct pcm_config *config)
+{
+	config->channels = bat->channels;
+	config->rate = bat->rate;
+	config->period_size = 1024;
+	config->period_count = 4;
+	config->start_threshold = 0;
+	config->stop_threshold = 0;
+	config->silence_threshold = 0;
+
+	return format_convert(bat, config);
+}
+
+/**
+ * Check that a parameter is inside bounds
+ */
+static int check_param(struct bat *bat, struct pcm_params *params,
+		unsigned int param, unsigned int value,
+		char *param_name, char *param_unit)
+{
+	unsigned int min;
+	unsigned int max;
+	int ret = 0;
+
+	min = pcm_params_get_min(params, param);
+	if (value < min) {
+		fprintf(bat->err,
+			_("%s is %u%s, device only supports >= %u%s!\n"),
+			param_name, value, param_unit, min, param_unit);
+		ret = -EINVAL;
+	}
+
+	max = pcm_params_get_max(params, param);
+	if (value > max) {
+		fprintf(bat->err,
+			_("%s is %u%s, device only supports <= %u%s!\n"),
+			param_name, value, param_unit, max, param_unit);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * Check all parameters
+ */
+static int check_playback_params(struct bat *bat,
+		struct pcm_config *config)
+{
+	struct pcm_params *params;
+	unsigned int card = bat->playback.card_tiny;
+	unsigned int device = bat->playback.device_tiny;
+	int err = 0;
+
+	params = pcm_params_get(card, device, PCM_OUT);
+	if (params == NULL) {
+		fprintf(bat->err, _("Unable to open PCM device %u!\n"),
+				device);
+		return -EINVAL;
+	}
+
+	err = check_param(bat, params, PCM_PARAM_RATE,
+			config->rate, "Sample rate", "Hz");
+	if (err < 0)
+		goto exit;
+	err = check_param(bat, params, PCM_PARAM_CHANNELS,
+			config->channels, "Sample", " channels");
+	if (err < 0)
+		goto exit;
+	err = check_param(bat, params, PCM_PARAM_SAMPLE_BITS,
+			bat->sample_size * 8, "Bitrate", " bits");
+	if (err < 0)
+		goto exit;
+	err = check_param(bat, params, PCM_PARAM_PERIOD_SIZE,
+			config->period_size, "Period size", "Hz");
+	if (err < 0)
+		goto exit;
+	err = check_param(bat, params, PCM_PARAM_PERIODS,
+			config->period_count, "Period count", "Hz");
+	if (err < 0)
+		goto exit;
+
+exit:
+	pcm_params_free(params);
+
+	return err;
+}
+
+/**
+ * Play sample
+ */
+static int play_sample(struct bat *bat, struct pcm *pcm,
+		void *buffer, int bytes)
+{
+	int err = 0;
+	FILE *fp = NULL;
+	int frames = bytes / bat->frame_size;
+	int bytes_total = 0;
+
+	if (bat->debugplay) {
+		fp = fopen(bat->debugplay, "wb");
+		if (fp == NULL) {
+			fprintf(bat->err, _("Cannot open file for capture: "));
+			fprintf(bat->err, _("%s %d\n"), bat->debugplay, -errno);
+			return -errno;
+		}
+		/* leave space for file header */
+		if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
+			fclose(fp);
+			return -errno;
+		}
+	}
+
+	do {
+		err = generate_input_data0(bat, buffer, bytes, frames);
+		if (err != 0)
+			break;
+
+		if (bat->debugplay) {
+			if (fwrite(buffer, 1, bytes, fp) != bytes) {
+				update_wav_header(bat, fp, bytes_total);
+				fclose(fp);
+				return -EIO;
+			}
+			bytes_total += bytes;
+		}
+
+		bat->periods_played++;
+		if (bat->period_is_limited
+				&& bat->periods_played >= bat->periods_total)
+			break;
+
+		err = pcm_write(pcm, buffer, bytes);
+		if (err != 0) {
+			fprintf(bat->err, _("Write PCM device error: %d\n"),
+					err);
+			break;
+		}
+	} while (is_playing);
+
+	if (bat->debugplay) {
+		err = update_wav_header(bat, fp, bytes_total);
+		fclose(fp);
+	}
+	return err;
+}
+
+/**
+ * Play
+ */
+void *playback_tinyalsa(struct bat *bat)
+{
+	int err = 0;
+	struct pcm_config config;
+	struct pcm *pcm = NULL;
+	void *buffer = NULL;
+	int bufbytes;
+	unsigned int card = bat->playback.card_tiny;
+	unsigned int device = bat->playback.device_tiny;
+
+	fprintf(bat->log, _("Entering playback thread (tinyalsa).\n"));
+
+	retval_play = 0;
+
+	/* init config */
+	err = init_config(bat, &config);
+	if (err < 0) {
+		retval_play = err;
+		goto exit1;
+	}
+
+	/* check param before open device */
+	err = check_playback_params(bat, &config);
+	if (err < 0) {
+		retval_play = err;
+		goto exit1;
+	}
+
+	/* init device */
+	pcm = pcm_open(card, device, PCM_OUT, &config);
+	if (!pcm || !pcm_is_ready(pcm)) {
+		fprintf(bat->err, _("Unable to open PCM device %u (%s)!\n"),
+				device, pcm_get_error(pcm));
+		retval_play = -EINVAL;
+		goto exit1;
+	}
+
+	/* init buffer */
+	bufbytes = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
+	buffer = malloc(bufbytes);
+	if (!buffer) {
+		retval_play = -ENOMEM;
+		goto exit2;
+	}
+
+	/* init playback source */
+	if (bat->playback.file == NULL) {
+		fprintf(bat->log, _("Playing generated audio sine wave"));
+		bat->sinus_duration == 0 ?
+			fprintf(bat->log, _(" endlessly\n")) :
+			fprintf(bat->log, _("\n"));
+	} else {
+		fprintf(bat->log, _("Playing input audio file: %s\n"),
+				bat->playback.file);
+		bat->fp = fopen(bat->playback.file, "rb");
+		if (bat->fp == NULL) {
+			fprintf(bat->err, _("Cannot open file for playback: "));
+			fprintf(bat->err, _("%s %d\n"),
+					bat->playback.file, -errno);
+			retval_play = -errno;
+			goto exit3;
+		}
+		/* Skip header */
+		err = read_wav_header(bat, bat->playback.file, bat->fp, true);
+		if (err != 0) {
+			retval_play = err;
+			goto exit4;
+		}
+	}
+
+	/* catch ctrl-c to shutdown cleanly */
+	signal(SIGINT, stream_close);
+
+	err = play_sample(bat, pcm, buffer, bufbytes);
+	if (err < 0) {
+		retval_play = err;
+		goto exit4;
+	}
+
+exit4:
+	if (bat->playback.file)
+		fclose(bat->fp);
+exit3:
+	free(buffer);
+exit2:
+	pcm_close(pcm);
+exit1:
+	pthread_exit(&retval_play);
+}
+
+/**
+ * Capture sample
+ */
+static int capture_sample(struct bat *bat, struct pcm *pcm,
+		void *buffer, unsigned int bytes)
+{
+	int err = 0;
+	FILE *fp = NULL;
+	unsigned int bytes_read = 0;
+	unsigned int bytes_count = bat->frames * bat->frame_size;
+
+	remove(bat->capture.file);
+	fp = fopen(bat->capture.file, "wb");
+	if (fp == NULL) {
+		fprintf(bat->err, _("Cannot open file for capture: %s %d\n"),
+				bat->capture.file, -errno);
+		return -errno;
+	}
+	/* leave space for file header */
+	if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
+		fclose(fp);
+		return -errno;
+	}
+
+	while (bytes_read < bytes_count && is_capturing
+			&& !pcm_read(pcm, buffer, bytes)) {
+		if (fwrite(buffer, 1, bytes, fp) != bytes)
+			break;
+
+		bytes_read += bytes;
+
+		bat->periods_played++;
+
+		if (bat->period_is_limited
+				&& bat->periods_played >= bat->periods_total)
+			break;
+	}
+
+	err = update_wav_header(bat, fp, bytes_read);
+
+	fclose(fp);
+	return err;
+}
+
+/**
+ * Record
+ */
+void *record_tinyalsa(struct bat *bat)
+{
+	int err = 0;
+	struct pcm_config config;
+	struct pcm *pcm;
+	void *buffer;
+	unsigned int bufbytes;
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+
+	fprintf(bat->log, _("Entering capture thread (tinyalsa).\n"));
+
+	retval_record = 0;
+
+	/* init config */
+	err = init_config(bat, &config);
+	if (err < 0) {
+		retval_record = err;
+		goto exit1;
+	}
+
+	/* init device */
+	pcm = pcm_open(bat->capture.card_tiny, bat->capture.device_tiny,
+			PCM_IN, &config);
+	if (!pcm || !pcm_is_ready(pcm)) {
+		fprintf(bat->err, _("Unable to open PCM device (%s)!\n"),
+				pcm_get_error(pcm));
+		retval_record = -EINVAL;
+		goto exit1;
+	}
+
+	/* init buffer */
+	bufbytes = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
+	buffer = malloc(bufbytes);
+	if (!buffer) {
+		retval_record = -ENOMEM;
+		goto exit2;
+	}
+
+	/* install signal handler and begin capturing Ctrl-C */
+	signal(SIGINT, sigint_handler);
+
+	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+	pthread_cleanup_push(close_handle, pcm);
+	pthread_cleanup_push(free, buffer);
+
+	fprintf(bat->log, _("Recording ...\n"));
+	err = capture_sample(bat, pcm, buffer, bufbytes);
+	if (err != 0) {
+		retval_record = err;
+		goto exit3;
+	}
+
+	/* Normally we will never reach this part of code (unless error in
+	 * previous call) (before exit3) as this thread will be cancelled
+	 *  by end of play thread. Except in single line mode. */
+	pthread_cleanup_pop(0);
+	pthread_cleanup_pop(0);
+	pthread_exit(&retval_record);
+
+exit3:
+	free(buffer);
+exit2:
+	pcm_close(pcm);
+exit1:
+	pthread_exit(&retval_record);
+}
diff --git a/bat/tinyalsa.h b/bat/tinyalsa.h
new file mode 100644
index 0000000..70e4749
--- /dev/null
+++ b/bat/tinyalsa.h
@@ -0,0 +1,23 @@ 
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+extern int retval_play;
+extern int retval_record;
+
+extern int is_capturing;
+extern int is_playing;
+
+void *playback_tinyalsa(struct bat *);
+void *record_tinyalsa(struct bat *);
diff --git a/configure.ac b/configure.ac
index f6f8103..87dd237 100644
--- a/configure.ac
+++ b/configure.ac
@@ -73,6 +73,9 @@  if test x$bat = xtrue; then
   dnl Check for libfftw3
   have_libfftw3="yes"
   AC_CHECK_LIB([fftw3], [fftw_malloc], , [have_libfftw3="no"])
+  dnl Check for libtinyalsa
+  have_libtinyalsa="yes"
+  AC_CHECK_LIB([tinyalsa], [pcm_open], , [have_libtinyalsa="no"])
   AC_CHECK_LIB([m], [sqrtf], , [AC_MSG_ERROR([Error: Need sqrtf])])
   AC_CHECK_LIB([pthread], [pthread_create], , [AC_MSG_ERROR([Error: need PTHREAD library])])
   FFTW_CFLAGS="$CFLAGS"
@@ -86,6 +89,7 @@  if test x$bat = xtrue; then
 
 fi
 AM_CONDITIONAL(HAVE_LIBFFTW3, test "$have_libfftw3" = "yes")
+AM_CONDITIONAL(HAVE_LIBTINYALSA, test "$have_libtinyalsa" = "yes")
 
 dnl Check for librt
 LIBRT=""