aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/Makefile34
-rw-r--r--utils/tinycap.c264
-rw-r--r--utils/tinymix.c329
-rw-r--r--utils/tinypcminfo.c203
-rw-r--r--utils/tinyplay.c303
-rw-r--r--utils/tinywavinfo.c214
6 files changed, 1347 insertions, 0 deletions
diff --git a/utils/Makefile b/utils/Makefile
new file mode 100644
index 0000000..0270975
--- /dev/null
+++ b/utils/Makefile
@@ -0,0 +1,34 @@
+DESTDIR ?=
+PREFIX ?= /usr/local
+CROSS_COMPILE ?=
+
+CC = $(CROSS_COMPILE)gcc
+CFLAGS ?= -Wall -Wextra -Werror -Wfatal-errors
+
+LD = $(CROSS_COMPILE)ld
+LDFLAGS ?=
+
+.PHONY: all
+all: tinyplay tinycap tinymix tinypcminfo
+
+tinyplay: tinyplay.c -ltinyalsa
+
+tinycap: tinycap.c -ltinyalsa
+
+tinymix: tinymix.c -ltinyalsa
+
+tinypcminfo: tinypcminfo.c -ltinyalsa
+
+.PHONY: clean
+clean:
+ rm -f tinyplay tinycap
+ rm -f tinymix
+ rm -f tinypcminfo
+
+.PHONY: install
+install: tinyplay tinycap tinymix tinypcminfo
+ cp -u tinyplay $(PREFIX)/bin/
+ cp -u tinycap $(PREFIX)/bin/
+ cp -u tinymix $(PREFIX)/bin/
+ cp -u tinypcminfo $(PREFIX)/bin/
+
diff --git a/utils/tinycap.c b/utils/tinycap.c
new file mode 100644
index 0000000..61d6b62
--- /dev/null
+++ b/utils/tinycap.c
@@ -0,0 +1,264 @@
+/* tinycap.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of The Android Open Source Project nor the names of
+** its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <signal.h>
+#include <string.h>
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+#define FORMAT_PCM 1
+
+struct wav_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t riff_fmt;
+ uint32_t fmt_id;
+ uint32_t fmt_sz;
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate;
+ uint16_t block_align;
+ uint16_t bits_per_sample;
+ uint32_t data_id;
+ uint32_t data_sz;
+};
+
+int capturing = 1;
+int prinfo = 1;
+
+unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
+ unsigned int channels, unsigned int rate,
+ enum pcm_format format, unsigned int period_size,
+ unsigned int period_count, unsigned int capture_time);
+
+void sigint_handler(int sig)
+{
+ if (sig == SIGINT){
+ capturing = 0;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ FILE *file;
+ struct wav_header header;
+ unsigned int card = 0;
+ unsigned int device = 0;
+ unsigned int channels = 2;
+ unsigned int rate = 44100;
+ unsigned int bits = 16;
+ unsigned int frames;
+ unsigned int period_size = 1024;
+ unsigned int period_count = 4;
+ unsigned int capture_time = 0;
+ enum pcm_format format;
+ int no_header = 0;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s {file.wav | --} [-D card] [-d device] [-c channels] "
+ "[-r rate] [-b bits] [-p period_size] [-n n_periods] [-t time_in_seconds]\n\n"
+ "Use -- for filename to send raw PCM to stdout\n", argv[0]);
+ return 1;
+ }
+
+ if (strcmp(argv[1],"--") == 0) {
+ file = stdout;
+ prinfo = 0;
+ no_header = 1;
+ } else {
+ file = fopen(argv[1], "wb");
+ if (!file) {
+ fprintf(stderr, "Unable to create file '%s'\n", argv[1]);
+ return 1;
+ }
+ }
+
+ /* parse command line arguments */
+ argv += 2;
+ while (*argv) {
+ if (strcmp(*argv, "-d") == 0) {
+ argv++;
+ if (*argv)
+ device = atoi(*argv);
+ } else if (strcmp(*argv, "-c") == 0) {
+ argv++;
+ if (*argv)
+ channels = atoi(*argv);
+ } else if (strcmp(*argv, "-r") == 0) {
+ argv++;
+ if (*argv)
+ rate = atoi(*argv);
+ } else if (strcmp(*argv, "-b") == 0) {
+ argv++;
+ if (*argv)
+ bits = atoi(*argv);
+ } else if (strcmp(*argv, "-D") == 0) {
+ argv++;
+ if (*argv)
+ card = atoi(*argv);
+ } else if (strcmp(*argv, "-p") == 0) {
+ argv++;
+ if (*argv)
+ period_size = atoi(*argv);
+ } else if (strcmp(*argv, "-n") == 0) {
+ argv++;
+ if (*argv)
+ period_count = atoi(*argv);
+ } else if (strcmp(*argv, "-t") == 0) {
+ argv++;
+ if (*argv)
+ capture_time = atoi(*argv);
+ }
+ if (*argv)
+ argv++;
+ }
+
+ header.riff_id = ID_RIFF;
+ header.riff_sz = 0;
+ header.riff_fmt = ID_WAVE;
+ header.fmt_id = ID_FMT;
+ header.fmt_sz = 16;
+ header.audio_format = FORMAT_PCM;
+ header.num_channels = channels;
+ header.sample_rate = rate;
+
+ switch (bits) {
+ case 32:
+ format = PCM_FORMAT_S32_LE;
+ break;
+ case 24:
+ format = PCM_FORMAT_S24_LE;
+ break;
+ case 16:
+ format = PCM_FORMAT_S16_LE;
+ break;
+ default:
+ fprintf(stderr, "%u bits is not supported.\n", bits);
+ return 1;
+ }
+
+ header.bits_per_sample = pcm_format_to_bits(format);
+ header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
+ header.block_align = channels * (header.bits_per_sample / 8);
+ header.data_id = ID_DATA;
+
+ /* leave enough room for header */
+ if (!no_header) {
+ fseek(file, sizeof(struct wav_header), SEEK_SET);
+ }
+
+ /* install signal handler and begin capturing */
+ signal(SIGINT, sigint_handler);
+ frames = capture_sample(file, card, device, header.num_channels,
+ header.sample_rate, format,
+ period_size, period_count, capture_time);
+ if (prinfo) {
+ printf("Captured %u frames\n", frames);
+ }
+
+ /* write header now all information is known */
+ if (!no_header) {
+ header.data_sz = frames * header.block_align;
+ header.riff_sz = header.data_sz + sizeof(header) - 8;
+ fseek(file, 0, SEEK_SET);
+ fwrite(&header, sizeof(struct wav_header), 1, file);
+ }
+
+ fclose(file);
+
+ return 0;
+}
+
+unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
+ unsigned int channels, unsigned int rate,
+ enum pcm_format format, unsigned int period_size,
+ unsigned int period_count, unsigned int capture_time)
+{
+ struct pcm_config config;
+ struct pcm *pcm;
+ char *buffer;
+ unsigned int size;
+ unsigned int bytes_read = 0;
+
+ memset(&config, 0, sizeof(config));
+ config.channels = channels;
+ config.rate = rate;
+ config.period_size = period_size;
+ config.period_count = period_count;
+ config.format = format;
+ config.start_threshold = 0;
+ config.stop_threshold = 0;
+ config.silence_threshold = 0;
+
+ pcm = pcm_open(card, device, PCM_IN, &config);
+ if (!pcm || !pcm_is_ready(pcm)) {
+ fprintf(stderr, "Unable to open PCM device (%s)\n",
+ pcm_get_error(pcm));
+ return 0;
+ }
+
+ size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
+ buffer = malloc(size);
+ if (!buffer) {
+ fprintf(stderr, "Unable to allocate %u bytes\n", size);
+ free(buffer);
+ pcm_close(pcm);
+ return 0;
+ }
+
+ if (prinfo) {
+ printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate,
+ pcm_format_to_bits(format));
+ }
+
+ while (capturing && !pcm_read(pcm, buffer, size)) {
+ if (capture_time > 0 &&
+ ((bytes_read + size) > pcm_frames_to_bytes(pcm, capture_time * rate))) {
+ size = pcm_frames_to_bytes(pcm, capture_time * rate) - bytes_read;
+ capturing = 0;
+ }
+ if (fwrite(buffer, 1, size, file) != size) {
+ fprintf(stderr,"Error capturing sample\n");
+ break;
+ }
+ bytes_read += size;
+ }
+
+ free(buffer);
+ pcm_close(pcm);
+ return pcm_bytes_to_frames(pcm, bytes_read);
+}
+
diff --git a/utils/tinymix.c b/utils/tinymix.c
new file mode 100644
index 0000000..5966df9
--- /dev/null
+++ b/utils/tinymix.c
@@ -0,0 +1,329 @@
+/* tinymix.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of The Android Open Source Project nor the names of
+** its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <tinyalsa/asoundlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+static void tinymix_list_controls(struct mixer *mixer);
+static void tinymix_detail_control(struct mixer *mixer, const char *control,
+ int print_all);
+static void tinymix_set_value(struct mixer *mixer, const char *control,
+ char **values, unsigned int num_values);
+static void tinymix_print_enum(struct mixer_ctl *ctl, int print_all);
+
+int main(int argc, char **argv)
+{
+ struct mixer *mixer;
+ int card = 0;
+
+ if ((argc > 2) && (strcmp(argv[1], "-D") == 0)) {
+ argv++;
+ if (argv[1]) {
+ card = atoi(argv[1]);
+ argv++;
+ argc -= 2;
+ } else {
+ argc -= 1;
+ }
+ }
+
+ mixer = mixer_open(card);
+ if (!mixer) {
+ fprintf(stderr, "Failed to open mixer\n");
+ return EXIT_FAILURE;
+ }
+
+
+ if (argc == 1) {
+ printf("Mixer name: '%s'\n", mixer_get_name(mixer));
+ tinymix_list_controls(mixer);
+ } else if (argc == 2) {
+ tinymix_detail_control(mixer, argv[1], 1);
+ } else if (argc >= 3) {
+ tinymix_set_value(mixer, argv[1], &argv[2], argc - 2);
+ } else {
+ printf("Usage: tinymix [-D card] [control id] [value to set]\n");
+ }
+
+ mixer_close(mixer);
+
+ return 0;
+}
+
+static void tinymix_list_controls(struct mixer *mixer)
+{
+ struct mixer_ctl *ctl;
+ const char *name, *type;
+ unsigned int num_ctls, num_values;
+ unsigned int i;
+
+ num_ctls = mixer_get_num_ctls(mixer);
+
+ printf("Number of controls: %u\n", num_ctls);
+
+ printf("ctl\ttype\tnum\t%-40s value\n", "name");
+ for (i = 0; i < num_ctls; i++) {
+ ctl = mixer_get_ctl(mixer, i);
+
+ name = mixer_ctl_get_name(ctl);
+ type = mixer_ctl_get_type_string(ctl);
+ num_values = mixer_ctl_get_num_values(ctl);
+ printf("%u\t%s\t%u\t%-40s", i, type, num_values, name);
+ tinymix_detail_control(mixer, name, 0);
+ }
+}
+
+static void tinymix_print_enum(struct mixer_ctl *ctl, int print_all)
+{
+ unsigned int num_enums;
+ unsigned int i;
+ const char *string;
+
+ num_enums = mixer_ctl_get_num_enums(ctl);
+
+ for (i = 0; i < num_enums; i++) {
+ string = mixer_ctl_get_enum_string(ctl, i);
+ if (print_all)
+ printf("\t%s%s", mixer_ctl_get_value(ctl, 0) == (int)i ? ">" : "",
+ string);
+ else if (mixer_ctl_get_value(ctl, 0) == (int)i)
+ printf(" %-s", string);
+ }
+}
+
+static void tinymix_detail_control(struct mixer *mixer, const char *control,
+ int print_all)
+{
+ struct mixer_ctl *ctl;
+ enum mixer_ctl_type type;
+ unsigned int num_values;
+ unsigned int i;
+ int min, max;
+ int ret;
+ char *buf = NULL;
+
+ if (isdigit(control[0]))
+ ctl = mixer_get_ctl(mixer, atoi(control));
+ else
+ ctl = mixer_get_ctl_by_name(mixer, control);
+
+ if (!ctl) {
+ fprintf(stderr, "Invalid mixer control\n");
+ return;
+ }
+
+ type = mixer_ctl_get_type(ctl);
+ num_values = mixer_ctl_get_num_values(ctl);
+
+ if ((type == MIXER_CTL_TYPE_BYTE) && (num_values > 0)) {
+ buf = calloc(1, num_values);
+ if (buf == NULL) {
+ fprintf(stderr, "Failed to alloc mem for bytes %u\n", num_values);
+ return;
+ }
+
+ ret = mixer_ctl_get_array(ctl, buf, num_values);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to mixer_ctl_get_array\n");
+ free(buf);
+ return;
+ }
+ }
+
+ if (print_all)
+ printf("%s:", mixer_ctl_get_name(ctl));
+
+ for (i = 0; i < num_values; i++) {
+ switch (type)
+ {
+ case MIXER_CTL_TYPE_INT:
+ printf(" %d", mixer_ctl_get_value(ctl, i));
+ break;
+ case MIXER_CTL_TYPE_BOOL:
+ printf(" %s", mixer_ctl_get_value(ctl, i) ? "On" : "Off");
+ break;
+ case MIXER_CTL_TYPE_ENUM:
+ tinymix_print_enum(ctl, print_all);
+ break;
+ case MIXER_CTL_TYPE_BYTE:
+ printf("%02x", buf[i]);
+ break;
+ default:
+ printf(" unknown");
+ break;
+ };
+ }
+
+ if (print_all) {
+ if (type == MIXER_CTL_TYPE_INT) {
+ min = mixer_ctl_get_range_min(ctl);
+ max = mixer_ctl_get_range_max(ctl);
+ printf(" (range %d->%d)", min, max);
+ }
+ }
+
+ free(buf);
+
+ printf("\n");
+}
+
+static void tinymix_set_byte_ctl(struct mixer_ctl *ctl,
+ char **values, unsigned int num_values)
+{
+ int ret;
+ char *buf;
+ char *end;
+ unsigned int i;
+ long n;
+
+ buf = calloc(1, num_values);
+ if (buf == NULL) {
+ fprintf(stderr, "set_byte_ctl: Failed to alloc mem for bytes %u\n", num_values);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < num_values; i++) {
+ errno = 0;
+ n = strtol(values[i], &end, 0);
+ if (*end) {
+ fprintf(stderr, "%s not an integer\n", values[i]);
+ goto fail;
+ }
+ if (errno) {
+ fprintf(stderr, "strtol: %s: %s\n", values[i],
+ strerror(errno));
+ goto fail;
+ }
+ if (n < 0 || n > 0xff) {
+ fprintf(stderr, "%s should be between [0, 0xff]\n",
+ values[i]);
+ goto fail;
+ }
+ buf[i] = n;
+ }
+
+ ret = mixer_ctl_set_array(ctl, buf, num_values);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to set binary control\n");
+ goto fail;
+ }
+
+ free(buf);
+ return;
+
+fail:
+ free(buf);
+ exit(EXIT_FAILURE);
+}
+
+static int is_int(char *value)
+{
+ char* end;
+ long int result;
+
+ errno = 0;
+ result = strtol(value, &end, 10);
+
+ if (result == LONG_MIN || result == LONG_MAX)
+ return 0;
+
+ return errno == 0 && *end == '\0';
+}
+
+static void tinymix_set_value(struct mixer *mixer, const char *control,
+ char **values, unsigned int num_values)
+{
+ struct mixer_ctl *ctl;
+ enum mixer_ctl_type type;
+ unsigned int num_ctl_values;
+ unsigned int i;
+
+ if (isdigit(control[0]))
+ ctl = mixer_get_ctl(mixer, atoi(control));
+ else
+ ctl = mixer_get_ctl_by_name(mixer, control);
+
+ if (!ctl) {
+ fprintf(stderr, "Invalid mixer control\n");
+ return;
+ }
+
+ type = mixer_ctl_get_type(ctl);
+ num_ctl_values = mixer_ctl_get_num_values(ctl);
+
+ if (type == MIXER_CTL_TYPE_BYTE) {
+ tinymix_set_byte_ctl(ctl, values, num_values);
+ return;
+ }
+
+ if (is_int(values[0])) {
+ if (num_values == 1) {
+ /* Set all values the same */
+ int value = atoi(values[0]);
+
+ for (i = 0; i < num_ctl_values; i++) {
+ if (mixer_ctl_set_value(ctl, i, value)) {
+ fprintf(stderr, "Error: invalid value\n");
+ return;
+ }
+ }
+ } else {
+ /* Set multiple values */
+ if (num_values > num_ctl_values) {
+ fprintf(stderr,
+ "Error: %u values given, but control only takes %u\n",
+ num_values, num_ctl_values);
+ return;
+ }
+ for (i = 0; i < num_values; i++) {
+ if (mixer_ctl_set_value(ctl, i, atoi(values[i]))) {
+ fprintf(stderr, "Error: invalid value for index %u\n", i);
+ return;
+ }
+ }
+ }
+ } else {
+ if (type == MIXER_CTL_TYPE_ENUM) {
+ if (num_values != 1) {
+ fprintf(stderr, "Enclose strings in quotes and try again\n");
+ return;
+ }
+ if (mixer_ctl_set_enum_by_string(ctl, values[0]))
+ fprintf(stderr, "Error: invalid enum value\n");
+ } else {
+ fprintf(stderr, "Error: only enum types can be set with strings\n");
+ }
+ }
+}
+
diff --git a/utils/tinypcminfo.c b/utils/tinypcminfo.c
new file mode 100644
index 0000000..4eb0afa
--- /dev/null
+++ b/utils/tinypcminfo.c
@@ -0,0 +1,203 @@
+/* tinypcminfo.c
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of The Android Open Source Project nor the names of
+** its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+#endif
+
+/* The format_lookup is in order of SNDRV_PCM_FORMAT_##index and
+ * matches the grouping in sound/asound.h. Note this is not
+ * continuous and has an empty gap from (25 - 30).
+ */
+static const char *format_lookup[] = {
+ /*[0] =*/ "S8",
+ "U8",
+ "S16_LE",
+ "S16_BE",
+ "U16_LE",
+ "U16_BE",
+ "S24_LE",
+ "S24_BE",
+ "U24_LE",
+ "U24_BE",
+ "S32_LE",
+ "S32_BE",
+ "U32_LE",
+ "U32_BE",
+ "FLOAT_LE",
+ "FLOAT_BE",
+ "FLOAT64_LE",
+ "FLOAT64_BE",
+ "IEC958_SUBFRAME_LE",
+ "IEC958_SUBFRAME_BE",
+ "MU_LAW",
+ "A_LAW",
+ "IMA_ADPCM",
+ "MPEG",
+ /*[24] =*/ "GSM",
+ [31] = "SPECIAL",
+ "S24_3LE",
+ "S24_3BE",
+ "U24_3LE",
+ "U24_3BE",
+ "S20_3LE",
+ "S20_3BE",
+ "U20_3LE",
+ "U20_3BE",
+ "S18_3LE",
+ "S18_3BE",
+ "U18_3LE",
+ /*[43] =*/ "U18_3BE",
+#if 0
+ /* recent additions, may not be present on local asound.h */
+ "G723_24",
+ "G723_24_1B",
+ "G723_40",
+ "G723_40_1B",
+ "DSD_U8",
+ "DSD_U16_LE",
+#endif
+};
+
+/* Returns a human readable name for the format associated with bit_index,
+ * NULL if bit_index is not known.
+ */
+static inline const char *pcm_get_format_name(unsigned bit_index)
+{
+ return bit_index < ARRAY_SIZE(format_lookup) ? format_lookup[bit_index] : NULL;
+}
+
+int main(int argc, char **argv)
+{
+ unsigned int device = 0;
+ unsigned int card = 0;
+ int i;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s -D card -d device\n", argv[0]);
+ return 1;
+ }
+
+ /* parse command line arguments */
+ argv += 1;
+ while (*argv) {
+ if (strcmp(*argv, "-D") == 0) {
+ argv++;
+ if (*argv)
+ card = atoi(*argv);
+ }
+ if (strcmp(*argv, "-d") == 0) {
+ argv++;
+ if (*argv)
+ device = atoi(*argv);
+ }
+ if (*argv)
+ argv++;
+ }
+
+ printf("Info for card %u, device %u:\n", card, device);
+
+ for (i = 0; i < 2; i++) {
+ struct pcm_params *params;
+ struct pcm_mask *m;
+ unsigned int min;
+ unsigned int max;
+
+ printf("\nPCM %s:\n", i == 0 ? "out" : "in");
+
+ params = pcm_params_get(card, device, i == 0 ? PCM_OUT : PCM_IN);
+ if (params == NULL) {
+ printf("Device does not exist.\n");
+ continue;
+ }
+
+ m = pcm_params_get_mask(params, PCM_PARAM_ACCESS);
+ if (m) { /* bitmask, refer to SNDRV_PCM_ACCESS_*, generally interleaved */
+ printf(" Access:\t%#08x\n", m->bits[0]);
+ }
+ m = pcm_params_get_mask(params, PCM_PARAM_FORMAT);
+ if (m) { /* bitmask, refer to: SNDRV_PCM_FORMAT_* */
+ unsigned j, k, count = 0;
+ const unsigned bitcount = sizeof(m->bits[0]) * 8;
+
+ /* we only check first two format masks (out of 8) - others are zero. */
+ printf(" Format[0]:\t%#08x\n", m->bits[0]);
+ printf(" Format[1]:\t%#08x\n", m->bits[1]);
+
+ /* print friendly format names, if they exist */
+ for (k = 0; k < 2; ++k) {
+ for (j = 0; j < bitcount; ++j) {
+ const char *name;
+
+ if (m->bits[k] & (1 << j)) {
+ name = pcm_get_format_name(j + k*bitcount);
+ if (name) {
+ if (count++ == 0) {
+ printf(" Format Name:\t");
+ } else {
+ printf (", ");
+ }
+ printf("%s", name);
+ }
+ }
+ }
+ }
+ if (count) {
+ printf("\n");
+ }
+ }
+ m = pcm_params_get_mask(params, PCM_PARAM_SUBFORMAT);
+ if (m) { /* bitmask, should be 1: SNDRV_PCM_SUBFORMAT_STD */
+ printf(" Subformat:\t%#08x\n", m->bits[0]);
+ }
+ min = pcm_params_get_min(params, PCM_PARAM_RATE);
+ max = pcm_params_get_max(params, PCM_PARAM_RATE);
+ printf(" Rate:\tmin=%uHz\tmax=%uHz\n", min, max);
+ min = pcm_params_get_min(params, PCM_PARAM_CHANNELS);
+ max = pcm_params_get_max(params, PCM_PARAM_CHANNELS);
+ printf(" Channels:\tmin=%u\t\tmax=%u\n", min, max);
+ min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS);
+ max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS);
+ printf(" Sample bits:\tmin=%u\t\tmax=%u\n", min, max);
+ min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE);
+ max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE);
+ printf(" Period size:\tmin=%u\t\tmax=%u\n", min, max);
+ min = pcm_params_get_min(params, PCM_PARAM_PERIODS);
+ max = pcm_params_get_max(params, PCM_PARAM_PERIODS);
+ printf("Period count:\tmin=%u\t\tmax=%u\n", min, max);
+
+ pcm_params_free(params);
+ }
+
+ return 0;
+}
diff --git a/utils/tinyplay.c b/utils/tinyplay.c
new file mode 100644
index 0000000..920b0e8
--- /dev/null
+++ b/utils/tinyplay.c
@@ -0,0 +1,303 @@
+/* tinyplay.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of The Android Open Source Project nor the names of
+** its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+struct riff_wave_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t wave_id;
+};
+
+struct chunk_header {
+ uint32_t id;
+ uint32_t sz;
+};
+
+struct chunk_fmt {
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate;
+ uint16_t block_align;
+ uint16_t bits_per_sample;
+};
+
+static int close = 0;
+
+void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
+ unsigned int rate, unsigned int bits, unsigned int period_size,
+ unsigned int period_count);
+
+void stream_close(int sig)
+{
+ /* allow the stream to be closed gracefully */
+ signal(sig, SIG_IGN);
+ close = 1;
+}
+
+int main(int argc, char **argv)
+{
+ FILE *file;
+ struct riff_wave_header riff_wave_header;
+ struct chunk_header chunk_header;
+ struct chunk_fmt chunk_fmt;
+ unsigned int device = 0;
+ unsigned int card = 0;
+ unsigned int period_size = 1024;
+ unsigned int period_count = 4;
+ unsigned int channels = 2;
+ unsigned int rate = 48000;
+ unsigned int bits = 16;
+ unsigned int is_raw = 0; /* Default wav file */
+ char *filename;
+ int more_chunks = 1;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage1: %s file.wav [-D card] [-d device] [-p period_size]"
+ " [-n n_periods] \n", argv[0]);
+ fprintf(stderr, "Usage2: %s file.raw [-D card] [-d device] [-p period_size] "
+ "[-n n_periods] [-c channels] [-r rate] [-b bits] -i raw \n", argv[0]);
+ return 1;
+ }
+
+ filename = argv[1];
+ file = fopen(filename, "rb");
+ if (!file) {
+ fprintf(stderr, "Unable to open file '%s'\n", filename);
+ return 1;
+ }
+
+ /* parse command line arguments */
+ argv += 2;
+ while (*argv) {
+ if (strcmp(*argv, "-d") == 0) {
+ argv++;
+ if (*argv)
+ device = atoi(*argv);
+ }
+ if (strcmp(*argv, "-p") == 0) {
+ argv++;
+ if (*argv)
+ period_size = atoi(*argv);
+ }
+ if (strcmp(*argv, "-n") == 0) {
+ argv++;
+ if (*argv)
+ period_count = atoi(*argv);
+ }
+ if (strcmp(*argv, "-D") == 0) {
+ argv++;
+ if (*argv)
+ card = atoi(*argv);
+ }
+ if (strcmp(*argv, "-c") == 0) {
+ argv++;
+ if (*argv)
+ channels = atoi(*argv);
+ }
+ if (strcmp(*argv, "-r") == 0) {
+ argv++;
+ if (*argv)
+ rate = atoi(*argv);
+ }
+ if (strcmp(*argv, "-b") == 0) {
+ argv++;
+ if (*argv)
+ bits = atoi(*argv);
+ }
+ if (strcmp(*argv, "-i") == 0) {
+ argv++;
+ if (*argv) {
+ if (strcasecmp(*argv, "raw") == 0) {
+ is_raw = 1;
+ }
+ }
+ }
+ if (*argv)
+ argv++;
+ }
+
+ if ( !is_raw ) {
+ fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
+ if ((riff_wave_header.riff_id != ID_RIFF) ||
+ (riff_wave_header.wave_id != ID_WAVE)) {
+ fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
+ fclose(file);
+ return 1;
+ }
+ do {
+ fread(&chunk_header, sizeof(chunk_header), 1, file);
+ switch (chunk_header.id) {
+ case ID_FMT:
+ fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
+ /* If the format header is larger, skip the rest */
+ if (chunk_header.sz > sizeof(chunk_fmt))
+ fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
+ break;
+ case ID_DATA:
+ /* Stop looking for chunks */
+ more_chunks = 0;
+ break;
+ default:
+ /* Unknown chunk, skip bytes */
+ fseek(file, chunk_header.sz, SEEK_CUR);
+ }
+ } while (more_chunks);
+ channels = chunk_fmt.num_channels;
+ rate = chunk_fmt.sample_rate;
+ bits = chunk_fmt.bits_per_sample;
+ }
+
+ play_sample(file, card, device, channels, rate, bits, period_size, period_count);
+
+ fclose(file);
+
+ return 0;
+}
+
+int check_param(struct pcm_params *params, unsigned int param, unsigned int value,
+ char *param_name, char *param_unit)
+{
+ unsigned int min;
+ unsigned int max;
+ int is_within_bounds = 1;
+
+ min = pcm_params_get_min(params, param);
+ if (value < min) {
+ fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value,
+ param_unit, min, param_unit);
+ is_within_bounds = 0;
+ }
+
+ max = pcm_params_get_max(params, param);
+ if (value > max) {
+ fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value,
+ param_unit, max, param_unit);
+ is_within_bounds = 0;
+ }
+
+ return is_within_bounds;
+}
+
+int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels,
+ unsigned int rate, unsigned int bits, unsigned int period_size,
+ unsigned int period_count)
+{
+ struct pcm_params *params;
+ int can_play;
+
+ params = pcm_params_get(card, device, PCM_OUT);
+ if (params == NULL) {
+ fprintf(stderr, "Unable to open PCM device %u.\n", device);
+ return 0;
+ }
+
+ can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz");
+ can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels");
+ can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");
+ can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", "Hz");
+ can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", "Hz");
+
+ pcm_params_free(params);
+
+ return can_play;
+}
+
+void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
+ unsigned int rate, unsigned int bits, unsigned int period_size,
+ unsigned int period_count)
+{
+ struct pcm_config config;
+ struct pcm *pcm;
+ char *buffer;
+ int size;
+ int num_read;
+
+ memset(&config, 0, sizeof(config));
+ config.channels = channels;
+ config.rate = rate;
+ config.period_size = period_size;
+ config.period_count = period_count;
+ if (bits == 32)
+ config.format = PCM_FORMAT_S32_LE;
+ else if (bits == 16)
+ config.format = PCM_FORMAT_S16_LE;
+ config.start_threshold = 0;
+ config.stop_threshold = 0;
+ config.silence_threshold = 0;
+
+ if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {
+ return;
+ }
+
+ pcm = pcm_open(card, device, PCM_OUT, &config);
+ if (!pcm || !pcm_is_ready(pcm)) {
+ fprintf(stderr, "Unable to open PCM device %u (%s)\n",
+ device, pcm_get_error(pcm));
+ return;
+ }
+
+ size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
+ buffer = malloc(size);
+ if (!buffer) {
+ fprintf(stderr, "Unable to allocate %d bytes\n", size);
+ free(buffer);
+ pcm_close(pcm);
+ return;
+ }
+
+ printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
+
+ /* catch ctrl-c to shutdown cleanly */
+ signal(SIGINT, stream_close);
+
+ do {
+ num_read = fread(buffer, 1, size, file);
+ if (num_read > 0) {
+ if (pcm_write(pcm, buffer, num_read)) {
+ fprintf(stderr, "Error playing sample\n");
+ break;
+ }
+ }
+ } while (!close && num_read > 0);
+
+ free(buffer);
+ pcm_close(pcm);
+}
+
diff --git a/utils/tinywavinfo.c b/utils/tinywavinfo.c
new file mode 100644
index 0000000..99ee5da
--- /dev/null
+++ b/utils/tinywavinfo.c
@@ -0,0 +1,214 @@
+/* tinywavinfo.c
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of The Android Open Source Project nor the names of
+** its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <math.h>
+#include <malloc.h>
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+struct riff_wave_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t wave_id;
+};
+
+struct chunk_header {
+ uint32_t id;
+ uint32_t sz;
+};
+
+struct chunk_fmt {
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate;
+ uint16_t block_align;
+ uint16_t bits_per_sample;
+};
+
+static int close = 0;
+
+void analyse_sample(FILE *file, unsigned int channels, unsigned int bits,
+ unsigned int data_chunk_size);
+
+void stream_close(int sig)
+{
+ /* allow the stream to be closed gracefully */
+ signal(sig, SIG_IGN);
+ close = 1;
+}
+
+int main(int argc, char **argv)
+{
+ FILE *file;
+ struct riff_wave_header riff_wave_header;
+ struct chunk_header chunk_header;
+ struct chunk_fmt chunk_fmt;
+ char *filename;
+ int more_chunks = 1;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s file.wav \n", argv[0]);
+ return 1;
+ }
+
+ filename = argv[1];
+ file = fopen(filename, "rb");
+ if (!file) {
+ fprintf(stderr, "Unable to open file '%s'\n", filename);
+ return 1;
+ }
+
+ fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
+ if ((riff_wave_header.riff_id != ID_RIFF) ||
+ (riff_wave_header.wave_id != ID_WAVE)) {
+ fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
+ fclose(file);
+ return 1;
+ }
+
+ do {
+ fread(&chunk_header, sizeof(chunk_header), 1, file);
+
+ switch (chunk_header.id) {
+ case ID_FMT:
+ fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
+ /* If the format header is larger, skip the rest */
+ if (chunk_header.sz > sizeof(chunk_fmt))
+ fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
+ break;
+ case ID_DATA:
+ /* Stop looking for chunks */
+ more_chunks = 0;
+ break;
+ default:
+ /* Unknown chunk, skip bytes */
+ fseek(file, chunk_header.sz, SEEK_CUR);
+ }
+ } while (more_chunks);
+
+ printf("Input File : %s \n", filename);
+ printf("Channels : %u \n", chunk_fmt.num_channels);
+ printf("Sample Rate : %u \n", chunk_fmt.sample_rate);
+ printf("Bits per sample : %u \n\n", chunk_fmt.bits_per_sample);
+
+ analyse_sample(file, chunk_fmt.num_channels, chunk_fmt.bits_per_sample,
+ chunk_header.sz);
+
+ fclose(file);
+
+ return 0;
+}
+
+void analyse_sample(FILE *file, unsigned int channels, unsigned int bits,
+ unsigned int data_chunk_size)
+{
+ void *buffer;
+ int size;
+ int num_read;
+ int i;
+ unsigned int ch;
+ int frame_size = 1024;
+ unsigned int byte_align = 0;
+ float *power;
+ int total_sample_per_channel;
+ float normalization_factor;
+
+ if (bits == 32)
+ byte_align = 4;
+ else if (bits == 16)
+ byte_align = 2;
+
+ normalization_factor = (float)pow(2.0, (bits-1));
+
+ size = channels * byte_align * frame_size;
+ buffer = memalign(byte_align, size);
+
+ if (!buffer) {
+ fprintf(stderr, "Unable to allocate %d bytes\n", size);
+ free(buffer);
+ return;
+ }
+
+ power = (float *) calloc(channels, sizeof(float));
+
+ total_sample_per_channel = data_chunk_size / (channels * byte_align);
+
+ /* catch ctrl-c to shutdown cleanly */
+ signal(SIGINT, stream_close);
+
+ do {
+ num_read = fread(buffer, 1, size, file);
+ if (num_read > 0) {
+ if (2 == byte_align) {
+ short *buffer_ptr = (short *)buffer;
+ for (i = 0; i < num_read; i += channels) {
+ for (ch = 0; ch < channels; ch++) {
+ int temp = *buffer_ptr++;
+ /* Signal Normalization */
+ float f = (float) temp / normalization_factor;
+ *(power + ch) += (float) (f * f);
+ }
+ }
+ }
+ if (4 == byte_align) {
+ int *buffer_ptr = (int *)buffer;
+ for (i = 0; i < num_read; i += channels) {
+ for (ch = 0; ch < channels; ch++) {
+ int temp = *buffer_ptr++;
+ /* Signal Normalization */
+ float f = (float) temp / normalization_factor;
+ *(power + ch) += (float) (f * f);
+ }
+ }
+ }
+ }
+ }while (!close && num_read > 0);
+
+ for (ch = 0; ch < channels; ch++) {
+ float average_power = 10 * log10((*(power + ch)) / total_sample_per_channel);
+ if(isinf (average_power)) {
+ printf("Channel [%2u] Average Power : NO signal or ZERO signal\n", ch);
+ } else {
+ printf("Channel [%2u] Average Power : %.2f dB\n", ch, average_power);
+ }
+ }
+
+ free(buffer);
+ free(power);
+
+}
+