[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