From d2cb5030091760256fcc8e755e686aab746b7697 Mon Sep 17 00:00:00 2001 From: Simon Wilson Date: Sat, 4 Jun 2011 00:57:17 -0700 Subject: Improve mixer support - support get/set of multiple values - add parameter checking to functions - add api function to get control type - add tinymix utility to list mixer controls --- Android.mk | 9 +++ Makefile | 5 +- include/tinyalsa/asoundlib.h | 32 +++++++-- mixer.c | 150 +++++++++++++++++++++++++++++-------------- tinymix.c | 61 ++++++++++++++++++ 5 files changed, 202 insertions(+), 55 deletions(-) create mode 100644 tinymix.c diff --git a/Android.mk b/Android.mk index d812319..9dbef10 100644 --- a/Android.mk +++ b/Android.mk @@ -17,3 +17,12 @@ LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinyalsa LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_C_INCLUDES:= external/tinyalsa/include +LOCAL_SRC_FILES:= tinymix.c +LOCAL_MODULE := tinymix +LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinyalsa +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) diff --git a/Makefile b/Makefile index ac40b6b..2013d59 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,14 @@ INC = include OBJECTS = mixer.o pcm.o LIB = libtinyalsa.so -all: $(LIB) tinyplay +all: $(LIB) tinyplay tinymix tinyplay: $(LIB) tinyplay.o gcc tinyplay.o -L. -ltinyalsa -o tinyplay +tinymix: $(LIB) tinymix.o + gcc tinymix.o -L. -ltinyalsa -o tinymix + $(LIB): $(OBJECTS) gcc -shared $(OBJECTS) -o $(LIB) diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h index f9920ce..34e5f8c 100644 --- a/include/tinyalsa/asoundlib.h +++ b/include/tinyalsa/asoundlib.h @@ -56,6 +56,19 @@ struct pcm_config { enum pcm_format format; }; +/* Mixer control types */ +enum mixer_ctl_type { + MIXER_CTL_TYPE_BOOL, + MIXER_CTL_TYPE_INT, + MIXER_CTL_TYPE_ENUM, + MIXER_CTL_TYPE_BYTE, + MIXER_CTL_TYPE_IEC958, + MIXER_CTL_TYPE_INT64, + MIXER_CTL_TYPE_UNKNOWN, + + MIXER_CTL_TYPE_MAX, +}; + /* Open and close a stream */ struct pcm *pcm_open(unsigned int card, unsigned int device, unsigned int flags, struct pcm_config *config); @@ -97,20 +110,25 @@ struct mixer *mixer_open(unsigned int card); void mixer_close(struct mixer *mixer); /* Obtain mixer controls */ -int mixer_get_num_ctls(struct mixer *mixer); +unsigned int mixer_get_num_ctls(struct mixer *mixer); 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 */ +int mixer_ctl_get_name(struct mixer_ctl *ctl, char *name, unsigned int size); +enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl); +const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl); +unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl); + /* Set and get mixer controls */ -int mixer_ctl_get_percent(struct mixer_ctl *ctl); -int mixer_ctl_set_percent(struct mixer_ctl *ctl, int percent); +int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id); +int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent); -int mixer_ctl_get_int(struct mixer_ctl *ctl); -int mixer_ctl_set_int(struct mixer_ctl *ctl, int); -int mixer_ctl_get_step(struct mixer_ctl *ctl); +int mixer_ctl_get_int(struct mixer_ctl *ctl, unsigned int id); +int mixer_ctl_set_int(struct mixer_ctl *ctl, unsigned int id, int value); int mixer_ctl_get_enum(struct mixer_ctl *ctl, const char *string, unsigned int size); -int mixer_ctl_set_enum(struct mixer_ctl *ctl, unsigned int id); +int mixer_ctl_set_enum(struct mixer_ctl *ctl, unsigned int value); int mixer_ctl_set_enum_by_name(struct mixer_ctl *ctl, const char *string); #endif diff --git a/mixer.c b/mixer.c index 97f4beb..c2fd78c 100644 --- a/mixer.c +++ b/mixer.c @@ -42,35 +42,6 @@ #include -static const char *elem_iface_name(snd_ctl_elem_iface_t n) -{ - switch (n) { - case SNDRV_CTL_ELEM_IFACE_CARD: return "CARD"; - case SNDRV_CTL_ELEM_IFACE_HWDEP: return "HWDEP"; - case SNDRV_CTL_ELEM_IFACE_MIXER: return "MIXER"; - case SNDRV_CTL_ELEM_IFACE_PCM: return "PCM"; - case SNDRV_CTL_ELEM_IFACE_RAWMIDI: return "MIDI"; - case SNDRV_CTL_ELEM_IFACE_TIMER: return "TIMER"; - case SNDRV_CTL_ELEM_IFACE_SEQUENCER: return "SEQ"; - default: return "???"; - } -} - -static const char *elem_type_name(snd_ctl_elem_type_t n) -{ - switch (n) { - case SNDRV_CTL_ELEM_TYPE_NONE: return "NONE"; - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL"; - case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT32"; - case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM"; - case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTES"; - case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958"; - case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64"; - default: return "???"; - } -} - - struct mixer_ctl { struct mixer *mixer; struct snd_ctl_elem_info *info; @@ -88,6 +59,11 @@ void mixer_close(struct mixer *mixer) { unsigned int n,m; + if (!mixer) { + errno = EINVAL; + return; + } + if (mixer->fd >= 0) close(mixer->fd); @@ -189,13 +165,23 @@ fail: return 0; } -int mixer_get_num_ctls(struct mixer *mixer) +unsigned int mixer_get_num_ctls(struct mixer *mixer) { + if (!mixer) { + errno = EINVAL; + return -1; + } + return mixer->count; } struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id) { + if (!mixer) { + errno = EINVAL; + return NULL; + } + if (id < mixer->count) return mixer->ctl + id; @@ -205,6 +191,12 @@ 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) { unsigned int n; + + if (!mixer) { + errno = EINVAL; + return NULL; + } + for (n = 0; n < mixer->count; n++) if (!strcmp(name, (char*) mixer->info[n].id.name)) return mixer->ctl + n; @@ -212,6 +204,63 @@ struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name) return NULL; } +int mixer_ctl_get_name(struct mixer_ctl *ctl, char *name, unsigned int size) +{ + if (!ctl || !name || (size == 0)) { + errno = EINVAL; + return -1; + } + + strncpy(name, (char *)ctl->info->id.name, size); + return 0; +} + +enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl) +{ + if (!ctl) { + errno = EINVAL; + return MIXER_CTL_TYPE_UNKNOWN; + } + + switch (ctl->info->type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL; + case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM; + case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE; + case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64; + default: return MIXER_CTL_TYPE_UNKNOWN; + }; +} + +const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl) +{ + if (!ctl) { + errno = EINVAL; + return ""; + } + + switch (ctl->info->type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL"; + case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT"; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM"; + case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE"; + case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958"; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64"; + default: return "Unknown"; + }; +} + +unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl) +{ + if (!ctl) { + errno = EINVAL; + return 0; + } + + return ctl->info->count; +} + static int percent_to_int(struct snd_ctl_elem_info *ei, int percent) { int range; @@ -236,30 +285,35 @@ static int int_to_percent(struct snd_ctl_elem_info *ei, int value) return ((value - ei->value.integer.min) / range) * 100; } -int mixer_ctl_get_percent(struct mixer_ctl *ctl) +int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id) { - if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER) { + if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) { errno = EINVAL; return -1; } - return int_to_percent(ctl->info, mixer_ctl_get_int(ctl)); + return int_to_percent(ctl->info, mixer_ctl_get_int(ctl, id)); } -int mixer_ctl_set_percent(struct mixer_ctl *ctl, int percent) +int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent) { - if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER) { + if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) { errno = EINVAL; return -1; } - return mixer_ctl_set_int(ctl, percent_to_int(ctl->info, percent)); + return mixer_ctl_set_int(ctl, id, percent_to_int(ctl->info, percent)); } -int mixer_ctl_get_int(struct mixer_ctl *ctl) +int mixer_ctl_get_int(struct mixer_ctl *ctl, unsigned int id) { struct snd_ctl_elem_value ev; + if (!ctl || (id >= ctl->info->count)) { + errno = EINVAL; + return -1; + } + memset(&ev, 0, sizeof(ev)); ev.id.numid = ctl->info->id.numid; if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev)) @@ -267,11 +321,11 @@ int mixer_ctl_get_int(struct mixer_ctl *ctl) switch (ctl->info->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - return !!ev.value.integer.value[0]; /* TODO: handle multiple return values */ + return !!ev.value.integer.value[id]; break; case SNDRV_CTL_ELEM_TYPE_INTEGER: - return ev.value.integer.value[0]; /* TODO: handle multiple return values */ + return ev.value.integer.value[id]; break; default: @@ -282,23 +336,25 @@ int mixer_ctl_get_int(struct mixer_ctl *ctl) return 0; } -int mixer_ctl_set_int(struct mixer_ctl *ctl, int value) +int mixer_ctl_set_int(struct mixer_ctl *ctl, unsigned int id, int value) { struct snd_ctl_elem_value ev; - unsigned int n; + + if (!ctl || (id >= ctl->info->count)) { + errno = EINVAL; + return -1; + } memset(&ev, 0, sizeof(ev)); ev.id.numid = ctl->info->id.numid; switch (ctl->info->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - for (n = 0; n < ctl->info->count; n++) - ev.value.integer.value[n] = !!value; + ev.value.integer.value[id] = !!value; break; case SNDRV_CTL_ELEM_TYPE_INTEGER: - for (n = 0; n < ctl->info->count; n++) - ev.value.integer.value[n] = value; + ev.value.integer.value[id] = value; break; default: @@ -313,7 +369,7 @@ int mixer_ctl_set_enum(struct mixer_ctl *ctl, unsigned int id) { struct snd_ctl_elem_value ev; - if ((ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) || + if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) || (id >= ctl->info->value.enumerated.items)) { errno = EINVAL; return -1; @@ -332,7 +388,7 @@ int mixer_ctl_set_enum_by_name(struct mixer_ctl *ctl, const char *string) unsigned int n, max; struct snd_ctl_elem_value ev; - if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) { + if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED)) { errno = EINVAL; return -1; } diff --git a/tinymix.c b/tinymix.c new file mode 100644 index 0000000..73694fd --- /dev/null +++ b/tinymix.c @@ -0,0 +1,61 @@ +/* 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 + +int main(int argc, char **argv) +{ + struct mixer *mixer; + struct mixer_ctl *ctl; + const char *type; + unsigned int num_ctls, num_values; + char buffer[256]; + unsigned int i; + + mixer = mixer_open(0); + num_ctls = mixer_get_num_ctls(mixer); + + printf("Number of controls: %d\n", num_ctls); + + printf("ctl\ttype\tnum\tname\n"); + for (i = 0; i < num_ctls; i++) { + ctl = mixer_get_ctl(mixer, i); + + mixer_ctl_get_name(ctl, buffer, sizeof(buffer)); + type = mixer_ctl_get_type_string(ctl); + num_values = mixer_ctl_get_num_values(ctl); + + printf("%d\t%s\t%d\t%s\n", i, type, num_values, buffer); + } + + mixer_close(mixer); + + return 0; +} + -- cgit v1.2.3