aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk18
-rw-r--r--include/tinyalsa/asoundlib.h1
-rw-r--r--mixer.c85
-rw-r--r--pcm.c2
-rw-r--r--tinycap.c1
-rw-r--r--tinymix.c47
-rw-r--r--tinypcminfo.c2
-rw-r--r--tinyplay.c1
-rw-r--r--tinywavinfo.c214
9 files changed, 333 insertions, 38 deletions
diff --git a/Android.mk b/Android.mk
index 3ebd972..0d58556 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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);
diff --git a/mixer.c b/mixer.c
index b6c854f..113f11e 100644
--- a/mixer.c
+++ b/mixer.c
@@ -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:
diff --git a/pcm.c b/pcm.c
index d58d6a3..2c1308d 100644
--- a/pcm.c
+++ b/pcm.c
@@ -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;
diff --git a/tinycap.c b/tinycap.c
index b9ae251..180a2dd 100644
--- a/tinycap.c
+++ b/tinycap.c
@@ -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;
diff --git a/tinymix.c b/tinymix.c
index fa3defc..71dd206 100644
--- a/tinymix.c
+++ b/tinymix.c
@@ -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;
}
diff --git a/tinyplay.c b/tinyplay.c
index f4fac9f..88c54ae 100644
--- a/tinyplay.c
+++ b/tinyplay.c
@@ -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);
+
+}
+