From: Takashi Iwai Date: Fri, 28 Jul 2006 12:36:37 +0000 (+0200) Subject: Move dB parser to mixer abstraction X-Git-Tag: v1.0.12rc2~1 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=ae7612999987185cf429d91c0ee0a9e3a25d7f72;p=alsa-lib.git Move dB parser to mixer abstraction Moved the parser of dB value to mixer abstraction from hcontrol layer. Also, cleaned up codes. --- diff --git a/include/control.h b/include/control.h index b84028c7..af0f91a6 100644 --- a/include/control.h +++ b/include/control.h @@ -504,7 +504,6 @@ int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value); int snd_hctl_elem_tlv_read(snd_hctl_elem_t *elem, unsigned int *tlv, unsigned int tlv_size); int snd_hctl_elem_tlv_write(snd_hctl_elem_t *elem, const unsigned int *tlv); int snd_hctl_elem_tlv_command(snd_hctl_elem_t *elem, const unsigned int *tlv); -int snd_hctl_elem_get_db_gain(snd_hctl_elem_t *elem, long volume, long *db_gain); snd_hctl_t *snd_hctl_elem_get_hctl(snd_hctl_elem_t *elem); diff --git a/src/control/hcontrol.c b/src/control/hcontrol.c index 761df411..ede08870 100644 --- a/src/control/hcontrol.c +++ b/src/control/hcontrol.c @@ -239,6 +239,7 @@ static int get_compare_weight(const snd_ctl_elem_id_t *id) "Tone Control", "3D Control", "PCM", + "Front", "Surround", "Center", "LFE", @@ -787,116 +788,6 @@ int snd_hctl_handle_events(snd_hctl_t *hctl) return count; } -static int snd_hctl_elem_decode_tlv_db_gain(unsigned int *tlv, unsigned int tlv_size, long volume, long *db_gain) -{ - unsigned int type; - unsigned int size; - unsigned int idx = 0; - int err = 0; - - type = tlv[idx++]; - size = tlv[idx++]; - tlv_size -= 2 * sizeof(unsigned int); - if (size > tlv_size) { - err = -EINVAL; - goto __exit_decode_db_gain; - } - switch (type) { - case SND_CTL_TLVT_CONTAINER: - size += sizeof(unsigned int) - 1; - size /= sizeof(unsigned int); - while (idx < size) { - if (tlv[idx+1] > (size - idx) * sizeof(unsigned int)) { - err = -EINVAL; - goto __exit_decode_db_gain; - } - err = snd_hctl_elem_decode_tlv_db_gain(tlv + idx, tlv[idx+1], volume, db_gain); - if (!err) - break; /* db_gain obtained */ - idx += 2 + (tlv[1] + sizeof(unsigned int) - 1) / sizeof(unsigned int); - } - break; - case SND_CTL_TLVT_DB_SCALE: - if (size != 2 * sizeof(unsigned int)) { -#if 0 - while (size > 0) { - printf("0x%x", tlv[idx++]); - size -= sizeof(unsigned int); - } - printf("\n"); -#endif - } else { - int min,step,mute; - min = tlv[2]; - step = (tlv[3] & 0xffff); - mute = (tlv[3] >> 16) & 1; - *db_gain = (volume * step) + min; - if (mute && (volume == 0)) - *db_gain = -9999999; - } - break; - default: -#if 0 - printf("unk-%i-", type); - while (size > 0) { - printf("0x%x", tlv[idx++]); - size -= sizeof(unsigned int); - } - printf("\n"); -#endif - break; - } - - __exit_decode_db_gain: - return err; -} - -/** - * \brief Get db_gain information for an HCTL element - * \param elem HCTL element - * \param volume The current control volume - * \param db_gain The return value in db_gain scale - * \return 0 otherwise a negative error code on failure - */ -int snd_hctl_elem_get_db_gain(snd_hctl_elem_t *elem, long volume, long *db_gain) -{ - snd_ctl_elem_info_t *info; - unsigned int *tlv; - unsigned int tlv_size = 4096; - int err; - - snd_ctl_elem_info_alloca(&info); - err = snd_hctl_elem_info(elem, info); - if (err < 0) - return err; - - if (tlv_size < 2 * sizeof(unsigned int)) { - err = -EINVAL; - goto __exit_db_gain; - } - if (!snd_ctl_elem_info_is_tlv_readable(info)) { - err = -EINVAL; - goto __exit_db_gain; - } - tlv = malloc(tlv_size); - if (tlv == NULL) { - err = -ENOMEM; - goto __exit_db_gain; - } - if ((err = snd_hctl_elem_tlv_read(elem, tlv, tlv_size)) < 0) - goto __free_exit_db_gain; - err = snd_hctl_elem_decode_tlv_db_gain(tlv, tlv_size, volume, db_gain); - __free_exit_db_gain: - free(tlv); - __exit_db_gain: - return err; -} - - - - - - /** * \brief Get information for an HCTL element * \param elem HCTL element diff --git a/src/mixer/simple_none.c b/src/mixer/simple_none.c index c13accaa..276d9c72 100644 --- a/src/mixer/simple_none.c +++ b/src/mixer/simple_none.c @@ -73,12 +73,15 @@ typedef struct _selem_none { sm_selem_t selem; selem_ctl_t ctls[CTL_LAST + 1]; unsigned int capture_item; - struct { + struct selem_str { unsigned int range: 1; /* Forced range */ + unsigned int db_initialized: 1; + unsigned int db_init_error: 1; long min, max; unsigned int channels; long vol[32]; unsigned int sw; + unsigned int *db_info; } str[2]; } selem_none_t; @@ -601,6 +604,9 @@ static void selem_free(snd_mixer_elem_t *elem) assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE); if (simple->selem.id) snd_mixer_selem_id_free(simple->selem.id); + /* free db range information */ + free(simple->str[0].db_info); + free(simple->str[1].db_info); free(simple); } @@ -971,6 +977,116 @@ static int get_volume_ops(snd_mixer_elem_t *elem, int dir, return 0; } +/* convert to index of integer array */ +#define int_index(size) (((size) + sizeof(int) - 1) / sizeof(int)) + +/* parse TLV stream and retrieve dB information + * return 0 if successly found and stored to rec, + * return 1 if no information is found, + * or return a negative error code + */ +static int parse_db_range(struct selem_str *rec, unsigned int *tlv, + unsigned int tlv_size) +{ + unsigned int type; + unsigned int size; + int err; + + type = tlv[0]; + size = tlv[1]; + tlv_size -= 2 * sizeof(int); + if (size > tlv_size) { + SNDERR("TLV size error"); + return -EINVAL; + } + switch (type) { + case SND_CTL_TLVT_CONTAINER: + size = int_index(size) * sizeof(int); + tlv += 2; + while (size > 0) { + unsigned int len; + err = parse_db_range(rec, tlv, size); + if (err <= 0) + return err; /* error or found dB */ + len = int_index(tlv[1]) + 2; + size -= len * sizeof(int); + tlv += len; + } + break; + case SND_CTL_TLVT_DB_SCALE: + rec->db_info = malloc(size + sizeof(int) * 2); + if (! rec->db_info) + return -ENOMEM; + memcpy(rec->db_info, tlv, size + sizeof(int) * 2); + return 0; + default: + break; + } + return 1; /* not found */ +} + +/* convert the given raw volume value to a dB gain + */ +static int convert_db_range(struct selem_str *rec, long volume, long *db_gain) +{ + int min, step, mute; + + switch (rec->db_info[0]) { + case SND_CTL_TLVT_DB_SCALE: + min = rec->db_info[2]; + step = (rec->db_info[3] & 0xffff); + mute = (rec->db_info[3] >> 16) & 1; + if (mute && volume == rec->min) + *db_gain = -9999999; + else + *db_gain = (volume - rec->min) * step + min; + return 0; + } + return -EINVAL; +} + +/* initialize dB range information, reading TLV via hcontrol + */ +static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec) +{ + snd_ctl_elem_info_t *info; + unsigned int *tlv = NULL; + const unsigned int tlv_size = 4096; + + snd_ctl_elem_info_alloca(&info); + if (snd_hctl_elem_info(ctl, info) < 0) + goto error; + if (! snd_ctl_elem_info_is_tlv_readable(info)) + goto error; + tlv = malloc(tlv_size); + if (! tlv) + return -ENOMEM; + if (snd_hctl_elem_tlv_read(ctl, tlv, tlv_size) < 0) + goto error; + if (parse_db_range(rec, tlv, tlv_size) < 0) + goto error; + free(tlv); + rec->db_initialized = 1; + return 0; + + error: + free(tlv); + rec->db_init_error = 1; + return -EINVAL; +} + +static int get_dB_gain(snd_hctl_elem_t *ctl, struct selem_str *rec, + long volume, long *db_gain) +{ + if (rec->db_init_error) + return -EINVAL; + if (! rec->db_initialized) { + if (init_db_range(ctl, rec) < 0) + return -EINVAL; + } + return convert_db_range(rec, volume, db_gain); +} + static int get_dB_ops(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, @@ -981,27 +1097,21 @@ static int get_dB_ops(snd_mixer_elem_t *elem, int err = -EINVAL; long volume, db_gain; - if (dir == SM_PLAY) { + if (dir == SM_PLAY) c = &s->ctls[CTL_PLAYBACK_VOLUME]; - if (c->type != 2) - goto _err; - } else if (dir == SM_CAPT) { + else if (dir == SM_CAPT) c = &s->ctls[CTL_CAPTURE_VOLUME]; - if (c->type != 2) - goto _err; - } else { + else goto _err; - } - err = get_volume_ops(elem, dir, channel, &volume); - if (err < 0) + if (c->type != 2) goto _err; - err = snd_hctl_elem_get_db_gain(c->elem, volume, &db_gain); - if (err < 0) + if ((err = get_volume_ops(elem, dir, channel, &volume)) < 0) + goto _err; + if ((err = get_dB_gain(c->elem, &s->str[dir], volume, &db_gain)) < 0) goto _err; err = 0; *value = db_gain; -_err: - //if (err) printf("get_dB_ops:err=%d\n",err); + _err: return err; }