[pulseaudio-commits] 14 commits - src/modules src/pulsecore

Tanu Kaskinen tanuk at kemper.freedesktop.org
Fri May 2 06:01:55 PDT 2014


 src/modules/alsa/alsa-sink.c                 |   17 +--
 src/modules/alsa/alsa-source.c               |   17 +--
 src/modules/dbus/iface-stream.c              |    4 
 src/modules/echo-cancel/module-echo-cancel.c |   15 --
 src/modules/module-role-cork.c               |    9 -
 src/modules/module-solaris.c                 |   19 ++-
 src/modules/module-stream-restore.c          |    4 
 src/modules/module-tunnel.c                  |    2 
 src/pulsecore/cli-text.c                     |    4 
 src/pulsecore/core.h                         |    8 +
 src/pulsecore/protocol-native.c              |    4 
 src/pulsecore/sink-input.c                   |  111 +++++++++-----------
 src/pulsecore/sink-input.h                   |    8 +
 src/pulsecore/sink.c                         |  147 +++++++++++++-------------
 src/pulsecore/sink.h                         |   33 ++++--
 src/pulsecore/source-output.c                |  104 +++++++++---------
 src/pulsecore/source-output.h                |    8 +
 src/pulsecore/source.c                       |  148 +++++++++++++--------------
 src/pulsecore/source.h                       |   33 ++++--
 19 files changed, 377 insertions(+), 318 deletions(-)

New commits:
commit c12aa68e02cd18d99c06ace96931b0ada9848ad2
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:16 2014 +0300

    sink-input, source-output: Add hooks for mute changes

diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 762157e..b443ce4 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -100,6 +100,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED,
+    PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
     PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
     PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
@@ -112,6 +113,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
     PA_CORE_HOOK_CLIENT_NEW,
     PA_CORE_HOOK_CLIENT_PUT,
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index ec504cf..6169d47 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1424,6 +1424,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
         i->mute_changed(i);
 
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], i);
 }
 
 /* Called from main thread */
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 18a1478..d3d15f1 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -1082,6 +1082,7 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
         o->mute_changed(o);
 
     pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED], o);
 }
 
 /* Called from main thread */

commit 2f6364dfe454d3fe21cf98854ac8942870e52436
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:15 2014 +0300

    sink, source: Add hooks for mute changes

diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 2276fb9..762157e 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -77,6 +77,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_PORT_CHANGED,
     PA_CORE_HOOK_SINK_FLAGS_CHANGED,
     PA_CORE_HOOK_SINK_VOLUME_CHANGED,
+    PA_CORE_HOOK_SINK_MUTE_CHANGED,
     PA_CORE_HOOK_SOURCE_NEW,
     PA_CORE_HOOK_SOURCE_FIXATE,
     PA_CORE_HOOK_SOURCE_PUT,
@@ -87,6 +88,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SOURCE_PORT_CHANGED,
     PA_CORE_HOOK_SOURCE_FLAGS_CHANGED,
     PA_CORE_HOOK_SOURCE_VOLUME_CHANGED,
+    PA_CORE_HOOK_SOURCE_MUTE_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_NEW,
     PA_CORE_HOOK_SINK_INPUT_FIXATE,
     PA_CORE_HOOK_SINK_INPUT_PUT,
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index d071c30..fbb14d2 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -2219,6 +2219,7 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
     pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED], s);
 }
 
 /* Called from main thread */
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index d0b73c9..61d42fa 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1812,6 +1812,7 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
     pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED], s);
 }
 
 /* Called from main thread */

commit 415dd6306a43ca7b2956f1a843012ba9e941d322
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:14 2014 +0300

    sink-input, source-output: Add hooks for volume changes

diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index d764ac4..2276fb9 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -97,6 +97,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL,
     PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
     PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
     PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
@@ -108,6 +109,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL,
     PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
     PA_CORE_HOOK_CLIENT_NEW,
     PA_CORE_HOOK_CLIENT_PUT,
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index cfec051..ec504cf 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -2235,4 +2235,5 @@ void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume)
         i->volume_changed(i);
 
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], i);
 }
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index d3888df..18a1478 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -1692,4 +1692,5 @@ void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *v
         o->volume_changed(o);
 
     pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED], o);
 }

commit df7d8ba804ebf3f2fb0f744880323f6738e93b69
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:13 2014 +0300

    sink, source: Add hooks for volume changes

diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index f268e42..d764ac4 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -76,6 +76,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
     PA_CORE_HOOK_SINK_PORT_CHANGED,
     PA_CORE_HOOK_SINK_FLAGS_CHANGED,
+    PA_CORE_HOOK_SINK_VOLUME_CHANGED,
     PA_CORE_HOOK_SOURCE_NEW,
     PA_CORE_HOOK_SOURCE_FIXATE,
     PA_CORE_HOOK_SOURCE_PUT,
@@ -85,6 +86,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED,
     PA_CORE_HOOK_SOURCE_PORT_CHANGED,
     PA_CORE_HOOK_SOURCE_FLAGS_CHANGED,
+    PA_CORE_HOOK_SOURCE_VOLUME_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_NEW,
     PA_CORE_HOOK_SINK_INPUT_FIXATE,
     PA_CORE_HOOK_SINK_INPUT_PUT,
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index a1cbfa5..d071c30 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -3800,4 +3800,5 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
                                             s->flags & PA_SINK_DECIBEL_VOLUME));
 
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s);
 }
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 5bbd7f1..d0b73c9 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -2869,4 +2869,5 @@ void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volum
                                             s->flags & PA_SOURCE_DECIBEL_VOLUME));
 
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED], s);
 }

commit f88912af9b3fc5aa7c692aecc32ac13a4a3869f9
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:12 2014 +0300

    solaris, tunnel: Remove some redundant boolean conversions

diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index 71a98e9..4f11000 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -564,7 +564,7 @@ static void sink_set_mute(pa_sink *s) {
     if (u->fd >= 0) {
         AUDIO_INITINFO(&info);
 
-        info.output_muted = !!s->muted;
+        info.output_muted = s->muted;
 
         if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
             pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 6b3512e..f0f0e31 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -1906,7 +1906,7 @@ static void sink_set_mute(pa_sink *sink) {
     pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
     pa_tagstruct_putu32(t, u->ctag++);
     pa_tagstruct_putu32(t, u->device_index);
-    pa_tagstruct_put_boolean(t, !!sink->muted);
+    pa_tagstruct_put_boolean(t, sink->muted);
     pa_pstream_send_tagstruct(u->pstream, t);
 }
 

commit ef4ae785aa1d4d67b5df1c9414f6c1a144bc3460
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:11 2014 +0300

    sink-input, source-output: Remove redundant get_mute() functions
    
    The functions just return the muted value. Callers can as well read
    the struct field directly, it's simpler that way.

diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
index 1cff95e..4cbcd74 100644
--- a/src/modules/dbus/iface-stream.c
+++ b/src/modules/dbus/iface-stream.c
@@ -765,7 +765,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t
             }
         }
 
-        new_mute = pa_sink_input_get_mute(s->sink_input);
+        new_mute = s->sink_input->muted;
 
         if (s->mute != new_mute) {
             s->mute = new_mute;
@@ -861,7 +861,7 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p
     else
         pa_cvolume_init(&s->volume);
 
-    s->mute = pa_sink_input_get_mute(sink_input);
+    s->mute = sink_input->muted;
     s->proplist = pa_proplist_copy(sink_input->proplist);
     s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
     s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s);
diff --git a/src/modules/module-role-cork.c b/src/modules/module-role-cork.c
index 6573cd6..8ca2109 100644
--- a/src/modules/module-role-cork.c
+++ b/src/modules/module-role-cork.c
@@ -102,7 +102,7 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in
     pa_sink_assert_ref(s);
 
     for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
-        bool corked, muted, corked_here;
+        bool corked, corked_here;
         const char *role;
 
         if (j == ignore)
@@ -119,10 +119,9 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in
             continue;
 
         corked = (pa_sink_input_get_state(j) == PA_SINK_INPUT_CORKED);
-        muted = pa_sink_input_get_mute(j);
         corked_here = !!pa_hashmap_get(u->cork_state, j);
 
-        if (cork && !corked && !muted) {
+        if (cork && !corked && !j->muted) {
             pa_log_debug("Found a '%s' stream that should be corked/muted.", cork_role);
             if (!corked_here)
                 pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1));
@@ -131,9 +130,9 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in
         } else if (!cork) {
             pa_hashmap_remove(u->cork_state, j);
 
-            if (corked_here && (corked || muted)) {
+            if (corked_here && (corked || j->muted)) {
                 pa_log_debug("Found a '%s' stream that should be uncorked/unmuted.", cork_role);
-                if (muted)
+                if (j->muted)
                     pa_sink_input_set_mute(j, false, false);
                 if (corked)
                     pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 3774d49..38d6aac 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -1299,7 +1299,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         }
 
         if (sink_input->save_muted) {
-            entry->muted = pa_sink_input_get_mute(sink_input);
+            entry->muted = sink_input->muted;
             entry->muted_valid = true;
 
             mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
@@ -1349,7 +1349,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         }
 
         if (source_output->save_muted) {
-            entry->muted = pa_source_output_get_mute(source_output);
+            entry->muted = source_output->muted;
             entry->muted_valid = true;
 
             mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index c7db0a6..2992ae8 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -536,7 +536,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
             state_table[pa_source_output_get_state(o)],
             o->source->index, o->source->name,
             volume_str,
-            pa_yes_no(pa_source_output_get_mute(o)),
+            pa_yes_no(o->muted),
             (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
             clt,
             pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
@@ -634,7 +634,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             state_table[pa_sink_input_get_state(i)],
             i->sink->index, i->sink->name,
             volume_str,
-            pa_yes_no(pa_sink_input_get_mute(i)),
+            pa_yes_no(i->muted),
             (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
             clt,
             pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 41b4b50..96df383 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3366,7 +3366,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
     pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
     pa_tagstruct_puts(t, s->driver);
     if (c->version >= 11)
-        pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s));
+        pa_tagstruct_put_boolean(t, s->muted);
     if (c->version >= 13)
         pa_tagstruct_put_proplist(t, s->proplist);
     if (c->version >= 19)
@@ -3413,7 +3413,7 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *
         pa_tagstruct_put_boolean(t, (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_CORKED));
     if (c->version >= 22) {
         pa_tagstruct_put_cvolume(t, &v);
-        pa_tagstruct_put_boolean(t, pa_source_output_get_mute(s));
+        pa_tagstruct_put_boolean(t, s->muted);
         pa_tagstruct_put_boolean(t, has_volume);
         pa_tagstruct_put_boolean(t, s->volume_writable);
         pa_tagstruct_put_format_info(t, s->format);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index e41afc9..cfec051 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1426,15 +1426,6 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 }
 
-/* Called from main context */
-bool pa_sink_input_get_mute(pa_sink_input *i) {
-    pa_sink_input_assert_ref(i);
-    pa_assert_ctl_context();
-    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-
-    return i->muted;
-}
-
 /* Called from main thread */
 void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
     pa_sink_input_assert_ref(i);
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 2be2c33..a48476c 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -375,7 +375,6 @@ int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key);
 pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool absolute);
 
 void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save);
-bool pa_sink_input_get_mute(pa_sink_input *i);
 
 void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
 
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index bb89384..d3888df 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -1084,15 +1084,6 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
     pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
 }
 
-/* Called from main context */
-bool pa_source_output_get_mute(pa_source_output *o) {
-    pa_source_output_assert_ref(o);
-    pa_assert_ctl_context();
-    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
-
-    return o->muted;
-}
-
 /* Called from main thread */
 void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
     pa_source_output_assert_ref(o);
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 27d6fd4..73170d3 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -318,7 +318,6 @@ void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume,
 pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, bool absolute);
 
 void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save);
-bool pa_source_output_get_mute(pa_source_output *o);
 
 void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p);
 

commit e4a7625ba884c5cce20468d75937857343751c35
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:10 2014 +0300

    sink, source: Assign to s->muted from only one place
    
    Forcing all mute changes to go through set_mute() makes it easier to
    check where the muted field is changed, and it also allows us to have
    only one place where notifications for changed mute are sent.

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 400f972..daa9061 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1395,18 +1395,17 @@ static void sink_write_volume_cb(pa_sink *s) {
     }
 }
 
-static void sink_get_mute_cb(pa_sink *s) {
+static int sink_get_mute_cb(pa_sink *s, bool *mute) {
     struct userdata *u = s->userdata;
-    bool b;
 
     pa_assert(u);
     pa_assert(u->mixer_path);
     pa_assert(u->mixer_handle);
 
-    if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0)
-        return;
+    if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0)
+        return -1;
 
-    s->muted = b;
+    return 0;
 }
 
 static void sink_set_mute_cb(pa_sink *s) {
@@ -2390,8 +2389,12 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
         if (u->sink->set_mute)
             u->sink->set_mute(u->sink);
     } else {
-        if (u->sink->get_mute)
-            u->sink->get_mute(u->sink);
+        if (u->sink->get_mute) {
+            bool mute;
+
+            if (u->sink->get_mute(u->sink, &mute) >= 0)
+                pa_sink_set_mute(u->sink, mute, false);
+        }
     }
 
     if ((data.volume_is_set || data.muted_is_set) && u->sink->write_volume)
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 425f4f0..c0759c6 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1277,18 +1277,17 @@ static void source_write_volume_cb(pa_source *s) {
     }
 }
 
-static void source_get_mute_cb(pa_source *s) {
+static int source_get_mute_cb(pa_source *s, bool *mute) {
     struct userdata *u = s->userdata;
-    bool b;
 
     pa_assert(u);
     pa_assert(u->mixer_path);
     pa_assert(u->mixer_handle);
 
-    if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0)
-        return;
+    if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0)
+        return -1;
 
-    s->muted = b;
+    return 0;
 }
 
 static void source_set_mute_cb(pa_source *s) {
@@ -2088,8 +2087,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
         if (u->source->set_mute)
             u->source->set_mute(u->source);
     } else {
-        if (u->source->get_mute)
-            u->source->get_mute(u->source);
+        if (u->source->get_mute) {
+            bool mute;
+
+            if (u->source->get_mute(u->source, &mute) >= 0)
+                pa_source_set_mute(u->source, mute, false);
+        }
     }
 
     if ((data.volume_is_set || data.muted_is_set) && u->source->write_volume)
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index b4fa734..71a98e9 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -571,18 +571,23 @@ static void sink_set_mute(pa_sink *s) {
     }
 }
 
-static void sink_get_mute(pa_sink *s) {
+static int sink_get_mute(pa_sink *s, bool *mute) {
     struct userdata *u = s->userdata;
     audio_info_t info;
 
     pa_assert(u);
 
-    if (u->fd >= 0) {
-        if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0)
-            pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
-        else
-            s->muted = !!info.output_muted;
+    if (u->fd < 0)
+        return -1;
+
+    if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0) {
+        pa_log("AUDIO_GETINFO: %s", pa_cstrerror(errno));
+        return -1;
     }
+
+    *mute = info.output_muted;
+
+    return 0;
 }
 
 static void process_rewind(struct userdata *u) {
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 94ddac9..a1cbfa5 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -508,7 +508,7 @@ void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
 }
 
-void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb) {
+void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_get_mute_cb_t cb) {
     pa_assert(s);
 
     s->get_mute = cb;
@@ -2228,21 +2228,15 @@ bool pa_sink_get_mute(pa_sink *s, bool force_refresh) {
     pa_assert_ctl_context();
     pa_assert(PA_SINK_IS_LINKED(s->state));
 
-    if (s->refresh_muted || force_refresh) {
-        bool old_muted = s->muted;
+    if ((s->refresh_muted || force_refresh) && s->get_mute) {
+        bool mute;
 
-        if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_mute)
-            s->get_mute(s);
-
-        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
-
-        if (old_muted != s->muted) {
-            s->save_muted = true;
-
-            pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-
-            /* Make sure the soft mute status stays in sync */
-            pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
+        if (s->flags & PA_SINK_DEFERRED_VOLUME) {
+            if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &mute, 0, NULL) >= 0)
+                pa_sink_mute_changed(s, mute);
+        } else {
+            if (s->get_mute(s, &mute) >= 0)
+                pa_sink_mute_changed(s, mute);
         }
     }
 
@@ -2750,7 +2744,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
         case PA_SINK_MESSAGE_GET_MUTE:
 
             if (s->flags & PA_SINK_DEFERRED_VOLUME && s->get_mute)
-                s->get_mute(s);
+                return s->get_mute(s, userdata);
 
             return 0;
 
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index ff97bda..9ff5360 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -58,6 +58,8 @@ static inline bool PA_SINK_IS_LINKED(pa_sink_state_t x) {
 /* A generic definition for void callback functions */
 typedef void(*pa_sink_cb_t)(pa_sink *s);
 
+typedef int (*pa_sink_get_mute_cb_t)(pa_sink *s, bool *mute);
+
 struct pa_sink {
     pa_msgobject parent;
 
@@ -191,14 +193,24 @@ struct pa_sink {
      * set this callback. */
     pa_sink_cb_t write_volume; /* may be NULL */
 
-    /* Called when the mute setting is queried. A PA_SINK_MESSAGE_GET_MUTE
-     * message will also be sent. Called from IO thread if PA_SINK_DEFERRED_VOLUME
-     * flag is set otherwise from main loop context. If refresh_mute is false
-     * neither this function is called nor a message is sent.
+    /* If the sink mute can change "spontaneously" (i.e. initiated by the sink
+     * implementation, not by someone else calling pa_sink_set_mute()), then
+     * the sink implementation can notify about changed mute either by calling
+     * pa_sink_mute_changed() or by calling pa_sink_get_mute() with
+     * force_refresh=true. If the implementation chooses the latter approach,
+     * it should implement the get_mute callback. Otherwise get_mute can be
+     * NULL.
+     *
+     * This is called when pa_sink_get_mute() is called with
+     * force_refresh=true. This is called from the IO thread if the
+     * PA_SINK_DEFERRED_VOLUME flag is set, otherwise this is called from the
+     * main thread. On success, the implementation is expected to return 0 and
+     * set the mute parameter that is passed as a reference. On failure, the
+     * implementation is expected to return -1.
      *
      * You must use the function pa_sink_set_get_mute_callback() to
      * set this callback. */
-    pa_sink_cb_t get_mute; /* may be NULL */
+    pa_sink_get_mute_cb_t get_mute;
 
     /* Called when the mute setting shall be changed. A PA_SINK_MESSAGE_SET_MUTE
      * message will also be sent. Called from IO thread if PA_SINK_DEFERRED_VOLUME
@@ -379,7 +391,7 @@ pa_sink* pa_sink_new(
 void pa_sink_set_get_volume_callback(pa_sink *s, pa_sink_cb_t cb);
 void pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb);
 void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb);
-void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb);
+void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_get_mute_cb_t cb);
 void pa_sink_set_set_mute_callback(pa_sink *s, pa_sink_cb_t cb);
 void pa_sink_enable_decibel_volume(pa_sink *s, bool enable);
 
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 041375a..5bbd7f1 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -459,7 +459,7 @@ void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb) {
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
 }
 
-void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb) {
+void pa_source_set_get_mute_callback(pa_source *s, pa_source_get_mute_cb_t cb) {
     pa_assert(s);
 
     s->get_mute = cb;
@@ -1821,21 +1821,15 @@ bool pa_source_get_mute(pa_source *s, bool force_refresh) {
     pa_assert_ctl_context();
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
-    if (s->refresh_muted || force_refresh) {
-        bool old_muted = s->muted;
+    if ((s->refresh_muted || force_refresh) && s->get_mute) {
+        bool mute;
 
-        if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->get_mute)
-            s->get_mute(s);
-
-        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
-
-        if (old_muted != s->muted) {
-            s->save_muted = true;
-
-            pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-
-            /* Make sure the soft mute status stays in sync */
-            pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
+        if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
+            if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &mute, 0, NULL) >= 0)
+                pa_source_mute_changed(s, mute);
+        } else {
+            if (s->get_mute(s, &mute) >= 0)
+                pa_source_mute_changed(s, mute);
         }
     }
 
@@ -2120,7 +2114,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
         case PA_SOURCE_MESSAGE_GET_MUTE:
 
             if (s->flags & PA_SOURCE_DEFERRED_VOLUME && s->get_mute)
-                s->get_mute(s);
+                return s->get_mute(s, userdata);
 
             return 0;
 
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index ca2ed59..1c9ecec 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -58,6 +58,8 @@ static inline bool PA_SOURCE_IS_LINKED(pa_source_state_t x) {
 /* A generic definition for void callback functions */
 typedef void(*pa_source_cb_t)(pa_source *s);
 
+typedef int (*pa_source_get_mute_cb_t)(pa_source *s, bool *mute);
+
 struct pa_source {
     pa_msgobject parent;
 
@@ -158,14 +160,24 @@ struct pa_source {
      * set this callback. */
     pa_source_cb_t write_volume; /* may be NULL */
 
-    /* Called when the mute setting is queried. Called from main loop
-     * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message
-     * will be sent to the IO thread instead. If refresh_mute is
-     * false neither this function is called nor a message is sent.
+    /* If the source mute can change "spontaneously" (i.e. initiated by the
+     * source implementation, not by someone else calling
+     * pa_source_set_mute()), then the source implementation can notify about
+     * changed mute either by calling pa_source_mute_changed() or by calling
+     * pa_source_get_mute() with force_refresh=true. If the implementation
+     * chooses the latter approach, it should implement the get_mute callback.
+     * Otherwise get_mute can be NULL.
+     *
+     * This is called when pa_source_get_mute() is called with
+     * force_refresh=true. This is called from the IO thread if the
+     * PA_SINK_DEFERRED_VOLUME flag is set, otherwise this is called from the
+     * main thread. On success, the implementation is expected to return 0 and
+     * set the mute parameter that is passed as a reference. On failure, the
+     * implementation is expected to return -1.
      *
      * You must use the function pa_source_set_get_mute_callback() to
      * set this callback. */
-    pa_source_cb_t get_mute; /* may be NULL */
+    pa_source_get_mute_cb_t get_mute;
 
     /* Called when the mute setting shall be changed. Called from main
      * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
@@ -316,7 +328,7 @@ pa_source* pa_source_new(
 void pa_source_set_get_volume_callback(pa_source *s, pa_source_cb_t cb);
 void pa_source_set_set_volume_callback(pa_source *s, pa_source_cb_t cb);
 void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb);
-void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb);
+void pa_source_set_get_mute_callback(pa_source *s, pa_source_get_mute_cb_t cb);
 void pa_source_set_set_mute_callback(pa_source *s, pa_source_cb_t cb);
 void pa_source_enable_decibel_volume(pa_source *s, bool enable);
 

commit dbd2a8f851437a79f62e992a52476056588c9780
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:09 2014 +0300

    sink, source: Call set_mute() from mute_changed()
    
    This refactoring reduces duplication, as mute_changed() used to do the
    same things as set_mute(). Other benefits are improved logging
    (set_mute() logs the mute change, mute_changed() used to not do that)
    and the soft mute state is kept up to date, because set_mute() sends
    the SET_MUTE message to the IO thread.
    
    The set_mute_in_progress flag is an extra precaution for preventing
    recursion in case a sink/source implementation's set_mute() callback
    causes mute_changed() to be called. Currently there are no such
    implementations, but I think that would be a valid thing to do, so
    some day there might be such implementation.

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 18b2848..94ddac9 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -2207,8 +2207,11 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
     s->muted = mute;
     s->save_muted = save;
 
-    if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute)
+    if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute) {
+        s->set_mute_in_progress = true;
         s->set_mute(s);
+        s->set_mute_in_progress = false;
+    }
 
     if (!PA_SINK_IS_LINKED(s->state))
         return;
@@ -2252,15 +2255,17 @@ void pa_sink_mute_changed(pa_sink *s, bool new_muted) {
     pa_assert_ctl_context();
     pa_assert(PA_SINK_IS_LINKED(s->state));
 
-    /* The sink implementor may call this if the volume changed to make sure everyone is notified */
-
-    if (s->muted == new_muted)
+    if (s->set_mute_in_progress)
         return;
 
-    s->muted = new_muted;
-    s->save_muted = true;
+    /* pa_sink_set_mute() does this same check, so this may appear redundant,
+     * but we must have this here also, because the save parameter of
+     * pa_sink_set_mute() would otherwise have unintended side effects (saving
+     * the mute state when it shouldn't be saved). */
+    if (new_muted == s->muted)
+        return;
 
-    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_sink_set_mute(s, new_muted, true);
 }
 
 /* Called from main thread */
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 3c796f0..ff97bda 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -118,6 +118,8 @@ struct pa_sink {
 
     unsigned priority;
 
+    bool set_mute_in_progress;
+
     /* Called when the main loop requests a state change. Called from
      * main loop context. If returns -1 the state change will be
      * inhibited */
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index bf7e977..041375a 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1800,8 +1800,11 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
     s->muted = mute;
     s->save_muted = save;
 
-    if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute)
+    if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute) {
+        s->set_mute_in_progress = true;
         s->set_mute(s);
+        s->set_mute_in_progress = false;
+    }
 
     if (!PA_SOURCE_IS_LINKED(s->state))
         return;
@@ -1845,15 +1848,17 @@ void pa_source_mute_changed(pa_source *s, bool new_muted) {
     pa_assert_ctl_context();
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
-    /* The source implementor may call this if the mute state changed to make sure everyone is notified */
-
-    if (s->muted == new_muted)
+    if (s->set_mute_in_progress)
         return;
 
-    s->muted = new_muted;
-    s->save_muted = true;
+    /* pa_source_set_mute() does this same check, so this may appear redundant,
+     * but we must have this here also, because the save parameter of
+     * pa_source_set_mute() would otherwise have unintended side effects
+     * (saving the mute state when it shouldn't be saved). */
+    if (new_muted == s->muted)
+        return;
 
-    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_source_set_mute(s, new_muted, true);
 }
 
 /* Called from main thread */
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 6318595..ca2ed59 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -118,6 +118,8 @@ struct pa_source {
 
     unsigned priority;
 
+    bool set_mute_in_progress;
+
     /* Called when the main loop requests a state change. Called from
      * main loop context. If returns -1 the state change will be
      * inhibited */

commit c93cfc1ca64a3d7e37a3ca63f42abb73ce72bfb6
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:08 2014 +0300

    echo-cancel: Remove redundant get_mute() callback
    
    The callback just called pa_source_output_get_mute(), which doesn't
    have any side effects, and the return value wasn't used either, so
    the callback was essentially a no-op.

diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index fbdb3b3..9df2899 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -646,20 +646,6 @@ static void sink_set_mute_cb(pa_sink *s) {
     pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
 }
 
-/* Called from main context */
-static void source_get_mute_cb(pa_source *s) {
-    struct userdata *u;
-
-    pa_source_assert_ref(s);
-    pa_assert_se(u = s->userdata);
-
-    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
-        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
-        return;
-
-    pa_source_output_get_mute(u->source_output);
-}
-
 /* Called from source I/O thread context. */
 static void apply_diff_time(struct userdata *u, int64_t diff_time) {
     int64_t diff;
@@ -1810,7 +1796,6 @@ int pa__init(pa_module*m) {
     u->source->parent.process_msg = source_process_msg_cb;
     u->source->set_state = source_set_state_cb;
     u->source->update_requested_latency = source_update_requested_latency_cb;
-    pa_source_set_get_mute_callback(u->source, source_get_mute_cb);
     pa_source_set_set_mute_callback(u->source, source_set_mute_cb);
     if (!u->use_volume_sharing) {
         pa_source_set_get_volume_callback(u->source, source_get_volume_cb);

commit 5f64ebdfc57f1a5728a7f84d9c69d3a815f467f1
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:07 2014 +0300

    sink, source: Allow calling set_mute() during initialization
    
    Currently the alsa sink and source write directly to s->muted during
    initialization, but I think it's better to avoid direct writes, and
    use the set_mute() function instead, because that makes it easier to
    figure out where s->muted is modified. This patch prevents the
    set_mute() call from crashing in the state assertion.

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 210f3dc..18b2848 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -2196,7 +2196,6 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
 
     pa_sink_assert_ref(s);
     pa_assert_ctl_context();
-    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     old_muted = s->muted;
 
@@ -2211,6 +2210,9 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
     if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute)
         s->set_mute(s);
 
+    if (!PA_SINK_IS_LINKED(s->state))
+        return;
+
     pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index da0dc68..bf7e977 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1789,7 +1789,6 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
 
     pa_source_assert_ref(s);
     pa_assert_ctl_context();
-    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     old_muted = s->muted;
 
@@ -1804,6 +1803,9 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
     if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute)
         s->set_mute(s);
 
+    if (!PA_SOURCE_IS_LINKED(s->state))
+        return;
+
     pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);

commit 080bdf1b6b1be0df0a77f77838594345481db072
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:06 2014 +0300

    sink-input, source-output: Add logging to set_mute()

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 451ca63..e41afc9 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1399,16 +1399,22 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool
 
 /* Called from main context */
 void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
+    bool old_mute;
+
     pa_sink_input_assert_ref(i);
     pa_assert_ctl_context();
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
 
-    if (mute == i->muted) {
+    old_mute = i->muted;
+
+    if (mute == old_mute) {
         i->save_muted |= save;
         return;
     }
 
     i->muted = mute;
+    pa_log_debug("The mute of sink input %u changed from %s to %s.", i->index, pa_yes_no(old_mute), pa_yes_no(mute));
+
     i->save_muted = save;
 
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 9709d77..bb89384 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -1057,16 +1057,22 @@ pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume,
 
 /* Called from main context */
 void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
+    bool old_mute;
+
     pa_source_output_assert_ref(o);
     pa_assert_ctl_context();
     pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
 
-    if (mute == o->muted) {
+    old_mute = o->muted;
+
+    if (mute == old_mute) {
         o->save_muted |= save;
         return;
     }
 
     o->muted = mute;
+    pa_log_debug("The mute of source output %u changed from %s to %s.", o->index, pa_yes_no(old_mute), pa_yes_no(mute));
+
     o->save_muted = save;
 
     pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);

commit 70441d40fb07b13cca85d08fe09c9ddb81eadaf0
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:05 2014 +0300

    sink, source: Return early from set_mute()
    
    This avoids redundant set_mute() callback calls.
    
    Some logging was added too.

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 7dffedf..210f3dc 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -2199,16 +2199,21 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
     pa_assert(PA_SINK_IS_LINKED(s->state));
 
     old_muted = s->muted;
+
+    if (mute == old_muted) {
+        s->save_muted |= save;
+        return;
+    }
+
     s->muted = mute;
-    s->save_muted = (old_muted == s->muted && s->save_muted) || save;
+    s->save_muted = save;
 
     if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute)
         s->set_mute(s);
 
+    pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
-
-    if (old_muted != s->muted)
-        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
 }
 
 /* Called from main thread */
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 5c1847e..da0dc68 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1792,16 +1792,21 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     old_muted = s->muted;
+
+    if (mute == old_muted) {
+        s->save_muted |= save;
+        return;
+    }
+
     s->muted = mute;
-    s->save_muted = (old_muted == s->muted && s->save_muted) || save;
+    s->save_muted = save;
 
     if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute)
         s->set_mute(s);
 
+    pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
-
-    if (old_muted != s->muted)
-        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
 }
 
 /* Called from main thread */

commit 7ac850d3b7ce803044b58a357b4e27730cf53bc7
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:04 2014 +0300

    sink-input, source-output: Assign to volume from only one place
    
    Forcing all volume changes to go through set_volume_direct() makes
    it easier to check where the stream volume is changed, and it also
    allows us to have only one place where notifications for changed
    volume are sent.

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 1754a8a..451ca63 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1260,7 +1260,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s
         return;
     }
 
-    i->volume = *volume;
+    pa_sink_input_set_volume_direct(i, volume);
     i->save_volume = save;
 
     if (pa_sink_flat_volume_enabled(i->sink)) {
@@ -1278,13 +1278,6 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s
         /* Copy the new soft_volume to the thread_info struct */
         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
     }
-
-    /* The volume changed, let's tell people so */
-    if (i->volume_changed)
-        i->volume_changed(i);
-
-    /* The virtual volume changed, let's tell people so */
-    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 }
 
 void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor) {
@@ -1631,7 +1624,6 @@ int pa_sink_input_start_move(pa_sink_input *i) {
  * then also the origin sink and all streams connected to it need to update
  * their volume - this function does all that by using recursion. */
 static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
-    pa_cvolume old_volume;
     pa_cvolume new_volume;
 
     pa_assert(i);
@@ -1681,19 +1673,11 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
              *          always have volume_factor as soft_volume, so no change
              *          should be needed) */
 
-            old_volume = i->volume;
-            pa_cvolume_reset(&i->volume, i->volume.channels);
+            pa_cvolume_reset(&new_volume, i->volume.channels);
+            pa_sink_input_set_volume_direct(i, &new_volume);
             pa_cvolume_reset(&i->reference_ratio, i->reference_ratio.channels);
             pa_assert(pa_cvolume_is_norm(&i->real_ratio));
             pa_assert(pa_cvolume_equal(&i->soft_volume, &i->volume_factor));
-
-            /* Notify others about the changed sink input volume. */
-            if (!pa_cvolume_equal(&i->volume, &old_volume)) {
-                if (i->volume_changed)
-                    i->volume_changed(i);
-
-                pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-            }
         }
 
         /* Additionally, the origin sink volume needs updating:
@@ -1725,8 +1709,6 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
             update_volume_due_to_moving(origin_sink_input, dest);
 
     } else {
-        old_volume = i->volume;
-
         if (pa_sink_flat_volume_enabled(i->sink)) {
             /* Ok, so this is a regular stream, and flat volume is enabled. The
              * volume will have to be updated as follows:
@@ -1738,9 +1720,10 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
              *     i->soft_volume := i->real_ratio * i->volume_factor
              *         (handled later by pa_sink_set_volume) */
 
-            i->volume = i->sink->reference_volume;
-            pa_cvolume_remap(&i->volume, &i->sink->channel_map, &i->channel_map);
-            pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
+            new_volume = i->sink->reference_volume;
+            pa_cvolume_remap(&new_volume, &i->sink->channel_map, &i->channel_map);
+            pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
+            pa_sink_input_set_volume_direct(i, &new_volume);
 
         } else {
             /* Ok, so this is a regular stream, and flat volume is disabled.
@@ -1751,21 +1734,10 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
              *     i->real_ratio := i->reference_ratio
              *     i->soft_volume := i->real_ratio * i->volume_factor */
 
-            i->volume = i->reference_ratio;
+            pa_sink_input_set_volume_direct(i, &i->reference_ratio);
             i->real_ratio = i->reference_ratio;
             pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
         }
-
-        /* Notify others about the changed sink input volume. */
-        if (!pa_cvolume_equal(&i->volume, &old_volume)) {
-            /* XXX: In case i->sink has flat volume enabled, then real_ratio
-             * and soft_volume are not updated yet. Let's hope that the
-             * callback implementation doesn't care about those variables... */
-            if (i->volume_changed)
-                i->volume_changed(i);
-
-            pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-        }
     }
 
     /* If i->sink == dest, then recursion has finished, and we can finally call
@@ -2242,3 +2214,28 @@ int pa_sink_input_update_rate(pa_sink_input *i) {
 
     return 0;
 }
+
+/* Called from the main thread. */
+void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume) {
+    pa_cvolume old_volume;
+    char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(i);
+    pa_assert(volume);
+
+    old_volume = i->volume;
+
+    if (pa_cvolume_equal(volume, &old_volume))
+        return;
+
+    i->volume = *volume;
+    pa_log_debug("The volume of sink input %u changed from %s to %s.", i->index,
+                 pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &i->channel_map, true),
+                 pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &i->channel_map, true));
+
+    if (i->volume_changed)
+        i->volume_changed(i);
+
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index da33717..2be2c33 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -417,6 +417,13 @@ bool pa_sink_input_process_underrun(pa_sink_input *i);
 
 pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
 
+/* Called from the main thread, from sink.c only. The normal way to set the
+ * sink input volume is to call pa_sink_input_set_volume(), but the flat volume
+ * logic in sink.c needs also a function that doesn't do all the extra stuff
+ * that pa_sink_input_set_volume() does. This function simply sets i->volume
+ * and fires change notifications. */
+void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume);
+
 #define pa_sink_input_assert_io_context(s) \
     pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state))
 
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index c1cd2db..7dffedf 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1835,20 +1835,13 @@ static void update_real_volume(pa_sink *s, const pa_cvolume *new_volume, pa_chan
     PA_IDXSET_FOREACH(i, s->inputs, idx) {
         if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
             if (pa_sink_flat_volume_enabled(s)) {
-                pa_cvolume old_volume = i->volume;
+                pa_cvolume new_input_volume;
 
                 /* Follow the root sink's real volume. */
-                i->volume = *new_volume;
-                pa_cvolume_remap(&i->volume, channel_map, &i->channel_map);
+                new_input_volume = *new_volume;
+                pa_cvolume_remap(&new_input_volume, channel_map, &i->channel_map);
+                pa_sink_input_set_volume_direct(i, &new_input_volume);
                 compute_reference_ratio(i);
-
-                /* The volume changed, let's tell people so */
-                if (!pa_cvolume_equal(&old_volume, &i->volume)) {
-                    if (i->volume_changed)
-                        i->volume_changed(i);
-
-                    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-                }
             }
 
             update_real_volume(i->origin_sink, new_volume, channel_map);
@@ -1903,7 +1896,7 @@ static void propagate_reference_volume(pa_sink *s) {
      * sink input volumes accordingly */
 
     PA_IDXSET_FOREACH(i, s->inputs, idx) {
-        pa_cvolume old_volume;
+        pa_cvolume new_volume;
 
         if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
             propagate_reference_volume(i->origin_sink);
@@ -1914,24 +1907,14 @@ static void propagate_reference_volume(pa_sink *s) {
             continue;
         }
 
-        old_volume = i->volume;
-
         /* This basically calculates:
          *
          * i->volume := s->reference_volume * i->reference_ratio  */
 
-        i->volume = s->reference_volume;
-        pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map);
-        pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
-
-        /* The volume changed, let's tell people so */
-        if (!pa_cvolume_equal(&old_volume, &i->volume)) {
-
-            if (i->volume_changed)
-                i->volume_changed(i);
-
-            pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-        }
+        new_volume = s->reference_volume;
+        pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
+        pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
+        pa_sink_input_set_volume_direct(i, &new_volume);
     }
 }
 
@@ -2126,7 +2109,7 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume)
     if (pa_sink_flat_volume_enabled(s)) {
 
         PA_IDXSET_FOREACH(i, s->inputs, idx) {
-            pa_cvolume old_volume = i->volume;
+            pa_cvolume new_volume;
 
             /* 2. Since the sink's reference and real volumes are equal
              * now our ratios should be too. */
@@ -2140,18 +2123,10 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume)
              * i->volume = s->reference_volume * i->reference_ratio
              *
              * This is identical to propagate_reference_volume() */
-            i->volume = s->reference_volume;
-            pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map);
-            pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
-
-            /* Notify if something changed */
-            if (!pa_cvolume_equal(&old_volume, &i->volume)) {
-
-                if (i->volume_changed)
-                    i->volume_changed(i);
-
-                pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-            }
+            new_volume = s->reference_volume;
+            pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
+            pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
+            pa_sink_input_set_volume_direct(i, &new_volume);
 
             if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
                 propagate_real_volume(i->origin_sink, old_real_volume);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 4323bcf..9709d77 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -972,7 +972,7 @@ void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume,
         return;
     }
 
-    o->volume = *volume;
+    pa_source_output_set_volume_direct(o, volume);
     o->save_volume = save;
 
     if (pa_source_flat_volume_enabled(o->source)) {
@@ -1262,7 +1262,6 @@ int pa_source_output_start_move(pa_source_output *o) {
  * then also the origin source and all streams connected to it need to update
  * their volume - this function does all that by using recursion. */
 static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
-    pa_cvolume old_volume;
     pa_cvolume new_volume;
 
     pa_assert(o);
@@ -1314,19 +1313,11 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
              *          always have volume_factor as soft_volume, so no change
              *          should be needed) */
 
-            old_volume = o->volume;
-            pa_cvolume_reset(&o->volume, o->volume.channels);
+            pa_cvolume_reset(&new_volume, o->volume.channels);
+            pa_source_output_set_volume_direct(o, &new_volume);
             pa_cvolume_reset(&o->reference_ratio, o->reference_ratio.channels);
             pa_assert(pa_cvolume_is_norm(&o->real_ratio));
             pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
-
-            /* Notify others about the changed source output volume. */
-            if (!pa_cvolume_equal(&o->volume, &old_volume)) {
-                if (o->volume_changed)
-                    o->volume_changed(o);
-
-                pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-            }
         }
 
         /* Additionally, the origin source volume needs updating:
@@ -1358,8 +1349,6 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
             update_volume_due_to_moving(destination_source_output, dest);
 
     } else {
-        old_volume = o->volume;
-
         if (pa_source_flat_volume_enabled(o->source)) {
             /* Ok, so this is a regular stream, and flat volume is enabled. The
              * volume will have to be updated as follows:
@@ -1371,9 +1360,10 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
              *     o->soft_volume := o->real_ratio * o->volume_factor
              *         (handled later by pa_source_set_volume) */
 
-            o->volume = o->source->reference_volume;
-            pa_cvolume_remap(&o->volume, &o->source->channel_map, &o->channel_map);
-            pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
+            new_volume = o->source->reference_volume;
+            pa_cvolume_remap(&new_volume, &o->source->channel_map, &o->channel_map);
+            pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
+            pa_source_output_set_volume_direct(o, &new_volume);
 
         } else {
             /* Ok, so this is a regular stream, and flat volume is disabled.
@@ -1384,21 +1374,10 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
              *     o->real_ratio := o->reference_ratio
              *     o->soft_volume := o->real_ratio * o->volume_factor */
 
-            o->volume = o->reference_ratio;
+            pa_source_output_set_volume_direct(o, &o->reference_ratio);
             o->real_ratio = o->reference_ratio;
             pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
         }
-
-        /* Notify others about the changed source output volume. */
-        if (!pa_cvolume_equal(&o->volume, &old_volume)) {
-            /* XXX: In case o->source has flat volume enabled, then real_ratio
-             * and soft_volume are not updated yet. Let's hope that the
-             * callback implementation doesn't care about those variables... */
-            if (o->volume_changed)
-                o->volume_changed(o);
-
-            pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-        }
     }
 
     /* If o->source == dest, then recursion has finished, and we can finally call
@@ -1692,3 +1671,28 @@ int pa_source_output_update_rate(pa_source_output *o) {
 
     return 0;
 }
+
+/* Called from the main thread. */
+void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume) {
+    pa_cvolume old_volume;
+    char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(o);
+    pa_assert(volume);
+
+    old_volume = o->volume;
+
+    if (pa_cvolume_equal(volume, &old_volume))
+        return;
+
+    o->volume = *volume;
+    pa_log_debug("The volume of source output %u changed from %s to %s.", o->index,
+                 pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &o->channel_map, true),
+                 pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &o->channel_map, true));
+
+    if (o->volume_changed)
+        o->volume_changed(o);
+
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 102fb8b..27d6fd4 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -353,6 +353,13 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
 
 pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
 
+/* Called from the main thread, from source.c only. The normal way to set the
+ * source output volume is to call pa_source_output_set_volume(), but the flat
+ * volume logic in source.c needs also a function that doesn't do all the extra
+ * stuff that pa_source_output_set_volume() does. This function simply sets
+ * o->volume and fires change notifications. */
+void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume);
+
 #define pa_source_output_assert_io_context(s) \
     pa_assert(pa_thread_mq_get() || !PA_SOURCE_OUTPUT_IS_LINKED((s)->state))
 
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index c0bc1c9..5c1847e 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1429,20 +1429,13 @@ static void update_real_volume(pa_source *s, const pa_cvolume *new_volume, pa_ch
     PA_IDXSET_FOREACH(o, s->outputs, idx) {
         if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
             if (pa_source_flat_volume_enabled(s)) {
-                pa_cvolume old_volume = o->volume;
+                pa_cvolume new_output_volume;
 
                 /* Follow the root source's real volume. */
-                o->volume = *new_volume;
-                pa_cvolume_remap(&o->volume, channel_map, &o->channel_map);
+                new_output_volume = *new_volume;
+                pa_cvolume_remap(&new_output_volume, channel_map, &o->channel_map);
+                pa_source_output_set_volume_direct(o, &new_output_volume);
                 compute_reference_ratio(o);
-
-                /* The volume changed, let's tell people so */
-                if (!pa_cvolume_equal(&old_volume, &o->volume)) {
-                    if (o->volume_changed)
-                        o->volume_changed(o);
-
-                    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-                }
             }
 
             update_real_volume(o->destination_source, new_volume, channel_map);
@@ -1497,7 +1490,7 @@ static void propagate_reference_volume(pa_source *s) {
      * source output volumes accordingly */
 
     PA_IDXSET_FOREACH(o, s->outputs, idx) {
-        pa_cvolume old_volume;
+        pa_cvolume new_volume;
 
         if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
             propagate_reference_volume(o->destination_source);
@@ -1508,24 +1501,14 @@ static void propagate_reference_volume(pa_source *s) {
             continue;
         }
 
-        old_volume = o->volume;
-
         /* This basically calculates:
          *
          * o->volume := o->reference_volume * o->reference_ratio  */
 
-        o->volume = s->reference_volume;
-        pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map);
-        pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
-
-        /* The volume changed, let's tell people so */
-        if (!pa_cvolume_equal(&old_volume, &o->volume)) {
-
-            if (o->volume_changed)
-                o->volume_changed(o);
-
-            pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-        }
+        new_volume = s->reference_volume;
+        pa_cvolume_remap(&new_volume, &s->channel_map, &o->channel_map);
+        pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
+        pa_source_output_set_volume_direct(o, &new_volume);
     }
 }
 
@@ -1718,9 +1701,8 @@ static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volum
     }
 
     if (pa_source_flat_volume_enabled(s)) {
-
         PA_IDXSET_FOREACH(o, s->outputs, idx) {
-            pa_cvolume old_volume = o->volume;
+            pa_cvolume new_volume;
 
             /* 2. Since the source's reference and real volumes are equal
              * now our ratios should be too. */
@@ -1734,18 +1716,10 @@ static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volum
              * o->volume = s->reference_volume * o->reference_ratio
              *
              * This is identical to propagate_reference_volume() */
-            o->volume = s->reference_volume;
-            pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map);
-            pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
-
-            /* Notify if something changed */
-            if (!pa_cvolume_equal(&old_volume, &o->volume)) {
-
-                if (o->volume_changed)
-                    o->volume_changed(o);
-
-                pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-            }
+            new_volume = s->reference_volume;
+            pa_cvolume_remap(&new_volume, &s->channel_map, &o->channel_map);
+            pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
+            pa_source_output_set_volume_direct(o, &new_volume);
 
             if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
                 propagate_real_volume(o->destination_source, old_real_volume);

commit fb70fa22c36f9220e8f4424948d0af476fb3d7a9
Author: Tanu Kaskinen <tanu.kaskinen at linux.intel.com>
Date:   Tue Apr 15 13:56:03 2014 +0300

    sink, source: Assign to reference_volume from only one place
    
    Forcing all reference volume changes to go through
    set_reference_volume_direct() makes it easier to check where the
    reference volume is changed, and it also allows us to have only one
    place where notifications for changed reference volume are sent.

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 4d685c3..1754a8a 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1632,6 +1632,7 @@ int pa_sink_input_start_move(pa_sink_input *i) {
  * their volume - this function does all that by using recursion. */
 static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
     pa_cvolume old_volume;
+    pa_cvolume new_volume;
 
     pa_assert(i);
     pa_assert(dest);
@@ -1703,25 +1704,21 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
          *         (sinks that use volume sharing should always have
          *          soft_volume of 0 dB) */
 
-        old_volume = i->origin_sink->reference_volume;
-
-        i->origin_sink->reference_volume = root_sink->reference_volume;
-        pa_cvolume_remap(&i->origin_sink->reference_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
+        new_volume = root_sink->reference_volume;
+        pa_cvolume_remap(&new_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
+        pa_sink_set_reference_volume_direct(i->origin_sink, &new_volume);
 
         i->origin_sink->real_volume = root_sink->real_volume;
         pa_cvolume_remap(&i->origin_sink->real_volume, &root_sink->channel_map, &i->origin_sink->channel_map);
 
         pa_assert(pa_cvolume_is_norm(&i->origin_sink->soft_volume));
 
-        /* Notify others about the changed sink volume. If you wonder whether
-         * i->origin_sink->set_volume() should be called somewhere, that's not
-         * the case, because sinks that use volume sharing shouldn't have any
-         * internal volume that set_volume() would update. If you wonder
-         * whether the thread_info variables should be synced, yes, they
-         * should, and it's done by the PA_SINK_MESSAGE_FINISH_MOVE message
-         * handler. */
-        if (!pa_cvolume_equal(&i->origin_sink->reference_volume, &old_volume))
-            pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, i->origin_sink->index);
+        /* If you wonder whether i->origin_sink->set_volume() should be called
+         * somewhere, that's not the case, because sinks that use volume
+         * sharing shouldn't have any internal volume that set_volume() would
+         * update. If you wonder whether the thread_info variables should be
+         * synced, yes, they should, and it's done by the
+         * PA_SINK_MESSAGE_FINISH_MOVE message handler. */
 
         /* Recursively update origin sink inputs. */
         PA_IDXSET_FOREACH(origin_sink_input, i->origin_sink->inputs, idx)
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index e308b3e..c1cd2db 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1954,13 +1954,11 @@ static bool update_reference_volume(pa_sink *s, const pa_cvolume *v, const pa_ch
     pa_cvolume_remap(&volume, channel_map, &s->channel_map);
 
     reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
-    s->reference_volume = volume;
+    pa_sink_set_reference_volume_direct(s, &volume);
 
     s->save_volume = (!reference_volume_changed && s->save_volume) || save;
 
-    if (reference_volume_changed)
-        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-    else if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
+    if (!reference_volume_changed && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
         /* If the root sink's volume doesn't change, then there can't be any
          * changes in the other sinks in the sink tree either.
          *
@@ -3798,3 +3796,27 @@ done:
 
     return out_formats;
 }
+
+/* Called from the main thread. */
+void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
+    pa_cvolume old_volume;
+    char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(s);
+    pa_assert(volume);
+
+    old_volume = s->reference_volume;
+
+    if (pa_cvolume_equal(volume, &old_volume))
+        return;
+
+    s->reference_volume = *volume;
+    pa_log_debug("The reference volume of sink %s changed from %s to %s.", s->name,
+                 pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map,
+                                            s->flags & PA_SINK_DECIBEL_VOLUME),
+                 pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map,
+                                            s->flags & PA_SINK_DECIBEL_VOLUME));
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 87b464e..3c796f0 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -503,6 +503,13 @@ void pa_sink_invalidate_requested_latency(pa_sink *s, bool dynamic);
 
 pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s);
 
+/* Called from the main thread, from sink-input.c only. The normal way to set
+ * the sink reference volume is to call pa_sink_set_volume(), but the flat
+ * volume logic in sink-input.c needs also a function that doesn't do all the
+ * extra stuff that pa_sink_set_volume() does. This function simply sets
+ * s->reference_volume and fires change notifications. */
+void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume);
+
 /* Verify that we called in IO context (aka 'thread context), or that
  * the sink is not yet set up, i.e. the thread not set up yet. See
  * pa_assert_io_context() in thread-mq.h for more information. */
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 34a4cb0..4323bcf 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -1263,6 +1263,7 @@ int pa_source_output_start_move(pa_source_output *o) {
  * their volume - this function does all that by using recursion. */
 static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
     pa_cvolume old_volume;
+    pa_cvolume new_volume;
 
     pa_assert(o);
     pa_assert(dest);
@@ -1336,25 +1337,21 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
          *         (sources that use volume sharing should always have
          *          soft_volume of 0 dB) */
 
-        old_volume = o->destination_source->reference_volume;
-
-        o->destination_source->reference_volume = root_source->reference_volume;
-        pa_cvolume_remap(&o->destination_source->reference_volume, &root_source->channel_map, &o->destination_source->channel_map);
+        new_volume = root_source->reference_volume;
+        pa_cvolume_remap(&new_volume, &root_source->channel_map, &o->destination_source->channel_map);
+        pa_source_set_reference_volume_direct(o->destination_source, &new_volume);
 
         o->destination_source->real_volume = root_source->real_volume;
         pa_cvolume_remap(&o->destination_source->real_volume, &root_source->channel_map, &o->destination_source->channel_map);
 
         pa_assert(pa_cvolume_is_norm(&o->destination_source->soft_volume));
 
-        /* Notify others about the changed source volume. If you wonder whether
-         * o->destination_source->set_volume() should be called somewhere, that's not
-         * the case, because sources that use volume sharing shouldn't have any
-         * internal volume that set_volume() would update. If you wonder
-         * whether the thread_info variables should be synced, yes, they
-         * should, and it's done by the PA_SOURCE_MESSAGE_FINISH_MOVE message
-         * handler. */
-        if (!pa_cvolume_equal(&o->destination_source->reference_volume, &old_volume))
-            pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, o->destination_source->index);
+        /* If you wonder whether o->destination_source->set_volume() should be
+         * called somewhere, that's not the case, because sources that use
+         * volume sharing shouldn't have any internal volume that set_volume()
+         * would update. If you wonder whether the thread_info variables should
+         * be synced, yes, they should, and it's done by the
+         * PA_SOURCE_MESSAGE_FINISH_MOVE message handler. */
 
         /* Recursively update origin source outputs. */
         PA_IDXSET_FOREACH(destination_source_output, o->destination_source->outputs, idx)
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 6745344..c0bc1c9 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1548,13 +1548,11 @@ static bool update_reference_volume(pa_source *s, const pa_cvolume *v, const pa_
     pa_cvolume_remap(&volume, channel_map, &s->channel_map);
 
     reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
-    s->reference_volume = volume;
+    pa_source_set_reference_volume_direct(s, &volume);
 
     s->save_volume = (!reference_volume_changed && s->save_volume) || save;
 
-    if (reference_volume_changed)
-        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-    else if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+    if (!reference_volume_changed && !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
         /* If the root source's volume doesn't change, then there can't be any
          * changes in the other source in the source tree either.
          *
@@ -2868,3 +2866,27 @@ done:
 
     return out_formats;
 }
+
+/* Called from the main thread. */
+void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume) {
+    pa_cvolume old_volume;
+    char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+    pa_assert(s);
+    pa_assert(volume);
+
+    old_volume = s->reference_volume;
+
+    if (pa_cvolume_equal(volume, &old_volume))
+        return;
+
+    s->reference_volume = *volume;
+    pa_log_debug("The reference volume of source %s changed from %s to %s.", s->name,
+                 pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map,
+                                            s->flags & PA_SOURCE_DECIBEL_VOLUME),
+                 pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map,
+                                            s->flags & PA_SOURCE_DECIBEL_VOLUME));
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 5c74a51..6318595 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -426,6 +426,13 @@ bool pa_source_volume_change_apply(pa_source *s, pa_usec_t *usec_to_next);
 void pa_source_invalidate_requested_latency(pa_source *s, bool dynamic);
 pa_usec_t pa_source_get_latency_within_thread(pa_source *s);
 
+/* Called from the main thread, from source-output.c only. The normal way to
+ * set the source reference volume is to call pa_source_set_volume(), but the
+ * flat volume logic in source-output.c needs also a function that doesn't do
+ * all the extra stuff that pa_source_set_volume() does. This function simply
+ * sets s->reference_volume and fires change notifications. */
+void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume);
+
 #define pa_source_assert_io_context(s) \
     pa_assert(pa_thread_mq_get() || !PA_SOURCE_IS_LINKED((s)->state))
 



More information about the pulseaudio-commits mailing list