[Spice-devel] [PATCH spice-gtk] pulse: sync volume and mute upon stream creation

Marc-André Lureau marcandre.lureau at gmail.com
Thu Feb 20 08:39:48 PST 2014


Whenever a stream is created, it should have current guest volume.
Otherwise, a 100% volume guest will have current client volume, and
when changing guest volume slightly, the volume will jump abruptly.

https://bugzilla.redhat.com/show_bug.cgi?id=1012868#c10
---
 gtk/spice-pulse.c | 253 +++++++++++++++++++++++++++++++++---------------------
 1 file changed, 155 insertions(+), 98 deletions(-)

diff --git a/gtk/spice-pulse.c b/gtk/spice-pulse.c
index c4241d0..14e32f5 100644
--- a/gtk/spice-pulse.c
+++ b/gtk/spice-pulse.c
@@ -327,11 +327,82 @@ static void stream_update_latency_callback(pa_stream *s, void *userdata)
     }
 }
 
+static gboolean get_playback_mute(SpicePulse *pulse)
+{
+    SpicePulsePrivate *p = pulse->priv;
+    gboolean mute;
+
+    g_object_get(p->pchannel, "mute", &mute, NULL);
+    return mute;
+}
+
+static void set_playback_mute(SpicePulse *pulse)
+{
+    SpicePulsePrivate *p = pulse->priv;
+    gboolean mute = get_playback_mute(pulse);
+    pa_operation *op;
+
+    if (!p->playback.stream ||
+        pa_stream_get_index(p->playback.stream) == PA_INVALID_INDEX)
+        return;
+
+    op = pa_context_set_sink_input_mute(p->context,
+                                        pa_stream_get_index(p->playback.stream),
+                                        mute, NULL, NULL);
+    if (!op)
+        g_warning("set_sink_input_mute() failed: %s",
+                  pa_strerror(pa_context_errno(p->context)));
+    else
+        pa_operation_unref(op);
+}
+
+static void get_playback_volume(SpicePulse *pulse, pa_cvolume *v)
+{
+    SpicePulsePrivate *p = pulse->priv;
+    guint16 *volume;
+    guint nchannels;
+    guint i;
+
+    g_object_get(p->pchannel,
+                 "volume", &volume,
+                 "nchannels", &nchannels,
+                 NULL);
+
+    pa_cvolume_init(v);
+    v->channels = p->playback.spec.channels;
+    for (i = 0; i < nchannels; ++i) {
+        v->values[i] = (PA_VOLUME_NORM - PA_VOLUME_MUTED) * volume[i] / G_MAXUINT16;
+        SPICE_DEBUG("playback volume changed %u", v->values[i]);
+    }
+}
+
+static void set_playback_volume(SpicePulse *pulse)
+{
+    SpicePulsePrivate *p = pulse->priv;
+    pa_operation *op;
+    pa_cvolume v;
+
+    if (!p->playback.stream ||
+        pa_stream_get_index(p->playback.stream) == PA_INVALID_INDEX)
+        return;
+
+    get_playback_volume(pulse, &v);
+    op = pa_context_set_sink_input_volume(p->context,
+                                          pa_stream_get_index(p->playback.stream),
+                                          &v, NULL, NULL);
+    if (!op)
+        g_warning("set_sink_input_volume() failed: %s",
+                  pa_strerror(pa_context_errno(p->context)));
+    else
+        pa_operation_unref(op);
+}
+
 static void create_playback(SpicePulse *pulse)
 {
     SpicePulsePrivate *p = SPICE_PULSE_GET_PRIVATE(pulse);
     pa_stream_flags_t flags;
     pa_buffer_attr buffer_attr = { 0, };
+    pa_cvolume volume;
 
     g_return_if_fail(p != NULL);
     g_return_if_fail(p->context != NULL);
@@ -350,9 +421,12 @@ static void create_playback(SpicePulse *pulse)
     buffer_attr.prebuf = -1;
     buffer_attr.minreq = -1;
     flags = PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE;
+    if (get_playback_mute(pulse))
+        flags |= PA_STREAM_START_MUTED;
+    get_playback_volume(pulse, &volume);
 
     if (pa_stream_connect_playback(p->playback.stream,
-                                   NULL, &buffer_attr, flags, NULL, NULL) < 0) {
+                                   NULL, &buffer_attr, flags, &volume, NULL) < 0) {
         g_warning("pa_stream_connect_playback() failed: %s",
                   pa_strerror(pa_context_errno(p->context)));
     }
@@ -489,6 +563,77 @@ static void stream_read_callback(pa_stream *s, size_t length, void *data)
     }
 }
 
+static gboolean get_record_mute(SpicePulse *pulse)
+{
+    SpicePulsePrivate *p = pulse->priv;
+    gboolean mute;
+
+    g_object_get(p->rchannel, "mute", &mute, NULL);
+    return mute;
+}
+
+static void set_record_mute(SpicePulse *pulse)
+{
+    SpicePulsePrivate *p = pulse->priv;
+    gboolean mute = get_record_mute(pulse);
+    pa_operation *op;
+
+    if (!p->record.stream ||
+        pa_stream_get_device_index(p->record.stream) == PA_INVALID_INDEX)
+        return;
+
+    op = pa_context_set_source_mute_by_index(p->context,
+                                             pa_stream_get_device_index(p->record.stream),
+                                             mute, NULL, NULL);
+    if (!op)
+        g_warning("set_source_mute() failed: %s",
+                  pa_strerror(pa_context_errno(p->context)));
+    else
+        pa_operation_unref(op);
+}
+
+static void get_record_volume(SpicePulse *pulse, pa_cvolume *v)
+{
+    SpicePulsePrivate *p = pulse->priv;
+    guint16 *volume;
+    guint nchannels;
+    guint i;
+
+    g_object_get(p->rchannel,
+                 "volume", &volume,
+                 "nchannels", &nchannels,
+                 NULL);
+
+    pa_cvolume_init(v);
+    v->channels = p->record.spec.channels;
+    for (i = 0; i < nchannels; ++i) {
+        v->values[i] = (PA_VOLUME_NORM - PA_VOLUME_MUTED) * volume[i] / G_MAXUINT16;
+        SPICE_DEBUG("record volume changed %u", v->values[i]);
+    }
+}
+
+static void set_record_volume(SpicePulse *pulse)
+{
+    SpicePulsePrivate *p = pulse->priv;
+    pa_operation *op;
+    pa_cvolume v;
+
+    if (!p->record.stream ||
+        pa_stream_get_device_index(p->record.stream) == PA_INVALID_INDEX)
+        return;
+
+    get_record_volume(pulse, &v);
+    /* FIXME: use the upcoming "set_source_output_volume" */
+    op = pa_context_set_source_volume_by_index(p->context,
+        pa_stream_get_device_index(p->record.stream),
+        &v, NULL, NULL);
+    if (!op)
+        g_warning("set_source_volume() failed: %s",
+                  pa_strerror(pa_context_errno(p->context)));
+    else
+        pa_operation_unref(op);
+}
+
 static void create_record(SpicePulse *pulse)
 {
     SpicePulsePrivate *p = SPICE_PULSE_GET_PRIVATE(pulse);
@@ -512,10 +657,15 @@ static void create_record(SpicePulse *pulse)
     buffer_attr.fragsize = buffer_attr.tlength = pa_usec_to_bytes(20 * PA_USEC_PER_MSEC, &p->record.spec);
     buffer_attr.minreq = (uint32_t) -1;
     flags = PA_STREAM_ADJUST_LATENCY;
+    if (get_record_mute(pulse))
+        flags |= PA_STREAM_START_MUTED;
 
     if (pa_stream_connect_record(p->record.stream, NULL, &buffer_attr, flags) < 0) {
         g_warning("pa_stream_connect_record() failed: %s",
                   pa_strerror(pa_context_errno(p->context)));
+    } else {
+        set_record_volume(pulse);
+        set_record_mute(pulse);
     }
 }
 
@@ -604,61 +754,15 @@ static void channel_event(SpiceChannel *channel, SpiceChannelEvent event,
 static void playback_volume_changed(GObject *object, GParamSpec *pspec, gpointer data)
 {
     SpicePulse *pulse = data;
-    SpicePulsePrivate *p = pulse->priv;
-    guint16 *volume;
-    guint nchannels;
-    pa_operation *op;
-    pa_cvolume v;
-    guint i;
 
-    g_object_get(object,
-                 "volume", &volume,
-                 "nchannels", &nchannels,
-                 NULL);
-
-    pa_cvolume_init(&v);
-    v.channels = p->playback.spec.channels;
-    for (i = 0; i < nchannels; ++i) {
-        v.values[i] = (PA_VOLUME_NORM - PA_VOLUME_MUTED) * volume[i] / G_MAXUINT16;
-        SPICE_DEBUG("playback volume changed %u", v.values[i]);
-    }
-
-    if (!p->playback.stream ||
-        pa_stream_get_index(p->playback.stream) == PA_INVALID_INDEX)
-        return;
-
-    op = pa_context_set_sink_input_volume(p->context,
-        pa_stream_get_index(p->playback.stream),
-        &v, NULL, NULL);
-    if (!op)
-        g_warning("set_sink_input_volume() failed: %s",
-                  pa_strerror(pa_context_errno(p->context)));
-    else
-        pa_operation_unref(op);
+    set_playback_volume(pulse);
 }
 
 static void playback_mute_changed(GObject *object, GParamSpec *pspec, gpointer data)
 {
     SpicePulse *pulse = data;
-    SpicePulsePrivate *p = pulse->priv;
-    gboolean mute;
-    pa_operation *op;
-
-    g_object_get(object, "mute", &mute, NULL);
-    SPICE_DEBUG("playback mute changed %u", mute);
-
-    if (!p->playback.stream ||
-        pa_stream_get_index(p->playback.stream) == PA_INVALID_INDEX)
-        return;
 
-    op = pa_context_set_sink_input_mute(p->context,
-        pa_stream_get_index(p->playback.stream),
-        mute, NULL, NULL);
-    if (!op)
-        g_warning("set_sink_input_mute() failed: %s",
-                  pa_strerror(pa_context_errno(p->context)));
-    else
-        pa_operation_unref(op);
+    set_playback_mute(pulse);
 }
 
 static void playback_min_latency_changed(GObject *object, GParamSpec *pspec, gpointer data)
@@ -683,62 +787,15 @@ static void playback_min_latency_changed(GObject *object, GParamSpec *pspec, gpo
 static void record_mute_changed(GObject *object, GParamSpec *pspec, gpointer data)
 {
     SpicePulse *pulse = data;
-    SpicePulsePrivate *p = pulse->priv;
-    gboolean mute;
-    pa_operation *op;
-
-    g_object_get(object, "mute", &mute, NULL);
-    SPICE_DEBUG("record mute changed %u", mute);
-
-    if (!p->record.stream ||
-        pa_stream_get_device_index(p->record.stream) == PA_INVALID_INDEX)
-        return;
 
-    op = pa_context_set_source_mute_by_index(p->context,
-        pa_stream_get_device_index(p->record.stream),
-        mute, NULL, NULL);
-    if (!op)
-        g_warning("set_source_mute() failed: %s",
-                  pa_strerror(pa_context_errno(p->context)));
-    else
-        pa_operation_unref(op);
+    set_record_mute(pulse);
 }
 
 static void record_volume_changed(GObject *object, GParamSpec *pspec, gpointer data)
 {
     SpicePulse *pulse = data;
-    SpicePulsePrivate *p = pulse->priv;
-    guint16 *volume;
-    guint nchannels;
-    pa_operation *op;
-    pa_cvolume v;
-    guint i;
-
-    g_object_get(object,
-                 "volume", &volume,
-                 "nchannels", &nchannels,
-                 NULL);
-
-    pa_cvolume_init(&v);
-    v.channels = p->record.spec.channels;
-    for (i = 0; i < nchannels; ++i) {
-        v.values[i] = (PA_VOLUME_NORM - PA_VOLUME_MUTED) * volume[i] / G_MAXUINT16;
-        SPICE_DEBUG("record volume changed %u", v.values[i]);
-    }
 
-    if (!p->record.stream ||
-        pa_stream_get_device_index(p->record.stream) == PA_INVALID_INDEX)
-        return;
-
-    /* FIXME: use the upcoming "set_source_output_volume" */
-    op = pa_context_set_source_volume_by_index(p->context,
-        pa_stream_get_device_index(p->record.stream),
-        &v, NULL, NULL);
-    if (!op)
-        g_warning("set_source_volume() failed: %s",
-                  pa_strerror(pa_context_errno(p->context)));
-    else
-        pa_operation_unref(op);
+    set_record_volume(pulse);
 }
 
 static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel)
-- 
1.8.5.3



More information about the Spice-devel mailing list