From: Takashi Iwai Date: Wed, 24 Oct 2007 11:04:14 +0000 (+0200) Subject: Export dB conversion helper functions X-Git-Tag: android-x86-9.0-r1~1322 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=f38e5feca3723603beea79c8f78e39414ff66bbd;p=android-x86%2Fexternal-alsa-lib.git Export dB conversion helper functions Export helper functions to convert dB level and range. snd_tlv_*dB*() are to convert dB level or range directly from TLV data. snd_ctl_*dB*() are to get dB level or range from a control element. --- diff --git a/include/control.h b/include/control.h index 33faea01..e61362d0 100644 --- a/include/control.h +++ b/include/control.h @@ -442,6 +442,21 @@ const void * snd_ctl_elem_value_get_bytes(const snd_ctl_elem_value_t *obj); void snd_ctl_elem_value_get_iec958(const snd_ctl_elem_value_t *obj, snd_aes_iec958_t *ptr); void snd_ctl_elem_value_set_iec958(snd_ctl_elem_value_t *obj, const snd_aes_iec958_t *ptr); +int snd_tlv_parse_dB_info(unsigned int *tlv, unsigned int tlv_size, + unsigned int **db_tlvp); +int snd_tlv_get_dB_range(unsigned int *tlv, long rangemin, long rangemax, + long *min, long *max); +int snd_tlv_convert_to_dB(unsigned int *tlv, long rangemin, long rangemax, + long volume, long *db_gain); +int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax, + long db_gain, long *value, int xdir); +int snd_ctl_get_dB_range(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + long *min, long *max); +int snd_ctl_convert_to_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + long volume, long *db_gain); +int snd_ctl_convert_from_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + long db_gain, long *value, int xdir); + /** * \defgroup HControl High level Control Interface * \ingroup Control diff --git a/src/Versions.in b/src/Versions.in index 3a9bb6c5..3b6dcf0d 100644 --- a/src/Versions.in +++ b/src/Versions.in @@ -296,3 +296,14 @@ ALSA_1.0.14 { @SYMBOL_PREFIX@snd_device_name_free_hint; @SYMBOL_PREFIX@snd_device_name_get_hint; } ALSA_1.0.12; + +ALSA_1.0.16 { + global: + @SYMBOL_PREFIX@snd_tlv_parse_dB_info; + @SYMBOL_PREFIX@snd_tlv_get_dB_range; + @SYMBOL_PREFIX@snd_tlv_convert_to_dB; + @SYMBOL_PREFIX@snd_tlv_convert_from_dB; + @SYMBOL_PREFIX@snd_ctl_get_dB_range; + @SYMBOL_PREFIX@snd_ctl_convert_to_dB; + @SYMBOL_PREFIX@snd_ctl_convert_from_dB; +} ALSA_1.0.14; diff --git a/src/control/Makefile.am b/src/control/Makefile.am index 90c000aa..d4c50c01 100644 --- a/src/control/Makefile.am +++ b/src/control/Makefile.am @@ -1,6 +1,6 @@ EXTRA_LTLIBRARIES = libcontrol.la -libcontrol_la_SOURCES = cards.c namehint.c hcontrol.c \ +libcontrol_la_SOURCES = cards.c tlv.c namehint.c hcontrol.c \ control.c control_hw.c setup.c control_symbols.c if BUILD_CTL_PLUGIN_SHM libcontrol_la_SOURCES += control_shm.c diff --git a/src/control/tlv.c b/src/control/tlv.c new file mode 100644 index 00000000..0006a87c --- /dev/null +++ b/src/control/tlv.c @@ -0,0 +1,429 @@ +/** + * \file control/tlv.c + * \brief dB conversion functions from control TLV information + * \author Takashi Iwai + * \date 2007 + */ +/* + * Control Interface - dB conversion functions from control TLV information + * + * Copyright (c) 2007 Takashi Iwai + * + * + * 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 +#ifndef HAVE_SOFT_FLOAT +#include +#endif +#include "control_local.h" + +#ifndef DOC_HIDDEN +/* convert to index of integer array */ +#define int_index(size) (((size) + sizeof(int) - 1) / sizeof(int)) +/* max size of a TLV entry for dB information (including compound one) */ +#define MAX_TLV_RANGE_SIZE 256 +#endif + +/** + * \brief Parse TLV stream and retrieve dB information + * \param tlv the TLV source + * \param tlv_size the byte size of TLV source + * \param db_tlvp the pointer stored the dB TLV information + * \return the byte size of dB TLV information if found in the given + * TLV source, or a negative error code. + * + * This function parses the given TLV source and stores the TLV start + * point if the TLV information regarding dB conversion is found. + * The stored TLV pointer can be passed to the convesion functions + * #snd_tlv_convert_to_dB(), #snd_tlv_convert_from_dB() and + * #snd_tlv_get_dB_range(). + */ +int snd_tlv_parse_dB_info(unsigned int *tlv, + unsigned int tlv_size, + unsigned int **db_tlvp) +{ + unsigned int type; + unsigned int size; + int err; + + *db_tlvp = NULL; + type = tlv[0]; + size = tlv[1]; + tlv_size -= 2 * sizeof(int); + if (size > tlv_size) { + SNDERR("TLV size error"); + return -EINVAL; + } + switch (type) { + case SND_CTL_TLVT_CONTAINER: + size = int_index(size) * sizeof(int); + tlv += 2; + while (size > 0) { + unsigned int len; + err = snd_tlv_parse_dB_info(tlv, size, db_tlvp); + if (err < 0) + return err; /* error */ + if (err > 0) + return err; /* found */ + len = int_index(tlv[1]) + 2; + size -= len * sizeof(int); + tlv += len; + } + break; + case SND_CTL_TLVT_DB_SCALE: +#ifndef HAVE_SOFT_FLOAT + case SND_CTL_TLVT_DB_LINEAR: +#endif + case SND_CTL_TLVT_DB_RANGE: { + unsigned int minsize; + if (type == SND_CTL_TLVT_DB_RANGE) + minsize = 4 * sizeof(int); + else + minsize = 2 * sizeof(int); + if (size < minsize) { + SNDERR("Invalid dB_scale TLV size"); + return -EINVAL; + } + if (size > MAX_TLV_RANGE_SIZE) { + SNDERR("Too big dB_scale TLV size: %d", size); + return -EINVAL; + } + *db_tlvp = tlv; + return size + sizeof(int) * 2; + } + default: + break; + } + return -EINVAL; /* not found */ +} + +/** + * \brief Get the dB min/max values + * \param tlv the TLV source returned by #snd_tlv_parse_dB_info() + * \param rangemin the minimum value of the raw volume + * \param rangemax the maximum value of the raw volume + * \param min the pointer to store the minimum dB value (in 0.01dB unit) + * \param max the pointer to store the maximum dB value (in 0.01dB unit) + * \return 0 if successful, or a negative error code + */ +int snd_tlv_get_dB_range(unsigned int *tlv, long rangemin, long rangemax, + long *min, long *max) +{ + int err; + + switch (tlv[0]) { + case SND_CTL_TLVT_DB_RANGE: { + unsigned int pos, len; + len = int_index(tlv[1]); + if (len > MAX_TLV_RANGE_SIZE) + return -EINVAL; + pos = 2; + while (pos + 4 <= len) { + long rmin, rmax; + rangemin = (int)tlv[pos]; + rangemax = (int)tlv[pos + 1]; + err = snd_tlv_get_dB_range(tlv + pos + 2, + rangemin, rangemax, + &rmin, &rmax); + if (err < 0) + return err; + if (pos > 2) { + if (rmin < *min) + *min = rmin; + if (rmax > *max) + *max = rmax; + } else { + *min = rmin; + *max = rmax; + } + pos += int_index(tlv[pos + 3]) + 4; + } + return 0; + } + case SND_CTL_TLVT_DB_SCALE: { + int step; + *min = (int)tlv[2]; + step = (tlv[3] & 0xffff); + *max = *min + (long)(step * (rangemax - rangemin)); + return 0; + } + case SND_CTL_TLVT_DB_LINEAR: + *min = (int)tlv[2]; + *max = (int)tlv[3]; + return 0; + } + return -EINVAL; +} + +/** + * \brief Convert the given raw volume value to a dB gain + * \param tlv the TLV source returned by #snd_tlv_parse_dB_info() + * \param rangemin the minimum value of the raw volume + * \param rangemax the maximum value of the raw volume + * \param volume the raw volume value to convert + * \param db_gain the dB gain (in 0.01dB unit) + * \return 0 if successful, or a negative error code + */ +int snd_tlv_convert_to_dB(unsigned int *tlv, long rangemin, long rangemax, + long volume, long *db_gain) +{ + switch (tlv[0]) { + case SND_CTL_TLVT_DB_RANGE: { + unsigned int pos, len; + len = int_index(tlv[1]); + if (len > MAX_TLV_RANGE_SIZE) + return -EINVAL; + pos = 2; + while (pos + 4 <= len) { + rangemin = (int)tlv[pos]; + rangemax = (int)tlv[pos + 1]; + if (volume >= rangemin && volume <= rangemax) + return snd_tlv_convert_to_dB(tlv + pos + 2, + rangemin, rangemax, + volume, db_gain); + pos += int_index(tlv[pos + 3]) + 4; + } + return -EINVAL; + } + case SND_CTL_TLVT_DB_SCALE: { + int min, step, mute; + min = tlv[2]; + step = (tlv[3] & 0xffff); + mute = (tlv[3] >> 16) & 1; + if (mute && volume == rangemin) + *db_gain = SND_CTL_TLV_DB_GAIN_MUTE; + else + *db_gain = (volume - rangemin) * step + min; + return 0; + } +#ifndef HAVE_SOFT_FLOAT + case SND_CTL_TLVT_DB_LINEAR: { + int mindb = tlv[2]; + int maxdb = tlv[3]; + if (volume <= rangemin || rangemax <= rangemin) + *db_gain = mindb; + else if (volume >= rangemax) + *db_gain = maxdb; + else { + double val = (double)(volume - rangemin) / + (double)(rangemax - rangemin); + if (mindb <= SND_CTL_TLV_DB_GAIN_MUTE) + *db_gain = (long)(100.0 * 20.0 * log10(val)) + + maxdb; + else { + /* FIXME: precalculate and cache these values */ + double lmin = pow(10.0, mindb/2000.0); + double lmax = pow(10.0, maxdb/2000.0); + val = (lmax - lmin) * val + lmin; + *db_gain = (long)(100.0 * 20.0 * log10(val)); + } + } + return 0; + } +#endif + } + return -EINVAL; +} + +/** + * \brief Convert from dB gain to the corresponding raw value + * \param tlv the TLV source returned by #snd_tlv_parse_dB_info() + * \param rangemin the minimum value of the raw volume + * \param rangemax the maximum value of the raw volume + * \param db_gain the dB gain to convert (in 0.01dB unit) + * \param value the pointer to store the converted raw volume value + * \param xdir the direction for round-up. The value is round up + * when this is positive. + * \return 0 if successful, or a negative error code + */ +int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax, + long db_gain, long *value, int xdir) +{ + switch (tlv[0]) { + case SND_CTL_TLVT_DB_RANGE: { + unsigned int pos, len; + len = int_index(tlv[1]); + if (len > MAX_TLV_RANGE_SIZE) + return -EINVAL; + pos = 2; + while (pos + 4 <= len) { + long dbmin, dbmax; + rangemin = (int)tlv[pos]; + rangemax = (int)tlv[pos + 1]; + if (!snd_tlv_get_dB_range(tlv + pos + 2, + rangemin, rangemax, + &dbmin, &dbmax) && + db_gain >= dbmin && db_gain <= dbmax) + return snd_tlv_convert_from_dB(tlv + pos + 2, + rangemin, rangemax, + db_gain, value, xdir); + pos += int_index(tlv[pos + 3]) + 4; + } + return -EINVAL; + } + case SND_CTL_TLVT_DB_SCALE: { + int min, step, max; + min = tlv[2]; + step = (tlv[3] & 0xffff); + max = min + (int)(step * (rangemax - rangemin)); + if (db_gain <= min) + *value = rangemin; + else if (db_gain >= max) + *value = rangemax; + else { + long v = (db_gain - min) * (rangemax - rangemin); + if (xdir > 0) + v += (max - min) - 1; + v = v / (max - min) + rangemin; + *value = v; + } + return 0; + } +#ifndef HAVE_SOFT_FLOAT + case SND_CTL_TLVT_DB_LINEAR: { + int min, max; + min = tlv[2]; + max = tlv[3]; + if (db_gain <= min) + *value = rangemin; + else if (db_gain >= max) + *value = rangemax; + else { + /* FIXME: precalculate and cache vmin and vmax */ + double vmin, vmax, v; + vmin = (min <= SND_CTL_TLV_DB_GAIN_MUTE) ? 0.0 : + pow(10.0, (double)min / 2000.0); + vmax = !max ? 1.0 : pow(10.0, (double)max / 2000.0); + v = pow(10.0, (double)db_gain / 2000.0); + v = (v - vmin) * (rangemax - rangemin) / (vmax - vmin); + if (xdir > 0) + v = ceil(v); + *value = (long)v + rangemin; + } + return 0; + } +#endif + default: + break; + } + return -EINVAL; +} + +#ifndef DOC_HIDDEN +#define TEMP_TLV_SIZE 4096 +struct tlv_info { + long minval, maxval; + unsigned int *tlv; + unsigned int buf[TEMP_TLV_SIZE]; +}; +#endif + +static int get_tlv_info(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + struct tlv_info *rec) +{ + snd_ctl_elem_info_t *info; + int err; + + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_info_set_id(info, id); + err = snd_ctl_elem_info(ctl, info); + if (err < 0) + return err; + if (!snd_ctl_elem_info_is_tlv_readable(info)) + return -EINVAL; + if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_INTEGER) + return -EINVAL; + rec->minval = snd_ctl_elem_info_get_min(info); + rec->maxval = snd_ctl_elem_info_get_max(info); + err = snd_ctl_elem_tlv_read(ctl, id, rec->buf, sizeof(rec->buf)); + if (err < 0) + return err; + err = snd_tlv_parse_dB_info(rec->buf, sizeof(rec->buf), &rec->tlv); + if (err < 0) + return err; + return 0; +} + +/** + * \brief Get the dB min/max values on the given control element + * \param ctl the control handler + * \param id the element id + * \param volume the raw volume value to convert + * \param min the pointer to store the minimum dB value (in 0.01dB unit) + * \param max the pointer to store the maximum dB value (in 0.01dB unit) + * \return 0 if successful, or a negative error code + */ +int snd_ctl_get_dB_range(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + long *min, long *max) +{ + struct tlv_info info; + int err; + + err = get_tlv_info(ctl, id, &info); + if (err < 0) + return err; + return snd_tlv_get_dB_range(info.tlv, info.minval, info.maxval, + min, max); +} + +/** + * \brief Convert the volume value to dB on the given control element + * \param ctl the control handler + * \param id the element id + * \param volume the raw volume value to convert + * \param db_gain the dB gain (in 0.01dB unit) + * \return 0 if successful, or a negative error code + */ +int snd_ctl_convert_to_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + long volume, long *db_gain) +{ + struct tlv_info info; + int err; + + err = get_tlv_info(ctl, id, &info); + if (err < 0) + return err; + return snd_tlv_convert_to_dB(info.tlv, info.minval, info.maxval, + volume, db_gain); +} + +/** + * \brief Convert from dB gain to the raw volume value on the given control element + * \param ctl the control handler + * \param id the element id + * \param db_gain the dB gain to convert (in 0.01dB unit) + * \param value the pointer to store the converted raw volume value + * \param xdir the direction for round-up. The value is round up + * when this is positive. + * \return 0 if successful, or a negative error code + */ +int snd_ctl_convert_from_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + long db_gain, long *value, int xdir) +{ + struct tlv_info info; + int err; + + err = get_tlv_info(ctl, id, &info); + if (err < 0) + return err; + return snd_tlv_convert_from_dB(info.tlv, info.minval, info.maxval, + db_gain, value, xdir); +} diff --git a/src/mixer/simple_none.c b/src/mixer/simple_none.c index 13272853..7e7af7b5 100644 --- a/src/mixer/simple_none.c +++ b/src/mixer/simple_none.c @@ -1068,150 +1068,13 @@ static int get_volume_ops(snd_mixer_elem_t *elem, int dir, static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec); -/* convert to index of integer array */ -#ifndef DOC_HIDDEN -#define int_index(size) (((size) + sizeof(int) - 1) / sizeof(int)) -#endif - -/* max size of a TLV entry for dB information (including compound one) */ -#ifndef DOC_HIDDEN -#define MAX_TLV_RANGE_SIZE 256 -#endif - -/* parse TLV stream and retrieve dB information - * return 0 if successly found and stored to rec, - * return 1 if no information is found, - * or return a negative error code - */ -static int parse_db_range(struct selem_str *rec, unsigned int *tlv, - unsigned int tlv_size) -{ - unsigned int type; - unsigned int size; - int err; - - type = tlv[0]; - size = tlv[1]; - tlv_size -= 2 * sizeof(int); - if (size > tlv_size) { - SNDERR("TLV size error"); - return -EINVAL; - } - switch (type) { - case SND_CTL_TLVT_CONTAINER: - size = int_index(size) * sizeof(int); - tlv += 2; - while (size > 0) { - unsigned int len; - err = parse_db_range(rec, tlv, size); - if (err <= 0) - return err; /* error or found dB */ - len = int_index(tlv[1]) + 2; - size -= len * sizeof(int); - tlv += len; - } - break; - case SND_CTL_TLVT_DB_SCALE: -#ifndef HAVE_SOFT_FLOAT - case SND_CTL_TLVT_DB_LINEAR: -#endif - case SND_CTL_TLVT_DB_RANGE: { - unsigned int minsize; - if (type == SND_CTL_TLVT_DB_RANGE) - minsize = 4 * sizeof(int); - else - minsize = 2 * sizeof(int); - if (size < minsize) { - SNDERR("Invalid dB_scale TLV size"); - return -EINVAL; - } - if (size > MAX_TLV_RANGE_SIZE) { - SNDERR("Too big dB_scale TLV size: %d", size); - return -EINVAL; - } - rec->db_info = malloc(size + sizeof(int) * 2); - if (! rec->db_info) - return -ENOMEM; - memcpy(rec->db_info, tlv, size + sizeof(int) * 2); - return 0; - } - default: - break; - } - return -EINVAL; /* not found */ -} - -/* convert the given raw volume value to a dB gain - */ - -static int do_convert_to_dB(unsigned int *tlv, long rangemin, long rangemax, - long volume, long *db_gain) -{ - switch (tlv[0]) { - case SND_CTL_TLVT_DB_RANGE: { - unsigned int pos, len; - len = int_index(tlv[1]); - if (len > MAX_TLV_RANGE_SIZE) - return -EINVAL; - pos = 2; - while (pos + 4 <= len) { - rangemin = (int)tlv[pos]; - rangemax = (int)tlv[pos + 1]; - if (volume >= rangemin && volume <= rangemax) - return do_convert_to_dB(tlv + pos + 2, - rangemin, rangemax, - volume, db_gain); - pos += int_index(tlv[pos + 3]) + 4; - } - return -EINVAL; - } - case SND_CTL_TLVT_DB_SCALE: { - int min, step, mute; - min = tlv[2]; - step = (tlv[3] & 0xffff); - mute = (tlv[3] >> 16) & 1; - if (mute && volume == rangemin) - *db_gain = SND_CTL_TLV_DB_GAIN_MUTE; - else - *db_gain = (volume - rangemin) * step + min; - return 0; - } -#ifndef HAVE_SOFT_FLOAT - case SND_CTL_TLVT_DB_LINEAR: { - int mindb = tlv[2]; - int maxdb = tlv[3]; - if (volume <= rangemin || rangemax <= rangemin) - *db_gain = mindb; - else if (volume >= rangemax) - *db_gain = maxdb; - else { - double val = (double)(volume - rangemin) / - (double)(rangemax - rangemin); - if (mindb <= SND_CTL_TLV_DB_GAIN_MUTE) - *db_gain = (long)(100.0 * 20.0 * log10(val)) + - maxdb; - else { - /* FIXME: precalculate and cache these values */ - double lmin = pow(10.0, mindb/2000.0); - double lmax = pow(10.0, maxdb/2000.0); - val = (lmax - lmin) * val + lmin; - *db_gain = (long)(100.0 * 20.0 * log10(val)); - } - } - return 0; - } -#endif - } - return -EINVAL; -} - static int convert_to_dB(snd_hctl_elem_t *ctl, struct selem_str *rec, long volume, long *db_gain) { if (init_db_range(ctl, rec) < 0) return -EINVAL; - return do_convert_to_dB(rec->db_info, rec->min, rec->max, - volume, db_gain); + return snd_tlv_convert_to_dB(rec->db_info, rec->min, rec->max, + volume, db_gain); } /* initialize dB range information, reading TLV via hcontrol @@ -1221,6 +1084,8 @@ static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec) snd_ctl_elem_info_t *info; unsigned int *tlv = NULL; const unsigned int tlv_size = 4096; + unsigned int *dbrec; + int db_size; if (rec->db_init_error) return -EINVAL; @@ -1237,8 +1102,13 @@ static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec) return -ENOMEM; if (snd_hctl_elem_tlv_read(ctl, tlv, tlv_size) < 0) goto error; - if (parse_db_range(rec, tlv, tlv_size) < 0) + db_size = snd_tlv_parse_dB_info(tlv, tlv_size, &dbrec); + if (db_size < 0) + goto error; + rec->db_info = malloc(db_size); + if (!rec->db_info) goto error; + memcpy(rec->db_info, dbrec, db_size); free(tlv); rec->db_initialized = 1; return 0; @@ -1269,59 +1139,13 @@ static selem_ctl_t *get_selem_ctl(selem_none_t *s, int dir) return c; } -/* Get the dB min/max values - */ -static int do_get_dB_range(unsigned int *tlv, long rangemin, long rangemax, - long *min, long *max) -{ - switch (tlv[0]) { - case SND_CTL_TLVT_DB_RANGE: { - unsigned int pos, len; - len = int_index(tlv[1]); - if (len > MAX_TLV_RANGE_SIZE) - return -EINVAL; - pos = 2; - while (pos + 4 <= len) { - long rmin, rmax; - rangemin = (int)tlv[pos]; - rangemax = (int)tlv[pos + 1]; - do_get_dB_range(tlv + pos + 2, rangemin, rangemax, - &rmin, &rmax); - if (pos > 2) { - if (rmin < *min) - *min = rmin; - if (rmax > *max) - *max = rmax; - } else { - *min = rmin; - *max = rmax; - } - pos += int_index(tlv[pos + 3]) + 4; - } - return 0; - } - case SND_CTL_TLVT_DB_SCALE: { - int step; - *min = (int)tlv[2]; - step = (tlv[3] & 0xffff); - *max = *min + (long)(step * (rangemax - rangemin)); - return 0; - } - case SND_CTL_TLVT_DB_LINEAR: - *min = (int)tlv[2]; - *max = (int)tlv[3]; - return 0; - } - return -EINVAL; -} - static int get_dB_range(snd_hctl_elem_t *ctl, struct selem_str *rec, long *min, long *max) { if (init_db_range(ctl, rec) < 0) return -EINVAL; - return do_get_dB_range(rec->db_info, rec->min, rec->max, min, max); + return snd_tlv_get_dB_range(rec->db_info, rec->min, rec->max, min, max); } static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir, @@ -1336,89 +1160,14 @@ static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir, return get_dB_range(c->elem, &s->str[dir], min, max); } -/* Convert from dB gain to the corresponding raw value. - * The value is round up when xdir > 0. - */ -static int do_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax, - long db_gain, long *value, int xdir) -{ - switch (tlv[0]) { - case SND_CTL_TLVT_DB_RANGE: { - unsigned int pos, len; - len = int_index(tlv[1]); - if (len > MAX_TLV_RANGE_SIZE) - return -EINVAL; - pos = 2; - while (pos + 4 <= len) { - long dbmin, dbmax; - rangemin = (int)tlv[pos]; - rangemax = (int)tlv[pos + 1]; - if (! do_get_dB_range(tlv + pos + 2, rangemin, rangemax, - &dbmin, &dbmax) && - db_gain >= dbmin && db_gain <= dbmax) - return do_convert_from_dB(tlv + pos + 2, - rangemin, rangemax, - db_gain, value, xdir); - pos += int_index(tlv[pos + 3]) + 4; - } - return -EINVAL; - } - case SND_CTL_TLVT_DB_SCALE: { - int min, step, max; - min = tlv[2]; - step = (tlv[3] & 0xffff); - max = min + (int)(step * (rangemax - rangemin)); - if (db_gain <= min) - *value = rangemin; - else if (db_gain >= max) - *value = rangemax; - else { - long v = (db_gain - min) * (rangemax - rangemin); - if (xdir > 0) - v += (max - min) - 1; - v = v / (max - min) + rangemin; - *value = v; - } - return 0; - } -#ifndef HAVE_SOFT_FLOAT - case SND_CTL_TLVT_DB_LINEAR: { - int min, max; - min = tlv[2]; - max = tlv[3]; - if (db_gain <= min) - *value = rangemin; - else if (db_gain >= max) - *value = rangemax; - else { - /* FIXME: precalculate and cache vmin and vmax */ - double vmin, vmax, v; - vmin = (min <= SND_CTL_TLV_DB_GAIN_MUTE) ? 0.0 : - pow(10.0, (double)min / 2000.0); - vmax = !max ? 1.0 : pow(10.0, (double)max / 2000.0); - v = pow(10.0, (double)db_gain / 2000.0); - v = (v - vmin) * (rangemax - rangemin) / (vmax - vmin); - if (xdir > 0) - v = ceil(v); - *value = (long)v + rangemin; - } - return 0; - } -#endif - default: - break; - } - return -EINVAL; -} - static int convert_from_dB(snd_hctl_elem_t *ctl, struct selem_str *rec, long db_gain, long *value, int xdir) { if (init_db_range(ctl, rec) < 0) return -EINVAL; - return do_convert_from_dB(rec->db_info, rec->min, rec->max, - db_gain, value, xdir); + return snd_tlv_convert_from_dB(rec->db_info, rec->min, rec->max, + db_gain, value, xdir); } static int get_dB_ops(snd_mixer_elem_t *elem,