[gnome-media] Bug 568936 – Add fade slider



commit e4c8d688709e54a8fabfb818664874d9d77ca42d
Author: Bastien Nocera <hadess hadess net>
Date:   Sun May 10 01:46:33 2009 +0100

    Bug 568936 â?? Add fade slider
    
    With help from Lennart Poettering and Matthias Clasen.
    
    - Add a new type of balance bar, the fade balance bar, to balance
      volume between front and rear
    - Don't update the UI when volume change operations are still pending
      on the sink we're modifying
    - Rework the internals to store pa_cvolume, like PA does internally,
      instead of balance/fade/volume separately, as the value isn't
      reversible to what we'd pass PA
---
 gnome-volume-control/src/gvc-balance-bar.c      |  228 +++++++++++++----------
 gnome-volume-control/src/gvc-balance-bar.h      |    8 +-
 gnome-volume-control/src/gvc-channel-map.c      |  102 ++++++++---
 gnome-volume-control/src/gvc-channel-map.h      |   19 ++-
 gnome-volume-control/src/gvc-mixer-control.c    |   17 ++-
 gnome-volume-control/src/gvc-mixer-dialog.c     |   21 ++-
 gnome-volume-control/src/gvc-mixer-sink-input.c |   18 +--
 gnome-volume-control/src/gvc-mixer-sink.c       |   76 +++++---
 gnome-volume-control/src/gvc-mixer-source.c     |   25 +--
 gnome-volume-control/src/gvc-mixer-stream.c     |   20 ++-
 gnome-volume-control/src/gvc-mixer-stream.h     |    2 +
 11 files changed, 336 insertions(+), 200 deletions(-)

diff --git a/gnome-volume-control/src/gvc-balance-bar.c b/gnome-volume-control/src/gvc-balance-bar.c
index 74d3b2a..c033902 100644
--- a/gnome-volume-control/src/gvc-balance-bar.c
+++ b/gnome-volume-control/src/gvc-balance-bar.c
@@ -38,6 +38,7 @@
 struct GvcBalanceBarPrivate
 {
         GvcChannelMap *channel_map;
+        GvcBalanceType btype;
         GtkWidget     *scale_box;
         GtkWidget     *start_box;
         GtkWidget     *end_box;
@@ -52,7 +53,8 @@ struct GvcBalanceBarPrivate
 enum
 {
         PROP_0,
-        PROP_CHANNEL_MAP
+        PROP_CHANNEL_MAP,
+        PROP_BALANCE_TYPE,
 };
 
 static void     gvc_balance_bar_class_init (GvcBalanceBarClass *klass);
@@ -68,6 +70,8 @@ static gboolean on_scale_button_release_event (GtkWidget      *widget,
 static gboolean on_scale_scroll_event         (GtkWidget      *widget,
                                                GdkEventScroll *event,
                                                GvcBalanceBar  *bar);
+static void on_adjustment_value_changed       (GtkAdjustment *adjustment,
+                                               GvcBalanceBar *bar);
 
 G_DEFINE_TYPE (GvcBalanceBar, gvc_balance_bar, GTK_TYPE_HBOX)
 
@@ -98,15 +102,27 @@ _scale_box_new (GvcBalanceBar *bar)
 
         gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, TRUE, 0);
 
-        str = g_strdup_printf ("<small>%s</small>", C_("balance", "Left"));
-        gtk_scale_add_mark (GTK_SCALE (priv->scale), adjustment->lower , 
-                            GTK_POS_BOTTOM, str);
-        g_free (str);
-
-        str = g_strdup_printf ("<small>%s</small>", C_("balance", "Right"));
-        gtk_scale_add_mark (GTK_SCALE (priv->scale),  adjustment->upper, 
-                            GTK_POS_BOTTOM, str);
-        g_free (str);
+        if (bar->priv->btype == BALANCE_TYPE_RL) {
+                str = g_strdup_printf ("<small>%s</small>", C_("balance", "Left"));
+                gtk_scale_add_mark (GTK_SCALE (priv->scale), adjustment->lower,
+                                    GTK_POS_BOTTOM, str);
+                g_free (str);
+
+                str = g_strdup_printf ("<small>%s</small>", C_("balance", "Right"));
+                gtk_scale_add_mark (GTK_SCALE (priv->scale),  adjustment->upper,
+                                    GTK_POS_BOTTOM, str);
+                g_free (str);
+        } else {
+                str = g_strdup_printf ("<small>%s</small>", C_("balance", "Front"));
+                gtk_scale_add_mark (GTK_SCALE (priv->scale), adjustment->lower,
+                                    GTK_POS_BOTTOM, str);
+                g_free (str);
+
+                str = g_strdup_printf ("<small>%s</small>", C_("balance", "Rear"));
+                gtk_scale_add_mark (GTK_SCALE (priv->scale),  adjustment->upper,
+                                    GTK_POS_BOTTOM, str);
+                g_free (str);
+        }
 
         gtk_scale_add_mark (GTK_SCALE (priv->scale), (adjustment->upper - adjustment->lower)/2 + adjustment->lower, 
                             GTK_POS_BOTTOM, NULL);
@@ -161,6 +177,32 @@ gvc_balance_bar_set_size_group (GvcBalanceBar *bar,
 }
 
 static void
+update_level_from_map (GvcBalanceBar *bar,
+                       GvcChannelMap *map)
+{
+        const gdouble *volumes;
+        gdouble val;
+
+        g_debug ("Volume changed (for %s bar)",
+                 bar->priv->btype == BALANCE_TYPE_RL ? "Balance" : "Fade");
+
+        volumes = gvc_channel_map_get_volume (map);
+        if (bar->priv->btype == BALANCE_TYPE_RL)
+                val = volumes[BALANCE];
+        else
+                val = volumes[FADE];
+
+        gtk_adjustment_set_value (bar->priv->adjustment, val);
+}
+
+static void
+on_channel_map_volume_changed (GvcChannelMap  *map,
+                               GvcBalanceBar  *bar)
+{
+        update_level_from_map (bar, map);
+}
+
+static void
 gvc_balance_bar_set_channel_map (GvcBalanceBar *bar,
                                  GvcChannelMap *map)
 {
@@ -171,10 +213,61 @@ gvc_balance_bar_set_channel_map (GvcBalanceBar *bar,
         }
         bar->priv->channel_map = g_object_ref (map);
 
+        update_level_from_map (bar, map);
+
+        g_signal_connect (G_OBJECT (map), "volume-changed",
+                          G_CALLBACK (on_channel_map_volume_changed), bar);
+
         g_object_notify (G_OBJECT (bar), "channel-map");
 }
 
 static void
+gvc_balance_bar_set_balance_type (GvcBalanceBar *bar,
+                                  GvcBalanceType btype)
+{
+        GtkWidget *frame;
+
+        g_return_if_fail (GVC_BALANCE_BAR (bar));
+
+        bar->priv->btype = btype;
+        bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0,
+                                                                    -1.0,
+                                                                    1.0,
+                                                                    0.5,
+                                                                    0.5,
+                                                                    0.5));
+        g_object_ref_sink (bar->priv->adjustment);
+        g_signal_connect (bar->priv->adjustment,
+                          "value-changed",
+                          G_CALLBACK (on_adjustment_value_changed),
+                          bar);
+
+        if (bar->priv->btype == BALANCE_TYPE_RL) {
+                bar->priv->label = gtk_label_new_with_mnemonic (_("_Balance:"));
+        } else {
+                bar->priv->label = gtk_label_new_with_mnemonic (_("_Fade:"));
+        }
+        gtk_misc_set_alignment (GTK_MISC (bar->priv->label),
+                                0.0,
+                                0.0);
+        /* frame */
+        frame = gtk_frame_new (NULL);
+        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+        gtk_container_add (GTK_CONTAINER (bar), frame);
+
+        /* box with scale */
+        bar->priv->scale_box = _scale_box_new (bar);
+        gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box);
+        gtk_widget_show_all (frame);
+
+        gtk_widget_set_direction (bar->priv->scale, GTK_TEXT_DIR_LTR);
+        gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label),
+                                       bar->priv->scale);
+
+        g_object_notify (G_OBJECT (bar), "balance-type");
+}
+
+static void
 gvc_balance_bar_set_property (GObject       *object,
                               guint          prop_id,
                               const GValue  *value,
@@ -186,6 +279,9 @@ gvc_balance_bar_set_property (GObject       *object,
         case PROP_CHANNEL_MAP:
                 gvc_balance_bar_set_channel_map (self, g_value_get_object (value));
                 break;
+        case PROP_BALANCE_TYPE:
+                gvc_balance_bar_set_balance_type (self, g_value_get_int (value));
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -242,36 +338,19 @@ gvc_balance_bar_class_init (GvcBalanceBarClass *klass)
                                                               "The channel map",
                                                               GVC_TYPE_CHANNEL_MAP,
                                                               G_PARAM_READWRITE));
+        g_object_class_install_property (object_class,
+                                         PROP_BALANCE_TYPE,
+                                         g_param_spec_int ("balance-type",
+                                                           "balance type",
+                                                           "Whether the balance is right-left or front-rear",
+                                                           BALANCE_TYPE_RL, BALANCE_TYPE_FR, BALANCE_TYPE_RL,
+                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
 
         g_type_class_add_private (klass, sizeof (GvcBalanceBarPrivate));
 }
 
 
 static gboolean
-on_left (pa_channel_position_t p)
-{
-    return
-        p == PA_CHANNEL_POSITION_FRONT_LEFT ||
-        p == PA_CHANNEL_POSITION_REAR_LEFT ||
-        p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
-        p == PA_CHANNEL_POSITION_SIDE_LEFT ||
-        p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
-        p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
-}
-
-static gboolean
-on_right (pa_channel_position_t p)
-{
-    return
-        p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
-        p == PA_CHANNEL_POSITION_REAR_RIGHT ||
-        p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
-        p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
-        p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
-        p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
-}
-
-static gboolean
 on_scale_button_press_event (GtkWidget      *widget,
                              GdkEventButton *event,
                              GvcBalanceBar  *bar)
@@ -322,83 +401,29 @@ on_adjustment_value_changed (GtkAdjustment *adjustment,
                              GvcBalanceBar *bar)
 {
         gdouble                val;
-        gdouble               *gains;
-        pa_channel_position_t *positions;
-        guint                  num_channels;
-        guint                  i;
-        gdouble                left_v;
-        gdouble                center_v;
-        gdouble                right_v;
+        pa_cvolume             cv;
+        const pa_channel_map  *pa_map;
 
-        val = gtk_adjustment_get_value (adjustment);
-
-        if (bar->priv->channel_map == NULL) {
+        if (bar->priv->channel_map == NULL)
                 return;
-        }
 
-        left_v = 1.0;
-        right_v = 1.0;
+        cv = *gvc_channel_map_get_cvolume_for_volumes (bar->priv->channel_map, -1);
+        val = gtk_adjustment_get_value (adjustment);
 
-        if (val > 0) {
-                left_v = 1.0 - val;
-        } else if (val < 0) {
-                right_v = 1.0 - ABS(val);
-        }
-        center_v = (left_v + right_v) / 2.0;
-
-        num_channels = gvc_channel_map_get_num_channels (bar->priv->channel_map);
-        positions = gvc_channel_map_get_positions (bar->priv->channel_map);
-        gains = gvc_channel_map_get_gains (bar->priv->channel_map);
-
-        for (i = 0; i < num_channels; i++) {
-                if (on_left (positions[i])) {
-                        gains[i] = left_v;
-                } else if (on_right (positions[i])) {
-                        gains[i] = right_v;
-                } else {
-                        gains[i] = center_v;
-                }
-        }
+        pa_map = gvc_channel_map_get_pa_channel_map (bar->priv->channel_map);
+
+        if (bar->priv->btype == BALANCE_TYPE_RL)
+                pa_cvolume_set_balance (&cv, pa_map, val);
+        else
+                pa_cvolume_set_fade (&cv, pa_map, val);
 
-        gvc_channel_map_gains_changed (bar->priv->channel_map);
+        gvc_channel_map_volume_changed (bar->priv->channel_map, &cv);
 }
 
 static void
 gvc_balance_bar_init (GvcBalanceBar *bar)
 {
-        GtkWidget *frame;
-
         bar->priv = GVC_BALANCE_BAR_GET_PRIVATE (bar);
-
-        bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0,
-                                                                    -1.0,
-                                                                    1.0,
-                                                                    0.05,
-                                                                    0.1,
-                                                                    0.1));
-        g_object_ref_sink (bar->priv->adjustment);
-        g_signal_connect (bar->priv->adjustment,
-                          "value-changed",
-                          G_CALLBACK (on_adjustment_value_changed),
-                          bar);
-
-        bar->priv->label = gtk_label_new_with_mnemonic (_("_Balance:"));
-        gtk_misc_set_alignment (GTK_MISC (bar->priv->label),
-                                0.0,
-                                0.0);
-        /* frame */
-        frame = gtk_frame_new (NULL);
-        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
-        gtk_container_add (GTK_CONTAINER (bar), frame);
-
-        /* box with scale */
-        bar->priv->scale_box = _scale_box_new (bar);
-        gtk_container_add (GTK_CONTAINER (frame), bar->priv->scale_box);
-        gtk_widget_show_all (frame);
-
-        gtk_widget_set_direction (bar->priv->scale, GTK_TEXT_DIR_LTR);
-        gtk_label_set_mnemonic_widget (GTK_LABEL (bar->priv->label),
-                                       bar->priv->scale);
 }
 
 static void
@@ -421,11 +446,12 @@ gvc_balance_bar_finalize (GObject *object)
 }
 
 GtkWidget *
-gvc_balance_bar_new (GvcChannelMap *channel_map)
+gvc_balance_bar_new (GvcChannelMap *channel_map, GvcBalanceType btype)
 {
         GObject *bar;
         bar = g_object_new (GVC_TYPE_BALANCE_BAR,
                             "channel-map", channel_map,
+                            "balance-type", btype,
                             NULL);
         return GTK_WIDGET (bar);
 }
diff --git a/gnome-volume-control/src/gvc-balance-bar.h b/gnome-volume-control/src/gvc-balance-bar.h
index e684530..01b2f13 100644
--- a/gnome-volume-control/src/gvc-balance-bar.h
+++ b/gnome-volume-control/src/gvc-balance-bar.h
@@ -34,6 +34,11 @@ G_BEGIN_DECLS
 #define GVC_IS_BALANCE_BAR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_BALANCE_BAR))
 #define GVC_BALANCE_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_BALANCE_BAR, GvcBalanceBarClass))
 
+typedef enum {
+        BALANCE_TYPE_RL,
+        BALANCE_TYPE_FR,
+} GvcBalanceType;
+
 typedef struct GvcBalanceBarPrivate GvcBalanceBarPrivate;
 
 typedef struct
@@ -49,7 +54,8 @@ typedef struct
 
 GType               gvc_balance_bar_get_type            (void);
 
-GtkWidget *         gvc_balance_bar_new                 (GvcChannelMap *map);
+GtkWidget *         gvc_balance_bar_new                 (GvcChannelMap *map,
+                                                         GvcBalanceType btype);
 
 void                gvc_balance_bar_set_size_group      (GvcBalanceBar *bar,
                                                          GtkSizeGroup  *group,
diff --git a/gnome-volume-control/src/gvc-channel-map.c b/gnome-volume-control/src/gvc-channel-map.c
index b8a0791..66a8ecf 100644
--- a/gnome-volume-control/src/gvc-channel-map.c
+++ b/gnome-volume-control/src/gvc-channel-map.c
@@ -35,14 +35,15 @@
 
 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[3]; /* volume, balance, fade */
         gboolean              can_balance;
+        gboolean              can_fade;
 };
 
 enum {
-        GAINS_CHANGED,
+        VOLUME_CHANGED,
         LAST_SIGNAL
 };
 
@@ -58,30 +59,70 @@ 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;
-}
 
-gdouble *
-gvc_channel_map_get_gains (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 0;
+
+        return map->priv->pa_map.channels;
 }
 
-pa_channel_position_t *
-gvc_channel_map_get_positions (GvcChannelMap *map)
+const gdouble *
+gvc_channel_map_get_volume (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;
+
+        map->priv->extern_volume[VOLUME] = (gdouble) pa_cvolume_max (&map->priv->pa_volume);
+        map->priv->extern_volume[BALANCE] = (gdouble) pa_cvolume_get_balance (&map->priv->pa_volume, &map->priv->pa_map);
+        map->priv->extern_volume[FADE] = (gdouble) pa_cvolume_get_fade (&map->priv->pa_volume, &map->priv->pa_map);
+
+        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 pa_channel_map *
+gvc_channel_map_get_pa_channel_map (GvcChannelMap  *map)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
+
+        if (!pa_channel_map_valid(&map->priv->pa_map))
+                return NULL;
+
+        return &map->priv->pa_map;
+}
+
+const pa_cvolume *
+gvc_channel_map_get_cvolume_for_volumes (GvcChannelMap  *map,
+                                         gint            volume)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
+
+        if (!pa_channel_map_valid(&map->priv->pa_map))
+                return NULL;
+
+        if (volume >= 0)
+                pa_cvolume_scale (&map->priv->pa_volume, (pa_volume_t) volume);
+
+        return &map->priv->pa_volume;
+}
+
 static void
 gvc_channel_map_class_init (GvcChannelMapClass *klass)
 {
@@ -89,11 +130,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);
@@ -102,10 +143,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
@@ -141,18 +191,18 @@ 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->num_channels = pa_map->channels;
 #ifdef HAVE_NEW_PULSE
         map->priv->can_balance = pa_channel_map_can_balance (pa_map);
+        map->priv->can_fade = pa_channel_map_can_fade (pa_map);
 #else
-	map->priv->can_balance = TRUE;
+        map->priv->can_balance = TRUE;
+        map->priv->can_fade = FALSE;
 #endif
-        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/gnome-volume-control/src/gvc-channel-map.h b/gnome-volume-control/src/gvc-channel-map.h
index de5815a..b00895a 100644
--- a/gnome-volume-control/src/gvc-channel-map.h
+++ b/gnome-volume-control/src/gvc-channel-map.h
@@ -44,20 +44,31 @@ typedef struct
 typedef struct
 {
         GObjectClass           parent_class;
-        void (*gains_changed) (GvcChannelMap *channel_map);
+        void (*volume_changed) (GvcChannelMap *channel_map);
 } GvcChannelMapClass;
 
+enum {
+        VOLUME,
+        BALANCE,
+        FADE,
+};
+
 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);
 
-void                    gvc_channel_map_gains_changed           (GvcChannelMap  *map);
+void                    gvc_channel_map_volume_changed          (GvcChannelMap    *map,
+                                                                 const pa_cvolume *cv);
 
+/* private */
+const pa_cvolume *      gvc_channel_map_get_cvolume_for_volumes (GvcChannelMap  *map,
+                                                                 int             volume);
+const pa_channel_map *  gvc_channel_map_get_pa_channel_map      (GvcChannelMap  *map);
 G_END_DECLS
 
 #endif /* __GVC_CHANNEL_MAP_H */
diff --git a/gnome-volume-control/src/gvc-mixer-control.c b/gnome-volume-control/src/gvc-mixer-control.c
index 4ac2c6c..fd69fe8 100644
--- a/gnome-volume-control/src/gvc-mixer-control.c
+++ b/gnome-volume-control/src/gvc-mixer-control.c
@@ -221,11 +221,11 @@ gvc_stream_collate (GvcMixerStream *a,
         nameb = gvc_mixer_stream_get_name (b);
 
         if (nameb == NULL && namea == NULL)
-        	return 0;
+                return 0;
         if (nameb == NULL)
-        	return 1;
+                return 1;
         if (namea == NULL)
-        	return -1;
+                return -1;
 
         return g_utf8_collate (namea, nameb);
 }
@@ -502,6 +502,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);
@@ -513,17 +514,21 @@ update_sink (GvcMixerControl    *control,
                  map_buff);
 #endif
 
+        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);
@@ -551,6 +556,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
diff --git a/gnome-volume-control/src/gvc-mixer-dialog.c b/gnome-volume-control/src/gvc-mixer-dialog.c
index c953ff3..036fe87 100644
--- a/gnome-volume-control/src/gvc-mixer-dialog.c
+++ b/gnome-volume-control/src/gvc-mixer-dialog.c
@@ -63,6 +63,7 @@ struct GvcMixerDialogPrivate
         GtkWidget       *output_treeview;
         GtkWidget       *output_settings_box;
         GtkWidget       *output_balance_bar;
+        GtkWidget       *output_fade_bar;
         GtkWidget       *input_treeview;
         GtkWidget       *sound_theme_chooser;
         GtkWidget       *click_feedback_button;
@@ -150,6 +151,11 @@ update_output_settings (GvcMixerDialog *dialog)
                                       dialog->priv->output_balance_bar);
                 dialog->priv->output_balance_bar = NULL;
         }
+        if (dialog->priv->output_fade_bar != NULL) {
+                gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box),
+                                      dialog->priv->output_fade_bar);
+                dialog->priv->output_fade_bar = NULL;
+        }
 
         stream = gvc_mixer_control_get_default_sink (dialog->priv->mixer_control);
         if (stream == NULL) {
@@ -163,7 +169,7 @@ update_output_settings (GvcMixerDialog *dialog)
                 return;
         }
 
-        dialog->priv->output_balance_bar = gvc_balance_bar_new (map);
+        dialog->priv->output_balance_bar = gvc_balance_bar_new (map, BALANCE_TYPE_RL);
         if (dialog->priv->size_group != NULL) {
                 gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_balance_bar),
                                                 dialog->priv->size_group,
@@ -174,6 +180,19 @@ update_output_settings (GvcMixerDialog *dialog)
                             FALSE, FALSE, 12);
         gtk_widget_show (dialog->priv->output_balance_bar);
 
+        if (gvc_channel_map_can_fade (map)) {
+                dialog->priv->output_fade_bar = gvc_balance_bar_new (map, BALANCE_TYPE_FR);
+                if (dialog->priv->size_group != NULL) {
+                        gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_fade_bar),
+                                                        dialog->priv->size_group,
+                                                        TRUE);
+                }
+                gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
+                                    dialog->priv->output_fade_bar,
+                                    FALSE, FALSE, 12);
+                gtk_widget_show (dialog->priv->output_fade_bar);
+        }
+
         /* We could make this into a "No settings" label instead */
         gtk_widget_set_sensitive (dialog->priv->output_balance_bar, gvc_channel_map_can_balance (map));
 }
diff --git a/gnome-volume-control/src/gvc-mixer-sink-input.c b/gnome-volume-control/src/gvc-mixer-sink-input.c
index b2c7172..963229e 100644
--- a/gnome-volume-control/src/gvc-mixer-sink-input.c
+++ b/gnome-volume-control/src/gvc-mixer-sink-input.c
@@ -52,32 +52,22 @@ gvc_mixer_sink_input_change_volume (GvcMixerStream *stream,
         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;
-        }
+        /* set the volume */
+        cv = gvc_channel_map_get_cvolume_for_volumes (map, volume);
 
         context = gvc_mixer_stream_get_pa_context (stream);
 
         o = pa_context_set_sink_input_volume (context,
                                               index,
-                                              &cv,
+                                              cv,
                                               NULL,
                                               NULL);
 
diff --git a/gnome-volume-control/src/gvc-mixer-sink.c b/gnome-volume-control/src/gvc-mixer-sink.c
index 76eb3d7..c242723 100644
--- a/gnome-volume-control/src/gvc-mixer-sink.c
+++ b/gnome-volume-control/src/gvc-mixer-sink.c
@@ -35,12 +35,13 @@
 
 struct GvcMixerSinkPrivate
 {
-        gpointer dummy;
+        pa_operation *change_volume_op;
 };
 
 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)
 
@@ -52,45 +53,35 @@ gvc_mixer_sink_change_volume (GvcMixerStream *stream,
         guint              index;
         GvcChannelMap     *map;
         pa_context        *context;
-        pa_cvolume         cv;
-        guint              i;
-        guint              num_channels;
-        gdouble           *gains;
+        const pa_cvolume  *cv;
+        GvcMixerSink      *sink = GVC_MIXER_SINK (stream);
 
         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);
+        g_debug ("Changing volume for sink: vol=%u", (guint)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_for_volumes (map, volume);
 
         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);
+        if (sink->priv->change_volume_op != NULL)
+                pa_operation_unref (sink->priv->change_volume_op);
+
+        sink->priv->change_volume_op = o;
 
         return TRUE;
 }
@@ -113,7 +104,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;
         }
 
@@ -122,6 +113,23 @@ gvc_mixer_sink_change_is_muted (GvcMixerStream *stream,
         return TRUE;
 }
 
+static gboolean
+gvc_mixer_sink_is_running (GvcMixerStream *stream)
+{
+        GvcMixerSink *sink = GVC_MIXER_SINK (stream);
+
+        if (sink->priv->change_volume_op == NULL)
+                return FALSE;
+
+        if ((pa_operation_get_state(sink->priv->change_volume_op) == PA_OPERATION_RUNNING))
+                return TRUE;
+
+        pa_operation_unref(sink->priv->change_volume_op);
+        sink->priv->change_volume_op = NULL;
+
+        return FALSE;
+}
+
 static GObject *
 gvc_mixer_sink_constructor (GType                  type,
                             guint                  n_construct_properties,
@@ -144,10 +152,12 @@ 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->change_is_muted = gvc_mixer_sink_change_is_muted;
+        stream_class->is_running = gvc_mixer_sink_is_running;
 
         g_type_class_add_private (klass, sizeof (GvcMixerSinkPrivate));
 }
@@ -160,6 +170,24 @@ gvc_mixer_sink_init (GvcMixerSink *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);
+
+        if (mixer_sink->priv->change_volume_op) {
+                pa_operation_unref(mixer_sink->priv->change_volume_op);
+                mixer_sink->priv->change_volume_op = NULL;
+        }
+
+        G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->dispose (object);
+}
+
+static void
 gvc_mixer_sink_finalize (GObject *object)
 {
         GvcMixerSink *mixer_sink;
diff --git a/gnome-volume-control/src/gvc-mixer-source.c b/gnome-volume-control/src/gvc-mixer-source.c
index de1b09e..6eeb107 100644
--- a/gnome-volume-control/src/gvc-mixer-source.c
+++ b/gnome-volume-control/src/gvc-mixer-source.c
@@ -52,38 +52,25 @@ gvc_mixer_source_change_volume (GvcMixerStream *stream,
         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_for_volumes (map, volume);
 
         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;
         }
 
@@ -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;
         }
 
diff --git a/gnome-volume-control/src/gvc-mixer-stream.c b/gnome-volume-control/src/gvc-mixer-stream.c
index 2e9a5f7..4978c60 100644
--- a/gnome-volume-control/src/gvc-mixer-stream.c
+++ b/gnome-volume-control/src/gvc-mixer-stream.c
@@ -283,10 +283,10 @@ gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
 }
 
 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");
+        g_debug ("Volume changed");
         gvc_mixer_stream_change_volume (stream, stream->priv->volume);
 }
 
@@ -302,7 +302,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);
         }
@@ -311,8 +311,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");
@@ -500,6 +500,14 @@ gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
         return ret;
 }
 
+gboolean
+gvc_mixer_stream_is_running (GvcMixerStream *stream)
+{
+        if (GVC_MIXER_STREAM_GET_CLASS (stream)->is_running != NULL)
+                return GVC_MIXER_STREAM_GET_CLASS (stream)->is_running (stream);
+        return FALSE;
+}
+
 static void
 gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
 {
diff --git a/gnome-volume-control/src/gvc-mixer-stream.h b/gnome-volume-control/src/gvc-mixer-stream.h
index 2848146..50cc8db 100644
--- a/gnome-volume-control/src/gvc-mixer-stream.h
+++ b/gnome-volume-control/src/gvc-mixer-stream.h
@@ -52,6 +52,7 @@ typedef struct
                                      guint           volume);
         gboolean (*change_is_muted) (GvcMixerStream *stream,
                                      gboolean        is_muted);
+        gboolean (*is_running)      (GvcMixerStream *stream);
 } GvcMixerStreamClass;
 
 GType               gvc_mixer_stream_get_type        (void);
@@ -70,6 +71,7 @@ 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);



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