From b88fd4ff9c6ae7af26c81b6fee069dc90ef2707d Mon Sep 17 00:00:00 2001 From: Taylor Holberton Date: Sat, 1 Oct 2016 12:33:46 -0400 Subject: put programs into utils dir --- Makefile | 19 +-- tinycap.c | 262 ----------------------------------------- tinymix.c | 329 ---------------------------------------------------- tinypcminfo.c | 203 -------------------------------- tinyplay.c | 303 ----------------------------------------------- tinywavinfo.c | 214 ---------------------------------- utils/Makefile | 34 ++++++ utils/tinycap.c | 264 +++++++++++++++++++++++++++++++++++++++++ utils/tinymix.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++++++++ utils/tinypcminfo.c | 203 ++++++++++++++++++++++++++++++++ utils/tinyplay.c | 303 +++++++++++++++++++++++++++++++++++++++++++++++ utils/tinywavinfo.c | 214 ++++++++++++++++++++++++++++++++++ 12 files changed, 1351 insertions(+), 1326 deletions(-) delete mode 100644 tinycap.c delete mode 100644 tinymix.c delete mode 100644 tinypcminfo.c delete mode 100644 tinyplay.c delete mode 100644 tinywavinfo.c create mode 100644 utils/Makefile create mode 100644 utils/tinycap.c create mode 100644 utils/tinymix.c create mode 100644 utils/tinypcminfo.c create mode 100644 utils/tinyplay.c create mode 100644 utils/tinywavinfo.c diff --git a/Makefile b/Makefile index d895384..55bd9e6 100644 --- a/Makefile +++ b/Makefile @@ -8,19 +8,8 @@ CROSS_COMPILE = PREFIX = /usr/local .PHONY: all -all: $(LIB) $(SHLIB) tinyplay tinycap tinymix tinypcminfo - -tinyplay: $(SHLIB) tinyplay.o - $(CROSS_COMPILE)$(CC) $(LDFLAGS) tinyplay.o -L. -ltinyalsa -o tinyplay - -tinycap: $(SHLIB) tinycap.o - $(CROSS_COMPILE)$(CC) $(LDFLAGS) tinycap.o -L. -ltinyalsa -o tinycap - -tinymix: $(SHLIB) tinymix.o - $(CROSS_COMPILE)$(CC) $(LDFLAGS) tinymix.o -L. -ltinyalsa -o tinymix - -tinypcminfo: $(SHLIB) tinypcminfo.o - $(CROSS_COMPILE)$(CC) $(LDFLAGS) tinypcminfo.o -L. -ltinyalsa -o tinypcminfo +all: $(LIB) $(SHLIB) + $(MAKE) -C utils $(SHLIB): $(OBJECTS) $(CROSS_COMPILE)$(CC) $(LDFLAGS) -shared $(OBJECTS) -o $(SHLIB) @@ -33,8 +22,8 @@ $(LIB): $(OBJECTS) .PHONY: clean clean: - -rm $(LIB) $(SHLIB) $(OBJECTS) tinyplay.o tinyplay tinycap.o tinycap \ - tinymix.o tinymix tinypcminfo.o tinypcminfo + -rm $(LIB) $(SHLIB) $(OBJECTS) + $(MAKE) -C utils clean .PHONY: install install: $(LIB) $(SHLIB) diff --git a/tinycap.c b/tinycap.c deleted file mode 100644 index 4e0e651..0000000 --- a/tinycap.c +++ /dev/null @@ -1,262 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include - -#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) -{ - 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/tinymix.c b/tinymix.c deleted file mode 100644 index 5966df9..0000000 --- a/tinymix.c +++ /dev/null @@ -1,329 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#include -#include - -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/tinypcminfo.c b/tinypcminfo.c deleted file mode 100644 index 4eb0afa..0000000 --- a/tinypcminfo.c +++ /dev/null @@ -1,203 +0,0 @@ -/* 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 -#include -#include -#include - -#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/tinyplay.c b/tinyplay.c deleted file mode 100644 index 920b0e8..0000000 --- a/tinyplay.c +++ /dev/null @@ -1,303 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include - -#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/tinywavinfo.c b/tinywavinfo.c deleted file mode 100644 index 99ee5da..0000000 --- a/tinywavinfo.c +++ /dev/null @@ -1,214 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#include - -#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); - -} - 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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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); + +} + -- cgit v1.2.3