|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \file control/tlv.c
|
|
Packit Service |
db8eaa |
* \brief dB conversion functions from control TLV information
|
|
Packit Service |
db8eaa |
* \author Takashi Iwai <tiwai@suse.de>
|
|
Packit Service |
db8eaa |
* \date 2007
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* Control Interface - dB conversion functions from control TLV information
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* This library is free software; you can redistribute it and/or modify
|
|
Packit Service |
db8eaa |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit Service |
db8eaa |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit Service |
db8eaa |
* the License, or (at your option) any later version.
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
db8eaa |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
db8eaa |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
db8eaa |
* GNU Lesser General Public License for more details.
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit Service |
db8eaa |
* License along with this library; if not, write to the Free Software
|
|
Packit Service |
db8eaa |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#include <stdio.h>
|
|
Packit Service |
db8eaa |
#include <stdlib.h>
|
|
Packit Service |
db8eaa |
#include <unistd.h>
|
|
Packit Service |
db8eaa |
#include <string.h>
|
|
Packit Service |
db8eaa |
#ifndef HAVE_SOFT_FLOAT
|
|
Packit Service |
db8eaa |
#include <math.h>
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
#include "control_local.h"
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
/* convert to index of integer array */
|
|
Packit Service |
db8eaa |
#define int_index(size) (((size) + sizeof(int) - 1) / sizeof(int))
|
|
Packit Service |
db8eaa |
/* max size of a TLV entry for dB information (including compound one) */
|
|
Packit Service |
db8eaa |
#define MAX_TLV_RANGE_SIZE 256
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Parse TLV stream and retrieve dB information
|
|
Packit Service |
db8eaa |
* \param tlv the TLV source
|
|
Packit Service |
db8eaa |
* \param tlv_size the byte size of TLV source
|
|
Packit Service |
db8eaa |
* \param db_tlvp the pointer stored the dB TLV information
|
|
Packit Service |
db8eaa |
* \return the byte size of dB TLV information if found in the given
|
|
Packit Service |
db8eaa |
* TLV source, or a negative error code.
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* This function parses the given TLV source and stores the TLV start
|
|
Packit Service |
db8eaa |
* point if the TLV information regarding dB conversion is found.
|
|
Packit Service |
db8eaa |
* The stored TLV pointer can be passed to the convesion functions
|
|
Packit Service |
db8eaa |
* #snd_tlv_convert_to_dB(), #snd_tlv_convert_from_dB() and
|
|
Packit Service |
db8eaa |
* #snd_tlv_get_dB_range().
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_tlv_parse_dB_info(unsigned int *tlv,
|
|
Packit Service |
db8eaa |
unsigned int tlv_size,
|
|
Packit Service |
db8eaa |
unsigned int **db_tlvp)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
unsigned int type;
|
|
Packit Service |
db8eaa |
unsigned int size;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
*db_tlvp = NULL;
|
|
Packit Service |
db8eaa |
type = tlv[SNDRV_CTL_TLVO_TYPE];
|
|
Packit Service |
db8eaa |
size = tlv[SNDRV_CTL_TLVO_LEN];
|
|
Packit Service |
db8eaa |
tlv_size -= 2 * sizeof(int);
|
|
Packit Service |
db8eaa |
if (size > tlv_size) {
|
|
Packit Service |
db8eaa |
SNDERR("TLV size error");
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
switch (type) {
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_CONTAINER:
|
|
Packit Service |
db8eaa |
size = int_index(size) * sizeof(int);
|
|
Packit Service |
db8eaa |
tlv += 2;
|
|
Packit Service |
db8eaa |
while (size > 0) {
|
|
Packit Service |
db8eaa |
unsigned int len;
|
|
Packit Service |
db8eaa |
err = snd_tlv_parse_dB_info(tlv, size, db_tlvp);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err; /* error */
|
|
Packit Service |
db8eaa |
if (err > 0)
|
|
Packit Service |
db8eaa |
return err; /* found */
|
|
Packit Service |
db8eaa |
len = int_index(tlv[SNDRV_CTL_TLVO_LEN]) + 2;
|
|
Packit Service |
db8eaa |
size -= len * sizeof(int);
|
|
Packit Service |
db8eaa |
tlv += len;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_SCALE:
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_MINMAX:
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_MINMAX_MUTE:
|
|
Packit Service |
db8eaa |
#ifndef HAVE_SOFT_FLOAT
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_LINEAR:
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_RANGE: {
|
|
Packit Service |
db8eaa |
unsigned int minsize;
|
|
Packit Service |
db8eaa |
if (type == SND_CTL_TLVT_DB_RANGE)
|
|
Packit Service |
db8eaa |
minsize = 4 * sizeof(int);
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
minsize = 2 * sizeof(int);
|
|
Packit Service |
db8eaa |
if (size < minsize) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid dB_scale TLV size");
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (size > MAX_TLV_RANGE_SIZE) {
|
|
Packit Service |
db8eaa |
SNDERR("Too big dB_scale TLV size: %d", size);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
*db_tlvp = tlv;
|
|
Packit Service |
db8eaa |
return size + sizeof(int) * 2;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return -EINVAL; /* not found */
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Get the dB min/max values
|
|
Packit Service |
db8eaa |
* \param tlv the TLV source returned by #snd_tlv_parse_dB_info()
|
|
Packit Service |
db8eaa |
* \param rangemin the minimum value of the raw volume
|
|
Packit Service |
db8eaa |
* \param rangemax the maximum value of the raw volume
|
|
Packit Service |
db8eaa |
* \param min the pointer to store the minimum dB value (in 0.01dB unit)
|
|
Packit Service |
db8eaa |
* \param max the pointer to store the maximum dB value (in 0.01dB unit)
|
|
Packit Service |
db8eaa |
* \return 0 if successful, or a negative error code
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_tlv_get_dB_range(unsigned int *tlv, long rangemin, long rangemax,
|
|
Packit Service |
db8eaa |
long *min, long *max)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch (tlv[SNDRV_CTL_TLVO_TYPE]) {
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_RANGE: {
|
|
Packit Service |
db8eaa |
unsigned int pos, len;
|
|
Packit Service |
db8eaa |
len = int_index(tlv[SNDRV_CTL_TLVO_LEN]);
|
|
Packit Service |
db8eaa |
if (len > MAX_TLV_RANGE_SIZE)
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
pos = 2;
|
|
Packit Service |
db8eaa |
while (pos + 4 <= len) {
|
|
Packit Service |
db8eaa |
long rmin, rmax;
|
|
Packit Service |
db8eaa |
long submin, submax;
|
|
Packit Service |
db8eaa |
submin = (int)tlv[pos];
|
|
Packit Service |
db8eaa |
submax = (int)tlv[pos + 1];
|
|
Packit Service |
db8eaa |
if (rangemax < submax)
|
|
Packit Service |
db8eaa |
submax = rangemax;
|
|
Packit Service |
db8eaa |
err = snd_tlv_get_dB_range(tlv + pos + 2,
|
|
Packit Service |
db8eaa |
submin, submax,
|
|
Packit Service |
db8eaa |
&rmin, &rmax);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
if (pos > 2) {
|
|
Packit Service |
db8eaa |
if (rmin < *min)
|
|
Packit Service |
db8eaa |
*min = rmin;
|
|
Packit Service |
db8eaa |
if (rmax > *max)
|
|
Packit Service |
db8eaa |
*max = rmax;
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
*min = rmin;
|
|
Packit Service |
db8eaa |
*max = rmax;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (rangemax == submax)
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
pos += int_index(tlv[pos + 3]) + 4;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_SCALE: {
|
|
Packit Service |
db8eaa |
int step;
|
|
Packit Service |
db8eaa |
if (tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & 0x10000)
|
|
Packit Service |
db8eaa |
*min = SND_CTL_TLV_DB_GAIN_MUTE;
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
*min = (int)tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN];
|
|
Packit Service |
db8eaa |
step = (tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & 0xffff);
|
|
Packit Service |
db8eaa |
*max = (int)tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] +
|
|
Packit Service |
db8eaa |
step * (rangemax - rangemin);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_MINMAX:
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_LINEAR:
|
|
Packit Service |
db8eaa |
*min = (int)tlv[SNDRV_CTL_TLVO_DB_LINEAR_MIN];
|
|
Packit Service |
db8eaa |
*max = (int)tlv[SNDRV_CTL_TLVO_DB_LINEAR_MAX];
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_MINMAX_MUTE:
|
|
Packit Service |
db8eaa |
*min = SND_CTL_TLV_DB_GAIN_MUTE;
|
|
Packit Service |
db8eaa |
*max = (int)tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX];
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Convert the given raw volume value to a dB gain
|
|
Packit Service |
db8eaa |
* \param tlv the TLV source returned by #snd_tlv_parse_dB_info()
|
|
Packit Service |
db8eaa |
* \param rangemin the minimum value of the raw volume
|
|
Packit Service |
db8eaa |
* \param rangemax the maximum value of the raw volume
|
|
Packit Service |
db8eaa |
* \param volume the raw volume value to convert
|
|
Packit Service |
db8eaa |
* \param db_gain the dB gain (in 0.01dB unit)
|
|
Packit Service |
db8eaa |
* \return 0 if successful, or a negative error code
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_tlv_convert_to_dB(unsigned int *tlv, long rangemin, long rangemax,
|
|
Packit Service |
db8eaa |
long volume, long *db_gain)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
unsigned int type = tlv[SNDRV_CTL_TLVO_TYPE];
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch (type) {
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_RANGE: {
|
|
Packit Service |
db8eaa |
unsigned int pos, len;
|
|
Packit Service |
db8eaa |
len = int_index(tlv[SNDRV_CTL_TLVO_LEN]);
|
|
Packit Service |
db8eaa |
if (len > MAX_TLV_RANGE_SIZE)
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
pos = 2;
|
|
Packit Service |
db8eaa |
while (pos + 4 <= len) {
|
|
Packit Service |
db8eaa |
rangemin = (int)tlv[pos];
|
|
Packit Service |
db8eaa |
rangemax = (int)tlv[pos + 1];
|
|
Packit Service |
db8eaa |
if (volume >= rangemin && volume <= rangemax)
|
|
Packit Service |
db8eaa |
return snd_tlv_convert_to_dB(tlv + pos + 2,
|
|
Packit Service |
db8eaa |
rangemin, rangemax,
|
|
Packit Service |
db8eaa |
volume, db_gain);
|
|
Packit Service |
db8eaa |
pos += int_index(tlv[pos + 3]) + 4;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_SCALE: {
|
|
Packit Service |
db8eaa |
int min, step, mute;
|
|
Packit Service |
db8eaa |
min = tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN];
|
|
Packit Service |
db8eaa |
step = (tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & 0xffff);
|
|
Packit Service |
db8eaa |
mute = (tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] >> 16) & 1;
|
|
Packit Service |
db8eaa |
if (mute && volume <= rangemin)
|
|
Packit Service |
db8eaa |
*db_gain = SND_CTL_TLV_DB_GAIN_MUTE;
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
*db_gain = (volume - rangemin) * step + min;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_MINMAX:
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_MINMAX_MUTE: {
|
|
Packit Service |
db8eaa |
int mindb, maxdb;
|
|
Packit Service |
db8eaa |
mindb = tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN];
|
|
Packit Service |
db8eaa |
maxdb = tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX];
|
|
Packit Service |
db8eaa |
if (volume <= rangemin || rangemax <= rangemin) {
|
|
Packit Service |
db8eaa |
if (type == SND_CTL_TLVT_DB_MINMAX_MUTE)
|
|
Packit Service |
db8eaa |
*db_gain = SND_CTL_TLV_DB_GAIN_MUTE;
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
*db_gain = mindb;
|
|
Packit Service |
db8eaa |
} else if (volume >= rangemax)
|
|
Packit Service |
db8eaa |
*db_gain = maxdb;
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
*db_gain = (maxdb - mindb) * (volume - rangemin) /
|
|
Packit Service |
db8eaa |
(rangemax - rangemin) + mindb;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
#ifndef HAVE_SOFT_FLOAT
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_LINEAR: {
|
|
Packit Service |
db8eaa |
int mindb = tlv[SNDRV_CTL_TLVO_DB_LINEAR_MIN];
|
|
Packit Service |
db8eaa |
int maxdb = tlv[SNDRV_CTL_TLVO_DB_LINEAR_MAX];
|
|
Packit Service |
db8eaa |
if (volume <= rangemin || rangemax <= rangemin)
|
|
Packit Service |
db8eaa |
*db_gain = mindb;
|
|
Packit Service |
db8eaa |
else if (volume >= rangemax)
|
|
Packit Service |
db8eaa |
*db_gain = maxdb;
|
|
Packit Service |
db8eaa |
else {
|
|
Packit Service |
db8eaa |
double val = (double)(volume - rangemin) /
|
|
Packit Service |
db8eaa |
(double)(rangemax - rangemin);
|
|
Packit Service |
db8eaa |
if (mindb <= SND_CTL_TLV_DB_GAIN_MUTE)
|
|
Packit Service |
db8eaa |
*db_gain = (long)(100.0 * 20.0 * log10(val)) +
|
|
Packit Service |
db8eaa |
maxdb;
|
|
Packit Service |
db8eaa |
else {
|
|
Packit Service |
db8eaa |
/* FIXME: precalculate and cache these values */
|
|
Packit Service |
db8eaa |
double lmin = pow(10.0, mindb/2000.0);
|
|
Packit Service |
db8eaa |
double lmax = pow(10.0, maxdb/2000.0);
|
|
Packit Service |
db8eaa |
val = (lmax - lmin) * val + lmin;
|
|
Packit Service |
db8eaa |
*db_gain = (long)(100.0 * 20.0 * log10(val));
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Convert from dB gain to the corresponding raw value
|
|
Packit Service |
db8eaa |
* \param tlv the TLV source returned by #snd_tlv_parse_dB_info()
|
|
Packit Service |
db8eaa |
* \param rangemin the minimum value of the raw volume
|
|
Packit Service |
db8eaa |
* \param rangemax the maximum value of the raw volume
|
|
Packit Service |
db8eaa |
* \param db_gain the dB gain to convert (in 0.01dB unit)
|
|
Packit Service |
db8eaa |
* \param value the pointer to store the converted raw volume value
|
|
Packit Service |
db8eaa |
* \param xdir the direction for round-up. The value is round up
|
|
Packit Service |
db8eaa |
* when this is positive. A negative value means round down.
|
|
Packit Service |
db8eaa |
* Zero means round-up to nearest.
|
|
Packit Service |
db8eaa |
* \return 0 if successful, or a negative error code
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax,
|
|
Packit Service |
db8eaa |
long db_gain, long *value, int xdir)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
unsigned int type = tlv[SNDRV_CTL_TLVO_TYPE];
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch (type) {
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_RANGE: {
|
|
Packit Service |
db8eaa |
long dbmin, dbmax, prev_submax;
|
|
Packit Service |
db8eaa |
unsigned int pos, len;
|
|
Packit Service |
db8eaa |
len = int_index(tlv[SNDRV_CTL_TLVO_LEN]);
|
|
Packit Service |
db8eaa |
if (len < 6 || len > MAX_TLV_RANGE_SIZE)
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
pos = 2;
|
|
Packit Service |
db8eaa |
prev_submax = 0;
|
|
Packit Service |
db8eaa |
while (pos + 4 <= len) {
|
|
Packit Service |
db8eaa |
long submin, submax;
|
|
Packit Service |
db8eaa |
submin = (int)tlv[pos];
|
|
Packit Service |
db8eaa |
submax = (int)tlv[pos + 1];
|
|
Packit Service |
db8eaa |
if (rangemax < submax)
|
|
Packit Service |
db8eaa |
submax = rangemax;
|
|
Packit Service |
db8eaa |
if (!snd_tlv_get_dB_range(tlv + pos + 2,
|
|
Packit Service |
db8eaa |
submin, submax,
|
|
Packit Service |
db8eaa |
&dbmin, &dbmax) &&
|
|
Packit Service |
db8eaa |
db_gain >= dbmin && db_gain <= dbmax)
|
|
Packit Service |
db8eaa |
return snd_tlv_convert_from_dB(tlv + pos + 2,
|
|
Packit Service |
db8eaa |
submin, submax,
|
|
Packit Service |
db8eaa |
db_gain, value, xdir);
|
|
Packit Service |
db8eaa |
else if (db_gain < dbmin) {
|
|
Packit Service |
db8eaa |
*value = xdir > 0 || pos == 2 ? submin : prev_submax;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
prev_submax = submax;
|
|
Packit Service |
db8eaa |
if (rangemax == submax)
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
pos += int_index(tlv[pos + 3]) + 4;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
*value = prev_submax;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_SCALE: {
|
|
Packit Service |
db8eaa |
int min, step, max, mute;
|
|
Packit Service |
db8eaa |
min = tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN];
|
|
Packit Service |
db8eaa |
step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & 0xffff;
|
|
Packit Service |
db8eaa |
mute = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & 0x10000;
|
|
Packit Service |
db8eaa |
max = min + (int)(step * (rangemax - rangemin));
|
|
Packit Service |
db8eaa |
if (db_gain <= min)
|
|
Packit Service |
db8eaa |
if (db_gain > SND_CTL_TLV_DB_GAIN_MUTE && xdir > 0 &&
|
|
Packit Service |
db8eaa |
mute)
|
|
Packit Service |
db8eaa |
*value = rangemin + 1;
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
*value = rangemin;
|
|
Packit Service |
db8eaa |
else if (db_gain >= max)
|
|
Packit Service |
db8eaa |
*value = rangemax;
|
|
Packit Service |
db8eaa |
else {
|
|
Packit Service |
db8eaa |
long v = (db_gain - min) * (rangemax - rangemin);
|
|
Packit Service |
db8eaa |
if (xdir > 0)
|
|
Packit Service |
db8eaa |
v += (max - min) - 1;
|
|
Packit Service |
db8eaa |
else if (xdir == 0)
|
|
Packit Service |
db8eaa |
v += ((max - min) + 1) / 2;
|
|
Packit Service |
db8eaa |
v = v / (max - min) + rangemin;
|
|
Packit Service |
db8eaa |
*value = v;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_MINMAX:
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_MINMAX_MUTE: {
|
|
Packit Service |
db8eaa |
int min, max;
|
|
Packit Service |
db8eaa |
min = tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN];
|
|
Packit Service |
db8eaa |
max = tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX];
|
|
Packit Service |
db8eaa |
if (db_gain <= min)
|
|
Packit Service |
db8eaa |
if (db_gain > SND_CTL_TLV_DB_GAIN_MUTE && xdir > 0 &&
|
|
Packit Service |
db8eaa |
type == SND_CTL_TLVT_DB_MINMAX_MUTE)
|
|
Packit Service |
db8eaa |
*value = rangemin + 1;
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
*value = rangemin;
|
|
Packit Service |
db8eaa |
else if (db_gain >= max)
|
|
Packit Service |
db8eaa |
*value = rangemax;
|
|
Packit Service |
db8eaa |
else {
|
|
Packit Service |
db8eaa |
long v = (db_gain - min) * (rangemax - rangemin);
|
|
Packit Service |
db8eaa |
if (xdir > 0)
|
|
Packit Service |
db8eaa |
v += (max - min) - 1;
|
|
Packit Service |
db8eaa |
else if (xdir == 0)
|
|
Packit Service |
db8eaa |
v += ((max - min) + 1) / 2;
|
|
Packit Service |
db8eaa |
v = v / (max - min) + rangemin;
|
|
Packit Service |
db8eaa |
*value = v;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
#ifndef HAVE_SOFT_FLOAT
|
|
Packit Service |
db8eaa |
case SND_CTL_TLVT_DB_LINEAR: {
|
|
Packit Service |
db8eaa |
int min, max;
|
|
Packit Service |
db8eaa |
min = tlv[SNDRV_CTL_TLVO_DB_LINEAR_MIN];
|
|
Packit Service |
db8eaa |
max = tlv[SNDRV_CTL_TLVO_DB_LINEAR_MAX];
|
|
Packit Service |
db8eaa |
if (db_gain <= min)
|
|
Packit Service |
db8eaa |
*value = rangemin;
|
|
Packit Service |
db8eaa |
else if (db_gain >= max)
|
|
Packit Service |
db8eaa |
*value = rangemax;
|
|
Packit Service |
db8eaa |
else {
|
|
Packit Service |
db8eaa |
/* FIXME: precalculate and cache vmin and vmax */
|
|
Packit Service |
db8eaa |
double vmin, vmax, v;
|
|
Packit Service |
db8eaa |
vmin = (min <= SND_CTL_TLV_DB_GAIN_MUTE) ? 0.0 :
|
|
Packit Service |
db8eaa |
pow(10.0, (double)min / 2000.0);
|
|
Packit Service |
db8eaa |
vmax = !max ? 1.0 : pow(10.0, (double)max / 2000.0);
|
|
Packit Service |
db8eaa |
v = pow(10.0, (double)db_gain / 2000.0);
|
|
Packit Service |
db8eaa |
v = (v - vmin) * (rangemax - rangemin) / (vmax - vmin);
|
|
Packit Service |
db8eaa |
if (xdir > 0)
|
|
Packit Service |
db8eaa |
v = ceil(v);
|
|
Packit Service |
db8eaa |
else if (xdir == 0)
|
|
Packit Service |
db8eaa |
v = lrint(v);
|
|
Packit Service |
db8eaa |
*value = (long)v + rangemin;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
#define TEMP_TLV_SIZE 4096
|
|
Packit Service |
db8eaa |
struct tlv_info {
|
|
Packit Service |
db8eaa |
long minval, maxval;
|
|
Packit Service |
db8eaa |
unsigned int *tlv;
|
|
Packit Service |
db8eaa |
unsigned int buf[TEMP_TLV_SIZE];
|
|
Packit Service |
db8eaa |
};
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int get_tlv_info(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
|
|
Packit Service |
db8eaa |
struct tlv_info *rec)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_ctl_elem_info_t info = {0};
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_ctl_elem_info_set_id(&info, id);
|
|
Packit Service |
db8eaa |
err = snd_ctl_elem_info(ctl, &info;;
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
if (!snd_ctl_elem_info_is_tlv_readable(&info))
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
if (snd_ctl_elem_info_get_type(&info) != SND_CTL_ELEM_TYPE_INTEGER)
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
rec->minval = snd_ctl_elem_info_get_min(&info;;
|
|
Packit Service |
db8eaa |
rec->maxval = snd_ctl_elem_info_get_max(&info;;
|
|
Packit Service |
db8eaa |
err = snd_ctl_elem_tlv_read(ctl, id, rec->buf, sizeof(rec->buf));
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
err = snd_tlv_parse_dB_info(rec->buf, sizeof(rec->buf), &rec->tlv);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Get the dB min/max values on the given control element
|
|
Packit Service |
db8eaa |
* \param ctl the control handler
|
|
Packit Service |
db8eaa |
* \param id the element id
|
|
Packit Service |
db8eaa |
* \param min the pointer to store the minimum dB value (in 0.01dB unit)
|
|
Packit Service |
db8eaa |
* \param max the pointer to store the maximum dB value (in 0.01dB unit)
|
|
Packit Service |
db8eaa |
* \return 0 if successful, or a negative error code
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_ctl_get_dB_range(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
|
|
Packit Service |
db8eaa |
long *min, long *max)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
struct tlv_info info;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
err = get_tlv_info(ctl, id, &info;;
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
return snd_tlv_get_dB_range(info.tlv, info.minval, info.maxval,
|
|
Packit Service |
db8eaa |
min, max);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Convert the volume value to dB on the given control element
|
|
Packit Service |
db8eaa |
* \param ctl the control handler
|
|
Packit Service |
db8eaa |
* \param id the element id
|
|
Packit Service |
db8eaa |
* \param volume the raw volume value to convert
|
|
Packit Service |
db8eaa |
* \param db_gain the dB gain (in 0.01dB unit)
|
|
Packit Service |
db8eaa |
* \return 0 if successful, or a negative error code
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_ctl_convert_to_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
|
|
Packit Service |
db8eaa |
long volume, long *db_gain)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
struct tlv_info info;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
err = get_tlv_info(ctl, id, &info;;
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
return snd_tlv_convert_to_dB(info.tlv, info.minval, info.maxval,
|
|
Packit Service |
db8eaa |
volume, db_gain);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Convert from dB gain to the raw volume value on the given control element
|
|
Packit Service |
db8eaa |
* \param ctl the control handler
|
|
Packit Service |
db8eaa |
* \param id the element id
|
|
Packit Service |
db8eaa |
* \param db_gain the dB gain to convert (in 0.01dB unit)
|
|
Packit Service |
db8eaa |
* \param value the pointer to store the converted raw volume value
|
|
Packit Service |
db8eaa |
* \param xdir the direction for round-up. The value is round up
|
|
Packit Service |
db8eaa |
* when this is positive.
|
|
Packit Service |
db8eaa |
* \return 0 if successful, or a negative error code
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_ctl_convert_from_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
|
|
Packit Service |
db8eaa |
long db_gain, long *value, int xdir)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
struct tlv_info info;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
err = get_tlv_info(ctl, id, &info;;
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
return snd_tlv_convert_from_dB(info.tlv, info.minval, info.maxval,
|
|
Packit Service |
db8eaa |
db_gain, value, xdir);
|
|
Packit Service |
db8eaa |
}
|