]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Change the behavior with hardware volume controls
authorTakashi Iwai <tiwai@suse.de>
Mon, 3 Jan 2005 14:37:39 +0000 (14:37 +0000)
committerTakashi Iwai <tiwai@suse.de>
Mon, 3 Jan 2005 14:37:39 +0000 (14:37 +0000)
When a hardware volume control is given, softvol plugin simply
passes the slave PCM without any additional changes.

src/pcm/pcm_softvol.c

index 0504b79d5775e14c44738de9f2964b2b75c8e633..d885f13d5907202d2fcffa9772dbb2eb9b7e0f88 100644 (file)
@@ -91,6 +91,11 @@ static unsigned short preset_dB_value[PRESET_RESOLUTION] = {
 
 #endif /* DOC_HIDDEN */
 
+/*
+ * apply volumue attenuation
+ *
+ * TODO: use SIMD operations
+ */
 static void snd_pcm_softvol_convert(snd_pcm_softvol_t *svol,
                                    const snd_pcm_channel_area_t *dst_areas,
                                    snd_pcm_uframes_t dst_offset,
@@ -134,6 +139,11 @@ static void snd_pcm_softvol_convert(snd_pcm_softvol_t *svol,
        }
 }
 
+/*
+ * get the current volume value from driver
+ *
+ * TODO: mmap support?
+ */
 static unsigned int get_current_volume(snd_pcm_softvol_t *svol)
 {
        unsigned int val;
@@ -145,17 +155,21 @@ static unsigned int get_current_volume(snd_pcm_softvol_t *svol)
        return val;
 }
 
-static int snd_pcm_softvol_close(snd_pcm_t *pcm)
+static void softvol_free(snd_pcm_softvol_t *svol)
 {
-       snd_pcm_softvol_t *svol = pcm->private_data;
-       int err = 0;
        if (svol->plug.close_slave)
-               err = snd_pcm_close(svol->plug.slave);
+               snd_pcm_close(svol->plug.slave);
        if (svol->ctl)
                snd_ctl_close(svol->ctl);
        if (svol->dB_value && svol->dB_value != preset_dB_value)
                free(svol->dB_value);
        free(svol);
+}
+
+static int snd_pcm_softvol_close(snd_pcm_t *pcm)
+{
+       snd_pcm_softvol_t *svol = pcm->private_data;
+       softvol_free(svol);
        return 0;
 }
 
@@ -330,40 +344,42 @@ static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
        snd_pcm_dump(svol->plug.slave, out);
 }
 
-int snd_ctl_elem_add(snd_ctl_t *ctl, snd_ctl_elem_info_t *info);
-int snd_ctl_elem_replace(snd_ctl_t *ctl, snd_ctl_elem_info_t *info);
-
 static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo)
 {
        return snd_ctl_elem_add_integer(svol->ctl, &cinfo->id, 1, 0, svol->max_val, 0);
 }
 
+/*
+ * load and set up user-control
+ * returns 0 if the user-control is found or created,
+ * returns 1 if the control is a hw control,
+ * or a negative error code
+ */
 static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
-                               char *ctl_name, snd_ctl_elem_id_t *ctl_id,
+                               int ctl_card, snd_ctl_elem_id_t *ctl_id,
                                double min_dB, int resolution)
 {
        char tmp_name[32];
        snd_pcm_info_t *info;
        snd_ctl_elem_info_t *cinfo;
-       int err, card;
+       int err;
        unsigned int i;
 
-       if (! ctl_name) {
+       if (ctl_card < 0) {
                snd_pcm_info_alloca(&info);
                err = snd_pcm_info(pcm, info);
                if (err < 0)
                        return err;
-               card = snd_pcm_info_get_card(info);
-               if (card < 0) {
-                       SNDERR("No card for this PCM");
+               ctl_card = snd_pcm_info_get_card(info);
+               if (ctl_card < 0) {
+                       SNDERR("No card defined for softvol control");
                        return -EINVAL;
                }
-               sprintf(tmp_name, "hw:%d", card);
-               ctl_name = tmp_name;
        }
-       err = snd_ctl_open(&svol->ctl, ctl_name, 0);
+       sprintf(tmp_name, "hw:%d", ctl_card);
+       err = snd_ctl_open(&svol->ctl, tmp_name, 0);
        if (err < 0) {
-               SNDERR("Cannot open CTL %s", ctl_name);
+               SNDERR("Cannot open CTL %s", tmp_name);
                return err;
        }
 
@@ -375,7 +391,7 @@ static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
        snd_ctl_elem_info_set_id(cinfo, ctl_id);
        if ((err = snd_ctl_elem_info(svol->ctl, cinfo)) < 0) {
                if (err != -ENOENT) {
-                       SNDERR("Cannot get info for CTL %s", ctl_name);
+                       SNDERR("Cannot get info for CTL %s", tmp_name);
                        return err;
                }
                err = add_user_ctl(svol, cinfo);
@@ -384,14 +400,14 @@ static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
                        return err;
                }
        } else {
-               if (cinfo->type != SND_CTL_ELEM_TYPE_INTEGER ||
+               if (! (cinfo->access & SNDRV_CTL_ELEM_ACCESS_USER)) {
+                       /* hardware control exists */
+                       return 1; /* notify */
+
+               } else if (cinfo->type != SND_CTL_ELEM_TYPE_INTEGER ||
                    cinfo->count != 1 ||
                    cinfo->value.integer.min != 0 ||
                    cinfo->value.integer.max != resolution - 1) {
-                       if (! (cinfo->access & SNDRV_CTL_ELEM_ACCESS_USER)) {
-                               SNDERR("Invalid control");
-                               return -EINVAL;
-                       }
                        snd_ctl_elem_remove(svol->ctl, &cinfo->id);
                        err = add_user_ctl(svol, cinfo);
                        if (err < 0) {
@@ -446,7 +462,7 @@ static snd_pcm_ops_t snd_pcm_softvol_ops = {
  * \param pcmp Returns created PCM handle
  * \param name Name of PCM
  * \param sformat Slave format
- * \param ctl_id Control ID
+ * \param card card index of the control
  * \param min_dB minimal dB value
  * \param resolution resolution of control
  * \param slave Slave PCM handle
@@ -458,7 +474,7 @@ static snd_pcm_ops_t snd_pcm_softvol_ops = {
  */
 int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
                         snd_pcm_format_t sformat,
-                        char *ctl_name, snd_ctl_elem_id_t *ctl_id,
+                        int ctl_card, snd_ctl_elem_id_t *ctl_id,
                         double min_dB, int resolution,
                         snd_pcm_t *slave, int close_slave)
 {
@@ -471,6 +487,18 @@ int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
        svol = calloc(1, sizeof(*svol));
        if (! svol)
                return -ENOMEM;
+       err = softvol_load_control(slave, svol, ctl_card, ctl_id, min_dB, resolution);
+       if (err < 0) {
+               softvol_free(svol);
+               return err;
+       }
+       if (err > 0) { /* hardware control - no need for softvol! */
+               softvol_free(svol);
+               *pcmp = slave; /* just pass the slave */
+               return 0;
+       }
+
+       /* do softvol */
        snd_pcm_plugin_init(&svol->plug);
        svol->sformat = sformat;
        svol->plug.read = snd_pcm_softvol_read_areas;
@@ -482,7 +510,7 @@ int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
 
        err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
        if (err < 0) {
-               free(svol);
+               softvol_free(svol);
                return err;
        }
        pcm->ops = &snd_pcm_softvol_ops;
@@ -492,17 +520,15 @@ int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
        pcm->poll_events = slave->poll_events;
        snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
        snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
-       err = softvol_load_control(pcm, svol, ctl_name, ctl_id, min_dB, resolution);
-       if (err < 0) {
-               snd_pcm_close(pcm);
-               return err;
-       }
        *pcmp = pcm;
 
        return 0;
 }
 
-static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, char **ctl_name)
+/*
+ * parse card index and id for the softvol control
+ */
+static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp)
 {
        snd_config_iterator_t i, next;
        int iface = SND_CTL_ELEM_IFACE_MIXER;
@@ -512,7 +538,7 @@ static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, char
        long subdevice = -1;
        int err;
 
-       *ctl_name = NULL;
+       *cardp = -1;
        snd_config_for_each(i, next, conf) {
                snd_config_t *n = snd_config_iterator_entry(i);
                const char *id;
@@ -521,14 +547,12 @@ static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, char
                if (strcmp(id, "comment") == 0)
                        continue;
                if (strcmp(id, "card") == 0) {
-                       const char *ptr;
-                       if ((err = snd_config_get_string(n, &ptr)) < 0) {
-                               SNDERR("field %s is not a string", id);
+                       long v;
+                       if ((err = snd_config_get_integer(n, &v)) < 0) {
+                               SNDERR("field %s is not an integer", id);
                                goto _err;
                        }
-                       if (*ctl_name)
-                               free(*ctl_name);
-                       *ctl_name = strdup(ptr);
+                       *cardp = v;
                        continue;
                }
                if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
@@ -604,6 +628,10 @@ static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, char
 This plugin applies the software volume attenuation.
 The format, rate and channels must match for both of source and destination.
 
+If the control already exists and it's a system control (i.e. no
+user-defined control), the plugin simply passes its slave without
+any changes.
+
 \code
 pcm.name {
         type softvol            # Soft Volume conversion PCM
@@ -663,7 +691,7 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
        snd_ctl_elem_id_t *ctl_id;
        int resolution = PRESET_RESOLUTION;
        double min_dB = PRESET_MIN_DB;
-       char *ctl_name;
+       int card = -1;
 
        snd_config_for_each(i, next, conf) {
                snd_config_t *n = snd_config_iterator_entry(i);
@@ -731,11 +759,11 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
        if (err < 0)
                return err;
        snd_ctl_elem_id_alloca(&ctl_id);
-       if ((err = parse_control_id(control, ctl_id, &ctl_name)) < 0) {
+       if ((err = parse_control_id(control, ctl_id, &card)) < 0) {
                snd_pcm_close(spcm);
                return err;
        }
-       err = snd_pcm_softvol_open(pcmp, name, sformat, ctl_name, ctl_id, min_dB, resolution, spcm, 1);
+       err = snd_pcm_softvol_open(pcmp, name, sformat, card, ctl_id, min_dB, resolution, spcm, 1);
        if (err < 0)
                snd_pcm_close(spcm);
        return err;