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
case 'h': default: usage(bat);break;
@@ -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