[gnome-settings-daemon] Update gnome-volume-control code from master



commit 118053701eb865d553782d73ec4554d8fbfe8811
Author: Bastien Nocera <hadess hadess net>
Date:   Mon Jun 22 15:40:19 2009 +0100

    Update gnome-volume-control code from master
    
    And make slight changes to gsd-media-keys-manager.c to match
    the new API.

 plugins/media-keys/cut-n-paste/gvc-channel-map.c   |  175 +++++++++++--
 plugins/media-keys/cut-n-paste/gvc-channel-map.h   |   24 ++-
 plugins/media-keys/cut-n-paste/gvc-mixer-control.c |  276 ++++++++++++++++----
 plugins/media-keys/cut-n-paste/gvc-mixer-control.h |    1 +
 .../media-keys/cut-n-paste/gvc-mixer-event-role.c  |   29 +-
 .../media-keys/cut-n-paste/gvc-mixer-event-role.h  |    5 +-
 .../media-keys/cut-n-paste/gvc-mixer-sink-input.c  |   40 ++--
 plugins/media-keys/cut-n-paste/gvc-mixer-sink.c    |   53 ++--
 .../cut-n-paste/gvc-mixer-source-output.c          |    6 +-
 plugins/media-keys/cut-n-paste/gvc-mixer-source.c  |   48 ++--
 plugins/media-keys/cut-n-paste/gvc-mixer-stream.c  |  225 ++++++++++++++---
 plugins/media-keys/cut-n-paste/gvc-mixer-stream.h  |   23 ++-
 plugins/media-keys/gsd-media-keys-manager.c        |   10 +-
 13 files changed, 708 insertions(+), 207 deletions(-)
---
diff --git a/plugins/media-keys/cut-n-paste/gvc-channel-map.c b/plugins/media-keys/cut-n-paste/gvc-channel-map.c
index e7c9b22..32750ef 100644
--- a/plugins/media-keys/cut-n-paste/gvc-channel-map.c
+++ b/plugins/media-keys/cut-n-paste/gvc-channel-map.c
@@ -33,15 +33,26 @@
 
 #define GVC_CHANNEL_MAP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMapPrivate))
 
+#ifndef PA_CHECK_VERSION
+#define PA_CHECK_VERSION(major,minor,micro)                             \
+        ((PA_MAJOR > (major)) ||                                        \
+         (PA_MAJOR == (major) && PA_MINOR > (minor)) ||                 \
+         (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))
+#endif
+
+
 struct GvcChannelMapPrivate
 {
-        guint                 num_channels;
-        pa_channel_position_t positions[PA_CHANNELS_MAX];
-        gdouble               gains[PA_CHANNELS_MAX];
+        pa_channel_map        pa_map;
+        pa_cvolume            pa_volume;
+        gdouble               extern_volume[NUM_TYPES]; /* volume, balance, fade, lfe */
+        gboolean              can_balance;
+        gboolean              can_fade;
+        gboolean              has_lfe;
 };
 
 enum {
-        GAINS_CHANGED,
+        VOLUME_CHANGED,
         LAST_SIGNAL
 };
 
@@ -53,25 +64,135 @@ static void     gvc_channel_map_finalize   (GObject            *object);
 
 G_DEFINE_TYPE (GvcChannelMap, gvc_channel_map, G_TYPE_OBJECT)
 
+/* FIXME remove when we depend on a newer PA */
+static int
+gvc_pa_channel_map_has_position (const pa_channel_map *map, pa_channel_position_t p) {
+        unsigned c;
+
+        g_return_val_if_fail(pa_channel_map_valid(map), 0);
+        g_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0);
+
+        for (c = 0; c < map->channels; c++)
+                if (map->map[c] == p)
+                        return 1;
+
+        return 0;
+}
+
+#if !PA_CHECK_VERSION(0,9,16)
+/* The PulseAudio master increase version only when tagged, so let's avoid clashing with pa_ namespace */
+#define pa_cvolume_get_position gvc_cvolume_get_position
+static pa_volume_t
+gvc_cvolume_get_position (pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t) {
+        unsigned c;
+        pa_volume_t v = PA_VOLUME_MUTED;
+
+        g_assert(cv);
+        g_assert(map);
+
+        g_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), PA_VOLUME_MUTED);
+        g_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, PA_VOLUME_MUTED);
+
+        for (c = 0; c < map->channels; c++)
+                if (map->map[c] == t)
+                        if (cv->values[c] > v)
+                                v = cv->values[c];
+
+        return v;
+}
+#endif
+
 guint
 gvc_channel_map_get_num_channels (GvcChannelMap *map)
 {
         g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), 0);
-        return map->priv->num_channels;
+
+        if (!pa_channel_map_valid(&map->priv->pa_map))
+                return 0;
+
+        return map->priv->pa_map.channels;
+}
+
+const gdouble *
+gvc_channel_map_get_volume (GvcChannelMap *map)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
+
+        if (!pa_channel_map_valid(&map->priv->pa_map))
+                return NULL;
+
+        map->priv->extern_volume[VOLUME] = (gdouble) pa_cvolume_max (&map->priv->pa_volume);
+        if (gvc_channel_map_can_balance (map))
+                map->priv->extern_volume[BALANCE] = (gdouble) pa_cvolume_get_balance (&map->priv->pa_volume, &map->priv->pa_map);
+        else
+                map->priv->extern_volume[BALANCE] = 0;
+        if (gvc_channel_map_can_fade (map))
+                map->priv->extern_volume[FADE] = (gdouble) pa_cvolume_get_fade (&map->priv->pa_volume, &map->priv->pa_map);
+        else
+                map->priv->extern_volume[FADE] = 0;
+        if (gvc_channel_map_has_lfe (map))
+                map->priv->extern_volume[LFE] = (gdouble) pa_cvolume_get_position (&map->priv->pa_volume, &map->priv->pa_map, PA_CHANNEL_POSITION_LFE);
+        else
+                map->priv->extern_volume[LFE] = 0;
+
+        return map->priv->extern_volume;
+}
+
+gboolean
+gvc_channel_map_can_balance (GvcChannelMap  *map)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
+
+        return map->priv->can_balance;
+}
+
+gboolean
+gvc_channel_map_can_fade (GvcChannelMap  *map)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
+
+        return map->priv->can_fade;
+}
+
+const char *
+gvc_channel_map_get_mapping (GvcChannelMap  *map)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
+
+        if (!pa_channel_map_valid(&map->priv->pa_map))
+                return NULL;
+
+        return pa_channel_map_to_pretty_name (&map->priv->pa_map);
 }
 
-gdouble *
-gvc_channel_map_get_gains (GvcChannelMap *map)
+gboolean
+gvc_channel_map_has_lfe (GvcChannelMap  *map)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
+
+        return map->priv->has_lfe;
+}
+
+const pa_channel_map *
+gvc_channel_map_get_pa_channel_map (GvcChannelMap  *map)
 {
         g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
-        return map->priv->gains;
+
+        if (!pa_channel_map_valid(&map->priv->pa_map))
+                return NULL;
+
+        return &map->priv->pa_map;
 }
 
-pa_channel_position_t *
-gvc_channel_map_get_positions (GvcChannelMap *map)
+const pa_cvolume *
+gvc_channel_map_get_cvolume (GvcChannelMap  *map)
 {
         g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
-        return map->priv->positions;
+
+        if (!pa_channel_map_valid(&map->priv->pa_map))
+                return NULL;
+
+        return &map->priv->pa_volume;
 }
 
 static void
@@ -81,11 +202,11 @@ gvc_channel_map_class_init (GvcChannelMapClass *klass)
 
         gobject_class->finalize = gvc_channel_map_finalize;
 
-        signals [GAINS_CHANGED] =
-                g_signal_new ("gains-changed",
+        signals [VOLUME_CHANGED] =
+                g_signal_new ("volume-changed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
-                              G_STRUCT_OFFSET (GvcChannelMapClass, gains_changed),
+                              G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed),
                               NULL, NULL,
                               g_cclosure_marshal_VOID__VOID,
                               G_TYPE_NONE, 0);
@@ -94,10 +215,19 @@ gvc_channel_map_class_init (GvcChannelMapClass *klass)
 }
 
 void
-gvc_channel_map_gains_changed (GvcChannelMap *map)
+gvc_channel_map_volume_changed (GvcChannelMap     *map,
+                                const pa_cvolume  *cv)
 {
         g_return_if_fail (GVC_IS_CHANNEL_MAP (map));
-        g_signal_emit (map, signals[GAINS_CHANGED], 0);
+        g_return_if_fail (cv != NULL);
+        g_return_if_fail (pa_cvolume_compatible_with_channel_map(cv, &map->priv->pa_map));
+
+        if (pa_cvolume_equal(cv, &map->priv->pa_volume))
+                return;
+
+        map->priv->pa_volume = *cv;
+
+        g_signal_emit (map, signals[VOLUME_CHANGED], 0);
 }
 
 static void
@@ -133,13 +263,14 @@ static void
 set_from_pa_map (GvcChannelMap        *map,
                  const pa_channel_map *pa_map)
 {
-        guint i;
+        g_assert (pa_channel_map_valid(pa_map));
+
+        map->priv->can_balance = pa_channel_map_can_balance (pa_map);
+        map->priv->can_fade = pa_channel_map_can_fade (pa_map);
+        map->priv->has_lfe = gvc_pa_channel_map_has_position (pa_map, PA_CHANNEL_POSITION_LFE);
 
-        map->priv->num_channels = pa_map->channels;
-        for (i = 0; i < pa_map->channels; i++) {
-                map->priv->positions[i] = pa_map->map[i];
-                map->priv->gains[i] = 1.0;
-        }
+        map->priv->pa_map = *pa_map;
+        pa_cvolume_set(&map->priv->pa_volume, pa_map->channels, PA_VOLUME_NORM);
 }
 
 GvcChannelMap *
diff --git a/plugins/media-keys/cut-n-paste/gvc-channel-map.h b/plugins/media-keys/cut-n-paste/gvc-channel-map.h
index 963904f..b35c9cb 100644
--- a/plugins/media-keys/cut-n-paste/gvc-channel-map.h
+++ b/plugins/media-keys/cut-n-paste/gvc-channel-map.h
@@ -44,19 +44,35 @@ typedef struct
 typedef struct
 {
         GObjectClass           parent_class;
-        void (*gains_changed) (GvcChannelMap *channel_map);
+        void (*volume_changed) (GvcChannelMap *channel_map);
 } GvcChannelMapClass;
 
+enum {
+        VOLUME,
+        BALANCE,
+        FADE,
+        LFE,
+};
+
+#define NUM_TYPES LFE + 1
+
 GType                   gvc_channel_map_get_type                (void);
 
 GvcChannelMap *         gvc_channel_map_new                     (void);
 GvcChannelMap *         gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *map);
 guint                   gvc_channel_map_get_num_channels        (GvcChannelMap  *map);
-pa_channel_position_t * gvc_channel_map_get_positions           (GvcChannelMap  *map);
-gdouble *               gvc_channel_map_get_gains               (GvcChannelMap  *map);
+const gdouble *         gvc_channel_map_get_volume              (GvcChannelMap  *map);
+gboolean                gvc_channel_map_can_balance             (GvcChannelMap  *map);
+gboolean                gvc_channel_map_can_fade                (GvcChannelMap  *map);
+gboolean                gvc_channel_map_has_lfe                 (GvcChannelMap  *map);
 
-void                    gvc_channel_map_gains_changed           (GvcChannelMap  *map);
+void                    gvc_channel_map_volume_changed          (GvcChannelMap    *map,
+                                                                 const pa_cvolume *cv);
+const char *            gvc_channel_map_get_mapping             (GvcChannelMap  *map);
 
+/* private */
+const pa_cvolume *      gvc_channel_map_get_cvolume             (GvcChannelMap  *map);
+const pa_channel_map *  gvc_channel_map_get_pa_channel_map      (GvcChannelMap  *map);
 G_END_DECLS
 
 #endif /* __GVC_CHANNEL_MAP_H */
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-control.c b/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
index e7dd18b..92b0286 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
@@ -48,6 +48,7 @@ struct GvcMixerControlPrivate
         pa_mainloop_api  *pa_api;
         pa_context       *pa_context;
         int               n_outstanding;
+        guint             reconnect_id;
 
         gboolean          default_sink_is_set;
         guint             default_sink_id;
@@ -65,9 +66,12 @@ struct GvcMixerControlPrivate
         GHashTable       *sink_inputs; /* routable output streams */
         GHashTable       *source_outputs; /* routable input streams */
         GHashTable       *clients;
+
+        GvcMixerStream   *new_default_stream; /* new default stream, used in gvc_mixer_control_set_default_sink () */
 };
 
 enum {
+        CONNECTING,
         READY,
         STREAM_ADDED,
         STREAM_REMOVED,
@@ -104,6 +108,42 @@ gvc_mixer_control_get_event_sink_input (GvcMixerControl *control)
         return stream;
 }
 
+static void
+gvc_mixer_control_stream_restore_cb (pa_context *c,
+                                     const pa_ext_stream_restore_info *info,
+                                     int eol,
+                                     void *userdata)
+{
+        pa_operation *o;
+        GvcMixerControl *control = (GvcMixerControl *) userdata;
+        pa_ext_stream_restore_info new_info;
+
+        if (eol || control->priv->new_default_stream == NULL)
+                return;
+
+        new_info.name = info->name;
+        new_info.channel_map = info->channel_map;
+        new_info.volume = info->volume;
+        new_info.mute = info->mute;
+
+        new_info.device = gvc_mixer_stream_get_name (control->priv->new_default_stream);
+
+        o = pa_ext_stream_restore_write (control->priv->pa_context,
+                                         PA_UPDATE_REPLACE,
+                                         &new_info, 1,
+                                         TRUE, NULL, NULL);
+
+        if (o == NULL) {
+                g_warning ("pa_ext_stream_restore_write() failed: %s",
+                           pa_strerror (pa_context_errno (control->priv->pa_context)));
+                return;
+        }
+
+        g_debug ("Changed default device for %s to %s", info->name, info->device);
+
+        pa_operation_unref (o);
+}
+
 gboolean
 gvc_mixer_control_set_default_sink (GvcMixerControl *control,
                                     GvcMixerStream  *stream)
@@ -118,7 +158,23 @@ gvc_mixer_control_set_default_sink (GvcMixerControl *control,
                                          NULL,
                                          NULL);
         if (o == NULL) {
-                g_warning ("pa_context_set_default_sink() failed");
+                g_warning ("pa_context_set_default_sink() failed: %s",
+                           pa_strerror (pa_context_errno (control->priv->pa_context)));
+                return FALSE;
+        }
+
+        pa_operation_unref (o);
+
+        control->priv->new_default_stream = stream;
+        g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_stream);
+
+        o = pa_ext_stream_restore_read (control->priv->pa_context,
+                                        gvc_mixer_control_stream_restore_cb,
+                                        control);
+
+        if (o == NULL) {
+                g_warning ("pa_ext_stream_restore_read() failed: %s",
+                           pa_strerror (pa_context_errno (control->priv->pa_context)));
                 return FALSE;
         }
 
@@ -220,6 +276,13 @@ gvc_stream_collate (GvcMixerStream *a,
         namea = gvc_mixer_stream_get_name (a);
         nameb = gvc_mixer_stream_get_name (b);
 
+        if (nameb == NULL && namea == NULL)
+                return 0;
+        if (nameb == NULL)
+                return 1;
+        if (namea == NULL)
+                return -1;
+
         return g_utf8_collate (namea, nameb);
 }
 
@@ -320,12 +383,14 @@ _set_default_source (GvcMixerControl *control,
 {
         guint new_id;
 
-        new_id = 0;
-
-        if (stream != NULL) {
-                new_id = gvc_mixer_stream_get_id (stream);
+        if (stream == NULL) {
+                control->priv->default_source_id = 0;
+                control->priv->default_source_is_set = FALSE;
+                return;
         }
 
+        new_id = gvc_mixer_stream_get_id (stream);
+
         if (control->priv->default_source_id != new_id) {
                 control->priv->default_source_id = new_id;
                 control->priv->default_source_is_set = TRUE;
@@ -342,12 +407,14 @@ _set_default_sink (GvcMixerControl *control,
 {
         guint new_id;
 
-        new_id = 0;
-
-        if (stream != NULL) {
-                new_id = gvc_mixer_stream_get_id (stream);
+        if (stream == NULL) {
+                control->priv->default_sink_id = 0;
+                control->priv->default_sink_is_set = FALSE;
+                return;
         }
 
+        new_id = gvc_mixer_stream_get_id (stream);
+
         if (control->priv->default_sink_id != new_id) {
                 control->priv->default_sink_id = new_id;
                 control->priv->default_sink_is_set = TRUE;
@@ -495,6 +562,7 @@ update_sink (GvcMixerControl    *control,
         GvcMixerStream *stream;
         gboolean        is_new;
         pa_volume_t     max_volume;
+        GvcChannelMap  *map;
         char            map_buff[PA_CHANNEL_MAP_SNPRINT_MAX];
 
         pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map);
@@ -506,22 +574,21 @@ update_sink (GvcMixerControl    *control,
                  map_buff);
 #endif
 
-        /* for now completely ignore virtual streams */
-        if (!(info->flags & PA_SINK_HARDWARE)) {
-                return;
-        }
-
+        map = NULL;
         is_new = FALSE;
         stream = g_hash_table_lookup (control->priv->sinks,
                                       GUINT_TO_POINTER (info->index));
         if (stream == NULL) {
-                GvcChannelMap *map;
                 map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
                 stream = gvc_mixer_sink_new (control->priv->pa_context,
                                              info->index,
                                              map);
                 g_object_unref (map);
                 is_new = TRUE;
+        } else if (gvc_mixer_stream_is_running (stream)) {
+                /* Ignore events if volume changes are outstanding */
+                g_debug ("Ignoring event, volume changes are outstanding");
+                return;
         }
 
         max_volume = pa_cvolume_max (&info->volume);
@@ -531,11 +598,6 @@ update_sink (GvcMixerControl    *control,
         gvc_mixer_stream_set_volume (stream, (guint)max_volume);
         gvc_mixer_stream_set_is_muted (stream, info->mute);
         gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SINK_DECIBEL_VOLUME));
-        if (!!(info->flags & PA_SINK_DECIBEL_VOLUME)) {
-                gdouble db;
-                db = pa_sw_volume_to_dB (max_volume);
-                gvc_mixer_stream_set_decibel (stream, db);
-        }
 
         if (is_new) {
                 g_hash_table_insert (control->priv->sinks,
@@ -549,6 +611,10 @@ update_sink (GvcMixerControl    *control,
             && strcmp (control->priv->default_sink_name, info->name) == 0) {
                 _set_default_sink (control, stream);
         }
+
+        if (map == NULL)
+                map = gvc_mixer_stream_get_channel_map (stream);
+        gvc_channel_map_volume_changed (map, &info->volume);
 }
 
 static void
@@ -566,8 +632,8 @@ update_source (GvcMixerControl      *control,
                  info->description);
 #endif
 
-        /* for now completely ignore virtual streams */
-        if (!(info->flags & PA_SOURCE_HARDWARE)) {
+        /* completely ignore monitors, they're not real sources */
+        if (info->monitor_of_sink != PA_INVALID_INDEX) {
                 return;
         }
 
@@ -583,6 +649,10 @@ update_source (GvcMixerControl      *control,
                                                map);
                 g_object_unref (map);
                 is_new = TRUE;
+        } else if (gvc_mixer_stream_is_running (stream)) {
+                /* Ignore events if volume changes are outstanding */
+                g_debug ("Ignoring event, volume changes are outstanding");
+                return;
         }
 
         max_volume = pa_cvolume_max (&info->volume);
@@ -593,11 +663,7 @@ update_source (GvcMixerControl      *control,
         gvc_mixer_stream_set_volume (stream, (guint)max_volume);
         gvc_mixer_stream_set_is_muted (stream, info->mute);
         gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SOURCE_DECIBEL_VOLUME));
-        if (!!(info->flags & PA_SINK_DECIBEL_VOLUME)) {
-                gdouble db;
-                db = pa_sw_volume_to_dB (max_volume);
-                gvc_mixer_stream_set_decibel (stream, db);
-        }
+        gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
 
         if (is_new) {
                 g_hash_table_insert (control->priv->sources,
@@ -662,6 +728,34 @@ set_icon_name_from_proplist (GvcMixerStream *stream,
 }
 
 static void
+set_is_event_stream_from_proplist (GvcMixerStream *stream,
+                                   pa_proplist    *l)
+{
+        const char *t;
+        gboolean is_event_stream;
+
+        is_event_stream = FALSE;
+
+        if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
+                if (g_str_equal (t, "event"))
+                        is_event_stream = TRUE;
+        }
+
+        gvc_mixer_stream_set_is_event_stream (stream, is_event_stream);
+}
+
+static void
+set_application_id_from_proplist (GvcMixerStream *stream,
+                                  pa_proplist    *l)
+{
+        const char *t;
+
+        if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ID))) {
+                gvc_mixer_stream_set_application_id (stream, t);
+        }
+}
+
+static void
 update_sink_input (GvcMixerControl          *control,
                    const pa_sink_input_info *info)
 {
@@ -690,6 +784,10 @@ update_sink_input (GvcMixerControl          *control,
                                                    map);
                 g_object_unref (map);
                 is_new = TRUE;
+        } else if (gvc_mixer_stream_is_running (stream)) {
+                /* Ignore events if volume changes are outstanding */
+                g_debug ("Ignoring event, volume changes are outstanding");
+                return;
         }
 
         max_volume = pa_cvolume_max (&info->volume);
@@ -699,9 +797,12 @@ update_sink_input (GvcMixerControl          *control,
         gvc_mixer_stream_set_name (stream, name);
         gvc_mixer_stream_set_description (stream, info->name);
 
+        set_application_id_from_proplist (stream, info->proplist);
+        set_is_event_stream_from_proplist (stream, info->proplist);
         set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia");
         gvc_mixer_stream_set_volume (stream, (guint)max_volume);
         gvc_mixer_stream_set_is_muted (stream, info->mute);
+        gvc_mixer_stream_set_is_virtual (stream, info->client == PA_INVALID_INDEX);
 
         if (is_new) {
                 g_hash_table_insert (control->priv->sink_inputs,
@@ -745,7 +846,9 @@ update_source_output (GvcMixerControl             *control,
 
         gvc_mixer_stream_set_name (stream, name);
         gvc_mixer_stream_set_description (stream, info->name);
-        set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia");
+        set_application_id_from_proplist (stream, info->proplist);
+        set_is_event_stream_from_proplist (stream, info->proplist);
+        set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
 
         if (is_new) {
                 g_hash_table_insert (control->priv->source_outputs,
@@ -938,8 +1041,16 @@ update_event_role_stream (GvcMixerControl                  *control,
         is_new = FALSE;
 
         if (!control->priv->event_sink_input_is_set) {
+                pa_channel_map pa_map;
+                GvcChannelMap *map;
+
+                pa_map.channels = 1;
+                pa_map.map[0] = PA_CHANNEL_POSITION_MONO;
+                map = gvc_channel_map_new_from_pa_channel_map (&pa_map);
+
                 stream = gvc_mixer_event_role_new (control->priv->pa_context,
-                                                   info->device);
+                                                   info->device,
+                                                   map);
                 control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream);
                 control->priv->event_sink_input_is_set = TRUE;
 
@@ -1346,6 +1457,78 @@ gvc_mixer_control_ready (GvcMixerControl *control)
 }
 
 static void
+gvc_mixer_new_pa_context (GvcMixerControl *self)
+{
+        pa_proplist     *proplist;
+
+        g_return_if_fail (self);
+        g_return_if_fail (!self->priv->pa_context);
+
+        /* FIXME: read these from an object property */
+        proplist = pa_proplist_new ();
+        pa_proplist_sets (proplist,
+                          PA_PROP_APPLICATION_NAME,
+                          _("GNOME Volume Control"));
+        pa_proplist_sets (proplist,
+                          PA_PROP_APPLICATION_ID,
+                          "org.gnome.VolumeControl");
+        pa_proplist_sets (proplist,
+                          PA_PROP_APPLICATION_ICON_NAME,
+                          "multimedia-volume-control");
+        pa_proplist_sets (proplist,
+                          PA_PROP_APPLICATION_VERSION,
+                          PACKAGE_VERSION);
+
+        self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);
+
+        pa_proplist_free (proplist);
+        g_assert (self->priv->pa_context);
+}
+
+static void
+remove_all_streams (GvcMixerControl *control, GHashTable *hash_table)
+{
+        GHashTableIter iter;
+        gpointer key, value;
+
+        g_hash_table_iter_init (&iter, hash_table);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                remove_stream (control, value);
+                g_hash_table_iter_remove (&iter);
+        }
+}
+
+static gboolean
+idle_reconnect (gpointer data)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (data);
+        GHashTableIter iter;
+        gpointer key, value;
+
+        g_return_val_if_fail (control, FALSE);
+
+        if (control->priv->pa_context) {
+                pa_context_unref (control->priv->pa_context);
+                control->priv->pa_context = NULL;
+                gvc_mixer_new_pa_context (control);
+        }
+
+        remove_all_streams (control, control->priv->sinks);
+        remove_all_streams (control, control->priv->sources);
+        remove_all_streams (control, control->priv->sink_inputs);
+        remove_all_streams (control, control->priv->source_outputs);
+
+        g_hash_table_iter_init (&iter, control->priv->clients);
+        while (g_hash_table_iter_next (&iter, &key, &value))
+                g_hash_table_iter_remove (&iter);
+
+        gvc_mixer_control_open (control); /* cannot fail */
+
+        control->priv->reconnect_id = 0;
+        return FALSE;
+}
+
+static void
 _pa_context_state_cb (pa_context *context,
                       void       *userdata)
 {
@@ -1363,7 +1546,9 @@ _pa_context_state_cb (pa_context *context,
                 break;
 
         case PA_CONTEXT_FAILED:
-                g_warning ("Connection failed");
+                g_warning ("Connection failed, reconnecting...");
+                if (control->priv->reconnect_id == 0)
+                        control->priv->reconnect_id = g_idle_add (idle_reconnect, control);
                 break;
 
         case PA_CONTEXT_TERMINATED:
@@ -1386,7 +1571,8 @@ gvc_mixer_control_open (GvcMixerControl *control)
                                        _pa_context_state_cb,
                                        control);
 
-        res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) 0, NULL);
+        g_signal_emit (G_OBJECT (control), signals[CONNECTING], 0);
+        res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) PA_CONTEXT_NOFAIL, NULL);
         if (res < 0) {
                 g_warning ("Failed to connect context: %s",
                            pa_strerror (pa_context_errno (control->priv->pa_context)));
@@ -1465,30 +1651,12 @@ gvc_mixer_control_constructor (GType                  type,
 {
         GObject         *object;
         GvcMixerControl *self;
-        pa_proplist     *proplist;
 
         object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params);
 
         self = GVC_MIXER_CONTROL (object);
 
-        /* FIXME: read these from an object property */
-        proplist = pa_proplist_new ();
-        pa_proplist_sets (proplist,
-                          PA_PROP_APPLICATION_NAME,
-                          _("GNOME Volume Control"));
-        pa_proplist_sets (proplist,
-                          PA_PROP_APPLICATION_ID,
-                          "org.gnome.VolumeControl");
-        pa_proplist_sets (proplist,
-                          PA_PROP_APPLICATION_ICON_NAME,
-                          "multimedia-volume-control");
-        pa_proplist_sets (proplist,
-                          PA_PROP_APPLICATION_VERSION,
-                          PACKAGE_VERSION);
-
-        self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);
-        g_assert (self->priv->pa_context);
-        pa_proplist_free (proplist);
+        gvc_mixer_new_pa_context (self);
 
         return object;
 }
@@ -1502,6 +1670,14 @@ gvc_mixer_control_class_init (GvcMixerControlClass *klass)
         object_class->dispose = gvc_mixer_control_dispose;
         object_class->finalize = gvc_mixer_control_finalize;
 
+        signals [CONNECTING] =
+                g_signal_new ("connecting",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GvcMixerControlClass, connecting),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
         signals [READY] =
                 g_signal_new ("ready",
                               G_TYPE_FROM_CLASS (klass),
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-control.h b/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
index 33e745a..3de9e62 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
@@ -46,6 +46,7 @@ typedef struct
 {
         GObjectClass            parent_class;
 
+        void (*connecting)             (GvcMixerControl *control);
         void (*ready)                  (GvcMixerControl *control);
         void (*stream_added)           (GvcMixerControl *control,
                                         guint            id);
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c
index b5469ca..69e38ce 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c
@@ -53,21 +53,22 @@ G_DEFINE_TYPE (GvcMixerEventRole, gvc_mixer_event_role, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
 update_settings (GvcMixerEventRole *role,
-                 guint              volume,
-                 gboolean           is_muted)
+                 gboolean           is_muted,
+                 gpointer          *op)
 {
         pa_operation              *o;
         guint                      index;
+        GvcChannelMap     *map;
         pa_context                *context;
         pa_ext_stream_restore_info info;
 
         index = gvc_mixer_stream_get_index (GVC_MIXER_STREAM (role));
 
-        pa_cvolume_set (&info.volume, 1, (pa_volume_t)volume);
+        map = gvc_mixer_stream_get_channel_map (GVC_MIXER_STREAM(role));
 
+        info.volume = *gvc_channel_map_get_cvolume(map);
         info.name = "sink-input-by-media-role:event";
-        info.channel_map.channels = 1;
-        info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
+        info.channel_map = *gvc_channel_map_get_pa_channel_map(map);
         info.device = role->priv->device;
         info.mute = is_muted;
 
@@ -86,18 +87,17 @@ update_settings (GvcMixerEventRole *role,
                 return FALSE;
         }
 
-        pa_operation_unref(o);
+	if (op != NULL)
+		*op = o;
 
         return TRUE;
 }
 
 static gboolean
-gvc_mixer_event_role_change_volume (GvcMixerStream *stream,
-                                    guint           volume)
+gvc_mixer_event_role_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         return update_settings (GVC_MIXER_EVENT_ROLE (stream),
-                                volume,
-                                gvc_mixer_stream_get_is_muted (stream));
+                                gvc_mixer_stream_get_is_muted (stream), op);
 }
 
 static gboolean
@@ -105,8 +105,7 @@ gvc_mixer_event_role_change_is_muted (GvcMixerStream *stream,
                                       gboolean        is_muted)
 {
         return update_settings (GVC_MIXER_EVENT_ROLE (stream),
-                                gvc_mixer_stream_get_volume (stream),
-                                is_muted);
+                                is_muted, NULL);
 }
 
 static gboolean
@@ -184,7 +183,7 @@ gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass)
         object_class->set_property = gvc_mixer_event_role_set_property;
         object_class->get_property = gvc_mixer_event_role_get_property;
 
-        stream_class->change_volume = gvc_mixer_event_role_change_volume;
+        stream_class->push_volume = gvc_mixer_event_role_push_volume;
         stream_class->change_is_muted = gvc_mixer_event_role_change_is_muted;
 
         g_object_class_install_property (object_class,
@@ -224,7 +223,8 @@ gvc_mixer_event_role_finalize (GObject *object)
 
 GvcMixerStream *
 gvc_mixer_event_role_new (pa_context *context,
-                          const char *device)
+                          const char *device,
+                          GvcChannelMap *channel_map)
 {
         GObject *object;
 
@@ -232,6 +232,7 @@ gvc_mixer_event_role_new (pa_context *context,
                                "pa-context", context,
                                "index", 0,
                                "device", device,
+                               "channel-map", channel_map,
                                NULL);
 
         return GVC_MIXER_STREAM (object);
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h
index 280c597..ab4c509 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h
@@ -48,8 +48,9 @@ typedef struct
 
 GType               gvc_mixer_event_role_get_type      (void);
 
-GvcMixerStream *    gvc_mixer_event_role_new           (pa_context *context,
-                                                        const char *device);
+GvcMixerStream *    gvc_mixer_event_role_new           (pa_context    *context,
+                                                        const char    *device,
+                                                        GvcChannelMap *channel_map);
 
 G_END_DECLS
 
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c b/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c
index b2c7172..35551bb 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c
@@ -40,44 +40,33 @@ struct GvcMixerSinkInputPrivate
 
 static void     gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass);
 static void     gvc_mixer_sink_input_init       (GvcMixerSinkInput      *mixer_sink_input);
-static void     gvc_mixer_sink_input_finalize   (GObject            *object);
+static void     gvc_mixer_sink_input_finalize   (GObject                *object);
+static void     gvc_mixer_sink_input_dispose    (GObject                *object);
 
 G_DEFINE_TYPE (GvcMixerSinkInput, gvc_mixer_sink_input, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
-gvc_mixer_sink_input_change_volume (GvcMixerStream *stream,
-                                    guint           volume)
+gvc_mixer_sink_input_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         pa_operation      *o;
         guint              index;
         GvcChannelMap     *map;
         pa_context        *context;
-        pa_cvolume         cv;
-        guint              i;
+        const pa_cvolume  *cv;
         guint              num_channels;
-        gdouble           *gains;
 
         index = gvc_mixer_stream_get_index (stream);
 
         map = gvc_mixer_stream_get_channel_map (stream);
         num_channels = gvc_channel_map_get_num_channels (map);
-        gains = gvc_channel_map_get_gains (map);
 
-        /* set all values to nominal level */
-        pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
-
-        /* apply channel gain mapping */
-        for (i = 0; i < num_channels; i++) {
-                pa_volume_t v;
-                v = (double) volume * gains[i];
-                cv.values[i] = v;
-        }
+        cv = gvc_channel_map_get_cvolume(map);
 
         context = gvc_mixer_stream_get_pa_context (stream);
 
         o = pa_context_set_sink_input_volume (context,
                                               index,
-                                              &cv,
+                                              cv,
                                               NULL,
                                               NULL);
 
@@ -86,7 +75,7 @@ gvc_mixer_sink_input_change_volume (GvcMixerStream *stream,
                 return FALSE;
         }
 
-        pa_operation_unref(o);
+        *op = o;
 
         return TRUE;
 }
@@ -140,9 +129,10 @@ gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass)
         GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
 
         object_class->constructor = gvc_mixer_sink_input_constructor;
+        object_class->dispose = gvc_mixer_sink_input_dispose;
         object_class->finalize = gvc_mixer_sink_input_finalize;
 
-        stream_class->change_volume = gvc_mixer_sink_input_change_volume;
+        stream_class->push_volume = gvc_mixer_sink_input_push_volume;
         stream_class->change_is_muted = gvc_mixer_sink_input_change_is_muted;
 
         g_type_class_add_private (klass, sizeof (GvcMixerSinkInputPrivate));
@@ -152,7 +142,19 @@ static void
 gvc_mixer_sink_input_init (GvcMixerSinkInput *sink_input)
 {
         sink_input->priv = GVC_MIXER_SINK_INPUT_GET_PRIVATE (sink_input);
+}
+
+static void
+gvc_mixer_sink_input_dispose (GObject *object)
+{
+        GvcMixerSinkInput *mixer_sink_input;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_MIXER_SINK_INPUT (object));
+
+        mixer_sink_input = GVC_MIXER_SINK_INPUT (object);
 
+        G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->dispose (object);
 }
 
 static void
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c b/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
index 76eb3d7..06e5af6 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
@@ -40,57 +40,41 @@ struct GvcMixerSinkPrivate
 
 static void     gvc_mixer_sink_class_init (GvcMixerSinkClass *klass);
 static void     gvc_mixer_sink_init       (GvcMixerSink      *mixer_sink);
-static void     gvc_mixer_sink_finalize   (GObject            *object);
+static void     gvc_mixer_sink_finalize   (GObject           *object);
+static void     gvc_mixer_sink_dispose    (GObject           *object);
 
 G_DEFINE_TYPE (GvcMixerSink, gvc_mixer_sink, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
-gvc_mixer_sink_change_volume (GvcMixerStream *stream,
-                              guint           volume)
+gvc_mixer_sink_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         pa_operation      *o;
         guint              index;
         GvcChannelMap     *map;
         pa_context        *context;
-        pa_cvolume         cv;
-        guint              i;
-        guint              num_channels;
-        gdouble           *gains;
+        const pa_cvolume  *cv;
 
         index = gvc_mixer_stream_get_index (stream);
 
-
         map = gvc_mixer_stream_get_channel_map (stream);
-        num_channels = gvc_channel_map_get_num_channels (map);
-        gains = gvc_channel_map_get_gains (map);
-
-        g_debug ("Changing volume for sink: n=%d vol=%u", num_channels, (guint)volume);
 
-        /* set all values to nominal level */
-        pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
-
-        /* apply channel gain mapping */
-        for (i = 0; i < num_channels; i++) {
-                pa_volume_t v;
-                v = (double) volume * gains[i];
-                g_debug ("Channel %d v=%u", i, v);
-                cv.values[i] = v;
-        }
+        /* set the volume */
+        cv = gvc_channel_map_get_cvolume(map);
 
         context = gvc_mixer_stream_get_pa_context (stream);
 
         o = pa_context_set_sink_volume_by_index (context,
                                                  index,
-                                                 &cv,
+                                                 cv,
                                                  NULL,
                                                  NULL);
 
         if (o == NULL) {
-                g_warning ("pa_context_set_sink_volume_by_index() failed");
+                g_warning ("pa_context_set_sink_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                 return FALSE;
         }
 
-        pa_operation_unref(o);
+        *op = o;
 
         return TRUE;
 }
@@ -113,7 +97,7 @@ gvc_mixer_sink_change_is_muted (GvcMixerStream *stream,
                                                NULL);
 
         if (o == NULL) {
-                g_warning ("pa_context_set_sink_mute_by_index() failed");
+                g_warning ("pa_context_set_sink_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                 return FALSE;
         }
 
@@ -127,7 +111,7 @@ gvc_mixer_sink_constructor (GType                  type,
                             guint                  n_construct_properties,
                             GObjectConstructParam *construct_params)
 {
-        GObject       *object;
+        GObject      *object;
         GvcMixerSink *self;
 
         object = G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->constructor (type, n_construct_properties, construct_params);
@@ -144,9 +128,10 @@ gvc_mixer_sink_class_init (GvcMixerSinkClass *klass)
         GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
 
         object_class->constructor = gvc_mixer_sink_constructor;
+        object_class->dispose = gvc_mixer_sink_dispose;
         object_class->finalize = gvc_mixer_sink_finalize;
 
-        stream_class->change_volume = gvc_mixer_sink_change_volume;
+        stream_class->push_volume = gvc_mixer_sink_push_volume;
         stream_class->change_is_muted = gvc_mixer_sink_change_is_muted;
 
         g_type_class_add_private (klass, sizeof (GvcMixerSinkPrivate));
@@ -156,7 +141,19 @@ static void
 gvc_mixer_sink_init (GvcMixerSink *sink)
 {
         sink->priv = GVC_MIXER_SINK_GET_PRIVATE (sink);
+}
+
+static void
+gvc_mixer_sink_dispose (GObject *object)
+{
+        GvcMixerSink *mixer_sink;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_MIXER_SINK (object));
+
+        mixer_sink = GVC_MIXER_SINK (object);
 
+        G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->dispose (object);
 }
 
 static void
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c b/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c
index b71ad23..b4cc34d 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c
@@ -45,10 +45,10 @@ static void     gvc_mixer_source_output_finalize   (GObject            *object);
 G_DEFINE_TYPE (GvcMixerSourceOutput, gvc_mixer_source_output, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
-gvc_mixer_source_output_change_volume (GvcMixerStream *stream,
-                                       guint           volume)
+gvc_mixer_source_output_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         /* FIXME: */
+        *op = NULL;
         return TRUE;
 }
 
@@ -84,7 +84,7 @@ gvc_mixer_source_output_class_init (GvcMixerSourceOutputClass *klass)
         object_class->constructor = gvc_mixer_source_output_constructor;
         object_class->finalize = gvc_mixer_source_output_finalize;
 
-        stream_class->change_volume = gvc_mixer_source_output_change_volume;
+        stream_class->push_volume = gvc_mixer_source_output_push_volume;
         stream_class->change_is_muted = gvc_mixer_source_output_change_is_muted;
 
         g_type_class_add_private (klass, sizeof (GvcMixerSourceOutputPrivate));
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-source.c b/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
index de1b09e..ae02d85 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
@@ -41,53 +41,40 @@ struct GvcMixerSourcePrivate
 static void     gvc_mixer_source_class_init (GvcMixerSourceClass *klass);
 static void     gvc_mixer_source_init       (GvcMixerSource      *mixer_source);
 static void     gvc_mixer_source_finalize   (GObject            *object);
+static void     gvc_mixer_source_dispose    (GObject           *object);
 
 G_DEFINE_TYPE (GvcMixerSource, gvc_mixer_source, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
-gvc_mixer_source_change_volume (GvcMixerStream *stream,
-                              guint           volume)
+gvc_mixer_source_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         pa_operation      *o;
         guint              index;
         GvcChannelMap     *map;
         pa_context        *context;
-        pa_cvolume         cv;
-        guint              num_channels;
-        guint              i;
-        gdouble           *gains;
+        const pa_cvolume  *cv;
 
         index = gvc_mixer_stream_get_index (stream);
 
         map = gvc_mixer_stream_get_channel_map (stream);
-        num_channels = gvc_channel_map_get_num_channels (map);
-        gains = gvc_channel_map_get_gains (map);
 
-        /* set all values to nominal level */
-        pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
-
-
-        /* apply channel gain mapping */
-        for (i = 0; i < num_channels; i++) {
-                pa_volume_t v;
-                v = (double) volume * gains[i];
-                cv.values[i] = v;
-        }
+        /* set the volume */
+        cv = gvc_channel_map_get_cvolume (map);
 
         context = gvc_mixer_stream_get_pa_context (stream);
 
         o = pa_context_set_source_volume_by_index (context,
                                                    index,
-                                                   &cv,
+                                                   cv,
                                                    NULL,
                                                    NULL);
 
         if (o == NULL) {
-                g_warning ("pa_context_set_source_volume_by_index() failed");
+                g_warning ("pa_context_set_source_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                 return FALSE;
         }
 
-        pa_operation_unref(o);
+        *op = o;
 
         return TRUE;
 }
@@ -110,7 +97,7 @@ gvc_mixer_source_change_is_muted (GvcMixerStream *stream,
                                                  NULL);
 
         if (o == NULL) {
-                g_warning ("pa_context_set_source_mute_by_index() failed");
+                g_warning ("pa_context_set_source_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                 return FALSE;
         }
 
@@ -124,7 +111,7 @@ gvc_mixer_source_constructor (GType                  type,
                             guint                  n_construct_properties,
                             GObjectConstructParam *construct_params)
 {
-        GObject       *object;
+        GObject        *object;
         GvcMixerSource *self;
 
         object = G_OBJECT_CLASS (gvc_mixer_source_parent_class)->constructor (type, n_construct_properties, construct_params);
@@ -141,9 +128,10 @@ gvc_mixer_source_class_init (GvcMixerSourceClass *klass)
         GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
 
         object_class->constructor = gvc_mixer_source_constructor;
+        object_class->dispose = gvc_mixer_source_dispose;
         object_class->finalize = gvc_mixer_source_finalize;
 
-        stream_class->change_volume = gvc_mixer_source_change_volume;
+        stream_class->push_volume = gvc_mixer_source_push_volume;
         stream_class->change_is_muted = gvc_mixer_source_change_is_muted;
 
         g_type_class_add_private (klass, sizeof (GvcMixerSourcePrivate));
@@ -153,7 +141,19 @@ static void
 gvc_mixer_source_init (GvcMixerSource *source)
 {
         source->priv = GVC_MIXER_SOURCE_GET_PRIVATE (source);
+}
+
+static void
+gvc_mixer_source_dispose (GObject *object)
+{
+        GvcMixerSource *mixer_source;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_MIXER_SOURCE (object));
+
+        mixer_source = GVC_MIXER_SOURCE (object);
 
+        G_OBJECT_CLASS (gvc_mixer_source_parent_class)->dispose (object);
 }
 
 static void
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
index 69d8598..e5cfb19 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
@@ -41,13 +41,16 @@ struct GvcMixerStreamPrivate
         guint          id;
         guint          index;
         GvcChannelMap *channel_map;
-        guint          volume;
-        gdouble        decibel;
         char          *name;
         char          *description;
+        char          *application_id;
         char          *icon_name;
         gboolean       is_muted;
         gboolean       can_decibel;
+        gboolean       is_event_stream;
+        gboolean       is_virtual;
+        pa_volume_t    base_volume;
+        pa_operation  *change_volume_op;
 };
 
 enum
@@ -59,11 +62,14 @@ enum
         PROP_INDEX,
         PROP_NAME,
         PROP_DESCRIPTION,
+        PROP_APPLICATION_ID,
         PROP_ICON_NAME,
         PROP_VOLUME,
         PROP_DECIBEL,
         PROP_IS_MUTED,
-        PROP_CAN_DECIBEL
+        PROP_CAN_DECIBEL,
+        PROP_IS_EVENT_STREAM,
+        PROP_IS_VIRTUAL,
 };
 
 static void     gvc_mixer_stream_class_init (GvcMixerStreamClass *klass);
@@ -114,28 +120,36 @@ gvc_mixer_stream_get_channel_map (GvcMixerStream *stream)
         return stream->priv->channel_map;
 }
 
-guint
+pa_volume_t
 gvc_mixer_stream_get_volume (GvcMixerStream *stream)
 {
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
-        return stream->priv->volume;
+
+        return (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME];
 }
 
 gdouble
 gvc_mixer_stream_get_decibel (GvcMixerStream *stream)
 {
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
-        return stream->priv->decibel;
+
+        return pa_sw_volume_to_dB(
+                        (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME]);
 }
 
 gboolean
 gvc_mixer_stream_set_volume (GvcMixerStream *stream,
-                             pa_volume_t     volume)
+                              pa_volume_t     volume)
 {
+        pa_cvolume cv;
+
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
 
-        if (volume != stream->priv->volume) {
-                stream->priv->volume = volume;
+        cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map);
+        pa_cvolume_scale(&cv, volume);
+
+        if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
+                gvc_channel_map_volume_changed(stream->priv->channel_map, &cv);
                 g_object_notify (G_OBJECT (stream), "volume");
         }
 
@@ -146,11 +160,16 @@ gboolean
 gvc_mixer_stream_set_decibel (GvcMixerStream *stream,
                               gdouble         db)
 {
+        pa_cvolume cv;
+
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
 
-        if (db != stream->priv->decibel) {
-                stream->priv->decibel = db;
-                g_object_notify (G_OBJECT (stream), "decibel");
+        cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map);
+        pa_cvolume_scale(&cv, pa_sw_volume_from_dB(db));
+
+        if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
+                gvc_channel_map_volume_changed(stream->priv->channel_map, &cv);
+                g_object_notify (G_OBJECT (stream), "volume");
         }
 
         return TRUE;
@@ -238,12 +257,73 @@ gvc_mixer_stream_set_description (GvcMixerStream *stream,
         return TRUE;
 }
 
+gboolean
+gvc_mixer_stream_is_event_stream (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        return stream->priv->is_event_stream;
+}
+
+gboolean
+gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream,
+                                      gboolean is_event_stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        stream->priv->is_event_stream = is_event_stream;
+        g_object_notify (G_OBJECT (stream), "is-event-stream");
+
+        return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_is_virtual (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        return stream->priv->is_virtual;
+}
+
+gboolean
+gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream,
+                                 gboolean is_virtual)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        stream->priv->is_virtual = is_virtual;
+        g_object_notify (G_OBJECT (stream), "is-virtual");
+
+        return TRUE;
+}
+
+const char *
+gvc_mixer_stream_get_application_id (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+        return stream->priv->application_id;
+}
+
+gboolean
+gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
+                                     const char *application_id)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        g_free (stream->priv->application_id);
+        stream->priv->application_id = g_strdup (application_id);
+        g_object_notify (G_OBJECT (stream), "application-id");
+
+        return TRUE;
+}
+
 static void
-on_channel_map_gains_changed (GvcChannelMap  *channel_map,
-                              GvcMixerStream *stream)
+on_channel_map_volume_changed (GvcChannelMap  *channel_map,
+                               GvcMixerStream *stream)
 {
-        g_debug ("Gains changed");
-        gvc_mixer_stream_change_volume (stream, stream->priv->volume);
+        gvc_mixer_stream_push_volume (stream);
+
+        g_object_notify (G_OBJECT (stream), "volume");
 }
 
 static gboolean
@@ -258,7 +338,7 @@ gvc_mixer_stream_set_channel_map (GvcMixerStream *stream,
 
         if (stream->priv->channel_map != NULL) {
                 g_signal_handlers_disconnect_by_func (stream->priv->channel_map,
-                                                      on_channel_map_gains_changed,
+                                                      on_channel_map_volume_changed,
                                                       stream);
                 g_object_unref (stream->priv->channel_map);
         }
@@ -267,8 +347,8 @@ gvc_mixer_stream_set_channel_map (GvcMixerStream *stream,
 
         if (stream->priv->channel_map != NULL) {
                 g_signal_connect (stream->priv->channel_map,
-                                  "gains-changed",
-                                  G_CALLBACK (on_channel_map_gains_changed),
+                                  "volume-changed",
+                                  G_CALLBACK (on_channel_map_volume_changed),
                                   stream);
 
                 g_object_notify (G_OBJECT (stream), "channel-map");
@@ -297,6 +377,25 @@ gvc_mixer_stream_set_icon_name (GvcMixerStream *stream,
         return TRUE;
 }
 
+pa_volume_t
+gvc_mixer_stream_get_base_volume (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+
+        return stream->priv->base_volume;
+}
+
+gboolean
+gvc_mixer_stream_set_base_volume (GvcMixerStream *stream,
+                                  pa_volume_t base_volume)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        stream->priv->base_volume = base_volume;
+
+        return TRUE;
+}
+
 static void
 gvc_mixer_stream_set_property (GObject       *object,
                                guint          prop_id,
@@ -324,6 +423,9 @@ gvc_mixer_stream_set_property (GObject       *object,
         case PROP_DESCRIPTION:
                 gvc_mixer_stream_set_description (self, g_value_get_string (value));
                 break;
+        case PROP_APPLICATION_ID:
+                gvc_mixer_stream_set_application_id (self, g_value_get_string (value));
+                break;
         case PROP_ICON_NAME:
                 gvc_mixer_stream_set_icon_name (self, g_value_get_string (value));
                 break;
@@ -336,6 +438,12 @@ gvc_mixer_stream_set_property (GObject       *object,
         case PROP_IS_MUTED:
                 gvc_mixer_stream_set_is_muted (self, g_value_get_boolean (value));
                 break;
+        case PROP_IS_EVENT_STREAM:
+                gvc_mixer_stream_set_is_event_stream (self, g_value_get_boolean (value));
+                break;
+        case PROP_IS_VIRTUAL:
+                gvc_mixer_stream_set_is_virtual (self, g_value_get_boolean (value));
+                break;
         case PROP_CAN_DECIBEL:
                 gvc_mixer_stream_set_can_decibel (self, g_value_get_boolean (value));
                 break;
@@ -372,18 +480,29 @@ gvc_mixer_stream_get_property (GObject     *object,
         case PROP_DESCRIPTION:
                 g_value_set_string (value, self->priv->description);
                 break;
+        case PROP_APPLICATION_ID:
+                g_value_set_string (value, self->priv->application_id);
+                break;
         case PROP_ICON_NAME:
                 g_value_set_string (value, self->priv->icon_name);
                 break;
         case PROP_VOLUME:
-                g_value_set_ulong (value, self->priv->volume);
+                g_value_set_ulong (value,
+                                   pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map)));
                 break;
         case PROP_DECIBEL:
-                g_value_set_double (value, self->priv->decibel);
+                g_value_set_double (value,
+                                    pa_sw_volume_to_dB(pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map))));
                 break;
         case PROP_IS_MUTED:
                 g_value_set_boolean (value, self->priv->is_muted);
                 break;
+        case PROP_IS_EVENT_STREAM:
+                g_value_set_boolean (value, self->priv->is_event_stream);
+                break;
+        case PROP_IS_VIRTUAL:
+                g_value_set_boolean (value, self->priv->is_virtual);
+                break;
         case PROP_CAN_DECIBEL:
                 g_value_set_boolean (value, self->priv->can_decibel);
                 break;
@@ -411,8 +530,7 @@ gvc_mixer_stream_constructor (GType                  type,
 }
 
 static gboolean
-gvc_mixer_stream_real_change_volume (GvcMixerStream *stream,
-                                     guint           volume)
+gvc_mixer_stream_real_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         return FALSE;
 }
@@ -425,12 +543,17 @@ gvc_mixer_stream_real_change_is_muted (GvcMixerStream *stream,
 }
 
 gboolean
-gvc_mixer_stream_change_volume (GvcMixerStream *stream,
-                                guint           volume)
+gvc_mixer_stream_push_volume (GvcMixerStream *stream)
 {
+        pa_operation *op;
         gboolean ret;
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
-        ret = GVC_MIXER_STREAM_GET_CLASS (stream)->change_volume (stream, volume);
+        ret = GVC_MIXER_STREAM_GET_CLASS (stream)->push_volume (stream, (gpointer *) &op);
+        if (ret) {
+                if (stream->priv->change_volume_op != NULL)
+                        pa_operation_unref (stream->priv->change_volume_op);
+                stream->priv->change_volume_op = op;
+        }
         return ret;
 }
 
@@ -444,6 +567,21 @@ gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
         return ret;
 }
 
+gboolean
+gvc_mixer_stream_is_running (GvcMixerStream *stream)
+{
+        if (stream->priv->change_volume_op == NULL)
+                return FALSE;
+
+        if ((pa_operation_get_state(stream->priv->change_volume_op) == PA_OPERATION_RUNNING))
+                return TRUE;
+
+        pa_operation_unref(stream->priv->change_volume_op);
+        stream->priv->change_volume_op = NULL;
+
+        return FALSE;
+}
+
 static void
 gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
 {
@@ -454,7 +592,7 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
         gobject_class->set_property = gvc_mixer_stream_set_property;
         gobject_class->get_property = gvc_mixer_stream_get_property;
 
-        klass->change_volume = gvc_mixer_stream_real_change_volume;
+        klass->push_volume = gvc_mixer_stream_real_push_volume;
         klass->change_is_muted = gvc_mixer_stream_real_change_is_muted;
 
         g_object_class_install_property (gobject_class,
@@ -490,7 +628,7 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
                                                              "Volume",
                                                              "The volume for this stream",
                                                              0, G_MAXULONG, 0,
-                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+                                                             G_PARAM_READWRITE));
         g_object_class_install_property (gobject_class,
                                          PROP_DECIBEL,
                                          g_param_spec_double ("decibel",
@@ -514,6 +652,13 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
                                                               NULL,
                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
         g_object_class_install_property (gobject_class,
+                                         PROP_APPLICATION_ID,
+                                         g_param_spec_string ("application-id",
+                                                              "Application identifier",
+                                                              "Application identifier for this stream",
+                                                              NULL,
+                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+        g_object_class_install_property (gobject_class,
                                          PROP_ICON_NAME,
                                          g_param_spec_string ("icon-name",
                                                               "Icon Name",
@@ -534,7 +679,20 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
                                                                "Whether stream volume can be converted to decibel units",
                                                                FALSE,
                                                                G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-
+        g_object_class_install_property (gobject_class,
+                                         PROP_IS_EVENT_STREAM,
+                                         g_param_spec_boolean ("is-event-stream",
+                                                               "is event stream",
+                                                               "Whether stream's role is to play an event",
+                                                               FALSE,
+                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+        g_object_class_install_property (gobject_class,
+                                         PROP_IS_VIRTUAL,
+                                         g_param_spec_boolean ("is-virtual",
+                                                               "is virtual stream",
+                                                               "Whether the stream is virtual",
+                                                               FALSE,
+                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
         g_type_class_add_private (klass, sizeof (GvcMixerStreamPrivate));
 }
 
@@ -542,7 +700,6 @@ static void
 gvc_mixer_stream_init (GvcMixerStream *stream)
 {
         stream->priv = GVC_MIXER_STREAM_GET_PRIVATE (stream);
-
 }
 
 static void
@@ -563,8 +720,16 @@ gvc_mixer_stream_finalize (GObject *object)
         g_free (mixer_stream->priv->description);
         mixer_stream->priv->description = NULL;
 
+        g_free (mixer_stream->priv->application_id);
+        mixer_stream->priv->application_id = NULL;
+
         g_free (mixer_stream->priv->icon_name);
         mixer_stream->priv->icon_name = NULL;
 
+       if (mixer_stream->priv->change_volume_op) {
+               pa_operation_unref(mixer_stream->priv->change_volume_op);
+               mixer_stream->priv->change_volume_op = NULL;
+       }
+
         G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->finalize (object);
 }
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
index 3dee03b..4171ca3 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
@@ -48,8 +48,7 @@ typedef struct
         GObjectClass           parent_class;
 
         /* vtable */
-        gboolean (*change_volume)   (GvcMixerStream *stream,
-                                     guint           volume);
+        gboolean (*push_volume)   (GvcMixerStream *stream, gpointer *operation);
         gboolean (*change_is_muted) (GvcMixerStream *stream,
                                      gboolean        is_muted);
 } GvcMixerStreamClass;
@@ -61,22 +60,26 @@ guint               gvc_mixer_stream_get_index       (GvcMixerStream *stream);
 guint               gvc_mixer_stream_get_id          (GvcMixerStream *stream);
 GvcChannelMap *     gvc_mixer_stream_get_channel_map (GvcMixerStream *stream);
 
-guint               gvc_mixer_stream_get_volume      (GvcMixerStream *stream);
+pa_volume_t         gvc_mixer_stream_get_volume      (GvcMixerStream *stream);
 gdouble             gvc_mixer_stream_get_decibel     (GvcMixerStream *stream);
-gboolean            gvc_mixer_stream_change_volume   (GvcMixerStream *stream,
-                                                      guint           volume);
+gboolean            gvc_mixer_stream_push_volume     (GvcMixerStream *stream);
+pa_volume_t         gvc_mixer_stream_get_base_volume (GvcMixerStream *stream);
 
 gboolean            gvc_mixer_stream_get_is_muted    (GvcMixerStream *stream);
 gboolean            gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream);
 gboolean            gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
                                                       gboolean        is_muted);
+gboolean            gvc_mixer_stream_is_running      (GvcMixerStream *stream);
 const char *        gvc_mixer_stream_get_name        (GvcMixerStream *stream);
 const char *        gvc_mixer_stream_get_icon_name   (GvcMixerStream *stream);
 const char *        gvc_mixer_stream_get_description (GvcMixerStream *stream);
+const char *        gvc_mixer_stream_get_application_id (GvcMixerStream *stream);
+gboolean            gvc_mixer_stream_is_event_stream (GvcMixerStream *stream);
+gboolean            gvc_mixer_stream_is_virtual      (GvcMixerStream *stream);
 
 /* private */
 gboolean            gvc_mixer_stream_set_volume      (GvcMixerStream *stream,
-                                                      guint           volume);
+                                                      pa_volume_t     volume);
 gboolean            gvc_mixer_stream_set_decibel     (GvcMixerStream *stream,
                                                       gdouble         db);
 gboolean            gvc_mixer_stream_set_is_muted    (GvcMixerStream *stream,
@@ -89,6 +92,14 @@ gboolean            gvc_mixer_stream_set_description (GvcMixerStream *stream,
                                                       const char     *description);
 gboolean            gvc_mixer_stream_set_icon_name   (GvcMixerStream *stream,
                                                       const char     *name);
+gboolean            gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream,
+                                                          gboolean is_event_stream);
+gboolean            gvc_mixer_stream_set_is_virtual  (GvcMixerStream *stream,
+                                                      gboolean is_event_stream);
+gboolean            gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
+                                                         const char *application_id);
+gboolean            gvc_mixer_stream_set_base_volume (GvcMixerStream *stream,
+                                                      pa_volume_t     base_volume);
 
 G_END_DECLS
 
diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index d35048c..3eeea71 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -687,17 +687,17 @@ do_sound_action (GsdMediaKeysManager *manager,
                 if (!muted && (vol <= norm_vol_step)) {
                         manager->priv->num_expected_update_signals = 2;
                         gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
-                        gvc_mixer_stream_change_volume (manager->priv->stream, 0);
+                        gvc_mixer_stream_set_volume (manager->priv->stream, 0);
                 } else if (!muted) {
                         manager->priv->num_expected_update_signals = 1;
-                        gvc_mixer_stream_change_volume (manager->priv->stream, vol - norm_vol_step);
+                        gvc_mixer_stream_set_volume (manager->priv->stream, vol - norm_vol_step);
                 }
                 break;
         case VOLUME_UP_KEY:
                 if (muted) {
                         if (vol == 0) {
                                 manager->priv->num_expected_update_signals = 2;
-                                gvc_mixer_stream_change_volume (manager->priv->stream, vol + norm_vol_step);
+                                gvc_mixer_stream_set_volume (manager->priv->stream, vol + norm_vol_step);
                                 gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
                         } else {
                                 manager->priv->num_expected_update_signals = 1;
@@ -707,9 +707,9 @@ do_sound_action (GsdMediaKeysManager *manager,
                         if (vol < MAX_VOLUME) {
                                 manager->priv->num_expected_update_signals = 1;
                                 if (vol + norm_vol_step >= MAX_VOLUME) {
-                                        gvc_mixer_stream_change_volume (manager->priv->stream, MAX_VOLUME);
+                                        gvc_mixer_stream_set_volume (manager->priv->stream, MAX_VOLUME);
                                 } else {
-                                        gvc_mixer_stream_change_volume (manager->priv->stream, vol + norm_vol_step);
+                                        gvc_mixer_stream_set_volume (manager->priv->stream, vol + norm_vol_step);
                                 }
                         }
                 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]