Index: mixer-support.c
===================================================================
--- mixer-support.c	(revision 0)
+++ mixer-support.c	(revision 0)
@@ -0,0 +1,137 @@
+/* GNOME Volume Applet
+ * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * load.c: applet boilerplate code
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <glib/glist.h>
+#include <glib/gi18n.h>
+#include <gst/audio/mixerutils.h>
+
+#include <gtk/gtkliststore.h>
+
+#include "mixer-support.h"
+
+GtkTreeModel *
+create_mixer_device_tree_model (void)
+{
+  GtkListStore *device_store;
+  GList *mixer_list, *l;
+  guint unknown = 0;
+
+  device_store = gtk_list_store_new (MIXER_DEVICE_MODEL_COLUMN_COUNT,
+      G_TYPE_STRING, G_TYPE_STRING, GST_TYPE_ELEMENT);
+
+  mixer_list = gst_audio_default_registry_mixer_filter(NULL, FALSE, NULL);
+
+  for (l = mixer_list; l != NULL; l = l->next) {
+    GstElement *mixer = GST_ELEMENT (l->data);
+    gchar *device_name = NULL, *device = NULL;
+    GstElementFactory *factory;
+    const gchar *longname, *factory_name;
+    gchar *name;
+    GtkTreeIter tree_iter;
+
+    gst_element_set_state (mixer, GST_STATE_READY);
+
+    /* fetch name */
+    if (g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (mixer)), "device-name")) {
+      g_object_get (mixer, "device-name", &device_name, NULL);
+    }
+    if (g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (mixer)), "device")) {
+      g_object_get (mixer, "device", &device, NULL);
+    }
+
+    factory = gst_element_get_factory (mixer);
+    longname = gst_element_factory_get_longname (factory);
+    factory_name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)); 
+
+    /* gst_element_set_state (mixer, GST_STATE_NULL); */
+
+    if (device_name) {
+      name = g_strdup_printf ("%s (%s)", device_name, longname);
+      g_free (device_name);
+    } else {
+      gchar *title;
+
+      unknown++;
+
+      title = g_strdup_printf (_("Unknown Volume Control %d"), unknown);
+      name = g_strdup_printf ("%s (%s)", title, longname);
+      g_free (title);
+    }
+
+    if (device) {
+      gchar *tmp;
+
+      tmp = g_strdup_printf ("%s:%s", factory_name, device);
+      g_free (device);
+      device = tmp;
+    } else {
+      device = g_strdup (factory_name); 
+    }
+
+    gtk_list_store_insert_with_values (device_store, &tree_iter, -1, 
+	MIXER_DEVICE_MODEL_NAME_COLUMN, name,
+	MIXER_DEVICE_MODEL_DEVICE_COLUMN, device,
+	MIXER_DEVICE_MODEL_MIXER_COLUMN, mixer,
+	-1);
+
+    gst_element_set_state (mixer, GST_STATE_NULL);
+    gst_object_unref (GST_OBJECT (mixer));
+
+    g_free (name);
+    g_free (device);
+  }
+
+  g_list_free (mixer_list);
+
+  return GTK_TREE_MODEL (device_store);
+}
+
+GtkTreeModel *
+create_mixer_tracks_tree_model_for_mixer (GstMixer *mixer)
+{
+  GtkListStore *tracks_store;
+  const GList *tracks, *l;
+
+  tracks_store = gtk_list_store_new (MIXER_TRACKS_MODEL_COLUMN_COUNT,
+      G_TYPE_STRING);
+
+  tracks = gst_mixer_list_tracks (mixer);
+  for (l = tracks; l != NULL; l = l->next) {
+    GstMixerTrack *track = l->data;
+    GtkTreeIter iter;
+
+    if (track->num_channels <= 0) {
+      continue;
+    }
+
+    gtk_list_store_insert_with_values (tracks_store, &iter, -1,
+	MIXER_TRACKS_MODEL_LABEL_COLUMN, track->label,
+	-1);
+  }
+
+  return GTK_TREE_MODEL (tracks_store);
+}
Index: mixer-support.h
===================================================================
--- mixer-support.h	(revision 0)
+++ mixer-support.h	(revision 0)
@@ -0,0 +1,27 @@
+
+#ifndef __GNOME_SETTINGS_MIXER_SUPPORT__
+#define __GNOME_SETTINGS_MIXER_SUPPORT__
+
+#include <gtk/gtktreemodel.h>
+#include <gst/interfaces/mixer.h>
+
+G_BEGIN_DECLS
+
+enum {
+  MIXER_DEVICE_MODEL_NAME_COLUMN,
+  MIXER_DEVICE_MODEL_DEVICE_COLUMN,
+  MIXER_DEVICE_MODEL_MIXER_COLUMN,
+  MIXER_DEVICE_MODEL_COLUMN_COUNT
+};
+
+enum {
+  MIXER_TRACKS_MODEL_LABEL_COLUMN,
+  MIXER_TRACKS_MODEL_COLUMN_COUNT
+};
+
+GtkTreeModel *create_mixer_device_tree_model (void);
+GtkTreeModel *create_mixer_tracks_tree_model_for_mixer (GstMixer *mixer);
+
+G_END_DECLS
+
+#endif
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 7095)
+++ Makefile.am	(working copy)
@@ -5,7 +5,7 @@ gnome_sound_properties_LDADD = \
 	$(GNOMECC_CAPPLETS_LIBS) \
 	$(SOUND_CAPPLET_LIBS) \
 	$(HAL_LIBS)
-gnome_sound_properties_SOURCES = sound-properties-capplet.c pipeline-tests.h pipeline-tests.c
+gnome_sound_properties_SOURCES = sound-properties-capplet.c pipeline-tests.h pipeline-tests.c mixer-support.h mixer-support.c
 
 @INTLTOOL_DESKTOP_RULE@
 
Index: sound-properties-capplet.c
===================================================================
--- sound-properties-capplet.c	(revision 7095)
+++ sound-properties-capplet.c	(working copy)
@@ -53,6 +53,8 @@
 #include "activate-settings-daemon.h"
 #include "pipeline-tests.h"
 
+#include "mixer-support.h"
+
 typedef enum {
 	AUDIO_PLAYBACK,
 	AUDIO_CAPTURE,
@@ -71,6 +73,8 @@ typedef struct _DeviceChooser
 
 #define ENABLE_ESD_KEY             "/desktop/gnome/sound/enable_esd"
 #define EVENT_SOUNDS_KEY           "/desktop/gnome/sound/event_sounds"
+#define DEFAULT_MIXER_DEVICE_KEY   "/desktop/gnome/sound/default_mixer_device"
+#define DEFAULT_MIXER_TRACKS_KEY   "/desktop/gnome/sound/default_mixer_tracks"
 #define VISUAL_BELL_KEY            "/apps/metacity/general/visual_bell"
 #define AUDIO_BELL_KEY             "/apps/metacity/general/audible_bell"
 #define VISUAL_BELL_TYPE_KEY       "/apps/metacity/general/visual_bell_type"
@@ -673,6 +677,231 @@ setup_devices ()
 	add_selected_device ("", AUDIO_CAPTURE);
 }
 
+static void
+mixer_device_combobox_changed (GtkComboBox *widget, gpointer user_data)
+{
+	GtkTreeIter iter;
+
+	if (gtk_combo_box_get_active_iter (widget, &iter)) {
+		GtkTreeModel *model;
+		gchar *device = NULL;
+
+		model = gtk_combo_box_get_model (widget);
+		gtk_tree_model_get (model, &iter, 
+				MIXER_DEVICE_MODEL_DEVICE_COLUMN, &device, -1);
+
+		gconf_client_set_string (gconf_client, DEFAULT_MIXER_DEVICE_KEY, device, NULL);
+		g_free (device);
+	}
+}
+
+static void
+add_track_label_to_list (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GSList **list)
+{
+	gchar *label;
+
+	gtk_tree_model_get (model, iter,
+			MIXER_TRACKS_MODEL_LABEL_COLUMN, &label,
+			-1);
+
+	*list = g_slist_prepend (*list, label);
+}
+
+static void
+mixer_tracks_selection_changed (GtkTreeSelection *selection, gpointer user_data)
+{
+	GSList *label_list = NULL;
+
+	gtk_tree_selection_selected_foreach (selection, 
+			(GtkTreeSelectionForeachFunc)add_track_label_to_list,
+			&label_list);
+
+	label_list = g_slist_reverse (label_list);
+	gconf_client_set_list (gconf_client, DEFAULT_MIXER_TRACKS_KEY, GCONF_VALUE_STRING, label_list, NULL);
+
+	g_slist_foreach (label_list, (GFunc)g_free, NULL);
+	g_slist_free (label_list);
+}
+
+static void
+update_mixer_tracks_selection (GSList *tracks, GladeXML *dialog)
+{
+	GtkWidget *tracks_widget;
+	GtkTreeModel *model;
+	GtkTreeSelection *selection;
+	GtkTreeIter iter;
+
+	tracks_widget = WID ("mixer_tracks");
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (tracks_widget));
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tracks_widget));
+
+	g_signal_handlers_block_by_func (G_OBJECT (selection), G_CALLBACK (mixer_tracks_selection_changed), NULL);
+
+	gtk_tree_selection_unselect_all (selection);
+
+	if (gtk_tree_model_get_iter_first (model, &iter)) {
+		do {
+			gchar *label;
+			GSList *t;
+
+			gtk_tree_model_get (model, &iter, MIXER_TRACKS_MODEL_LABEL_COLUMN, &label, -1);
+
+			for (t = tracks; t != NULL; t = t->next) {
+				if (!strcmp (label, t->data)) {
+					gtk_tree_selection_select_iter (selection, &iter);
+					break;
+				}
+			}
+			g_free (label);
+		} while (gtk_tree_model_iter_next (model, &iter));		
+	}
+		
+	g_signal_handlers_unblock_by_func (G_OBJECT (selection), G_CALLBACK (mixer_tracks_selection_changed), NULL);
+
+	/* FIXME: if none selected, select master track */
+}
+
+static void
+default_mixer_tracks_notify (GConfClient *client, guint cnxn_id, GConfEntry *entry, GladeXML *dialog)
+{
+	GSList *tracks;
+
+	tracks = gconf_client_get_list (gconf_client, DEFAULT_MIXER_TRACKS_KEY, GCONF_VALUE_STRING, NULL);
+	
+	update_mixer_tracks_selection (tracks, dialog);
+
+	g_slist_foreach (tracks, (GFunc) g_free, NULL);
+	g_slist_free (tracks);
+}
+
+static void
+update_mixer_device_combobox (const gchar *mixer_device, GladeXML *dialog)
+{
+	GtkWidget *device_widget;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+
+	device_widget = WID ("mixer_device");
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (device_widget));
+
+	/* try to find stored factory and device in the mixer device list */
+	if (gtk_tree_model_get_iter_first (model, &iter)) {
+		do {
+			gchar *device = NULL;
+
+			gtk_tree_model_get (model, &iter,
+					MIXER_DEVICE_MODEL_DEVICE_COLUMN, &device,
+					-1);
+
+			if (!strcmp (device, mixer_device)) {
+				gtk_combo_box_set_active_iter (GTK_COMBO_BOX (device_widget), &iter);
+
+				g_free (device);
+				break;
+			}
+
+			g_free (device);
+		} while (gtk_tree_model_iter_next (model, &iter));
+	}
+
+	/* try to select first mixer entry */
+	if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (device_widget), &iter)) {
+		if (gtk_tree_model_get_iter_first (model, &iter)) {
+			gtk_combo_box_set_active_iter (GTK_COMBO_BOX (device_widget), &iter);
+		}
+	}
+
+	/* fill track list */
+	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (device_widget), &iter)) {
+		GtkWidget *tracks_widget;
+		GstElement *mixer;
+		GtkTreeSelection *selection;
+		GtkTreeModel *tracks_model;
+		GSList *tracks;
+
+		tracks_widget = WID ("mixer_tracks");
+
+		gtk_tree_model_get (model, &iter,
+				MIXER_DEVICE_MODEL_MIXER_COLUMN, &mixer,
+				-1);
+
+		gst_element_set_state (mixer, GST_STATE_READY);
+
+		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tracks_widget));
+		g_signal_handlers_block_by_func (G_OBJECT (selection), G_CALLBACK (mixer_tracks_selection_changed), NULL);
+
+		tracks_model = create_mixer_tracks_tree_model_for_mixer (GST_MIXER (mixer));
+		gtk_tree_view_set_model (GTK_TREE_VIEW (tracks_widget), tracks_model);
+		g_object_unref (tracks_model);
+
+		gst_element_set_state (mixer, GST_STATE_NULL);
+		gst_object_unref (GST_OBJECT (mixer));
+
+		/* updated mixer tracks selection */
+		tracks = gconf_client_get_list (gconf_client, DEFAULT_MIXER_TRACKS_KEY, GCONF_VALUE_STRING, NULL);
+		update_mixer_tracks_selection (tracks, dialog);
+		g_slist_foreach (tracks, (GFunc) g_free, NULL);
+		g_slist_free (tracks);
+
+		g_signal_handlers_unblock_by_func (G_OBJECT (selection), G_CALLBACK (mixer_tracks_selection_changed), NULL);
+	}
+}
+
+static void
+default_mixer_device_notify (GConfClient *client, guint cnxn_id, GConfEntry *entry, GladeXML *dialog)
+{
+	const gchar *mixer_device;
+
+	mixer_device = gconf_value_get_string (gconf_entry_get_value (entry));
+
+	update_mixer_device_combobox (mixer_device, dialog);
+}
+
+static void
+setup_default_mixer (GladeXML *dialog)
+{
+	GtkWidget *device_widget, *tracks_widget;
+	GtkTreeModel *model;
+	GtkCellRenderer *renderer;
+	GtkTreeSelection *selection;
+	gchar *mixer_device;
+
+	device_widget = WID ("mixer_device");
+	tracks_widget = WID ("mixer_tracks");
+
+	model = create_mixer_device_tree_model ();
+	gtk_combo_box_set_model (GTK_COMBO_BOX (device_widget), model);
+	g_object_unref (G_OBJECT (model)); 
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (device_widget), renderer, TRUE);
+	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (device_widget), renderer,
+			"text", MIXER_DEVICE_MODEL_NAME_COLUMN);
+
+	g_signal_connect (G_OBJECT (device_widget), "changed",
+			G_CALLBACK (mixer_device_combobox_changed), NULL);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tracks_widget),
+			-1, NULL, renderer,
+			"text", MIXER_TRACKS_MODEL_LABEL_COLUMN, NULL);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tracks_widget));
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+	g_signal_connect (G_OBJECT (selection), "changed",
+			G_CALLBACK (mixer_tracks_selection_changed), NULL);
+
+	gconf_client_notify_add (gconf_client, DEFAULT_MIXER_DEVICE_KEY,
+			(GConfClientNotifyFunc) default_mixer_device_notify, dialog, NULL, NULL);
+	gconf_client_notify_add (gconf_client, DEFAULT_MIXER_TRACKS_KEY,
+			(GConfClientNotifyFunc) default_mixer_tracks_notify, dialog, NULL, NULL);
+
+	mixer_device = gconf_client_get_string (gconf_client, DEFAULT_MIXER_DEVICE_KEY, NULL);
+	update_mixer_device_combobox (mixer_device, dialog);
+	g_free (mixer_device);
+}
+
 /* setup_dialog
  *
  * Set up the property editors for our dialog
@@ -695,6 +924,7 @@ setup_dialog (GladeXML *dialog, GConfCha
 	gtk_size_group_add_widget (label_size_group, WID ("music_playback_label"));
 	gtk_size_group_add_widget (label_size_group, WID ("chat_audio_playback_label"));
 	gtk_size_group_add_widget (label_size_group, WID ("chat_audio_capture_label"));
+	gtk_size_group_add_widget (label_size_group, WID ("mixer_device_label"));
 	gtk_size_group_add_widget (combobox_size_group, WID ("sounds_playback_device"));
 	gtk_size_group_add_widget (combobox_size_group, WID ("music_playback_device"));
 	gtk_size_group_add_widget (combobox_size_group, WID ("chat_audio_playback_device"));
@@ -734,6 +964,8 @@ setup_dialog (GladeXML *dialog, GConfCha
 					"conv-to-widget-cb", bell_flash_to_widget,
 					"conv-from-widget-cb", bell_flash_from_widget,
 					NULL);
+
+	setup_default_mixer (dialog);
 }
 
 /* get_legacy_settings
Index: sound-properties.glade
===================================================================
--- sound-properties.glade	(revision 7095)
+++ sound-properties.glade	(working copy)
@@ -771,6 +771,143 @@
 	      <property name="fill">False</property>
 	    </packing>
 	  </child>
+            <child>
+              <widget class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <property name="spacing">6</property>
+                <child>
+                  <widget class="GtkLabel" id="label10">
+                    <property name="visible">True</property>
+                    <property name="xalign">0.000000</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Default Mixer Tracks&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkHBox" id="hbox7">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <widget class="GtkLabel" id="label11">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.000000</property>
+                        <property name="label" translatable="yes">    </property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="mixer_device_label">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.000000</property>
+                        <property name="label" translatable="yes">Device:</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkComboBox" id="mixer_device">
+                        <property name="visible">True</property>
+                      </widget>
+                      <packing>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkHBox" id="hbox15">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <widget class="GtkLabel" id="label13">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.000000</property>
+                        <property name="label" translatable="yes">    </property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                        <property name="visible">True</property>
+                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <child>
+                          <widget class="GtkTreeView" id="mixer_tracks">
+                            <property name="visible">True</property>
+                            <property name="headers_visible">False</property>
+                          </widget>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkHBox" id="hbox17">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <widget class="GtkLabel" id="label15">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.000000</property>
+                        <property name="label" translatable="yes">    </property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label16">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.000000</property>
+                        <property name="label" translatable="yes">Select the device and tracks to control with the keyboard. Use the Shift and Control keys to select multiple tracks if required.</property>
+                        <property name="use_markup">True</property>
+                        <property name="wrap">True</property>
+                      </widget>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">3</property>
+              </packing>
+	    </child>
 	</widget>
 	<packing>
 	  <property name="tab_expand">False</property>
Index: configure.in
===================================================================
--- configure.in	(revision 7095)
+++ configure.in	(working copy)
@@ -108,7 +108,9 @@ PKG_CHECK_MODULES(DBUS, dbus-1 dbus-glib
 PKG_CHECK_MODULES(VFS_CAPPLET, $COMMON_MODULES gnome-vfs-module-2.0 gnome-vfs-2.0)
 PKG_CHECK_MODULES(GNOME_DESKTOP, gnome-desktop-2.0)
 PKG_CHECK_MODULES(DEFAULT_APPLICATIONS_CAPPLET, libxml-2.0)
-PKG_CHECK_MODULES(SOUND_CAPPLET, esound gstreamer-0.10)
+PKG_CHECK_MODULES(SOUND_CAPPLET, esound gstreamer-0.10 gstreamer-plugins-base-0.10)
+SOUND_CAPPLET_LIBS="$SOUND_CAPPLET_LIBS -lgstaudio-0.10 -lgstinterfaces-0.10"
+AC_SUBST(SOUND_CAPPLET_LIBS)
 PKG_CHECK_MODULES(METACITY, libmetacity-private)
 
 dnl
Index: gnome-settings-daemon/actions/acme-volume-gstreamer.c
===================================================================
--- gnome-settings-daemon/actions/acme-volume-gstreamer.c	(revision 7095)
+++ gnome-settings-daemon/actions/acme-volume-gstreamer.c	(working copy)
@@ -27,27 +27,28 @@
 #include "config.h"
 #include "acme-volume-gstreamer.h"
 
-#ifdef HAVE_GST10
 #include <gst/gst.h>
 #include <gst/audio/mixerutils.h>
 #include <gst/interfaces/mixer.h>
 #include <gst/interfaces/propertyprobe.h>
-#else
-#include <gst/gst.h>
-#include <gst/mixer/mixer.h>
-#include <gst/propertyprobe/propertyprobe.h>
-#endif
+
+#include <gconf/gconf-client.h>
 
 #include <string.h>
 
 #define TIMEOUT	4000
+
+#define DEFAULT_MIXER_DEVICE_KEY   "/desktop/gnome/sound/default_mixer_device"
+#define DEFAULT_MIXER_TRACKS_KEY   "/desktop/gnome/sound/default_mixer_tracks"
  
 struct AcmeVolumeGStreamerPrivate
 {
   	GstMixer      *mixer;
-  	GstMixerTrack *track;
+	GList         *mixer_tracks;
  	guint timer_id;
-	gint state;
+	gdouble      volume;
+	gboolean     mute;
+	GConfClient *gconf_client;
 };
 
 static GObjectClass *parent_class = NULL;
@@ -78,7 +79,12 @@ acme_volume_gstreamer_finalize (GObject 
 		self->_priv->timer_id = 0;
 	}
 	acme_volume_gstreamer_close_real (self);
-				
+
+	if (self->_priv->gconf_client != NULL) {
+		g_object_unref (G_OBJECT (self->_priv->gconf_client));
+		self->_priv->gconf_client = NULL;
+	}
+
 	g_free (self->_priv);
 
 	G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -88,27 +94,36 @@ static void
 acme_volume_gstreamer_set_mute (AcmeVolume *vol, gboolean val)
 {
 	AcmeVolumeGStreamer *self = (AcmeVolumeGStreamer *) vol;
+	GList *t;
 	
 	if (acme_volume_gstreamer_open (self) == FALSE)
 		return;
-		
-	gst_mixer_set_mute (self->_priv->mixer,
-			    self->_priv->track,
-			    val);
 
-	if (val)
-		self->_priv->state |= 1;
-	else {
-		GstMixerTrack *track = self->_priv->track;
-		gint *volumes, n;
+	for (t = self->_priv->mixer_tracks; t != NULL; t = t->next)
+	{
+		GstMixerTrack *track = GST_MIXER_TRACK (t->data);
+		gst_mixer_set_mute (self->_priv->mixer, track, val);
+	}
 
-		self->_priv->state &= ~1;
-		volumes = g_new0 (gint, track->num_channels);
-		for (n = 0; n < track->num_channels; n++)
-			volumes[n] = (self->_priv->state >> 1) /
-				track->num_channels;
-		gst_mixer_set_volume (self->_priv->mixer, track, volumes);
-		g_free (volumes);
+	if (val)
+	{
+		self->_priv->mute = TRUE;
+	} else {
+		self->_priv->mute = FALSE;
+
+		for (t = self->_priv->mixer_tracks; t != NULL; t = t->next)
+		{
+			GstMixerTrack *track = GST_MIXER_TRACK (t->data);
+			gint *volumes, n;
+			gdouble scale = (track->max_volume - track->min_volume) / 100.0;
+			gint vol = (gint) self->_priv->volume * scale + track->min_volume;
+
+			volumes = g_new0 (gint, track->num_channels);
+			for (n = 0; n < track->num_channels; n++)
+				volumes[n] = vol;
+			gst_mixer_set_volume (self->_priv->mixer, track, volumes);
+			g_free (volumes);
+		}
 	}
 
 	acme_volume_gstreamer_close (self);
@@ -117,8 +132,9 @@ acme_volume_gstreamer_set_mute (AcmeVolu
 static void
 update_state (AcmeVolumeGStreamer * self)
 {
-	gint *volumes, vol = 0, n;
-	GstMixerTrack *track = self->_priv->track;
+	gint *volumes, n;
+	gdouble vol;
+	GstMixerTrack *track = GST_MIXER_TRACK(self->_priv->mixer_tracks->data);
 
 	/* update mixer by getting volume */
 	volumes = g_new0 (gint, track->num_channels);
@@ -126,13 +142,15 @@ update_state (AcmeVolumeGStreamer * self
 	for (n = 0; n < track->num_channels; n++)
 		vol += volumes[n];
 	g_free (volumes);
+	vol /= track->num_channels;
+	vol = 100 * vol / (track->max_volume - track->min_volume);
 
 	/* update mute flag, and volume if not muted */
 	if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE) ||
-	    (vol == 0 && (self->_priv->state >> 1) != 0))
-		self->_priv->state |= 1;
+	    (vol == 0 && self->_priv->volume != 0))
+		self->_priv->mute = TRUE;
 	else
-		self->_priv->state = vol << 1;
+		self->_priv->volume = vol;
 }
 
 static gboolean
@@ -146,58 +164,51 @@ acme_volume_gstreamer_get_mute (AcmeVolu
 	update_state (self);
 	acme_volume_gstreamer_close (self);
 
-	return (self->_priv->state & 1);
+	return self->_priv->mute;
 }
 
 static int
 acme_volume_gstreamer_get_volume (AcmeVolume *vol)
 {
-	double volume;
 	AcmeVolumeGStreamer *self = (AcmeVolumeGStreamer *) vol;
-	GstMixerTrack *track;
 	
 	if (acme_volume_gstreamer_open (self) == FALSE)
 		return 0;
-	track = self->_priv->track;
 
 	update_state (self);
 
-	/* normalize to [0,100] scale that acme wants */
-	volume = (self->_priv->state >> 1) / (double) track->num_channels;
-	volume = 100 * (volume - track->min_volume) /
-		(track->max_volume - track->min_volume);
-
 	acme_volume_gstreamer_close (self);
 
-	return (gint) volume;
+	return (gint) self->_priv->volume;
 }
 
 static void
 acme_volume_gstreamer_set_volume (AcmeVolume *vol, int val)
 {
-	gint i, *volumes, volume;
 	AcmeVolumeGStreamer *self = (AcmeVolumeGStreamer *) vol;
-	GstMixerTrack *track;
+	GList *t;
 
 	if (acme_volume_gstreamer_open (self) == FALSE)
 		return;
 
-	track = self->_priv->track;
 	val = CLAMP (val, 0, 100);
 
-	/* Rescale the volume from [0, 100] to [track min, track max]. */
-	volume = (val / 100.0) * (track->max_volume - track->min_volume) +
-		track->min_volume;
-
-	volumes = g_new (gint, track->num_channels);
-	for (i = 0; i < track->num_channels; ++i)
-		volumes[i] = (gint) volume;
-	gst_mixer_set_volume (self->_priv->mixer, track, volumes);
-	g_free (volumes);
+	for (t = self->_priv->mixer_tracks; t != NULL; t = t->next)
+	{
+		GstMixerTrack *track = GST_MIXER_TRACK (t->data);
+		gint *volumes, n;
+		gdouble scale = (track->max_volume - track->min_volume) / 100.0;
+		gint vol = (gint) val * scale + track->min_volume;
+
+		volumes = g_new0 (gint, track->num_channels);
+		for (n = 0; n < track->num_channels; n++)
+			volumes[n] = vol;
+		gst_mixer_set_volume (self->_priv->mixer, track, volumes);
+		g_free (volumes);
+	}
  	
 	/* update state */
-	self->_priv->state = (self->_priv->state & 1) |
-		((volume * track->num_channels) << 1);
+	self->_priv->volume = val;
 
  	acme_volume_gstreamer_close (self);
 }
@@ -211,17 +222,17 @@ acme_volume_gstreamer_close_real (AcmeVo
 	if (self->_priv->mixer != NULL)
 	{
 		gst_element_set_state (GST_ELEMENT(self->_priv->mixer), GST_STATE_NULL);
-		gst_object_unref (GST_OBJECT (self->_priv->mixer));	    
-		g_object_unref (G_OBJECT (self->_priv->track));
+		gst_object_unref (GST_OBJECT (self->_priv->mixer));
+		g_list_foreach (self->_priv->mixer_tracks, (GFunc)g_object_unref, NULL);
+		g_list_free (self->_priv->mixer_tracks);
 		self->_priv->mixer=NULL;
-		self->_priv->track=NULL;
+		self->_priv->mixer_tracks=NULL;
 	}
 	
 	self->_priv->timer_id = 0;
 	return FALSE;
 }
 
-#ifdef HAVE_GST10
 /*
  * _acme_set_mixer
  * Arguments: mixer - pointer to mixer element
@@ -244,10 +255,7 @@ _acme_set_mixer(GstMixer *mixer, gpointe
          self = ACME_VOLUME_GSTREAMER (user_data);
 
          self->_priv->mixer = mixer;
-         self->_priv->track = track;
-
-         /* no need to ref the mixer element */
-         g_object_ref (self->_priv->track);
+         self->_priv->mixer_tracks = g_list_append (self->_priv->mixer_tracks, g_object_ref (track));
          return TRUE;
       }
 
@@ -256,19 +264,14 @@ _acme_set_mixer(GstMixer *mixer, gpointe
 
    return FALSE;
 }
-#endif
 
 /* This is a modified version of code from gnome-media's gst-mixer */
 static gboolean
 acme_volume_gstreamer_open (AcmeVolumeGStreamer *vol)
 {
   	AcmeVolumeGStreamer *self = (AcmeVolumeGStreamer *) vol;
-#ifdef HAVE_GST10
+	gchar *mixer_device, **factory_and_device;
 	GList *mixer_list;
-#else
-  	const GList *elements;
-  	gint num = 0;
-#endif
 
 	if (self->_priv == NULL)
 		return FALSE;
@@ -279,7 +282,89 @@ acme_volume_gstreamer_open (AcmeVolumeGS
 		self->_priv->timer_id = 0;
 		return TRUE;
 	}
-		
+
+	mixer_device = gconf_client_get_string (self->_priv->gconf_client, DEFAULT_MIXER_DEVICE_KEY, NULL);
+	factory_and_device = g_strsplit (mixer_device, ":", 2);
+
+	if (factory_and_device != NULL && factory_and_device[0] != NULL)
+	{
+		GstElement *element;
+
+		element = gst_element_factory_make (factory_and_device[0], NULL);
+
+		if (element != NULL) {
+			gst_element_set_state (element, GST_STATE_READY);
+
+			if (!GST_IS_MIXER (element))
+			{
+				gst_element_set_state (element, GST_STATE_NULL);
+				gst_object_unref (element);
+			} else {
+				self->_priv->mixer = GST_MIXER (element);
+			
+				if (factory_and_device[1] != NULL && 
+						g_object_class_find_property (G_OBJECT_GET_CLASS (self->_priv->mixer), "device"))
+				{
+					g_object_set (G_OBJECT (self->_priv->mixer), "device", &factory_and_device[1], NULL);
+				}
+			}
+		}
+	}
+
+	g_free (mixer_device);
+	g_strfreev (factory_and_device);
+
+	if (self->_priv->mixer != NULL)
+	{
+		const GList *m;
+		GSList *tracks, *t;
+
+		/* Try to use tracks saved in GConf */
+		tracks = gconf_client_get_list (self->_priv->gconf_client, DEFAULT_MIXER_TRACKS_KEY, GCONF_VALUE_STRING, NULL);
+
+		for (m = gst_mixer_list_tracks (self->_priv->mixer); m != NULL; m = m->next)
+		{
+			GstMixerTrack *track = GST_MIXER_TRACK (m->data);
+
+			for (t = tracks; t != NULL; t = t->next)
+			{
+				if (!strcmp (t->data, track->label))
+				{
+					self->_priv->mixer_tracks = g_list_append (self->_priv->mixer_tracks, g_object_ref (track));
+				}
+			}
+			
+		}
+
+		g_slist_foreach (tracks, (GFunc)g_free, NULL);
+		g_slist_free (tracks);
+
+		/* If no track stored in GConf is avaiable try to use master track */
+		if (self->_priv->mixer_tracks == NULL)
+		{
+			for (m = gst_mixer_list_tracks (self->_priv->mixer); m != NULL; m = m->next)
+			{
+				GstMixerTrack *track = GST_MIXER_TRACK (m->data);
+
+				if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MASTER)) {
+					self->_priv->mixer_tracks = g_list_append (self->_priv->mixer_tracks, track);
+					break;
+				}
+			}
+		}
+	}
+
+	if (self->_priv->mixer != NULL)
+	{
+		if (self->_priv->mixer_tracks != NULL)
+		{
+			return TRUE;
+		} else {
+			gst_element_set_state (GST_ELEMENT (self->_priv->mixer), GST_STATE_NULL);
+			gst_object_unref (self->_priv->mixer);
+		}
+	}
+
 	/* Go through all elements of a certain class and check whether
 	 * they implement a mixer. If so, walk through the tracks and look
 	 * for first one named "volume".
@@ -288,101 +373,17 @@ acme_volume_gstreamer_open (AcmeVolumeGS
 	 * appropriate mixer/track.  But now we do something stupid...
 	 * everything just becomes a no-op.
 	 */
-#ifdef HAVE_GST10
-   mixer_list = gst_audio_default_registry_mixer_filter (_acme_set_mixer,
-                                                          TRUE,
-                                                          self);
-
-   if (mixer_list == NULL)
-      return FALSE;
-
-   /* do not unref the mixer as we keep the ref for self->priv->mixer */
-   g_list_free (mixer_list);
-
-   return TRUE;
-#else
-	elements = gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY);
-	for ( ; elements != NULL && self->_priv->mixer == NULL; elements = elements->next) {
-		GstElementFactory *factory = GST_ELEMENT_FACTORY (elements->data);
-		gchar *title = NULL;
-		const gchar *klass;
-		GstElement *element = NULL;
-		const GParamSpec *devspec;
-		GstPropertyProbe *probe;
-		GValueArray *array = NULL;
-		gint n;
-		const GList *tracks;
-		
-		/* check category */
-		klass = gst_element_factory_get_klass (factory);
-		if (strcmp (klass, "Generic/Audio"))
-			goto next;
-		
-		/* create element */
-		title = g_strdup_printf ("gst-mixer-%d", num);
-		element = gst_element_factory_create (factory, title);
-		if (!element)
-			goto next;
-		
-		if (!GST_IS_PROPERTY_PROBE (element))
-			goto next;
-		
-		probe = GST_PROPERTY_PROBE (element);
-		devspec = gst_property_probe_get_property (probe, "device");
-		if (devspec == NULL)
-			goto next;
-		array = gst_property_probe_probe_and_get_values (probe, devspec);
-		if (array == NULL)
-			goto next;
-
-		/* set all devices and test for mixer */
-		for (n = 0; n < array->n_values; n++) {
-			GValue *device = g_value_array_get_nth (array, n);
-			
-			/* set this device */
-			g_object_set_property (G_OBJECT (element), "device", device);
-			if (gst_element_set_state (element,
-						   GST_STATE_READY) == GST_STATE_FAILURE)
-				continue;
-			
-			/* Is this device a mixer?  If so, add it to the list. */
-			if (!GST_IS_MIXER (element)) {
-				gst_element_set_state (element, GST_STATE_NULL);
-				continue;
-			}
-			
-			tracks = gst_mixer_list_tracks (GST_MIXER (element));
-			for (; tracks != NULL; tracks = tracks->next) {
-				GstMixerTrack *track = tracks->data;
-				
-				if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MASTER)) {
-					self->_priv->mixer = GST_MIXER (element);
-					self->_priv->track = track;
+	mixer_list = gst_audio_default_registry_mixer_filter (_acme_set_mixer,
+			TRUE,
+			self);
 
-					g_object_ref (self->_priv->mixer);
-					g_object_ref (self->_priv->track);
-					if (array)
-						g_value_array_free (array);
-					return TRUE;
-				}
-			}
-			
-			num++;
-			
-			/* and recreate this object, since we give it to the mixer */
-			title = g_strdup_printf ("gst-mixer-%d", num);
-			element = gst_element_factory_create (factory, title);
-		}
-		
-	next:
-		if (element)
-			gst_object_unref (GST_OBJECT (element));
-		if (array)
-			g_value_array_free (array);
-		g_free (title);
-	}
-	return FALSE;
-#endif
+	if (mixer_list == NULL)
+		return FALSE;
+
+	/* do not unref the mixer as we keep the ref for self->priv->mixer */
+	g_list_free (mixer_list);
+
+	return TRUE;
 }
 
 static void
@@ -397,7 +398,9 @@ acme_volume_gstreamer_init (AcmeVolumeGS
 {
 	
 	self->_priv = g_new0 (AcmeVolumeGStreamerPrivate, 1);
-	
+
+	self->_priv->gconf_client = gconf_client_get_default ();
+
 	if (acme_volume_gstreamer_open (self) == FALSE)
 	{
 		g_free (self->_priv);
Index: gnome-settings-daemon/actions/Makefile.am
===================================================================
--- gnome-settings-daemon/actions/Makefile.am	(revision 7095)
+++ gnome-settings-daemon/actions/Makefile.am	(working copy)
@@ -9,9 +9,6 @@ libacme_la_SOURCES = \
 libacme_la_LIBADD = 
 
 if HAVE_GSTREAMER
-if HAVE_GST10
-INCLUDES += -DHAVE_GST10
-endif
 INCLUDES += -DHAVE_GSTREAMER
 libacme_la_SOURCES += acme-volume-gstreamer.c acme-volume-gstreamer.h
 libacme_la_LIBADD += $(GST_LIBS)
