changeset 37089:86893b300a45

alsa: Better volume control handling, especially for S/PDIF. If no mixer channel was specified and PCM does not exist, just pick the first. If no volume control is available, at least support mute and full volume switching if a playback switch exists. This is also useful for being able to unmute S/PDIF devices. Especially useful since there seem to be several driver/ALSA bugs that mean these devices often start as muted after every boot.
author reimar
date Tue, 06 May 2014 19:02:03 +0000
parents 3a9a804e488b
children 91628537e24e
files libao2/ao_alsa.c
diffstat 1 files changed, 37 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/libao2/ao_alsa.c	Tue May 06 19:02:02 2014 +0000
+++ b/libao2/ao_alsa.c	Tue May 06 19:02:03 2014 +0000
@@ -169,13 +169,50 @@
       }
 
       elem = snd_mixer_find_selem(handle, sid);
+      if (!elem && !mixer_channel) {
+        // if nothing specified just try picking the first
+        elem = snd_mixer_first_elem(handle);
+        if (elem)
+            mp_msg(MSGT_AO, MSGL_V, "[AO_ALSA] Auto-selected element %s\n", snd_mixer_selem_get_name(elem));
+      }
       if (!elem) {
 	mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToFindSimpleControl,
 	       snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
+        mp_msg(MSGT_AO, MSGL_INFO, "[AO_ALSA] Available elements for device:\n");
+        for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem))
+            mp_msg(MSGT_AO, MSGL_INFO, "[AO_ALSA]  %s\n", snd_mixer_selem_get_name(elem));
+        mp_msg(MSGT_AO, MSGL_INFO, "\n");
 	snd_mixer_close(handle);
 	return CONTROL_ERROR;
 	}
 
+      if (!snd_mixer_selem_has_playback_volume(elem) &&
+          !snd_mixer_selem_has_playback_volume_joined(elem))
+      {
+          if (!snd_mixer_selem_has_playback_switch(elem))
+              return CONTROL_FALSE;
+          if (cmd == AOCONTROL_GET_VOLUME) {
+              int v;
+              snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &v);
+              vol->left = v ? 100 : 0;
+              snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &v);
+              vol->right = v ? 100 : 0;
+              return CONTROL_TRUE;
+          }
+          // special case: only mute supported
+          if (vol->left != 0 && vol->left != 100 ||
+              vol->right != 0 && vol->right != 100)
+              return CONTROL_FALSE;
+          if (snd_mixer_selem_has_playback_switch_joined(elem)) {
+              if (vol->left != vol->right)
+                  return CONTROL_FALSE;
+          } else {
+              snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, vol->right != 0);
+          }
+          snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, vol->left != 0);
+          return CONTROL_TRUE;
+      }
+
       snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax);
       f_multi = (100 / (float)(pmax - pmin));