]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/sink.c
echo-cancel: Add alternative echo-cancellation implementation
[pulseaudio] / src / pulsecore / sink.c
index bda92fccb4ec6e34cdc0a5b5b457a0eae0d8194b..b68ad3aa8a557a9d0c0dfb2e2b3c10b2119b2964 100644 (file)
@@ -926,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;
@@ -1164,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;
@@ -1291,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);
     }
 
@@ -1342,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;
@@ -1355,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 */
 
@@ -1411,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)
@@ -1733,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);
     }
 }