]> git.alsa-project.org Git - alsa-lib.git/commitdiff
tlv: Handle 'holes' in SND_CTL_TLVT_DB_RANGE array
authorPeter Ujfalusi <peter.ujfalusi@nokia.com>
Tue, 20 Jul 2010 07:34:53 +0000 (10:34 +0300)
committerTakashi Iwai <tiwai@suse.de>
Tue, 20 Jul 2010 10:55:51 +0000 (12:55 +0200)
When converting from dB to raw value, and DB_RANGE is
used with non overlapping map, dB value in between the
sub ranges will be not found.

For example, if the control has the following:
0: -10dB
1: -5dB
2: 0dB
3: 2dB
4: 4dB

static const unsigned int nonoverlapping_tlv[] = {
        TLV_DB_RANGE_HEAD(2),
        0, 2, TLV_DB_SCALE_ITEM(-1000, 500, 0),
        3, 4, TLV_DB_SCALE_ITEM(200, 200, 0),
};

Range 1: -10 .. 0dB
Range 2: 2 .. 4dB

If user asks for 1dB the snd_tlv_convert_from_dB will not find
the raw value, since the 1dB is not part of either range.

To fix this, we will store the previous non maching range's
maximum raw value. If the dB value is not found in the next range,
we will check, if the requested dB value is in between the current
and the previous range, and if it is than pick the apropriate raw
value based on the xdir (up or down rounding).

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
src/control/tlv.c

index 9f26f355631b85b4f404f0a622f353dc7a75f4aa..49f9afec0a8799e93d347e123f6dde93613ce6d0 100644 (file)
@@ -285,7 +285,7 @@ int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax,
 {
        switch (tlv[0]) {
        case SND_CTL_TLVT_DB_RANGE: {
-               long dbmin, dbmax;
+               long dbmin, dbmax, prev_rangemax;
                unsigned int pos, len;
                len = int_index(tlv[1]);
                if (len > MAX_TLV_RANGE_SIZE)
@@ -301,6 +301,7 @@ int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax,
                        return 0;
                }
                pos = 2;
+               prev_rangemax = 0;
                while (pos + 4 <= len) {
                        rangemin = (int)tlv[pos];
                        rangemax = (int)tlv[pos + 1];
@@ -311,6 +312,11 @@ int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax,
                                return snd_tlv_convert_from_dB(tlv + pos + 2,
                                                               rangemin, rangemax,
                                                               db_gain, value, xdir);
+                       else if (db_gain < dbmin) {
+                               *value = xdir ? rangemin : prev_rangemax;
+                               return 0;
+                       }
+                       prev_rangemax = rangemax;
                        pos += int_index(tlv[pos + 3]) + 4;
                }
                return -EINVAL;