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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel