diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/Makefile | 34 | ||||
-rw-r--r-- | utils/tinycap.c | 264 | ||||
-rw-r--r-- | utils/tinymix.c | 329 | ||||
-rw-r--r-- | utils/tinypcminfo.c | 203 | ||||
-rw-r--r-- | utils/tinyplay.c | 303 | ||||
-rw-r--r-- | utils/tinywavinfo.c | 214 |
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); + +} + |