[alsa-devel] [PATCH v2 - alsa-lib 0/3] Fixes for snd_tlv_convert_from_dB
Hello,
Changes since the initial version: - In patch one the range check has been converted to be more intuitive
Intro part from the first version:
One cosmetic fix, and two real fixes for alsa-lib's tlv handling.
There were two separate issue with the snd_tlv_convert_from_dB function: 1. When user asked for out of range dB value, and the control had SND_CTL_TLVT_DB_RANGE, the control's full range was not checked against the requested value, which resulted failure. 2. When there are 'holes' in the SND_CTL_TLVT_DB_RANGE array, snd_tlv_convert_from_dB will fail to find the apropriate raw value.
For issue #2, I have sent patches for the drivers I maintain, but Mark Brown suggested to fix the issue in alsa-lib instead of converting the driver's non-overlapping to overlapping mapping: http://mailman.alsa-project.org/pipermail/alsa-devel/2010-July/029483.html
So the following series for alsa-lib, will fix the SND_CTL_TLVT_DB_RANGE handling.
--- Peter Ujfalusi (3): tlv: Check out of range dB with SND_CTL_TLVT_DB_RANGE tlv: Handle 'holes' in SND_CTL_TLVT_DB_RANGE array tlv: Remove tailing tab after snd_ctl_get_dB_range function
src/control/tlv.c | 20 ++++++++++++++++++-- 1 files changed, 18 insertions(+), 2 deletions(-)
When converting from dB value to raw value, the control's full range was not checked in case of SND_CTL_TLVT_DB_RANGE.
Check out of range dB values, and return apropriate raw value for the caller.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- src/control/tlv.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/src/control/tlv.c b/src/control/tlv.c index 0ff052e..9f26f35 100644 --- a/src/control/tlv.c +++ b/src/control/tlv.c @@ -285,13 +285,23 @@ 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; unsigned int pos, len; len = int_index(tlv[1]); if (len > MAX_TLV_RANGE_SIZE) return -EINVAL; + if (snd_tlv_get_dB_range(tlv, rangemin, rangemax, + &dbmin, &dbmax)) + return -EINVAL; + if (db_gain <= dbmin) { + *value = rangemin; + return 0; + } else if (db_gain >= dbmax) { + *value = rangemax; + return 0; + } pos = 2; while (pos + 4 <= len) { - long dbmin, dbmax; rangemin = (int)tlv[pos]; rangemax = (int)tlv[pos + 1]; if (!snd_tlv_get_dB_range(tlv + pos + 2,
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 --- src/control/tlv.c | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-)
diff --git a/src/control/tlv.c b/src/control/tlv.c index 9f26f35..49f9afe 100644 --- a/src/control/tlv.c +++ b/src/control/tlv.c @@ -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;
Cosmetic fix. There was a tab instead of new line after snd_ctl_get_dB_range function.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- src/control/tlv.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/control/tlv.c b/src/control/tlv.c index 49f9afe..ba52752 100644 --- a/src/control/tlv.c +++ b/src/control/tlv.c @@ -442,7 +442,7 @@ int snd_ctl_get_dB_range(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, return snd_tlv_get_dB_range(info.tlv, info.minval, info.maxval, min, max); } - + /** * \brief Convert the volume value to dB on the given control element * \param ctl the control handler
participants (1)
-
Peter Ujfalusi