3 * \brief dB conversion functions from control TLV information
4 * \author Takashi Iwai <tiwai@suse.de>
8 * Control Interface - dB conversion functions from control TLV information
10 * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
13 * This library is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as
15 * published by the Free Software Foundation; either version 2.1 of
16 * the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #ifndef HAVE_SOFT_FLOAT
36 #include "control_local.h"
39 /* convert to index of integer array */
40 #define int_index(size) (((size) + sizeof(int) - 1) / sizeof(int))
41 /* max size of a TLV entry for dB information (including compound one) */
42 #define MAX_TLV_RANGE_SIZE 256
46 * \brief Parse TLV stream and retrieve dB information
47 * \param tlv the TLV source
48 * \param tlv_size the byte size of TLV source
49 * \param db_tlvp the pointer stored the dB TLV information
50 * \return the byte size of dB TLV information if found in the given
51 * TLV source, or a negative error code.
53 * This function parses the given TLV source and stores the TLV start
54 * point if the TLV information regarding dB conversion is found.
55 * The stored TLV pointer can be passed to the convesion functions
56 * #snd_tlv_convert_to_dB(), #snd_tlv_convert_from_dB() and
57 * #snd_tlv_get_dB_range().
59 int snd_tlv_parse_dB_info(unsigned int *tlv,
60 unsigned int tlv_size,
61 unsigned int **db_tlvp)
70 tlv_size -= 2 * sizeof(int);
71 if (size > tlv_size) {
72 SNDERR("TLV size error");
76 case SND_CTL_TLVT_CONTAINER:
77 size = int_index(size) * sizeof(int);
81 err = snd_tlv_parse_dB_info(tlv, size, db_tlvp);
83 return err; /* error */
85 return err; /* found */
86 len = int_index(tlv[1]) + 2;
87 size -= len * sizeof(int);
91 case SND_CTL_TLVT_DB_SCALE:
92 case SND_CTL_TLVT_DB_MINMAX:
93 case SND_CTL_TLVT_DB_MINMAX_MUTE:
94 #ifndef HAVE_SOFT_FLOAT
95 case SND_CTL_TLVT_DB_LINEAR:
97 case SND_CTL_TLVT_DB_RANGE: {
99 if (type == SND_CTL_TLVT_DB_RANGE)
100 minsize = 4 * sizeof(int);
102 minsize = 2 * sizeof(int);
103 if (size < minsize) {
104 SNDERR("Invalid dB_scale TLV size");
107 if (size > MAX_TLV_RANGE_SIZE) {
108 SNDERR("Too big dB_scale TLV size: %d", size);
112 return size + sizeof(int) * 2;
117 return -EINVAL; /* not found */
121 * \brief Get the dB min/max values
122 * \param tlv the TLV source returned by #snd_tlv_parse_dB_info()
123 * \param rangemin the minimum value of the raw volume
124 * \param rangemax the maximum value of the raw volume
125 * \param min the pointer to store the minimum dB value (in 0.01dB unit)
126 * \param max the pointer to store the maximum dB value (in 0.01dB unit)
127 * \return 0 if successful, or a negative error code
129 int snd_tlv_get_dB_range(unsigned int *tlv, long rangemin, long rangemax,
130 long *min, long *max)
135 case SND_CTL_TLVT_DB_RANGE: {
136 unsigned int pos, len;
137 len = int_index(tlv[1]);
138 if (len > MAX_TLV_RANGE_SIZE)
141 while (pos + 4 <= len) {
143 rangemin = (int)tlv[pos];
144 rangemax = (int)tlv[pos + 1];
145 err = snd_tlv_get_dB_range(tlv + pos + 2,
159 pos += int_index(tlv[pos + 3]) + 4;
163 case SND_CTL_TLVT_DB_SCALE: {
166 step = (tlv[3] & 0xffff);
167 *max = *min + (long)(step * (rangemax - rangemin));
170 case SND_CTL_TLVT_DB_MINMAX:
171 case SND_CTL_TLVT_DB_MINMAX_MUTE:
172 case SND_CTL_TLVT_DB_LINEAR:
181 * \brief Convert the given raw volume value to a dB gain
182 * \param tlv the TLV source returned by #snd_tlv_parse_dB_info()
183 * \param rangemin the minimum value of the raw volume
184 * \param rangemax the maximum value of the raw volume
185 * \param volume the raw volume value to convert
186 * \param db_gain the dB gain (in 0.01dB unit)
187 * \return 0 if successful, or a negative error code
189 int snd_tlv_convert_to_dB(unsigned int *tlv, long rangemin, long rangemax,
190 long volume, long *db_gain)
193 case SND_CTL_TLVT_DB_RANGE: {
194 unsigned int pos, len;
195 len = int_index(tlv[1]);
196 if (len > MAX_TLV_RANGE_SIZE)
199 while (pos + 4 <= len) {
200 rangemin = (int)tlv[pos];
201 rangemax = (int)tlv[pos + 1];
202 if (volume >= rangemin && volume <= rangemax)
203 return snd_tlv_convert_to_dB(tlv + pos + 2,
206 pos += int_index(tlv[pos + 3]) + 4;
210 case SND_CTL_TLVT_DB_SCALE: {
213 step = (tlv[3] & 0xffff);
214 mute = (tlv[3] >> 16) & 1;
215 if (mute && volume == rangemin)
216 *db_gain = SND_CTL_TLV_DB_GAIN_MUTE;
218 *db_gain = (volume - rangemin) * step + min;
221 case SND_CTL_TLVT_DB_MINMAX:
222 case SND_CTL_TLVT_DB_MINMAX_MUTE: {
226 if (volume <= rangemin || rangemax <= rangemin) {
227 if (tlv[0] == SND_CTL_TLVT_DB_MINMAX_MUTE)
228 *db_gain = SND_CTL_TLV_DB_GAIN_MUTE;
231 } else if (volume >= rangemax)
234 *db_gain = (maxdb - mindb) * (volume - rangemin) /
235 (rangemax - rangemin) + mindb;
238 #ifndef HAVE_SOFT_FLOAT
239 case SND_CTL_TLVT_DB_LINEAR: {
242 if (volume <= rangemin || rangemax <= rangemin)
244 else if (volume >= rangemax)
247 double val = (double)(volume - rangemin) /
248 (double)(rangemax - rangemin);
249 if (mindb <= SND_CTL_TLV_DB_GAIN_MUTE)
250 *db_gain = (long)(100.0 * 20.0 * log10(val)) +
253 /* FIXME: precalculate and cache these values */
254 double lmin = pow(10.0, mindb/2000.0);
255 double lmax = pow(10.0, maxdb/2000.0);
256 val = (lmax - lmin) * val + lmin;
257 *db_gain = (long)(100.0 * 20.0 * log10(val));
268 * \brief Convert from dB gain to the corresponding raw value
269 * \param tlv the TLV source returned by #snd_tlv_parse_dB_info()
270 * \param rangemin the minimum value of the raw volume
271 * \param rangemax the maximum value of the raw volume
272 * \param db_gain the dB gain to convert (in 0.01dB unit)
273 * \param value the pointer to store the converted raw volume value
274 * \param xdir the direction for round-up. The value is round up
275 * when this is positive.
276 * \return 0 if successful, or a negative error code
278 int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax,
279 long db_gain, long *value, int xdir)
282 case SND_CTL_TLVT_DB_RANGE: {
283 unsigned int pos, len;
284 len = int_index(tlv[1]);
285 if (len > MAX_TLV_RANGE_SIZE)
288 while (pos + 4 <= len) {
290 rangemin = (int)tlv[pos];
291 rangemax = (int)tlv[pos + 1];
292 if (!snd_tlv_get_dB_range(tlv + pos + 2,
295 db_gain >= dbmin && db_gain <= dbmax)
296 return snd_tlv_convert_from_dB(tlv + pos + 2,
298 db_gain, value, xdir);
299 pos += int_index(tlv[pos + 3]) + 4;
303 case SND_CTL_TLVT_DB_SCALE: {
306 step = (tlv[3] & 0xffff);
307 max = min + (int)(step * (rangemax - rangemin));
310 else if (db_gain >= max)
313 long v = (db_gain - min) * (rangemax - rangemin);
315 v += (max - min) - 1;
316 v = v / (max - min) + rangemin;
321 case SND_CTL_TLVT_DB_MINMAX:
322 case SND_CTL_TLVT_DB_MINMAX_MUTE: {
328 else if (db_gain >= max)
331 long v = (db_gain - min) * (rangemax - rangemin);
333 v += (max - min) - 1;
334 v = v / (max - min) + rangemin;
339 #ifndef HAVE_SOFT_FLOAT
340 case SND_CTL_TLVT_DB_LINEAR: {
346 else if (db_gain >= max)
349 /* FIXME: precalculate and cache vmin and vmax */
350 double vmin, vmax, v;
351 vmin = (min <= SND_CTL_TLV_DB_GAIN_MUTE) ? 0.0 :
352 pow(10.0, (double)min / 2000.0);
353 vmax = !max ? 1.0 : pow(10.0, (double)max / 2000.0);
354 v = pow(10.0, (double)db_gain / 2000.0);
355 v = (v - vmin) * (rangemax - rangemin) / (vmax - vmin);
358 *value = (long)v + rangemin;
370 #define TEMP_TLV_SIZE 4096
374 unsigned int buf[TEMP_TLV_SIZE];
378 static int get_tlv_info(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
379 struct tlv_info *rec)
381 snd_ctl_elem_info_t *info;
384 snd_ctl_elem_info_alloca(&info);
385 snd_ctl_elem_info_set_id(info, id);
386 err = snd_ctl_elem_info(ctl, info);
389 if (!snd_ctl_elem_info_is_tlv_readable(info))
391 if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_INTEGER)
393 rec->minval = snd_ctl_elem_info_get_min(info);
394 rec->maxval = snd_ctl_elem_info_get_max(info);
395 err = snd_ctl_elem_tlv_read(ctl, id, rec->buf, sizeof(rec->buf));
398 err = snd_tlv_parse_dB_info(rec->buf, sizeof(rec->buf), &rec->tlv);
405 * \brief Get the dB min/max values on the given control element
406 * \param ctl the control handler
407 * \param id the element id
408 * \param min the pointer to store the minimum dB value (in 0.01dB unit)
409 * \param max the pointer to store the maximum dB value (in 0.01dB unit)
410 * \return 0 if successful, or a negative error code
412 int snd_ctl_get_dB_range(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
413 long *min, long *max)
415 struct tlv_info info;
418 err = get_tlv_info(ctl, id, &info);
421 return snd_tlv_get_dB_range(info.tlv, info.minval, info.maxval,
426 * \brief Convert the volume value to dB on the given control element
427 * \param ctl the control handler
428 * \param id the element id
429 * \param volume the raw volume value to convert
430 * \param db_gain the dB gain (in 0.01dB unit)
431 * \return 0 if successful, or a negative error code
433 int snd_ctl_convert_to_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
434 long volume, long *db_gain)
436 struct tlv_info info;
439 err = get_tlv_info(ctl, id, &info);
442 return snd_tlv_convert_to_dB(info.tlv, info.minval, info.maxval,
447 * \brief Convert from dB gain to the raw volume value on the given control element
448 * \param ctl the control handler
449 * \param id the element id
450 * \param db_gain the dB gain to convert (in 0.01dB unit)
451 * \param value the pointer to store the converted raw volume value
452 * \param xdir the direction for round-up. The value is round up
453 * when this is positive.
454 * \return 0 if successful, or a negative error code
456 int snd_ctl_convert_from_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
457 long db_gain, long *value, int xdir)
459 struct tlv_info info;
462 err = get_tlv_info(ctl, id, &info);
465 return snd_tlv_convert_from_dB(info.tlv, info.minval, info.maxval,
466 db_gain, value, xdir);