--- a/src/minidesk_helpers.c Sat Jun 14 12:16:04 2008 +0200
+++ b/src/minidesk_helpers.c Wed Jun 18 00:44:20 2008 +0200
@@ -34,23 +34,313 @@
#include <sys/socket.h>
#include <sys/un.h>
+#include <alsa/asoundlib.h>
+#include <alsa/mixer.h>
+#include <alsa/control.h>
+
+
#include "minidesk.h"
#include "minidesk_helpers.h"
#include "minidesk_desktop_view.h"
-#if 0
-typedef enum
-{
- MUTE = 0,
- UNMUTE,
- TOGGLE
-} t_volumeState;
-
-static gchar* get_wallpaper_from_preferences(const char *);
-static int alsa_init(void);
-static int alsa_volume_change(int percent_to_add);
-static int alsa_volume_mute(t_volumeState wanted);
-#endif
+
+
+
+
+static snd_mixer_t *alsa_handle = NULL;
+static snd_mixer_elem_t *mixer_elem = NULL;
+static char *card = "default";
+static const char *mixer_elements[] =
+{
+ "PCM",
+ "Master",
+ "Analog Front",
+ "Speaker"
+};
+
+static int
+alsa_find_card(const char *soundcard_name)
+{
+ int err, card = -1;
+ char *name;
+
+ trace("Searching for [%s]\n", soundcard_name);
+
+ err = snd_card_next(&card);
+ if (err < 0)
+ return -1;
+
+ while (card >= 0)
+ {
+ /*
+ * Check if the soundcard_name match the short name
+ * "Intel ICH5"
+ * "USB Camera"
+ * "Logitech USB Headset"
+ */
+ err = snd_card_get_name(card, &name);
+ if (err ==0 && strcmp(name, soundcard_name) == 0)
+ {
+ free(name);
+ return card;
+ }
+ trace("CARD %d: shortname \"%s\"\n", card, name?name:"error");
+ /*
+ * Check if the soundcard_name match the beginning of the long name
+ * "Intel ICH5 with AD1981B at irq 20"
+ * "OmniVision Technologies, Inc. USB Camera at usb-0000:00:1d.3-2, full speed"
+ * "Logitech Logitech USB Headset at usb-0000:00:1d.7-1.1, full speed"
+ */
+
+ err = snd_card_get_longname(card, &name);
+ if (err ==0 && strncmp(name, soundcard_name, sizeof(soundcard_name)) == 0)
+ {
+ free(name);
+ return card;
+ }
+ trace("CARD %d: longname \"%s\"\n", card, name?name:"error");
+
+ err = snd_card_next(&card);
+ if (err < 0)
+ break;
+ }
+
+ return -1;
+}
+
+int
+alsa_init(const char *soundcard_name)
+{
+ unsigned int i;
+ int err;
+ snd_mixer_selem_id_t *sid;
+ static snd_mixer_elem_t *elem = NULL;
+ char hw[16];
+
+ if (alsa_handle)
+ return 0;
+
+ err = snd_mixer_open(&alsa_handle, 0);
+ if (err < 0 || alsa_handle == NULL)
+ {
+ error("Mixer %s open error: %s\n", card, snd_strerror(err));
+ return -1;
+ }
+
+ if (soundcard_name)
+ {
+ int card_index = alsa_find_card(soundcard_name);
+ if (card_index>=0)
+ {
+ snprintf(hw, sizeof(hw), "hw:%d", card_index);
+ card = hw;
+ }
+ }
+ trace("Trying to attach soundcard %s\n", card);
+ err = snd_mixer_attach(alsa_handle, card);
+ if (err < 0)
+ {
+ error("Mixer \"%s\" attach error: %s\n", card, snd_strerror(err));
+error_close_mixer:
+ snd_mixer_close(alsa_handle);
+ alsa_handle = NULL;
+ return -1;
+ }
+
+ err = snd_mixer_selem_register(alsa_handle, NULL, NULL);
+ if (err < 0)
+ {
+ error("Mixer %s register error: %s\n", card, snd_strerror(err));
+ goto error_close_mixer;
+ }
+
+ err = snd_mixer_load(alsa_handle);
+ if (err < 0)
+ {
+ error("Mixer %s load error: %s\n", card, snd_strerror(err));
+ goto error_close_mixer;
+ }
+
+ snd_mixer_selem_id_alloca(&sid);
+ for (i=0; i<sizeof(mixer_elements)/sizeof(mixer_elements[0]); i++)
+ {
+ snd_mixer_selem_id_set_index(sid, 0);
+ snd_mixer_selem_id_set_name(sid, mixer_elements[i]);
+
+ elem = snd_mixer_find_selem(alsa_handle, sid);
+ if (elem)
+ {
+ if ( snd_mixer_selem_has_playback_volume(elem)
+ && snd_mixer_selem_is_active(elem))
+ break;
+ }
+ }
+ if (elem == NULL)
+ goto error_close_mixer;
+
+ mixer_elem = elem;
+
+ trace("Mixer selected: %s\n", snd_mixer_selem_id_get_name(sid));
+ return 0;
+}
+
+static int
+alsa_convert_volume_percent_to_hardware_value(int vol, int pmin, int pmax)
+{
+ return (int)ceil( ((double)vol * (pmax-pmin) / 100.0) + pmin );
+}
+
+static int
+alsa_convert_volume_hardware_value_to_percent(int vol, int pmin, int pmax)
+{
+ int range = pmax - pmin;
+
+ if (range == 0)
+ return 0;
+
+ return (int)rint( (double)(vol - pmin) / range * 100.0 );
+}
+
+/*
+ *
+ *
+ */
+int
+alsa_volume_change(int percent_to_add)
+{
+ long pmin=0, pmax=0, pvol=0;
+ int newvol = 0;
+ unsigned int chn;
+ int vol_in_percent = 75;
+
+
+ if (alsa_init(NULL) < 0)
+ return -1;
+
+ snd_mixer_handle_events(alsa_handle);
+
+ snd_mixer_selem_get_playback_volume_range(mixer_elem, &pmin, &pmax);
+
+ trace("Volume range: [%ld-%ld]\n", pmin, pmax);
+
+ for (chn=0; chn<=SND_MIXER_SCHN_LAST; chn++)
+ {
+ if (snd_mixer_selem_has_playback_channel(mixer_elem, chn) == 0)
+ continue;
+
+ if (snd_mixer_selem_has_playback_volume(mixer_elem) == 0)
+ continue;
+
+ snd_mixer_selem_get_playback_volume(mixer_elem, chn, &pvol);
+
+ vol_in_percent = alsa_convert_volume_hardware_value_to_percent(pvol, pmin, pmax);
+ trace("Current channel: %s [%ld] %d%%\n", snd_mixer_selem_channel_name(chn), pvol, vol_in_percent);
+
+ if (percent_to_add != 0)
+ {
+ /* Use a non linear volume slider */
+ if (vol_in_percent <= 10)
+ vol_in_percent += percent_to_add * 2;
+ else if (vol_in_percent < 30)
+ vol_in_percent += percent_to_add * 4;
+ else if (vol_in_percent < 60)
+ vol_in_percent += percent_to_add * 6;
+ else
+ vol_in_percent += percent_to_add * 8;
+
+ if (vol_in_percent > 100)
+ vol_in_percent = 100;
+ else if (vol_in_percent < 0)
+ vol_in_percent = 0;
+
+ newvol = alsa_convert_volume_percent_to_hardware_value(vol_in_percent, pmin, pmax);
+
+ /* Sometimes, with low value, we can have the same number, so force the number to change */
+ if (newvol == pvol)
+ {
+ if (percent_to_add > 0)
+ newvol++;
+ else
+ newvol--;
+ }
+
+ /* clamp value */
+ if (newvol < pmin)
+ newvol = pmin;
+ else if (newvol > pmax)
+ newvol = pmax;
+
+ trace("new volume set to %d (%d%%)\n", newvol, vol_in_percent);
+ snd_mixer_selem_set_playback_volume(mixer_elem, chn, newvol);
+ }
+ else
+ newvol = pvol;
+ }
+
+ if (newvol < 1)
+ snd_mixer_selem_set_playback_switch_all(mixer_elem, 0);
+ else
+ snd_mixer_selem_set_playback_switch_all(mixer_elem, 1);
+
+ return vol_in_percent;
+}
+
+/*
+ *
+ *
+ */
+int
+alsa_volume_mute(t_volumeState wanted)
+{
+ int chn;
+ int first_channel = 1;
+
+ if (alsa_init(NULL) < 0)
+ return -1;
+
+ if (snd_mixer_selem_has_playback_switch(mixer_elem) == 0)
+ return -1;
+
+ for (chn=0; chn<=SND_MIXER_SCHN_LAST; chn++)
+ {
+ int ival;
+ if (snd_mixer_selem_has_playback_channel(mixer_elem, chn) == 0)
+ continue;
+
+ if (wanted == TOGGLE)
+ {
+ snd_mixer_selem_get_playback_switch(mixer_elem, chn, &ival);
+ ival = !ival;
+ }
+ else
+ ival = wanted;
+
+ snd_mixer_selem_set_playback_switch(mixer_elem, chn, ival);
+
+ /* Only change the first channel
+ * but sometimes, the left and right channel have separated channel */
+ if (snd_mixer_selem_has_playback_switch_joined(mixer_elem))
+ return ival;
+ if (first_channel == 0)
+ return ival;
+ first_channel = 0;
+ }
+
+ return -1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
@@ -476,3 +766,4 @@ minidesk_expand_command_line(const gchar
}
}
+