]> code.delx.au - pulseaudio/commitdiff
alsa-sink/source: Make sure volumes are synchronised after fast user switching
authorDavid Henningsson <david.henningsson@canonical.com>
Wed, 4 Apr 2012 15:07:13 +0000 (17:07 +0200)
committerDavid Henningsson <david.henningsson@canonical.com>
Wed, 11 Apr 2012 22:13:11 +0000 (00:13 +0200)
Log in as user A, fast user switch to user B, let user B change
port, volume or mute status, then switch back to user A.

At this point we must make sure that the ALSA and PA volumes are
synchronised by writing to the ALSA mixer when the ALSA device
becomes available.

BugLink: https://bugs.launchpad.net/bugs/915035
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
src/modules/alsa/alsa-sink.c
src/modules/alsa/alsa-source.c
src/pulsecore/sink.c
src/pulsecore/sink.h
src/pulsecore/source.c
src/pulsecore/source.h

index 65f12c21b968f90263d39743a8900a04987260fa..c3d18e39c8c7483598d32a18f5b1611a405a9d40 100644 (file)
@@ -1210,8 +1210,10 @@ static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     if (!PA_SINK_IS_LINKED(u->sink->state))
         return 0;
 
-    if (u->sink->suspend_cause & PA_SUSPEND_SESSION)
+    if (u->sink->suspend_cause & PA_SUSPEND_SESSION) {
+        pa_sink_set_mixer_dirty(u->sink, TRUE);
         return 0;
+    }
 
     if (mask & SND_CTL_EVENT_MASK_VALUE) {
         pa_sink_get_volume(u->sink, TRUE);
@@ -1230,8 +1232,10 @@ static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     if (mask == SND_CTL_EVENT_MASK_REMOVE)
         return 0;
 
-    if (u->sink->suspend_cause & PA_SUSPEND_SESSION)
+    if (u->sink->suspend_cause & PA_SUSPEND_SESSION) {
+        pa_sink_set_mixer_dirty(u->sink, TRUE);
         return 0;
+    }
 
     if (mask & SND_CTL_EVENT_MASK_VALUE)
         pa_sink_update_volume_and_mute(u->sink);
index ab7335d8d137d6dd5c47e7f6f2d82aa44951a525..97092bb5b2ddb641632a686146ed9a9fc8904989 100644 (file)
@@ -1113,8 +1113,10 @@ static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     if (!PA_SOURCE_IS_LINKED(u->source->state))
         return 0;
 
-    if (u->source->suspend_cause & PA_SUSPEND_SESSION)
+    if (u->source->suspend_cause & PA_SUSPEND_SESSION) {
+        pa_source_set_mixer_dirty(u->source, TRUE);
         return 0;
+    }
 
     if (mask & SND_CTL_EVENT_MASK_VALUE) {
         pa_source_get_volume(u->source, TRUE);
@@ -1133,8 +1135,10 @@ static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     if (mask == SND_CTL_EVENT_MASK_REMOVE)
         return 0;
 
-    if (u->source->suspend_cause & PA_SUSPEND_SESSION)
+    if (u->source->suspend_cause & PA_SUSPEND_SESSION) {
+        pa_source_set_mixer_dirty(u->source, TRUE);
         return 0;
+    }
 
     if (mask & SND_CTL_EVENT_MASK_VALUE)
         pa_source_update_volume_and_mute(u->source);
index 2d214cf0612fe5233fe9d7ea102d8cd06822ca26..e4c343da2867e619f4ea02aaac99464a4035cf67 100644 (file)
@@ -248,6 +248,7 @@ pa_sink* pa_sink_new(
     s->flags = flags;
     s->priority = 0;
     s->suspend_cause = 0;
+    pa_sink_set_mixer_dirty(s, FALSE);
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
     s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
@@ -795,6 +796,12 @@ int pa_sink_update_status(pa_sink*s) {
     return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
 }
 
+/* Called from any context - must be threadsafe */
+void pa_sink_set_mixer_dirty(pa_sink *s, pa_bool_t is_dirty)
+{
+    pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0);
+}
+
 /* Called from main context */
 int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
     pa_sink_assert_ref(s);
@@ -810,6 +817,27 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
         s->monitor_source->suspend_cause &= ~cause;
     }
 
+    if (!(s->suspend_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) {
+        /* This might look racy but isn't: If somebody sets mixer_dirty exactly here,
+           it'll be handled just fine. */
+        pa_sink_set_mixer_dirty(s, FALSE);
+        pa_log_debug("Mixer is now accessible. Updating alsa mixer settings.");
+        if (s->active_port && s->set_port) {
+            if (s->flags & PA_SINK_DEFERRED_VOLUME) {
+                struct sink_message_set_port msg = { .port = s->active_port, .ret = 0 };
+                pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
+            }
+            else
+                s->set_port(s, s->active_port);
+        }
+        else {
+            if (s->set_mute)
+                s->set_mute(s);
+            if (s->set_volume)
+                s->set_volume(s);
+        }
+    }
+
     if ((pa_sink_get_state(s) == PA_SINK_SUSPENDED) == !!s->suspend_cause)
         return 0;
 
index 56fa735a43ecebcfce6abe80f9e4191c0d2d0f40..9bc8047534d6c3414d3a0289d1bf90e992cd9a81 100644 (file)
@@ -111,6 +111,7 @@ struct pa_sink {
 
     pa_hashmap *ports;
     pa_device_port *active_port;
+    pa_atomic_t mixer_dirty;
 
     unsigned priority;
 
@@ -438,6 +439,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);
 pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
 
 int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save);
+void pa_sink_set_mixer_dirty(pa_sink *s, pa_bool_t is_dirty);
 
 unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
 unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
index a5d5af0b1a4cfac4effd7549d72580c4f69a99cc..6308f54d050cbe6ddb62412e49fbc892f0598132 100644 (file)
@@ -235,6 +235,7 @@ pa_source* pa_source_new(
     s->flags = flags;
     s->priority = 0;
     s->suspend_cause = 0;
+    pa_source_set_mixer_dirty(s, FALSE);
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
     s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
@@ -713,6 +714,12 @@ int pa_source_update_status(pa_source*s) {
     return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
 }
 
+/* Called from any context - must be threadsafe */
+void pa_source_set_mixer_dirty(pa_source *s, pa_bool_t is_dirty)
+{
+    pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0);
+}
+
 /* Called from main context */
 int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
     pa_source_assert_ref(s);
@@ -728,6 +735,27 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause)
     else
         s->suspend_cause &= ~cause;
 
+    if (!(s->suspend_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) {
+        /* This might look racy but isn't: If somebody sets mixer_dirty exactly here,
+           it'll be handled just fine. */
+        pa_source_set_mixer_dirty(s, FALSE);
+        pa_log_debug("Mixer is now accessible. Updating alsa mixer settings.");
+        if (s->active_port && s->set_port) {
+            if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
+                struct source_message_set_port msg = { .port = s->active_port, .ret = 0 };
+                pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
+            }
+            else
+                s->set_port(s, s->active_port);
+        }
+        else {
+            if (s->set_mute)
+                s->set_mute(s);
+            if (s->set_volume)
+                s->set_volume(s);
+        }
+    }
+
     if ((pa_source_get_state(s) == PA_SOURCE_SUSPENDED) == !!s->suspend_cause)
         return 0;
 
index e6f7028fd40276fa71ca9184d9fadbac5f52efd6..d81782aad240b79d73c8547f5d45561dfb1182eb 100644 (file)
@@ -111,6 +111,7 @@ struct pa_source {
 
     pa_hashmap *ports;
     pa_device_port *active_port;
+    pa_atomic_t mixer_dirty;
 
     unsigned priority;
 
@@ -368,6 +369,8 @@ pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
 pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p);
 
 int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save);
+void pa_source_set_mixer_dirty(pa_source *s, pa_bool_t is_dirty);
+
 pa_bool_t pa_source_update_rate(pa_source *s, uint32_t rate, pa_bool_t passthrough);
 
 unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */