From 9b6df1cf64ea7ccb329b4922d138c1f36ace00c0 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 4 Nov 2010 19:58:44 +0100 Subject: control: add ASCII parsers from amixer Signed-off-by: Jaroslav Kysela --- include/control.h | 7 + src/control/Makefile.am | 3 +- src/control/ctlparse.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 360 insertions(+), 1 deletion(-) create mode 100644 src/control/ctlparse.c diff --git a/include/control.h b/include/control.h index 3d6b0a5f..e8f38bb4 100644 --- a/include/control.h +++ b/include/control.h @@ -284,6 +284,13 @@ unsigned int snd_ctl_event_elem_get_index(const snd_ctl_event_t *obj); int snd_ctl_elem_list_alloc_space(snd_ctl_elem_list_t *obj, unsigned int entries); void snd_ctl_elem_list_free_space(snd_ctl_elem_list_t *obj); +char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id); +int snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str); +int snd_ctl_ascii_value_parse(snd_ctl_t *handle, + snd_ctl_elem_value_t *dst, + snd_ctl_elem_info_t *info, + const char *value); + size_t snd_ctl_elem_id_sizeof(void); /** \hideinitializer * \brief allocate an invalid #snd_ctl_elem_id_t using standard alloca diff --git a/src/control/Makefile.am b/src/control/Makefile.am index d4c50c01..8076c732 100644 --- a/src/control/Makefile.am +++ b/src/control/Makefile.am @@ -1,7 +1,8 @@ EXTRA_LTLIBRARIES = libcontrol.la libcontrol_la_SOURCES = cards.c tlv.c namehint.c hcontrol.c \ - control.c control_hw.c setup.c control_symbols.c + control.c control_hw.c setup.c ctlparse.c \ + control_symbols.c if BUILD_CTL_PLUGIN_SHM libcontrol_la_SOURCES += control_shm.c endif diff --git a/src/control/ctlparse.c b/src/control/ctlparse.c new file mode 100644 index 00000000..c8300f3b --- /dev/null +++ b/src/control/ctlparse.c @@ -0,0 +1,351 @@ +/** + * \file control/control.c + * \brief CTL interface - parse ASCII identifiers and values + * \author Jaroslav Kysela + * \date 2010 + */ +/* + * Control Interface - ASCII parser + * Copyright (c) 2010 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "control_local.h" + +/* Function to convert from percentage to volume. val = percentage */ + +#define convert_prange1(val, min, max) \ + ceil((val) * ((max) - (min)) * 0.01 + (min)) + +#define check_range(val, min, max) \ + ((val < min) ? (min) : ((val > max) ? (max) : (val))) + +static long get_integer(const char **ptr, long min, long max) +{ + long val = min; + char *p = (char *)*ptr, *s; + + if (*p == ':') + p++; + if (*p == '\0' || (!isdigit(*p) && *p != '-')) + goto out; + + s = p; + val = strtol(s, &p, 10); + if (*p == '.') { + p++; + strtol(p, &p, 10); + } + if (*p == '%') { + val = (long)convert_prange1(strtod(s, NULL), min, max); + p++; + } + val = check_range(val, min, max); + if (*p == ',') + p++; + out: + *ptr = p; + return val; +} + +static long long get_integer64(const char **ptr, long long min, long long max) +{ + long long val = min; + char *p = (char *)*ptr, *s; + + if (*p == ':') + p++; + if (*p == '\0' || (!isdigit(*p) && *p != '-')) + goto out; + + s = p; + val = strtol(s, &p, 10); + if (*p == '.') { + p++; + strtol(p, &p, 10); + } + if (*p == '%') { + val = (long long)convert_prange1(strtod(s, NULL), min, max); + p++; + } + val = check_range(val, min, max); + if (*p == ',') + p++; + out: + *ptr = p; + return val; +} + +/** + * \brief return ASCII CTL element identifier name + * \param id CTL identifier + * \return ascii identifier of CTL element + * + * The string is allocated using strdup(). + */ +char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id) +{ + unsigned int index, device, subdevice; + char buf[256], buf1[32]; + + snprintf(buf, sizeof(buf), "numid=%u,iface=%s,name='%s'", + snd_ctl_elem_id_get_numid(id), + snd_ctl_elem_iface_name( + snd_ctl_elem_id_get_interface(id)), + snd_ctl_elem_id_get_name(id)); + buf[sizeof(buf)-1] = '\0'; + index = snd_ctl_elem_id_get_index(id); + device = snd_ctl_elem_id_get_device(id); + subdevice = snd_ctl_elem_id_get_subdevice(id); + if (index) { + snprintf(buf1, sizeof(buf1), ",index=%i", index); + if (strlen(buf) + strlen(buf1) < sizeof(buf)) + strcat(buf, buf1); + } + if (device) { + snprintf(buf1, sizeof(buf1), ",device=%i", device); + if (strlen(buf) + strlen(buf1) < sizeof(buf)) + strcat(buf, buf1); + } + if (subdevice) { + snprintf(buf1, sizeof(buf1), ",subdevice=%i", subdevice); + if (strlen(buf) + strlen(buf1) < sizeof(buf)) + strcat(buf, buf1); + } + return strdup(buf); +} + +/** + * \brief parse ASCII string as CTL element identifier + * \param dst destination CTL identifier + * \param str source ASCII string + * \return zero on success, otherwise a negative error code + */ +int snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str) +{ + int c, size, numid; + char *ptr; + + while (*str == ' ' || *str == '\t') + str++; + if (!(*str)) + return -EINVAL; + snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_MIXER); /* default */ + while (*str) { + if (!strncasecmp(str, "numid=", 6)) { + str += 6; + numid = atoi(str); + if (numid <= 0) { + fprintf(stderr, "amixer: Invalid numid %d\n", numid); + return -EINVAL; + } + snd_ctl_elem_id_set_numid(dst, atoi(str)); + while (isdigit(*str)) + str++; + } else if (!strncasecmp(str, "iface=", 6)) { + str += 6; + if (!strncasecmp(str, "card", 4)) { + snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_CARD); + str += 4; + } else if (!strncasecmp(str, "mixer", 5)) { + snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_MIXER); + str += 5; + } else if (!strncasecmp(str, "pcm", 3)) { + snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_PCM); + str += 3; + } else if (!strncasecmp(str, "rawmidi", 7)) { + snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_RAWMIDI); + str += 7; + } else if (!strncasecmp(str, "timer", 5)) { + snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_TIMER); + str += 5; + } else if (!strncasecmp(str, "sequencer", 9)) { + snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_SEQUENCER); + str += 9; + } else { + return -EINVAL; + } + } else if (!strncasecmp(str, "name=", 5)) { + char buf[64]; + str += 5; + ptr = buf; + size = 0; + if (*str == '\'' || *str == '\"') { + c = *str++; + while (*str && *str != c) { + if (size < (int)sizeof(buf)) { + *ptr++ = *str; + size++; + } + str++; + } + if (*str == c) + str++; + } else { + while (*str && *str != ',') { + if (size < (int)sizeof(buf)) { + *ptr++ = *str; + size++; + } + str++; + } + *ptr = '\0'; + } + snd_ctl_elem_id_set_name(dst, buf); + } else if (!strncasecmp(str, "index=", 6)) { + str += 6; + snd_ctl_elem_id_set_index(dst, atoi(str)); + while (isdigit(*str)) + str++; + } else if (!strncasecmp(str, "device=", 7)) { + str += 7; + snd_ctl_elem_id_set_device(dst, atoi(str)); + while (isdigit(*str)) + str++; + } else if (!strncasecmp(str, "subdevice=", 10)) { + str += 10; + snd_ctl_elem_id_set_subdevice(dst, atoi(str)); + while (isdigit(*str)) + str++; + } + if (*str == ',') { + str++; + } else { + if (*str) + return -EINVAL; + } + } + return 0; +} + +static int get_ctl_enum_item_index(snd_ctl_t *handle, + snd_ctl_elem_info_t *info, + const char **ptrp) +{ + char *ptr = (char *)*ptrp; + int items, i, len; + const char *name; + + items = snd_ctl_elem_info_get_items(info); + if (items <= 0) + return -1; + + for (i = 0; i < items; i++) { + snd_ctl_elem_info_set_item(info, i); + if (snd_ctl_elem_info(handle, info) < 0) + return -1; + name = snd_ctl_elem_info_get_item_name(info); + len = strlen(name); + if (! strncmp(name, ptr, len)) { + if (! ptr[len] || ptr[len] == ',' || ptr[len] == '\n') { + ptr += len; + *ptrp = ptr; + return i; + } + } + } + return -1; +} + +/** + * \brief parse ASCII string as CTL element value + * \param dst destination CTL element value + * \param info CTL element info structure + * \param value source ASCII string + * \return zero on success, otherwise a negative error code + */ +int snd_ctl_ascii_value_parse(snd_ctl_t *handle, + snd_ctl_elem_value_t *dst, + snd_ctl_elem_info_t *info, + const char *value) +{ + const char *ptr = value; + snd_ctl_elem_id_t *myid; + snd_ctl_elem_type_t type; + unsigned int idx, count; + long tmp; + long long tmp64; + + snd_ctl_elem_id_alloca(&myid); + snd_ctl_elem_info_get_id(info, myid); + type = snd_ctl_elem_info_get_type(info); + count = snd_ctl_elem_info_get_count(info); + snd_ctl_elem_value_set_id(dst, myid); + + for (idx = 0; idx < count && idx < 128 && ptr && *ptr; idx++) { + switch (type) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + tmp = 0; + if (!strncasecmp(ptr, "on", 2) || + !strncasecmp(ptr, "up", 2)) { + tmp = 1; + ptr += 2; + } else if (!strncasecmp(ptr, "yes", 3)) { + tmp = 1; + ptr += 3; + } else if (!strncasecmp(ptr, "toggle", 6)) { + tmp = snd_ctl_elem_value_get_boolean(dst, idx); + tmp = tmp > 0 ? 0 : 1; + ptr += 6; + } else if (isdigit(*ptr)) { + tmp = atoi(ptr) > 0 ? 1 : 0; + while (isdigit(*ptr)) + ptr++; + } else { + while (*ptr && *ptr != ',') + ptr++; + } + snd_ctl_elem_value_set_boolean(dst, idx, tmp); + break; + case SND_CTL_ELEM_TYPE_INTEGER: + tmp = get_integer(&ptr, + snd_ctl_elem_info_get_min(info), + snd_ctl_elem_info_get_max(info)); + snd_ctl_elem_value_set_integer(dst, idx, tmp); + break; + case SND_CTL_ELEM_TYPE_INTEGER64: + tmp64 = get_integer64(&ptr, + snd_ctl_elem_info_get_min64(info), + snd_ctl_elem_info_get_max64(info)); + snd_ctl_elem_value_set_integer64(dst, idx, tmp64); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + tmp = get_ctl_enum_item_index(handle, info, &ptr); + if (tmp < 0) + tmp = get_integer(&ptr, 0, + snd_ctl_elem_info_get_items(info) - 1); + snd_ctl_elem_value_set_enumerated(dst, idx, tmp); + break; + case SND_CTL_ELEM_TYPE_BYTES: + tmp = get_integer(&ptr, 0, 255); + snd_ctl_elem_value_set_byte(dst, idx, tmp); + break; + default: + break; + } + if (!strchr(value, ',')) + ptr = value; + else if (*ptr == ',') + ptr++; + } + return 0; +} -- cgit v1.2.1