]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/sink.c
echo-cancel: Add alternative echo-cancellation implementation
[pulseaudio] / src / pulsecore / sink.c
index 9388d3095d4c3764ed01129ac1af9e8338c56e1f..b68ad3aa8a557a9d0c0dfb2e2b3c10b2119b2964 100644 (file)
@@ -778,7 +778,7 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns
 /* Called from IO thread context */
 static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
     pa_sink_input *i;
-    void *state = NULL;
+    void *state;
     unsigned p = 0;
     unsigned n_unreffed = 0;
 
@@ -790,7 +790,7 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *
 
     /* We optimize for the case where the order of the inputs has not changed */
 
-    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
         unsigned j;
         pa_mix_info* m = NULL;
 
@@ -884,8 +884,6 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
     pa_assert(pa_frame_aligned(length, &s->sample_spec));
     pa_assert(result);
 
-    pa_sink_ref(s);
-
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
@@ -893,11 +891,11 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
         result->memblock = pa_memblock_ref(s->silence.memblock);
         result->index = s->silence.index;
         result->length = PA_MIN(s->silence.length, length);
-
-        pa_sink_unref(s);
         return;
     }
 
+    pa_sink_ref(s);
+
     if (length <= 0)
         length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
 
@@ -928,18 +926,16 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
 
         pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
 
-        if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
-            if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
-                pa_memblock_unref(result->memblock);
-                pa_silence_memchunk_get(&s->core->silence_cache,
-                                        s->core->mempool,
-                                        result,
-                                        &s->sample_spec,
-                                        result->length);
-            } else {
-                pa_memchunk_make_writable(result, 0);
-                pa_volume_memchunk(result, &s->sample_spec, &volume);
-            }
+        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
+            pa_memblock_unref(result->memblock);
+            pa_silence_memchunk_get(&s->core->silence_cache,
+                                    s->core->mempool,
+                                    result,
+                                    &s->sample_spec,
+                                    result->length);
+        } else if (!pa_cvolume_is_norm(&volume)) {
+            pa_memchunk_make_writable(result, 0);
+            pa_volume_memchunk(result, &s->sample_spec, &volume);
         }
     } else {
         void *ptr;
@@ -975,17 +971,16 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
     pa_assert(target->length > 0);
     pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
 
-    pa_sink_ref(s);
-
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
     if (s->thread_info.state == PA_SINK_SUSPENDED) {
         pa_silence_memchunk(target, &s->sample_spec);
-        pa_sink_unref(s);
         return;
     }
 
+    pa_sink_ref(s);
+
     length = target->length;
     block_size_max = pa_mempool_block_size_max(s->core->mempool);
     if (length > block_size_max)
@@ -1060,11 +1055,16 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
     pa_assert(target->length > 0);
     pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
 
-    pa_sink_ref(s);
-
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
+    if (s->thread_info.state == PA_SINK_SUSPENDED) {
+        pa_silence_memchunk(target, &s->sample_spec);
+        return;
+    }
+
+    pa_sink_ref(s);
+
     l = target->length;
     d = 0;
     while (l > 0) {
@@ -1083,10 +1083,6 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
 
 /* Called from IO thread context */
 void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
-    pa_mix_info info[MAX_MIX_CHANNELS];
-    size_t length1st = length;
-    unsigned n;
-
     pa_sink_assert_ref(s);
     pa_sink_assert_io_context(s);
     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
@@ -1094,72 +1090,12 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
     pa_assert(pa_frame_aligned(length, &s->sample_spec));
     pa_assert(result);
 
-    pa_sink_ref(s);
-
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
-    if (s->thread_info.state == PA_SINK_SUSPENDED) {
-        pa_silence_memchunk_get(&s->core->silence_cache,
-                                s->core->mempool,
-                                result,
-                                &s->sample_spec,
-                                length1st);
-
-        pa_sink_unref(s);
-        return;
-    }
-
-    n = fill_mix_info(s, &length1st, info, MAX_MIX_CHANNELS);
-
-    if (n == 0) {
-        pa_silence_memchunk_get(&s->core->silence_cache,
-                                s->core->mempool,
-                                result,
-                                &s->sample_spec,
-                                length1st);
-    } else if (n == 1) {
-        pa_cvolume volume;
-
-        *result = info[0].chunk;
-        pa_memblock_ref(result->memblock);
-
-        if (result->length > length)
-            result->length = length;
-
-        pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
-
-        if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
-            if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
-                pa_memblock_unref(result->memblock);
-                pa_silence_memchunk_get(&s->core->silence_cache,
-                                        s->core->mempool,
-                                        result,
-                                        &s->sample_spec,
-                                        result->length);
-            } else {
-                pa_memchunk_make_writable(result, length);
-                pa_volume_memchunk(result, &s->sample_spec, &volume);
-            }
-        }
-    } else {
-        void *ptr;
-
-        result->index = 0;
-        result->memblock = pa_memblock_new(s->core->mempool, length);
-
-        ptr = pa_memblock_acquire(result->memblock);
-
-        result->length = pa_mix(info, n,
-                                (uint8_t*) ptr + result->index, length1st,
-                                &s->sample_spec,
-                                &s->thread_info.soft_volume,
-                                s->thread_info.soft_muted);
-
-        pa_memblock_release(result->memblock);
-    }
+    pa_sink_ref(s);
 
-    inputs_drop(s, info, n, result);
+    pa_sink_render(s, length, result);
 
     if (result->length < length) {
         pa_memchunk chunk;
@@ -1226,6 +1162,46 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
     return usec;
 }
 
+static pa_cvolume* cvolume_remap_minimal_impact(
+        pa_cvolume *v,
+        const pa_cvolume *template,
+        const pa_channel_map *from,
+        const pa_channel_map *to) {
+
+    pa_cvolume t;
+
+    pa_assert(v);
+    pa_assert(template);
+    pa_assert(from);
+    pa_assert(to);
+
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL);
+    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(template, to), NULL);
+
+    /* Much like pa_cvolume_remap(), but tries to minimize impact when
+     * mapping from sink input to sink volumes:
+     *
+     * If template is a possible remapping from v it is used instead
+     * of remapping anew.
+     *
+     * If the channel maps don't match we set an all-channel volume on
+     * the sink to ensure that changing a volume on one stream has no
+     * effect that cannot be compensated for in another stream that
+     * does not have the same channel map as the sink. */
+
+    if (pa_channel_map_equal(from, to))
+        return v;
+
+    t = *template;
+    if (pa_cvolume_equal(pa_cvolume_remap(&t, to, from), v)) {
+        *v = *template;
+        return v;
+    }
+
+    pa_cvolume_set(v, to->channels, pa_cvolume_max(v));
+    return v;
+}
+
 /* Called from main context */
 static void compute_reference_ratios(pa_sink *s) {
     uint32_t idx;
@@ -1353,7 +1329,7 @@ static void compute_real_volume(pa_sink *s) {
         pa_cvolume remapped;
 
         remapped = i->volume;
-        pa_cvolume_remap(&remapped, &i->channel_map, &s->channel_map);
+        cvolume_remap_minimal_impact(&remapped, &s->real_volume, &i->channel_map, &s->channel_map);
         pa_cvolume_merge(&s->real_volume, &s->real_volume, &remapped);
     }
 
@@ -1404,7 +1380,7 @@ static void propagate_reference_volume(pa_sink *s) {
 void pa_sink_set_volume(
         pa_sink *s,
         const pa_cvolume *volume,
-        pa_bool_t sendmsg,
+        pa_bool_t send_msg,
         pa_bool_t save) {
 
     pa_cvolume old_reference_volume;
@@ -1417,6 +1393,24 @@ void pa_sink_set_volume(
     pa_assert(volume || (s->flags & PA_SINK_FLAT_VOLUME));
     pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
 
+    /* make sure we don't change the volume when a PASSTHROUGH input is connected */
+    if (s->flags & PA_SINK_PASSTHROUGH) {
+        pa_sink_input *alt_i;
+        uint32_t idx;
+
+        /* one and only one PASSTHROUGH input can possibly be connected */
+        if (pa_idxset_size(s->inputs) == 1) {
+
+            alt_i = pa_idxset_first(s->inputs, &idx);
+
+            if (alt_i->flags & PA_SINK_INPUT_PASSTHROUGH) {
+                /* FIXME: Need to notify client that volume control is disabled */
+                pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
+                return;
+            }
+        }
+    }
+
     /* As a special exception we accept mono volumes on all sinks --
      * even on those with more complex channel maps */
 
@@ -1473,7 +1467,7 @@ void pa_sink_set_volume(
         s->soft_volume = s->real_volume;
 
     /* This tells the sink that soft and/or virtual volume changed */
-    if (sendmsg)
+    if (send_msg)
         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
 
     if (reference_changed)
@@ -1795,10 +1789,14 @@ static void sync_input_volumes_within_thread(pa_sink *s) {
     pa_sink_assert_io_context(s);
 
     PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
+        if (pa_atomic_load(&i->before_ramping_v))
+            i->thread_info.future_soft_volume = i->soft_volume;
+
         if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
             continue;
 
-        i->thread_info.soft_volume = i->soft_volume;
+        if (!pa_atomic_load(&i->before_ramping_v))
+            i->thread_info.soft_volume = i->soft_volume;
         pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
     }
 }