diff options
-rw-r--r-- | Android.mk | 18 | ||||
-rw-r--r-- | include/tinyalsa/asoundlib.h | 1 | ||||
-rw-r--r-- | mixer.c | 85 | ||||
-rw-r--r-- | pcm.c | 2 | ||||
-rw-r--r-- | tinycap.c | 1 | ||||
-rw-r--r-- | tinymix.c | 47 | ||||
-rw-r--r-- | tinypcminfo.c | 2 | ||||
-rw-r--r-- | tinyplay.c | 1 | ||||
-rw-r--r-- | tinywavinfo.c | 214 |
9 files changed, 333 insertions, 38 deletions
@@ -21,6 +21,24 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_C_INCLUDES:= external/tinyalsa/include +LOCAL_SRC_FILES:= tinywavinfo.c +LOCAL_MODULE := tinywavinfo +LOCAL_SHARED_LIBRARIES:= libcutils libutils libm +LOCAL_MODULE_TAGS := optional +include $(BUILD_EXECUTABLE) + +ifeq ($(HOST_OS), linux) +include $(CLEAR_VARS) +LOCAL_C_INCLUDES:= external/tinyalsa/include +LOCAL_SRC_FILES:= tinywavinfo.c +LOCAL_MODULE := tinywavinfo +LOCAL_STATIC_LIBRARIES:= libcutils libutils +LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_EXECUTABLE) +endif + +include $(CLEAR_VARS) +LOCAL_C_INCLUDES:= external/tinyalsa/include LOCAL_SRC_FILES:= tinycap.c LOCAL_MODULE := tinycap LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinyalsa diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h index 045c8b5..8fb7c95 100644 --- a/include/tinyalsa/asoundlib.h +++ b/include/tinyalsa/asoundlib.h @@ -231,6 +231,7 @@ struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id); struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name); /* Get info about mixer controls */ +unsigned int mixer_ctl_get_id(struct mixer_ctl *ctl); const char *mixer_ctl_get_name(struct mixer_ctl *ctl); enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl); const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl); @@ -33,6 +33,7 @@ #include <fcntl.h> #include <errno.h> #include <ctype.h> +#include <limits.h> #include <sys/ioctl.h> @@ -209,6 +210,17 @@ void mixer_ctl_update(struct mixer_ctl *ctl) ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info); } +unsigned int mixer_ctl_get_id(struct mixer_ctl *ctl) +{ + if (!ctl) + return UINT_MAX; + + /* numid values start at 1, return a 0-base value that + * can be passed to mixer_get_ctl() + */ + return ctl->info->id.numid - 1; +} + const char *mixer_ctl_get_name(struct mixer_ctl *ctl) { if (!ctl) @@ -259,14 +271,11 @@ unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl) static int percent_to_int(struct snd_ctl_elem_info *ei, int percent) { - int range; - - if (percent > 100) - percent = 100; - else if (percent < 0) - percent = 0; + if ((percent > 100) || (percent < 0)) { + return -EINVAL; + } - range = (ei->value.integer.max - ei->value.integer.min); + int range = (ei->value.integer.max - ei->value.integer.min); return ei->value.integer.min + (range * percent) / 100; } @@ -334,7 +343,7 @@ int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id) int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count) { struct snd_ctl_elem_value ev; - int ret; + int ret = 0; size_t size; void *source; @@ -344,21 +353,41 @@ int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count) memset(&ev, 0, sizeof(ev)); ev.id.numid = ctl->info->id.numid; - ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); - if (ret < 0) - return ret; - switch (ctl->info->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: case SNDRV_CTL_ELEM_TYPE_INTEGER: + ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); + if (ret < 0) + return ret; size = sizeof(ev.value.integer.value[0]); source = ev.value.integer.value; break; case SNDRV_CTL_ELEM_TYPE_BYTES: - size = sizeof(ev.value.bytes.data[0]); - source = ev.value.bytes.data; - break; + /* check if this is new bytes TLV */ + if (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { + struct snd_ctl_tlv *tlv; + int ret; + + tlv = calloc(1, sizeof(*tlv) + count); + tlv->numid = ctl->info->id.numid; + tlv->length = count; + ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv); + + source = tlv->tlv; + memcpy(array, source, count); + + free(tlv); + + return ret; + } else { + ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); + if (ret < 0) + return ret; + size = sizeof(ev.value.bytes.data[0]); + source = ev.value.bytes.data; + break; + } default: return -EINVAL; @@ -383,9 +412,6 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value) if (ret < 0) return ret; - if (value > mixer_ctl_get_range_max(ctl)) - value = mixer_ctl_get_range_max(ctl); - switch (ctl->info->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: ev.value.integer.value[id] = !!value; @@ -404,6 +430,10 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value) ev.value.enumerated.item[id] = value; break; + case SNDRV_CTL_ELEM_TYPE_BYTES: + ev.value.bytes.data[id] = value; + break; + default: return -EINVAL; } @@ -431,8 +461,23 @@ int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count) break; case SNDRV_CTL_ELEM_TYPE_BYTES: - size = sizeof(ev.value.bytes.data[0]); - dest = ev.value.bytes.data; + /* check if this is new bytes TLV */ + if (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { + struct snd_ctl_tlv *tlv; + int ret = 0; + tlv = calloc(1, sizeof(*tlv) + count); + tlv->numid = ctl->info->id.numid; + tlv->length = count; + memcpy(tlv->tlv, array, count); + + ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv); + free(tlv); + + return ret; + } else { + size = sizeof(ev.value.bytes.data[0]); + dest = ev.value.bytes.data; + } break; default: @@ -835,7 +835,7 @@ static inline int pcm_mmap_playback_avail(struct pcm *pcm) if (avail < 0) avail += pcm->boundary; - else if (avail > (int)pcm->boundary) + else if (avail >= (int)pcm->boundary) avail -= pcm->boundary; return avail; @@ -206,6 +206,7 @@ unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device, unsigned int size; unsigned int bytes_read = 0; + memset(&config, 0, sizeof(config)); config.channels = channels; config.rate = rate; config.period_size = period_size; @@ -131,7 +131,7 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control, unsigned int i; int min, max; int ret; - char buf[512] = { 0 }; + char *buf = NULL; size_t len; if (isdigit(control[0])) @@ -148,14 +148,18 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control, num_values = mixer_ctl_get_num_values(ctl); if (type == MIXER_CTL_TYPE_BYTE) { - len = num_values; - if (len > sizeof(buf)) { - fprintf(stderr, "Truncating get to %zu bytes\n", sizeof(buf)); - len = sizeof(buf); + + buf = calloc(1, num_values); + if (buf == NULL) { + fprintf(stderr, "Failed to alloc mem for bytes %d\n", num_values); + return; } + + len = num_values; ret = mixer_ctl_get_array(ctl, buf, len); if (ret < 0) { fprintf(stderr, "Failed to mixer_ctl_get_array\n"); + free(buf); return; } } @@ -191,21 +195,25 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control, printf(" (range %d->%d)", min, max); } } + + free(buf); + printf("\n"); } -static void tinymix_set_byte_ctl(struct mixer_ctl *ctl, const char *control, +static void tinymix_set_byte_ctl(struct mixer_ctl *ctl, char **values, unsigned int num_values) { int ret; - char buf[512] = { 0 }; + char *buf; char *end; - int i; + unsigned int i; long n; - if (num_values > sizeof(buf)) { - fprintf(stderr, "Truncating set to %zu bytes\n", sizeof(buf)); - num_values = sizeof(buf); + buf = calloc(1, num_values); + if (buf == NULL) { + fprintf(stderr, "set_byte_ctl: Failed to alloc mem for bytes %d\n", num_values); + exit(EXIT_FAILURE); } for (i = 0; i < num_values; i++) { @@ -213,17 +221,17 @@ static void tinymix_set_byte_ctl(struct mixer_ctl *ctl, const char *control, n = strtol(values[i], &end, 0); if (*end) { fprintf(stderr, "%s not an integer\n", values[i]); - exit(EXIT_FAILURE); + goto fail; } if (errno) { fprintf(stderr, "strtol: %s: %s\n", values[i], strerror(errno)); - exit(EXIT_FAILURE); + goto fail; } if (n < 0 || n > 0xff) { fprintf(stderr, "%s should be between [0, 0xff]\n", values[i]); - exit(EXIT_FAILURE); + goto fail; } buf[i] = n; } @@ -231,8 +239,15 @@ static void tinymix_set_byte_ctl(struct mixer_ctl *ctl, const char *control, ret = mixer_ctl_set_array(ctl, buf, num_values); if (ret < 0) { fprintf(stderr, "Failed to set binary control\n"); - exit(EXIT_FAILURE); + goto fail; } + + free(buf); + return; + +fail: + free(buf); + exit(EXIT_FAILURE); } static int is_int(char *value) @@ -271,7 +286,7 @@ static void tinymix_set_value(struct mixer *mixer, const char *control, num_ctl_values = mixer_ctl_get_num_values(ctl); if (type == MIXER_CTL_TYPE_BYTE) { - tinymix_set_byte_ctl(ctl, control, values, num_values); + tinymix_set_byte_ctl(ctl, values, num_values); return; } diff --git a/tinypcminfo.c b/tinypcminfo.c index b2d11bc..99eec34 100644 --- a/tinypcminfo.c +++ b/tinypcminfo.c @@ -92,7 +92,7 @@ static const char *format_lookup[] = { /* Returns a human readable name for the format associated with bit_index, * NULL if bit_index is not known. */ -inline const char *pcm_get_format_name(unsigned bit_index) +static inline const char *pcm_get_format_name(unsigned bit_index) { return bit_index < ARRAY_SIZE(format_lookup) ? format_lookup[bit_index] : NULL; } @@ -218,6 +218,7 @@ void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned in int size; int num_read; + memset(&config, 0, sizeof(config)); config.channels = channels; config.rate = rate; config.period_size = period_size; diff --git a/tinywavinfo.c b/tinywavinfo.c new file mode 100644 index 0000000..9d15668 --- /dev/null +++ b/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 [%2d] Average Power : NO signal or ZERO signal\n", ch); + } else { + printf("Channel [%2d] Average Power : %.2f dB\n", ch, average_power); + } + } + + free(buffer); + free(power); + +} + |