--- /dev/null
+/**
+ * \file control/tlv.c
+ * \brief dB conversion functions from control TLV information
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 2007
+ */
+/*
+ * Control Interface - dB conversion functions from control TLV information
+ *
+ * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#ifndef HAVE_SOFT_FLOAT
+#include <math.h>
+#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);
+}
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
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;
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;
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,
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,