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 _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.sourceforge.net/lists/listinfo/alsa-devel