]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/sink.c
core: Move pa_mix() into new file mix.c
[pulseaudio] / src / pulsecore / sink.c
index 2d214cf0612fe5233fe9d7ea102d8cd06822ca26..175cfe50f879ac25caf2fad723020834290f6fb8 100644 (file)
@@ -42,6 +42,7 @@
 #include <pulsecore/namereg.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/sample-util.h>
+#include <pulsecore/mix.h>
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
@@ -81,6 +82,7 @@ pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
 
     pa_zero(*data);
     data->proplist = pa_proplist_new();
+    data->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
     return data;
 }
@@ -247,7 +249,8 @@ pa_sink* pa_sink_new(
     s->state = PA_SINK_INIT;
     s->flags = flags;
     s->priority = 0;
-    s->suspend_cause = 0;
+    s->suspend_cause = data->suspend_cause;
+    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));
@@ -294,11 +297,11 @@ pa_sink* pa_sink_new(
     s->active_port = NULL;
     s->save_port = FALSE;
 
-    if (data->active_port && s->ports)
+    if (data->active_port)
         if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
             s->save_port = data->save_port;
 
-    if (!s->active_port && s->ports) {
+    if (!s->active_port) {
         void *state;
         pa_device_port *p;
 
@@ -307,6 +310,11 @@ pa_sink* pa_sink_new(
                 s->active_port = p;
     }
 
+    if (s->active_port)
+        s->latency_offset = s->active_port->latency_offset;
+    else
+        s->latency_offset = 0;
+
     s->save_volume = data->save_volume;
     s->save_muted = data->save_muted;
 
@@ -337,6 +345,7 @@ pa_sink* pa_sink_new(
     pa_sw_cvolume_multiply(&s->thread_info.current_hw_volume, &s->soft_volume, &s->real_volume);
     s->thread_info.volume_change_safety_margin = core->deferred_volume_safety_margin_usec;
     s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
+    s->thread_info.latency_offset = s->latency_offset;
 
     /* FIXME: This should probably be moved to pa_sink_put() */
     pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
@@ -642,7 +651,10 @@ void pa_sink_put(pa_sink* s) {
     pa_assert(s->monitor_source->thread_info.min_latency == s->thread_info.min_latency);
     pa_assert(s->monitor_source->thread_info.max_latency == s->thread_info.max_latency);
 
-    pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
+    if (s->suspend_cause)
+        pa_assert_se(sink_set_state(s, PA_SINK_SUSPENDED) == 0);
+    else
+        pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
 
     pa_source_put(s->monitor_source);
 
@@ -795,6 +807,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 +828,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;
 
@@ -900,9 +939,6 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
     s->thread_info.rewind_nbytes = 0;
     s->thread_info.rewind_requested = FALSE;
 
-    if (s->thread_info.state == PA_SINK_SUSPENDED)
-        return;
-
     if (nbytes > 0) {
         pa_log_debug("Processing rewind...");
         if (s->flags & PA_SINK_DEFERRED_VOLUME)
@@ -1354,9 +1390,13 @@ pa_bool_t pa_sink_update_rate(pa_sink *s, uint32_t rate, pa_bool_t passthrough)
             desired_rate = rate; /* use stream sampling rate, discard default/alternate settings */
         }
 
+        if (desired_rate == s->sample_spec.rate)
+            return FALSE;
+
         if (!passthrough && pa_sink_used_by(s) > 0)
             return FALSE;
 
+        pa_log_debug("Suspending sink %s due to changing the sample rate.", s->name);
         pa_sink_suspend(s, TRUE, PA_SUSPEND_IDLE); /* needed before rate update, will be resumed automatically */
 
         if (s->update_rate(s, desired_rate) == TRUE) {
@@ -1394,6 +1434,13 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
 
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
 
+    /* usec is unsigned, so check that the offset can be added to usec without
+     * underflowing. */
+    if (-s->latency_offset <= (int64_t) usec)
+        usec += s->latency_offset;
+    else
+        usec = 0;
+
     return usec;
 }
 
@@ -1421,6 +1468,13 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
     if (o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
         return -1;
 
+    /* usec is unsigned, so check that the offset can be added to usec without
+     * underflowing. */
+    if (-s->thread_info.latency_offset <= (int64_t) usec)
+        usec += s->thread_info.latency_offset;
+    else
+        usec = 0;
+
     return usec;
 }
 
@@ -1479,8 +1533,10 @@ void pa_sink_enter_passthrough(pa_sink *s) {
     pa_cvolume volume;
 
     /* disable the monitor in passthrough mode */
-    if (s->monitor_source)
+    if (s->monitor_source) {
+        pa_log_debug("Suspending monitor source %s, because the sink is entering the passthrough mode.", s->monitor_source->name);
         pa_source_suspend(s->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
+    }
 
     /* set the volume to NORM */
     s->saved_volume = *pa_sink_get_volume(s, TRUE);
@@ -1493,8 +1549,10 @@ void pa_sink_enter_passthrough(pa_sink *s) {
 /* Called from main context */
 void pa_sink_leave_passthrough(pa_sink *s) {
     /* Unsuspend monitor */
-    if (s->monitor_source)
+    if (s->monitor_source) {
+        pa_log_debug("Resuming monitor source %s, because the sink is leaving the passthrough mode.", s->monitor_source->name);
         pa_source_suspend(s->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
+    }
 
     /* Restore sink volume to what it was before we entered passthrough mode */
     pa_sink_set_volume(s, &s->saved_volume, TRUE, s->saved_save_volume);
@@ -2377,8 +2435,16 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
 
             pa_sink_input_set_state_within_thread(i, i->state);
 
-            /* The requested latency of the sink input needs to be
-             * fixed up and then configured on the sink */
+            /* The requested latency of the sink input needs to be fixed up and
+             * then configured on the sink. If this causes the sink latency to
+             * go down, the sink implementor is responsible for doing a rewind
+             * in the update_requested_latency() callback to ensure that the
+             * sink buffer doesn't contain more data than what the new latency
+             * allows.
+             *
+             * XXX: Does it really make sense to push this responsibility to
+             * the sink implementors? Wouldn't it be better to do it once in
+             * the core than many times in the modules? */
 
             if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
                 pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
@@ -2389,19 +2455,11 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             /* We don't rewind here automatically. This is left to the
              * sink input implementor because some sink inputs need a
              * slow start, i.e. need some time to buffer client
-             * samples before beginning streaming. */
-
-            /* FIXME: Actually rewinding should be requested before
-             * updating the sink requested latency, because updating
-             * the requested latency updates also max_rewind of the
-             * sink. Now consider this: a sink has a 10 s buffer and
-             * nobody has requested anything less. Then a new stream
-             * appears while the sink buffer is full. The new stream
-             * requests e.g. 100 ms latency. That request is forwarded
-             * to the sink, so now max_rewind is 100 ms. When a rewind
-             * is requested, the sink will only rewind 100 ms, and the
-             * new stream will have to wait about 10 seconds before it
-             * becomes audible. */
+             * samples before beginning streaming.
+             *
+             * XXX: Does it really make sense to push this functionality to
+             * the sink implementors? Wouldn't it be better to do it once in
+             * the core than many times in the modules? */
 
             /* In flat volume mode we need to update the volume as
              * well */
@@ -2784,6 +2842,10 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             pa_sink_get_mute(s, TRUE);
             return 0;
 
+        case PA_SINK_MESSAGE_SET_LATENCY_OFFSET:
+            s->thread_info.latency_offset = offset;
+            return 0;
+
         case PA_SINK_MESSAGE_GET_LATENCY:
         case PA_SINK_MESSAGE_MAX:
             ;
@@ -2870,9 +2932,6 @@ void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
     pa_sink_assert_io_context(s);
     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
 
-    if (s->thread_info.state == PA_SINK_SUSPENDED)
-        return;
-
     if (nbytes == (size_t) -1)
         nbytes = s->thread_info.max_rewind;
 
@@ -3195,6 +3254,18 @@ void pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency) {
     pa_source_set_fixed_latency_within_thread(s->monitor_source, latency);
 }
 
+/* Called from main context */
+void pa_sink_set_latency_offset(pa_sink *s, int64_t offset) {
+    pa_sink_assert_ref(s);
+
+    s->latency_offset = offset;
+
+    if (PA_SINK_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_OFFSET, NULL, offset, NULL) == 0);
+    else
+        s->thread_info.latency_offset = offset;
+}
+
 /* Called from main context */
 size_t pa_sink_get_max_rewind(pa_sink *s) {
     size_t r;
@@ -3236,7 +3307,7 @@ int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
         return -PA_ERR_NOTIMPLEMENTED;
     }
 
-    if (!s->ports || !name)
+    if (!name)
         return -PA_ERR_NOENTITY;
 
     if (!(port = pa_hashmap_get(s->ports, name)))
@@ -3265,6 +3336,8 @@ int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
     s->active_port = port;
     s->save_port = save;
 
+    pa_sink_set_latency_offset(s, s->active_port->latency_offset);
+
     pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s);
 
     return 0;