]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/sink.c
core: add a seperate fixed_latency field for sinks/sources with fixed latency
[pulseaudio] / src / pulsecore / sink.c
index 886402a6f85b9491f4b65509dc8577a0ff5f1ab1..93800d14f2674acede6d58f2daa749c8621474ee 100644 (file)
@@ -50,6 +50,7 @@
 #define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
 #define ABSOLUTE_MIN_LATENCY (500)
 #define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
+#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
 
 static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
 
@@ -208,6 +209,8 @@ pa_sink* pa_sink_new(
     s->muted = data->muted;
     s->refresh_volume = s->refresh_muted = FALSE;
 
+    s->fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+
     reset_callbacks(s);
     s->userdata = NULL;
 
@@ -363,8 +366,13 @@ void pa_sink_put(pa_sink* s) {
     if (s->flags & PA_SINK_LATENCY)
         s->monitor_source->flags |= PA_SOURCE_LATENCY;
 
-    if (s->flags & PA_SINK_DYNAMIC_LATENCY)
+    if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
         s->monitor_source->flags |= PA_SOURCE_DYNAMIC_LATENCY;
+        s->fixed_latency = 0;
+    } else if (s->fixed_latency <= 0)
+        s->fixed_latency = DEFAULT_FIXED_LATENCY;
+
+    s->monitor_source->fixed_latency = s->fixed_latency;
 
     pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
 
@@ -515,8 +523,12 @@ pa_queue *pa_sink_move_all_start(pa_sink *s) {
     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
         n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
 
+        pa_sink_input_ref(i);
+
         if (pa_sink_input_start_move(i) >= 0)
-            pa_queue_push(q, pa_sink_input_ref(i));
+            pa_queue_push(q, i);
+        else
+            pa_sink_input_unref(i);
     }
 
     return q;
@@ -990,22 +1002,32 @@ static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volu
     pa_sink_input_assert_ref(i);
     pa_assert(new_volume->channels == i->sample_spec.channels);
 
-    /* This basically calculates i->soft_volume := i->virtual_volume / new_volume * i->volume_factor */
+    /*
+     * This basically calculates:
+     *
+     * i->relative_volume := i->virtual_volume / new_volume
+     * i->soft_volume := i->relative_volume * i->volume_factor
+     */
 
     /* The new sink volume passed in here must already be remapped to
      * the sink input's channel map! */
 
+    i->soft_volume.channels = i->sample_spec.channels;
+
     for (c = 0; c < i->sample_spec.channels; c++)
 
         if (new_volume->values[c] <= PA_VOLUME_MUTED)
+            /* We leave i->relative_volume untouched */
             i->soft_volume.values[c] = PA_VOLUME_MUTED;
-        else
-            i->soft_volume.values[c] = pa_sw_volume_from_linear(
-                    pa_sw_volume_to_linear(i->virtual_volume.values[c]) *
-                    pa_sw_volume_to_linear(i->volume_factor.values[c]) /
-                    pa_sw_volume_to_linear(new_volume->values[c]));
+        else {
+            i->relative_volume[c] =
+                pa_sw_volume_to_linear(i->virtual_volume.values[c]) /
+                pa_sw_volume_to_linear(new_volume->values[c]);
 
-    i->soft_volume.channels = i->sample_spec.channels;
+            i->soft_volume.values[c] = pa_sw_volume_from_linear(
+                    i->relative_volume[c] *
+                    pa_sw_volume_to_linear(i->volume_factor.values[c]));
+        }
 
     /* Hooks have the ability to play games with i->soft_volume */
     pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
@@ -1071,12 +1093,11 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
 }
 
 /* Called from main thread */
-void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
+void pa_sink_propagate_flat_volume(pa_sink *s) {
     pa_sink_input *i;
     uint32_t idx;
 
     pa_sink_assert_ref(s);
-    pa_assert(old_volume);
     pa_assert(PA_SINK_IS_LINKED(s->state));
     pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
 
@@ -1085,26 +1106,18 @@ void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
      * sink input volumes accordingly */
 
     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
-        pa_cvolume remapped_old_volume, remapped_new_volume, new_virtual_volume;
+        pa_cvolume sink_volume, new_virtual_volume;
         unsigned c;
 
-        /* This basically calculates i->virtual_volume := i->virtual_volume * s->virtual_volume / old_volume */
-
-        remapped_new_volume = s->virtual_volume;
-        pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
+        /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume  */
 
-        remapped_old_volume = *old_volume;
-        pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map);
+        sink_volume = s->virtual_volume;
+        pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map);
 
         for (c = 0; c < i->sample_spec.channels; c++)
-
-            if (remapped_old_volume.values[c] <= PA_VOLUME_MUTED)
-                new_virtual_volume.values[c] = remapped_new_volume.values[c];
-            else
-                new_virtual_volume.values[c] = pa_sw_volume_from_linear(
-                        pa_sw_volume_to_linear(i->virtual_volume.values[c]) *
-                        pa_sw_volume_to_linear(remapped_new_volume.values[c]) /
-                        pa_sw_volume_to_linear(remapped_old_volume.values[c]));
+            new_virtual_volume.values[c] = pa_sw_volume_from_linear(
+                    i->relative_volume[c] *
+                    pa_sw_volume_to_linear(sink_volume.values[c]));
 
         new_virtual_volume.channels = i->sample_spec.channels;
 
@@ -1116,7 +1129,7 @@ void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
              * especially when the old volume was
              * PA_VOLUME_MUTED. Hence let's recalculate the soft
              * volumes here. */
-            compute_new_soft_volume(i, &remapped_new_volume);
+            compute_new_soft_volume(i, &sink_volume);
 
             /* 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);
@@ -1130,7 +1143,6 @@ void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
 
 /* Called from main thread */
 void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg) {
-    pa_cvolume old_virtual_volume;
     pa_bool_t virtual_volume_changed;
 
     pa_sink_assert_ref(s);
@@ -1139,14 +1151,13 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
     pa_assert(pa_cvolume_valid(volume));
     pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
 
-    old_virtual_volume = s->virtual_volume;
+    virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
     s->virtual_volume = *volume;
-    virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
 
     /* Propagate this volume change back to the inputs */
     if (virtual_volume_changed)
         if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
-            pa_sink_propagate_flat_volume(s, &old_virtual_volume);
+            pa_sink_propagate_flat_volume(s);
 
     if (s->set_volume) {
         /* If we have a function set_volume(), then we do not apply a
@@ -1197,7 +1208,7 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
         if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
 
             if (s->flags & PA_SINK_FLAT_VOLUME)
-                pa_sink_propagate_flat_volume(s, &old_virtual_volume);
+                pa_sink_propagate_flat_volume(s);
 
             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
         }
@@ -1821,6 +1832,9 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
 
     pa_sink_assert_ref(s);
 
+    if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+        return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+
     if (s->thread_info.requested_latency_valid)
         return s->thread_info.requested_latency;
 
@@ -1836,13 +1850,8 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
         (result == (pa_usec_t) -1 || result > monitor_latency))
         result = monitor_latency;
 
-    if (result != (pa_usec_t) -1) {
-        if (result > s->thread_info.max_latency)
-            result = s->thread_info.max_latency;
-
-        if (result < s->thread_info.min_latency)
-            result = s->thread_info.min_latency;
-    }
+    if (result != (pa_usec_t) -1)
+        result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
 
     s->thread_info.requested_latency = result;
     s->thread_info.requested_latency_valid = TRUE;
@@ -1931,6 +1940,9 @@ void pa_sink_invalidate_requested_latency(pa_sink *s) {
 
     pa_sink_assert_ref(s);
 
+    if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+        return;
+
     s->thread_info.requested_latency_valid = FALSE;
 
     if (PA_SINK_IS_LINKED(s->thread_info.state)) {