* [RFC] dB range compound
@ 2006-09-01 17:46 Takashi Iwai
2006-09-04 12:40 ` James Courtier-Dutton
0 siblings, 1 reply; 5+ messages in thread
From: Takashi Iwai @ 2006-09-01 17:46 UTC (permalink / raw)
To: alsa-devel
[-- Attachment #1: Type: text/plain, Size: 482 bytes --]
Hi,
I found that some hardwares require the combination of dB scale
ranges. For example, es1938 has volume controls with two curves,
e.g. the volume step in the value 0-8 is 3 dB while it's 1.5 dB in the
values 8-15.
The patch below introduces a new TLV type, DB_RANGE, to implement
the compound dB scales. It can contain one or more child TLVs,
together with min and max values for each.
The first patch is for the kernel and the second for alsa-lib.
Any comments?
Takashi
[-- Attachment #2: tlv-range-kernel.diff --]
[-- Type: application/octet-stream, Size: 8101 bytes --]
diff -r a42a910c5302 include/tlv.h
--- a/include/tlv.h Fri Sep 01 17:09:44 2006 +0200
+++ b/include/tlv.h Fri Sep 01 19:11:32 2006 +0200
@@ -34,19 +34,26 @@
#define SNDRV_CTL_TLVT_CONTAINER 0 /* one level down - group of TLVs */
#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */
#define SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */
+#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */
+#define TLV_DB_SCALE_ITEM(min, step, mute) \
+ SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \
+ (min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0)
#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
-unsigned int name[] = { \
- SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \
- (min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0) \
-}
+ unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
/* linear volume between min_dB and max_dB (.01dB unit) */
+#define TLV_DB_LINEAR_ITEM(min_dB, max_dB) \
+ SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \
+ (min_dB), (max_dB)
#define DECLARE_TLV_DB_LINEAR(name, min_dB, max_dB) \
-unsigned int name[] = { \
- SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \
- (min_dB), (max_dB) \
-}
+ unsigned int name[] = { TLV_DB_LINEAR_ITEM(min_dB, max_dB) }
+
+/* dB range container */
+/* Each item is: <min> <max> <TLV> */
+/* The below assumes that each item TLV is 4 words like DB_SCALE or LINEAR */
+#define TLV_DB_RANGE_HEAD(num) \
+ SNDRV_CTL_TLVT_DB_RANGE, 6 * (num) * sizeof(unsigned int)
#define TLV_DB_GAIN_MUTE -9999999
diff -r a42a910c5302 pci/es1938.c
--- a/pci/es1938.c Fri Sep 01 17:09:44 2006 +0200
+++ b/pci/es1938.c Fri Sep 01 19:11:11 2006 +0200
@@ -62,6 +62,7 @@
#include <sound/opl3.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include <asm/io.h>
@@ -1164,6 +1165,14 @@ static int snd_es1938_reg_read(struct es
return snd_es1938_read(chip, reg);
}
+#define ES1938_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\
+ .name = xname, .index = xindex, \
+ .info = snd_es1938_info_single, \
+ .get = snd_es1938_get_single, .put = snd_es1938_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+ .tlv = { .p = xtlv } }
#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es1938_info_single, \
@@ -1217,6 +1226,14 @@ static int snd_es1938_put_single(struct
return snd_es1938_reg_bits(chip, reg, mask, val) != val;
}
+#define ES1938_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\
+ .name = xname, .index = xindex, \
+ .info = snd_es1938_info_double, \
+ .get = snd_es1938_get_double, .put = snd_es1938_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
+ .tlv = { .p = xtlv } }
#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es1938_info_double, \
@@ -1297,8 +1314,41 @@ static int snd_es1938_put_double(struct
return change;
}
+static unsigned int db_scale_master[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 54, TLV_DB_SCALE_ITEM(-3600, 50, 1),
+ 54, 63, TLV_DB_SCALE_ITEM(-900, 100, 0),
+};
+
+static unsigned int db_scale_audio1[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 8, TLV_DB_SCALE_ITEM(-3300, 300, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-900, 150, 0),
+};
+
+static unsigned int db_scale_audio2[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 8, TLV_DB_SCALE_ITEM(-3450, 300, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-1050, 150, 0),
+};
+
+static unsigned int db_scale_mic[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 8, TLV_DB_SCALE_ITEM(-2400, 300, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(0, 150, 0),
+};
+
+static unsigned int db_scale_line[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 8, TLV_DB_SCALE_ITEM(-3150, 300, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0),
+};
+
+static DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0);
+
static struct snd_kcontrol_new snd_es1938_controls[] = {
-ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
+ES1938_DOUBLE_TLV("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0,
+ db_scale_master),
ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1309,19 +1359,28 @@ ES1938_DOUBLE("Master Playback Switch",
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Hardware Master Playback Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.info = snd_es1938_info_hw_switch,
.get = snd_es1938_get_hw_switch,
+ .tlv = { .p = db_scale_master },
},
ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0),
-ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
+ES1938_DOUBLE_TLV("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0,
+ db_scale_line),
ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
-ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
-ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
-ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
-ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
-ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
+ES1938_DOUBLE_TLV("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0,
+ db_scale_mic),
+ES1938_DOUBLE_TLV("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0,
+ db_scale_mic),
+ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0,
+ db_scale_capture),
ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
@@ -1332,16 +1391,26 @@ ES1938_SINGLE("Capture Switch", 0, 0x1c,
.get = snd_es1938_get_mux,
.put = snd_es1938_put_mux,
},
-ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
-ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
-ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
-ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
-ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
-ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0),
-ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
-ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0),
-ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
-ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0),
+ES1938_DOUBLE_TLV("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0,
+ db_scale_audio2),
+ES1938_DOUBLE_TLV("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0,
+ db_scale_mic),
+ES1938_DOUBLE_TLV("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0,
+ db_scale_mic),
+ES1938_DOUBLE_TLV("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0,
+ db_scale_audio2),
+ES1938_DOUBLE_TLV("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0,
+ db_scale_audio1),
ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
[-- Attachment #3: tlv-range-lib.diff --]
[-- Type: application/octet-stream, Size: 8436 bytes --]
diff -r 0c766669b691 include/control.h
--- a/include/control.h Thu Aug 31 10:06:08 2006 +0200
+++ b/include/control.h Fri Sep 01 19:12:02 2006 +0200
@@ -172,6 +172,8 @@ typedef enum _snd_ctl_event_type {
#define SND_CTL_TLVT_DB_SCALE 0x0001
/** TLV type - linear volume */
#define SND_CTL_TLVT_DB_LINEAR 0x0002
+/** TLV type - dB range container */
+#define SND_CTL_TLVT_DB_RANGE 0x0003
/** Mute state */
#define SND_CTL_TLV_DB_GAIN_MUTE -9999999
diff -r 0c766669b691 src/mixer/simple_none.c
--- a/src/mixer/simple_none.c Thu Aug 31 10:06:08 2006 +0200
+++ b/src/mixer/simple_none.c Fri Sep 01 19:12:02 2006 +0200
@@ -1011,7 +1011,13 @@ static int parse_db_range(struct selem_s
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR:
#endif
- if (size < 2 * sizeof(int)) {
+ 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;
}
@@ -1020,6 +1026,7 @@ static int parse_db_range(struct selem_s
return -ENOMEM;
memcpy(rec->db_info, tlv, size + sizeof(int) * 2);
return 0;
+ }
default:
break;
}
@@ -1029,35 +1036,47 @@ static int parse_db_range(struct selem_s
/* convert the given raw volume value to a dB gain
*/
-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)
+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 = tlv[1] / sizeof(int);
+ 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 += tlv[pos + 3] + 4;
+ }
return -EINVAL;
-
- switch (rec->db_info[0]) {
+ }
case SND_CTL_TLVT_DB_SCALE: {
int min, step, mute;
- min = rec->db_info[2];
- step = (rec->db_info[3] & 0xffff);
- mute = (rec->db_info[3] >> 16) & 1;
- if (mute && volume == rec->min)
+ 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 - rec->min) * step + min;
+ *db_gain = (volume - rangemin) * step + min;
return 0;
}
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR: {
- int mindb = rec->db_info[2];
- int maxdb = rec->db_info[3];
- if (volume <= rec->min || rec->max <= rec->min)
+ int mindb = tlv[2];
+ int maxdb = tlv[3];
+ if (volume <= rangemin || rangemax <= rangemin)
*db_gain = mindb;
- else if (volume >= rec->max)
+ else if (volume >= rangemax)
*db_gain = maxdb;
else {
- double val = (double)(volume - rec->min) /
- (double)(rec->max - rec->min);
+ 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;
@@ -1074,6 +1093,15 @@ static int convert_to_dB(snd_hctl_elem_t
#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);
}
/* initialize dB range information, reading TLV via hcontrol
@@ -1133,26 +1161,55 @@ static selem_ctl_t *get_selem_ctl(selem_
/* 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 = tlv[1] / sizeof(int);
+ 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 += 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)
{
- int step;
-
if (init_db_range(ctl, rec) < 0)
return -EINVAL;
- switch (rec->db_info[0]) {
- case SND_CTL_TLVT_DB_SCALE:
- *min = (int)rec->db_info[2];
- step = (rec->db_info[3] & 0xffff);
- *max = *min + (long)(step * (rec->max - rec->min));
- return 0;
- case SND_CTL_TLVT_DB_LINEAR:
- *min = (int)rec->db_info[2];
- *max = (int)rec->db_info[3];
- return 0;
- }
- return -EINVAL;
+ return do_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,
@@ -1160,7 +1217,6 @@ static int get_dB_range_ops(snd_mixer_el
{
selem_none_t *s = snd_mixer_elem_get_private(elem);
selem_ctl_t *c;
- int err;
c = get_selem_ctl(s, dir);
if (! c)
@@ -1171,27 +1227,42 @@ static int get_dB_range_ops(snd_mixer_el
/* Convert from dB gain to the corresponding raw value.
* The value is round up when xdir > 0.
*/
-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)
+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 = tlv[1] / sizeof(int);
+ 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 += tlv[pos + 3] + 4;
+ }
return -EINVAL;
-
- switch (rec->db_info[0]) {
+ }
case SND_CTL_TLVT_DB_SCALE: {
int min, step, max;
- min = rec->db_info[2];
- step = (rec->db_info[3] & 0xffff);
- max = min + (int)(step * rec->max);
+ min = tlv[2];
+ step = (tlv[3] & 0xffff);
+ max = min + (int)(step * rangemax);
if (db_gain <= min)
- *value = rec->min;
+ *value = rangemin;
else if (db_gain >= max)
- *value = rec->max;
+ *value = rangemax;
else {
- long v = (db_gain - min) * (rec->max - rec->min);
+ long v = (db_gain - min) * (rangemax - rangemin);
if (xdir > 0)
v += (max - min) - 1;
- v = v / (max - min) + rec->min;
+ v = v / (max - min) + rangemin;
*value = v;
}
return 0;
@@ -1199,12 +1270,12 @@ static int convert_from_dB(snd_hctl_elem
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR: {
int min, max;
- min = rec->db_info[2];
- max = rec->db_info[3];
+ min = tlv[2];
+ max = tlv[3];
if (db_gain <= min)
- *value = rec->min;
+ *value = rangemin;
else if (db_gain >= max)
- *value = rec->max;
+ *value = rangemax;
else {
/* FIXME: precalculate and cache vmin and vmax */
double vmin, vmax, v;
@@ -1212,10 +1283,10 @@ static int convert_from_dB(snd_hctl_elem
pow(10.0, (double)min / 20.0);
vmax = !max ? 1.0 : pow(10.0, (double)max / 20.0);
v = pow(10.0, (double)db_gain / 20.0);
- v = (v - vmin) * (rec->max - rec->min) / (vmax - vmin);
+ v = (v - vmin) * (rangemax - rangemin) / (vmax - vmin);
if (xdir > 0)
v = ceil(v);
- *value = (long)v + rec->min;
+ *value = (long)v + rangemin;
}
return 0;
}
@@ -1224,6 +1295,16 @@ static int convert_from_dB(snd_hctl_elem
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);
}
static int get_dB_ops(snd_mixer_elem_t *elem,
[-- Attachment #4: Type: text/plain, Size: 373 bytes --]
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
[-- Attachment #5: Type: text/plain, Size: 161 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/alsa-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC] dB range compound
2006-09-01 17:46 [RFC] dB range compound Takashi Iwai
@ 2006-09-04 12:40 ` James Courtier-Dutton
2006-09-04 13:31 ` Takashi Iwai
0 siblings, 1 reply; 5+ messages in thread
From: James Courtier-Dutton @ 2006-09-04 12:40 UTC (permalink / raw)
To: Takashi Iwai; +Cc: alsa-devel
Takashi Iwai wrote:
> Hi,
>
> I found that some hardwares require the combination of dB scale
> ranges. For example, es1938 has volume controls with two curves,
> e.g. the volume step in the value 0-8 is 3 dB while it's 1.5 dB in the
> values 8-15.
>
> The patch below introduces a new TLV type, DB_RANGE, to implement
> the compound dB scales. It can contain one or more child TLVs,
> together with min and max values for each.
>
> The first patch is for the kernel and the second for alsa-lib.
>
> Any comments?
>
>
> Takashi
>
+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 = tlv[1] / sizeof(int);
+ pos = 2;
Be careful with this len limit in the while loop. I suggest adding a
further comparison to limit the loop to a constant size:
e.g.
while (pos + 4 <= len && (pos + 4 < MAX_BUFFER_SIZE) ) {
There are similar cases elsewhere in the patch.
+ 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 += tlv[pos + 3] + 4;
+ }
return -EINVAL;
Apart from that, It looks OK to me.
With regard to this control actually being to separate register in the
chip, I think it might be nice to implement two separate controls
between kernel and alsa-lib, but add some TLV tag to link the two
controls together so that they appear as one. This will save code size
in the kernel.
For example, with the EMU DSPs one control can do 0 dB to -inf gain, and
another can to 0 dB to X gain. I was thinking of implementing these in
the emu10k1 module, with special code to link the two at the kernel
module level, but it might save code duplication if we let alsa-lib do
this task.
We might be able to do a similar link between mic gain and mic boost
switches.
James
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC] dB range compound
2006-09-04 12:40 ` James Courtier-Dutton
@ 2006-09-04 13:31 ` Takashi Iwai
2006-09-04 18:40 ` Takashi Iwai
0 siblings, 1 reply; 5+ messages in thread
From: Takashi Iwai @ 2006-09-04 13:31 UTC (permalink / raw)
To: James Courtier-Dutton; +Cc: alsa-devel
At Mon, 04 Sep 2006 13:40:13 +0100,
James Courtier-Dutton wrote:
>
> Takashi Iwai wrote:
> > Hi,
> >
> > I found that some hardwares require the combination of dB scale
> > ranges. For example, es1938 has volume controls with two curves,
> > e.g. the volume step in the value 0-8 is 3 dB while it's 1.5 dB in the
> > values 8-15.
> >
> > The patch below introduces a new TLV type, DB_RANGE, to implement
> > the compound dB scales. It can contain one or more child TLVs,
> > together with min and max values for each.
> >
> > The first patch is for the kernel and the second for alsa-lib.
> >
> > Any comments?
> >
> >
> > Takashi
> >
> +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 = tlv[1] / sizeof(int);
> + pos = 2;
>
> Be careful with this len limit in the while loop. I suggest adding a
> further comparison to limit the loop to a constant size:
> e.g.
> while (pos + 4 <= len && (pos + 4 < MAX_BUFFER_SIZE) ) {
> There are similar cases elsewhere in the patch.
Yes, it'd be safer. Will fix them.
>
> + 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 += tlv[pos + 3] + 4;
> + }
> return -EINVAL;
>
>
> Apart from that, It looks OK to me.
>
> With regard to this control actually being to separate register in the
> chip, I think it might be nice to implement two separate controls
> between kernel and alsa-lib, but add some TLV tag to link the two
> controls together so that they appear as one. This will save code size
> in the kernel.
Agreed, this should really depend on the implementation.
In the case of es1938, however, it's clearly a single control (just
the dB scale curve is non-linear).
Thanks,
Takashi
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC] dB range compound
2006-09-04 13:31 ` Takashi Iwai
@ 2006-09-04 18:40 ` Takashi Iwai
2006-09-05 14:36 ` Takashi Iwai
0 siblings, 1 reply; 5+ messages in thread
From: Takashi Iwai @ 2006-09-04 18:40 UTC (permalink / raw)
To: James Courtier-Dutton; +Cc: alsa-devel
At Mon, 04 Sep 2006 15:31:31 +0200,
I wrote:
>
> At Mon, 04 Sep 2006 13:40:13 +0100,
> James Courtier-Dutton wrote:
> >
> > Takashi Iwai wrote:
> > > Hi,
> > >
> > > I found that some hardwares require the combination of dB scale
> > > ranges. For example, es1938 has volume controls with two curves,
> > > e.g. the volume step in the value 0-8 is 3 dB while it's 1.5 dB in the
> > > values 8-15.
> > >
> > > The patch below introduces a new TLV type, DB_RANGE, to implement
> > > the compound dB scales. It can contain one or more child TLVs,
> > > together with min and max values for each.
> > >
> > > The first patch is for the kernel and the second for alsa-lib.
> > >
> > > Any comments?
> > >
> > >
> > > Takashi
> > >
> > +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 = tlv[1] / sizeof(int);
> > + pos = 2;
> >
> > Be careful with this len limit in the while loop. I suggest adding a
> > further comparison to limit the loop to a constant size:
> > e.g.
> > while (pos + 4 <= len && (pos + 4 < MAX_BUFFER_SIZE) ) {
> > There are similar cases elsewhere in the patch.
>
> Yes, it'd be safer. Will fix them.
The below is the fixed version.
Takashi
diff -r 0c766669b691 include/control.h
--- a/include/control.h Thu Aug 31 10:06:08 2006 +0200
+++ b/include/control.h Fri Sep 01 19:12:02 2006 +0200
@@ -172,6 +172,8 @@ typedef enum _snd_ctl_event_type {
#define SND_CTL_TLVT_DB_SCALE 0x0001
/** TLV type - linear volume */
#define SND_CTL_TLVT_DB_LINEAR 0x0002
+/** TLV type - dB range container */
+#define SND_CTL_TLVT_DB_RANGE 0x0003
/** Mute state */
#define SND_CTL_TLV_DB_GAIN_MUTE -9999999
diff -r 0c766669b691 src/mixer/simple_none.c
--- a/src/mixer/simple_none.c Thu Aug 31 10:06:08 2006 +0200
+++ b/src/mixer/simple_none.c Mon Sep 04 16:54:31 2006 +0200
@@ -974,6 +974,9 @@ static int init_db_range(snd_hctl_elem_t
/* 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
+
/* parse TLV stream and retrieve dB information
* return 0 if successly found and stored to rec,
* return 1 if no information is found,
@@ -1011,8 +1014,18 @@ static int parse_db_range(struct selem_s
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR:
#endif
- if (size < 2 * sizeof(int)) {
+ 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);
@@ -1020,6 +1033,7 @@ static int parse_db_range(struct selem_s
return -ENOMEM;
memcpy(rec->db_info, tlv, size + sizeof(int) * 2);
return 0;
+ }
default:
break;
}
@@ -1029,35 +1043,49 @@ static int parse_db_range(struct selem_s
/* convert the given raw volume value to a dB gain
*/
-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)
+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 = tlv[1] / sizeof(int);
+ 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 += tlv[pos + 3] + 4;
+ }
return -EINVAL;
-
- switch (rec->db_info[0]) {
+ }
case SND_CTL_TLVT_DB_SCALE: {
int min, step, mute;
- min = rec->db_info[2];
- step = (rec->db_info[3] & 0xffff);
- mute = (rec->db_info[3] >> 16) & 1;
- if (mute && volume == rec->min)
+ 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 - rec->min) * step + min;
+ *db_gain = (volume - rangemin) * step + min;
return 0;
}
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR: {
- int mindb = rec->db_info[2];
- int maxdb = rec->db_info[3];
- if (volume <= rec->min || rec->max <= rec->min)
+ int mindb = tlv[2];
+ int maxdb = tlv[3];
+ if (volume <= rangemin || rangemax <= rangemin)
*db_gain = mindb;
- else if (volume >= rec->max)
+ else if (volume >= rangemax)
*db_gain = maxdb;
else {
- double val = (double)(volume - rec->min) /
- (double)(rec->max - rec->min);
+ 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;
@@ -1074,6 +1102,15 @@ static int convert_to_dB(snd_hctl_elem_t
#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);
}
/* initialize dB range information, reading TLV via hcontrol
@@ -1133,26 +1170,57 @@ static selem_ctl_t *get_selem_ctl(selem_
/* 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 = tlv[1] / sizeof(int);
+ 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 += 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)
{
- int step;
-
if (init_db_range(ctl, rec) < 0)
return -EINVAL;
- switch (rec->db_info[0]) {
- case SND_CTL_TLVT_DB_SCALE:
- *min = (int)rec->db_info[2];
- step = (rec->db_info[3] & 0xffff);
- *max = *min + (long)(step * (rec->max - rec->min));
- return 0;
- case SND_CTL_TLVT_DB_LINEAR:
- *min = (int)rec->db_info[2];
- *max = (int)rec->db_info[3];
- return 0;
- }
- return -EINVAL;
+ return do_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,
@@ -1160,7 +1228,6 @@ static int get_dB_range_ops(snd_mixer_el
{
selem_none_t *s = snd_mixer_elem_get_private(elem);
selem_ctl_t *c;
- int err;
c = get_selem_ctl(s, dir);
if (! c)
@@ -1171,27 +1238,44 @@ static int get_dB_range_ops(snd_mixer_el
/* Convert from dB gain to the corresponding raw value.
* The value is round up when xdir > 0.
*/
-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)
+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 = tlv[1] / sizeof(int);
+ 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 += tlv[pos + 3] + 4;
+ }
return -EINVAL;
-
- switch (rec->db_info[0]) {
+ }
case SND_CTL_TLVT_DB_SCALE: {
int min, step, max;
- min = rec->db_info[2];
- step = (rec->db_info[3] & 0xffff);
- max = min + (int)(step * rec->max);
+ min = tlv[2];
+ step = (tlv[3] & 0xffff);
+ max = min + (int)(step * rangemax);
if (db_gain <= min)
- *value = rec->min;
+ *value = rangemin;
else if (db_gain >= max)
- *value = rec->max;
+ *value = rangemax;
else {
- long v = (db_gain - min) * (rec->max - rec->min);
+ long v = (db_gain - min) * (rangemax - rangemin);
if (xdir > 0)
v += (max - min) - 1;
- v = v / (max - min) + rec->min;
+ v = v / (max - min) + rangemin;
*value = v;
}
return 0;
@@ -1199,12 +1283,12 @@ static int convert_from_dB(snd_hctl_elem
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR: {
int min, max;
- min = rec->db_info[2];
- max = rec->db_info[3];
+ min = tlv[2];
+ max = tlv[3];
if (db_gain <= min)
- *value = rec->min;
+ *value = rangemin;
else if (db_gain >= max)
- *value = rec->max;
+ *value = rangemax;
else {
/* FIXME: precalculate and cache vmin and vmax */
double vmin, vmax, v;
@@ -1212,10 +1296,10 @@ static int convert_from_dB(snd_hctl_elem
pow(10.0, (double)min / 20.0);
vmax = !max ? 1.0 : pow(10.0, (double)max / 20.0);
v = pow(10.0, (double)db_gain / 20.0);
- v = (v - vmin) * (rec->max - rec->min) / (vmax - vmin);
+ v = (v - vmin) * (rangemax - rangemin) / (vmax - vmin);
if (xdir > 0)
v = ceil(v);
- *value = (long)v + rec->min;
+ *value = (long)v + rangemin;
}
return 0;
}
@@ -1224,6 +1308,16 @@ static int convert_from_dB(snd_hctl_elem
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);
}
static int get_dB_ops(snd_mixer_elem_t *elem,
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC] dB range compound
2006-09-04 18:40 ` Takashi Iwai
@ 2006-09-05 14:36 ` Takashi Iwai
0 siblings, 0 replies; 5+ messages in thread
From: Takashi Iwai @ 2006-09-05 14:36 UTC (permalink / raw)
To: James Courtier-Dutton; +Cc: alsa-devel
At Mon, 04 Sep 2006 20:40:04 +0200,
I wrote:
>
> At Mon, 04 Sep 2006 15:31:31 +0200,
> I wrote:
> >
> > At Mon, 04 Sep 2006 13:40:13 +0100,
> > James Courtier-Dutton wrote:
> > >
> > > Takashi Iwai wrote:
> > > > Hi,
> > > >
> > > > I found that some hardwares require the combination of dB scale
> > > > ranges. For example, es1938 has volume controls with two curves,
> > > > e.g. the volume step in the value 0-8 is 3 dB while it's 1.5 dB in the
> > > > values 8-15.
> > > >
> > > > The patch below introduces a new TLV type, DB_RANGE, to implement
> > > > the compound dB scales. It can contain one or more child TLVs,
> > > > together with min and max values for each.
> > > >
> > > > The first patch is for the kernel and the second for alsa-lib.
> > > >
> > > > Any comments?
> > > >
> > > >
> > > > Takashi
> > > >
> > > +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 = tlv[1] / sizeof(int);
> > > + pos = 2;
> > >
> > > Be careful with this len limit in the while loop. I suggest adding a
> > > further comparison to limit the loop to a constant size:
> > > e.g.
> > > while (pos + 4 <= len && (pos + 4 < MAX_BUFFER_SIZE) ) {
> > > There are similar cases elsewhere in the patch.
> >
> > Yes, it'd be safer. Will fix them.
>
> The below is the fixed version.
... and it was buggy. The version below is the correct one.
I'll commit it if no further objection arises.
Takashi
diff -r 0c766669b691 include/control.h
--- a/include/control.h Thu Aug 31 10:06:08 2006 +0200
+++ b/include/control.h Fri Sep 01 19:12:02 2006 +0200
@@ -172,6 +172,8 @@ typedef enum _snd_ctl_event_type {
#define SND_CTL_TLVT_DB_SCALE 0x0001
/** TLV type - linear volume */
#define SND_CTL_TLVT_DB_LINEAR 0x0002
+/** TLV type - dB range container */
+#define SND_CTL_TLVT_DB_RANGE 0x0003
/** Mute state */
#define SND_CTL_TLV_DB_GAIN_MUTE -9999999
diff -r 0c766669b691 src/mixer/simple_none.c
--- a/src/mixer/simple_none.c Thu Aug 31 10:06:08 2006 +0200
+++ b/src/mixer/simple_none.c Tue Sep 05 16:37:47 2006 +0200
@@ -974,6 +974,9 @@ static int init_db_range(snd_hctl_elem_t
/* 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
+
/* parse TLV stream and retrieve dB information
* return 0 if successly found and stored to rec,
* return 1 if no information is found,
@@ -1011,8 +1014,18 @@ static int parse_db_range(struct selem_s
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR:
#endif
- if (size < 2 * sizeof(int)) {
+ 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);
@@ -1020,6 +1033,7 @@ static int parse_db_range(struct selem_s
return -ENOMEM;
memcpy(rec->db_info, tlv, size + sizeof(int) * 2);
return 0;
+ }
default:
break;
}
@@ -1029,35 +1043,49 @@ static int parse_db_range(struct selem_s
/* convert the given raw volume value to a dB gain
*/
-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)
+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;
-
- switch (rec->db_info[0]) {
+ }
case SND_CTL_TLVT_DB_SCALE: {
int min, step, mute;
- min = rec->db_info[2];
- step = (rec->db_info[3] & 0xffff);
- mute = (rec->db_info[3] >> 16) & 1;
- if (mute && volume == rec->min)
+ 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 - rec->min) * step + min;
+ *db_gain = (volume - rangemin) * step + min;
return 0;
}
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR: {
- int mindb = rec->db_info[2];
- int maxdb = rec->db_info[3];
- if (volume <= rec->min || rec->max <= rec->min)
+ int mindb = tlv[2];
+ int maxdb = tlv[3];
+ if (volume <= rangemin || rangemax <= rangemin)
*db_gain = mindb;
- else if (volume >= rec->max)
+ else if (volume >= rangemax)
*db_gain = maxdb;
else {
- double val = (double)(volume - rec->min) /
- (double)(rec->max - rec->min);
+ 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;
@@ -1074,6 +1102,15 @@ static int convert_to_dB(snd_hctl_elem_t
#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);
}
/* initialize dB range information, reading TLV via hcontrol
@@ -1133,26 +1170,57 @@ static selem_ctl_t *get_selem_ctl(selem_
/* 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)
{
- int step;
-
if (init_db_range(ctl, rec) < 0)
return -EINVAL;
- switch (rec->db_info[0]) {
- case SND_CTL_TLVT_DB_SCALE:
- *min = (int)rec->db_info[2];
- step = (rec->db_info[3] & 0xffff);
- *max = *min + (long)(step * (rec->max - rec->min));
- return 0;
- case SND_CTL_TLVT_DB_LINEAR:
- *min = (int)rec->db_info[2];
- *max = (int)rec->db_info[3];
- return 0;
- }
- return -EINVAL;
+ return do_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,
@@ -1160,7 +1228,6 @@ static int get_dB_range_ops(snd_mixer_el
{
selem_none_t *s = snd_mixer_elem_get_private(elem);
selem_ctl_t *c;
- int err;
c = get_selem_ctl(s, dir);
if (! c)
@@ -1171,27 +1238,44 @@ static int get_dB_range_ops(snd_mixer_el
/* Convert from dB gain to the corresponding raw value.
* The value is round up when xdir > 0.
*/
-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)
+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;
-
- switch (rec->db_info[0]) {
+ }
case SND_CTL_TLVT_DB_SCALE: {
int min, step, max;
- min = rec->db_info[2];
- step = (rec->db_info[3] & 0xffff);
- max = min + (int)(step * rec->max);
+ min = tlv[2];
+ step = (tlv[3] & 0xffff);
+ max = min + (int)(step * (rangemax - rangemin));
if (db_gain <= min)
- *value = rec->min;
+ *value = rangemin;
else if (db_gain >= max)
- *value = rec->max;
+ *value = rangemax;
else {
- long v = (db_gain - min) * (rec->max - rec->min);
+ long v = (db_gain - min) * (rangemax - rangemin);
if (xdir > 0)
v += (max - min) - 1;
- v = v / (max - min) + rec->min;
+ v = v / (max - min) + rangemin;
*value = v;
}
return 0;
@@ -1199,12 +1283,12 @@ static int convert_from_dB(snd_hctl_elem
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR: {
int min, max;
- min = rec->db_info[2];
- max = rec->db_info[3];
+ min = tlv[2];
+ max = tlv[3];
if (db_gain <= min)
- *value = rec->min;
+ *value = rangemin;
else if (db_gain >= max)
- *value = rec->max;
+ *value = rangemax;
else {
/* FIXME: precalculate and cache vmin and vmax */
double vmin, vmax, v;
@@ -1212,10 +1296,10 @@ static int convert_from_dB(snd_hctl_elem
pow(10.0, (double)min / 20.0);
vmax = !max ? 1.0 : pow(10.0, (double)max / 20.0);
v = pow(10.0, (double)db_gain / 20.0);
- v = (v - vmin) * (rec->max - rec->min) / (vmax - vmin);
+ v = (v - vmin) * (rangemax - rangemin) / (vmax - vmin);
if (xdir > 0)
v = ceil(v);
- *value = (long)v + rec->min;
+ *value = (long)v + rangemin;
}
return 0;
}
@@ -1224,6 +1308,16 @@ static int convert_from_dB(snd_hctl_elem
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);
}
static int get_dB_ops(snd_mixer_elem_t *elem,
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2006-09-05 14:36 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-09-01 17:46 [RFC] dB range compound Takashi Iwai
2006-09-04 12:40 ` James Courtier-Dutton
2006-09-04 13:31 ` Takashi Iwai
2006-09-04 18:40 ` Takashi Iwai
2006-09-05 14:36 ` Takashi Iwai
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.