diff -r d5a6770e7faf include/control.h --- a/include/control.h Fri May 19 18:26:41 2006 +0200 +++ b/include/control.h Sat May 27 19:54:17 2006 +0100 @@ -52,6 +52,9 @@ typedef struct snd_aes_iec958 { /** CTL card info container */ typedef struct _snd_ctl_card_info snd_ctl_card_info_t; + +/** CTL misc container */ +typedef struct _snd_ctl_misc snd_ctl_misc_t; /** CTL element identifier container */ typedef struct _snd_ctl_elem_id snd_ctl_elem_id_t; @@ -212,6 +215,7 @@ int snd_ctl_poll_descriptors_revents(snd int snd_ctl_poll_descriptors_revents(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); int snd_ctl_subscribe_events(snd_ctl_t *ctl, int subscribe); int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info); +int snd_ctl_misc(snd_ctl_t *ctl, snd_ctl_misc_t *misc); int snd_ctl_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t * list); int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info); int snd_ctl_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *value); @@ -485,6 +489,8 @@ int snd_hctl_elem_info(snd_hctl_elem_t * int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t * info); int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value); int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value); +int snd_hctl_elem_misc(snd_hctl_elem_t *elem, snd_ctl_misc_t * misc); +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 -r d5a6770e7faf include/local.h --- a/include/local.h Fri May 19 18:26:41 2006 +0200 +++ b/include/local.h Sat May 27 19:54:17 2006 +0100 @@ -46,6 +46,7 @@ #define _snd_pcm_status sndrv_pcm_status #define _snd_ctl_card_info sndrv_ctl_card_info +#define _snd_ctl_misc sndrv_ctl_misc #define _snd_ctl_elem_id sndrv_ctl_elem_id #define _snd_ctl_elem_list sndrv_ctl_elem_list #define _snd_ctl_elem_info sndrv_ctl_elem_info diff -r d5a6770e7faf include/sound/asound.h --- a/include/sound/asound.h Fri May 19 18:26:41 2006 +0200 +++ b/include/sound/asound.h Sat May 27 19:54:17 2006 +0100 @@ -26,6 +26,7 @@ #if defined(LINUX) || defined(__LINUX__) || defined(__linux__) #include +#include #ifdef __KERNEL__ @@ -847,6 +848,18 @@ struct sndrv_ctl_elem_value { unsigned char reserved[128-sizeof(struct timespec)]; }; +struct sndrv_ctl_misc { + uint32_t version; /* This should be uint32_t */ + uint32_t length; /* This should be uint32_t */ + unsigned char message[]; +}; + +#define SND_MISC_DB_SCALE 2 +/* Request */ +#define SND_MISC_ELEM_NUMID 2 +/* Response */ +#define SND_MISC_ELEM_DB_SCALE_TYPE1 1 + enum { SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct sndrv_ctl_card_info), @@ -862,6 +875,7 @@ enum { SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct sndrv_ctl_elem_id), SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct sndrv_hwdep_info), + SNDRV_CTL_IOCTL_MISC = _IOR('U', 0x22, struct sndrv_ctl_misc), SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), SNDRV_CTL_IOCTL_PCM_INFO = _IOWR('U', 0x31, struct sndrv_pcm_info), SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE = _IOW('U', 0x32, int), diff -r d5a6770e7faf src/control/control.c --- a/src/control/control.c Fri May 19 18:26:41 2006 +0200 +++ b/src/control/control.c Sat May 27 19:54:17 2006 +0100 @@ -234,6 +234,20 @@ int snd_ctl_card_info(snd_ctl_t *ctl, sn { assert(ctl && info); return ctl->ops->card_info(ctl, info); +} + +/** + * \brief Get/Set misc related information + * \param ctl CTL handle + * \param misc pointer + * \return 0 on success otherwise a negative error code + */ +int snd_ctl_misc(snd_ctl_t *ctl, snd_ctl_misc_t *misc) +{ + int err; + assert(ctl && misc); + err = ctl->ops->misc(ctl, misc); + return err; } /** diff -r d5a6770e7faf src/control/control_hw.c --- a/src/control/control_hw.c Fri May 19 18:26:41 2006 +0200 +++ b/src/control/control_hw.c Sat May 27 19:54:17 2006 +0100 @@ -128,6 +128,16 @@ static int snd_ctl_hw_card_info(snd_ctl_ return 0; } +static int snd_ctl_hw_misc(snd_ctl_t *handle, snd_ctl_misc_t *misc) +{ + snd_ctl_hw_t *hw = handle->private_data; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_MISC, misc) < 0) { + //SYSERR("SNDRV_CTL_IOCTL_MISC failed"); + return -errno; + } + return 0; +} + static int snd_ctl_hw_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list) { snd_ctl_hw_t *hw = handle->private_data; @@ -296,6 +306,7 @@ snd_ctl_ops_t snd_ctl_hw_ops = { .async = snd_ctl_hw_async, .subscribe_events = snd_ctl_hw_subscribe_events, .card_info = snd_ctl_hw_card_info, + .misc = snd_ctl_hw_misc, .element_list = snd_ctl_hw_elem_list, .element_info = snd_ctl_hw_elem_info, .element_add = snd_ctl_hw_elem_add, diff -r d5a6770e7faf src/control/control_local.h --- a/src/control/control_local.h Fri May 19 18:26:41 2006 +0200 +++ b/src/control/control_local.h Sat May 27 19:54:17 2006 +0100 @@ -27,6 +27,7 @@ typedef struct _snd_ctl_ops { int (*async)(snd_ctl_t *handle, int sig, pid_t pid); int (*subscribe_events)(snd_ctl_t *handle, int subscribe); int (*card_info)(snd_ctl_t *handle, snd_ctl_card_info_t *info); + int (*misc)(snd_ctl_t *handle, snd_ctl_misc_t *info); int (*element_list)(snd_ctl_t *handle, snd_ctl_elem_list_t *list); int (*element_info)(snd_ctl_t *handle, snd_ctl_elem_info_t *info); int (*element_add)(snd_ctl_t *handle, snd_ctl_elem_info_t *info); diff -r d5a6770e7faf src/control/hcontrol.c --- a/src/control/hcontrol.c Fri May 19 18:26:41 2006 +0200 +++ b/src/control/hcontrol.c Sat May 27 19:54:17 2006 +0100 @@ -49,6 +49,7 @@ to reduce overhead accessing the real co #include #include #include +#include #ifndef DOC_HIDDEN #define __USE_GNU #endif @@ -757,6 +758,181 @@ int snd_hctl_handle_events(snd_hctl_t *h return count; } + +static int uint32_to_message(snd_ctl_misc_t *misc, uint32_t value) +{ + unsigned int pointer = misc->length; + uint32_t *store = (uint32_t*) &misc->message[pointer]; + *store = value; + misc->length += sizeof(uint32_t); + return 0; +} + +static int message_to_uint32(snd_ctl_misc_t *misc, uint32_t *value) +{ + unsigned int pointer = misc->length; + uint32_t *store = (uint32_t*) &misc->message[pointer]; + *value = *store; + misc->length += sizeof(uint32_t); + return 0; +} + + +static int add_int32_tlv_to_container(snd_ctl_misc_t *misc, uint32_t type, uint32_t value) +{ + uint32_to_message(misc, type); + uint32_to_message(misc, 4); + uint32_to_message(misc, value); + /* FIXME: Check for overflows */ + return 0; +} + +static int add_message_tlv_to_container( + snd_ctl_misc_t *misc, uint32_t length, unsigned char *message) +{ + unsigned int pointer = misc->length; + unsigned int n; + for(n=0; nmessage[pointer++] = message[n]; + } + misc->length = pointer; + /* FIXME: Check for overflows */ + return 0; +} + + +static int container_open(snd_ctl_misc_t *misc, uint32_t type) +{ + uint32_to_message(misc, type); + uint32_to_message(misc, 0); /* This gets updated at container close */ + return 0; +} + +static int container_close(snd_ctl_misc_t *misc) +{ + uint32_t *store = (uint32_t*) &misc->message[sizeof(uint32_t)]; + *store = misc->length; + return 0; +} + + +struct snd_ctl_elem_db_scale_type1 { + /* Type=1 for simple + * dB = (X * db_per_devision) - db_offset + * This equation is used instead of a more normal + * dB = (( X - db_offset) * db_numerator) / db_denominator + * in order to avoid any devision by zero possibilities, + * save 4 bytes per db_scale and avoid floating point maths. + * + * Types >= 2 are for future more complex conversion functions. + * All types have to have a length value, in case the user land + * alsa lib does not yet understand the type. The length field allows + * user land to safely skip this TLV. + */ + uint32_t type; /* 1 */ + uint32_t length; /* 12 */ + int32_t db_per_devision; /* Use db per devision * 100 */ + int32_t db_offset; /* Use dB offset * 100. This identifies the 0dB point */ + int32_t db_mute; /* The value, before conversion, that is equivalent to MUTE */ +}; + +/** + * \brief Get misc information for an HCTL element + * \param elem HCTL element + * \param info HCTL element information + * \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_misc_t *misc; + int err=0; + unsigned int received_length; + unsigned int tlv_type; + unsigned int tlv_length; + unsigned int container_type; + unsigned int container_length; + struct snd_ctl_elem_db_scale_type1 *db_scale_1; + + assert(elem); + assert(elem->hctl); + misc=malloc(2048); + if (misc == NULL) { + err = -ENOMEM; + return err; + } + misc->version=1; + misc->length=0; + container_open(misc, SND_MISC_DB_SCALE); + add_int32_tlv_to_container(misc, SND_MISC_ELEM_NUMID, elem->id.numid); + container_close(misc); + misc->length=2040; + + err = snd_ctl_misc(elem->hctl->ctl, misc); + if (err) goto _err; + + received_length = misc->length; + misc->length=0; + message_to_uint32(misc, &container_type); + switch (container_type) { + case SND_MISC_DB_SCALE: + message_to_uint32(misc, &container_length); + db_scale_1 = (struct snd_ctl_elem_db_scale_type1*) &misc->message[misc->length]; + /* FIXME: Do more sanity checks here */ + message_to_uint32(misc, &tlv_type); + switch (tlv_type) { + case SND_MISC_ELEM_DB_SCALE_TYPE1: + message_to_uint32(misc, &tlv_length); + if (tlv_length != 12) { + err = -EINVAL; + goto _err; + } + misc->length+=tlv_length; + if (volume == db_scale_1->db_mute) { + *db_gain=-9999999; /* Muted */ + break; + } + /* Scale ca0106: from -51.50 dB to +12.00 dB in steps of 0.75 dB. 256 steps. */ + /* Scale emu10k1 dsp controls: from -39.60 dB to 0.00 dB in steps of 0.4 dB. 100 steps. */ + *db_gain=(volume * db_scale_1->db_per_devision) - db_scale_1->db_offset; + break; + default: + err = -EINVAL; + goto _err; + break; + } + break; + default: + err = -EINVAL; + goto _err; + } + +_err: + free(misc); + return err; +#if 0 + switch (tlv_value) { + case 1: /* Scale from -51.50 dB to +12.00 dB in steps of 0.75 dB. 256 steps. */ + if (volume == 0) { + *db_gain=-9999999; /* Muted */ + break; + } + *db_gain=(volume * 25) - 5175; /* For ca0106 controls */ + break; + case 2: /* Scale from -39.60 dB to 0.00 dB in steps of 0.4 dB. 100 steps. */ + if (volume == 0) { + *db_gain=-9999999; /* Muted */ + break; + } + *db_gain=(volume * 40) - 4000; /* For emu10k1 dsp controls */ + break; + default: + *db_gain=-9999999; /* Muted */ + return 1; + } + return 0; +#endif +} + /** * \brief Get information for an HCTL element * \param elem HCTL element diff -r d5a6770e7faf src/mixer/simple_none.c --- a/src/mixer/simple_none.c Fri May 19 18:26:41 2006 +0200 +++ b/src/mixer/simple_none.c Sat May 27 19:54:17 2006 +0100 @@ -971,12 +971,41 @@ static int get_volume_ops(snd_mixer_elem return 0; } -static int get_dB_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED, - int dir ATTRIBUTE_UNUSED, - snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED, - long *value ATTRIBUTE_UNUSED) -{ - return -ENXIO; +static int get_dB_ops(snd_mixer_elem_t *elem, + int dir, + snd_mixer_selem_channel_id_t channel, + long *value) +{ + selem_none_t *s = snd_mixer_elem_get_private(elem); + selem_ctl_t *c; + int err=-EINVAL; + long volume, db_gain; + if (dir==SM_PLAY) { + c = &s->ctls[CTL_PLAYBACK_VOLUME]; + if (c->type==2) { + if (get_volume_ops(elem, dir, channel, &volume)) + goto _err; + if ((err = snd_hctl_elem_get_db_gain(c->elem, volume, &db_gain)) < 0) + goto _err; + } else + goto _err; + } else if (dir==SM_CAPT) { + c = &s->ctls[CTL_CAPTURE_VOLUME]; + if (c->type==2) { + if (get_volume_ops(elem, dir, channel, &volume)) + goto _err; + if ((err = snd_hctl_elem_get_db_gain(c->elem, volume, &db_gain)) < 0) + goto _err; + } else + goto _err; + + } else + goto _err; + err=0; + *value=db_gain; +_err: + //if (err) printf("get_dB_ops:err=%d\n",err); + return err; } static int get_switch_ops(snd_mixer_elem_t *elem, int dir,