volume ramp: add volume ramping to sink
[platform/upstream/pulseaudio.git] / src / pulsecore / sink.c
index 2f5174e..61656ab 100644 (file)
@@ -82,7 +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);
+    data->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_device_port_unref);
 
     return data;
 }
@@ -142,7 +142,7 @@ void pa_sink_new_data_done(pa_sink_new_data *data) {
     pa_proplist_free(data->proplist);
 
     if (data->ports)
-        pa_hashmap_free(data->ports, (pa_free_cb_t) pa_device_port_unref);
+        pa_hashmap_free(data->ports);
 
     pa_xfree(data->name);
     pa_xfree(data->active_port);
@@ -304,9 +304,18 @@ pa_sink* pa_sink_new(
         void *state;
         pa_device_port *p;
 
-        PA_HASHMAP_FOREACH(p, s->ports, state)
+        PA_HASHMAP_FOREACH(p, s->ports, state) {
+            if (p->available == PA_AVAILABLE_NO)
+                continue;
+
             if (!s->active_port || p->priority > s->active_port->priority)
                 s->active_port = p;
+        }
+        if (!s->active_port) {
+            PA_HASHMAP_FOREACH(p, s->ports, state)
+                if (!s->active_port || p->priority > s->active_port->priority)
+                    s->active_port = p;
+        }
     }
 
     if (s->active_port)
@@ -324,8 +333,11 @@ pa_sink* pa_sink_new(
             &s->sample_spec,
             0);
 
+    pa_cvolume_ramp_int_init(&s->ramp, PA_VOLUME_NORM, data->sample_spec.channels);
+
     s->thread_info.rtpoll = NULL;
-    s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    s->thread_info.inputs = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
+                                                (pa_free_cb_t) pa_sink_input_unref);
     s->thread_info.soft_volume =  s->soft_volume;
     s->thread_info.soft_muted = s->muted;
     s->thread_info.state = s->state;
@@ -346,6 +358,8 @@ pa_sink* pa_sink_new(
     s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
     s->thread_info.latency_offset = s->latency_offset;
 
+    s->thread_info.ramp = s->ramp;
+
     /* FIXME: This should probably be moved to pa_sink_put() */
     pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
 
@@ -730,7 +744,7 @@ static void sink_free(pa_object *o) {
     }
 
     pa_idxset_free(s->inputs, NULL);
-    pa_hashmap_free(s->thread_info.inputs, (pa_free_cb_t) pa_sink_input_unref);
+    pa_hashmap_free(s->thread_info.inputs);
 
     if (s->silence.memblock)
         pa_memblock_unref(s->silence.memblock);
@@ -742,7 +756,7 @@ static void sink_free(pa_object *o) {
         pa_proplist_free(s->proplist);
 
     if (s->ports)
-        pa_hashmap_free(s->ports, (pa_free_cb_t) pa_device_port_unref);
+        pa_hashmap_free(s->ports);
 
     pa_xfree(s);
 }
@@ -1179,6 +1193,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
 
     } else if (n == 1) {
         pa_cvolume volume;
+        pa_cvolume target;
 
         *result = info[0].chunk;
         pa_memblock_ref(result->memblock);
@@ -1195,12 +1210,25 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
                                     result,
                                     &s->sample_spec,
                                     result->length);
-        } else if (!pa_cvolume_is_norm(&volume)) {
+        } else if (!pa_cvolume_is_norm(&volume) || pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
             pa_memchunk_make_writable(result, 0);
-            pa_volume_memchunk(result, &s->sample_spec, &volume);
+            if (pa_cvolume_ramp_active(&s->thread_info.ramp)) {
+                if (!pa_cvolume_is_norm(&volume))
+                    pa_volume_memchunk(result, &s->sample_spec, &volume);
+                pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp));
+            }
+            else {
+                if (pa_cvolume_ramp_target_active(&s->thread_info.ramp)) {
+                    pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target);
+                    pa_sw_cvolume_multiply(&volume, &volume, &target);
+                }
+                pa_volume_memchunk(result, &s->sample_spec, &volume);
+            }
         }
     } else {
         void *ptr;
+        pa_cvolume target_vol;
+
         result->memblock = pa_memblock_new(s->core->mempool, length);
 
         ptr = pa_memblock_acquire(result->memblock);
@@ -1209,6 +1237,16 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
                                 &s->sample_spec,
                                 &s->thread_info.soft_volume,
                                 s->thread_info.soft_muted);
+
+        if (pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
+            if (pa_cvolume_ramp_active(&s->thread_info.ramp))
+                pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp));
+                else {
+                    pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol);
+                    pa_volume_memchunk(result, &s->sample_spec, &target_vol);
+                }
+        }
+
         pa_memblock_release(result->memblock);
 
         result->index = 0;
@@ -1269,6 +1307,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
             pa_silence_memchunk(target, &s->sample_spec);
         else {
             pa_memchunk vchunk;
+            pa_cvolume target_vol;
 
             vchunk = info[0].chunk;
             pa_memblock_ref(vchunk.memblock);
@@ -1276,9 +1315,20 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
             if (vchunk.length > length)
                 vchunk.length = length;
 
-            if (!pa_cvolume_is_norm(&volume)) {
+            if (!pa_cvolume_is_norm(&volume) || pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
                 pa_memchunk_make_writable(&vchunk, 0);
-                pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+                if (pa_cvolume_ramp_active(&s->thread_info.ramp)) {
+                    if (!pa_cvolume_is_norm(&volume))
+                        pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+                    pa_volume_ramp_memchunk(&vchunk, &s->sample_spec, &(s->thread_info.ramp));
+                }
+                else {
+                    if (pa_cvolume_ramp_target_active(&s->thread_info.ramp)) {
+                        pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol);
+                        pa_sw_cvolume_multiply(&volume, &volume, &target_vol);
+                    }
+                    pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+                }
             }
 
             pa_memchunk_memcpy(target, &vchunk);
@@ -1287,6 +1337,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
 
     } else {
         void *ptr;
+        pa_cvolume target_vol;
 
         ptr = pa_memblock_acquire(target->memblock);
 
@@ -1296,6 +1347,15 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
                                 &s->thread_info.soft_volume,
                                 s->thread_info.soft_muted);
 
+        if (pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) {
+            if (pa_cvolume_ramp_active(&s->thread_info.ramp))
+                pa_volume_ramp_memchunk(target, &s->sample_spec, &(s->thread_info.ramp));
+            else {
+                pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol);
+                pa_volume_memchunk(target, &s->sample_spec, &target_vol);
+            }
+        }
+
         pa_memblock_release(target->memblock);
     }
 
@@ -1377,8 +1437,8 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
 }
 
 /* Called from main thread */
-bool pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) {
-    bool ret = false;
+int pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) {
+    int ret = -1;
     uint32_t desired_rate = rate;
     uint32_t default_rate = s->default_sample_rate;
     uint32_t alternate_rate = s->alternate_sample_rate;
@@ -1386,37 +1446,38 @@ bool pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) {
     pa_sink_input *i;
     bool use_alternate = false;
 
+    if (rate == s->sample_spec.rate)
+        return 0;
+
     if (!s->update_rate)
-        return false;
+        return -1;
 
-    if (PA_UNLIKELY(default_rate == alternate_rate)) {
-        pa_log_warn("Default and alternate sample rates are the same.");
-        return false;
+    if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough)) {
+        pa_log_debug("Default and alternate sample rates are the same.");
+        return -1;
     }
 
     if (PA_SINK_IS_RUNNING(s->state)) {
         pa_log_info("Cannot update rate, SINK_IS_RUNNING, will keep using %u Hz",
                     s->sample_spec.rate);
-        return false;
+        return -1;
     }
 
     if (s->monitor_source) {
         if (PA_SOURCE_IS_RUNNING(s->monitor_source->state) == true) {
             pa_log_info("Cannot update rate, monitor source is RUNNING");
-            return false;
+            return -1;
         }
     }
 
-    if (PA_UNLIKELY (desired_rate < 8000 ||
-                     desired_rate > PA_RATE_MAX))
-        return false;
+    if (PA_UNLIKELY(!pa_sample_rate_valid(desired_rate)))
+        return -1;
 
     if (!passthrough) {
-        pa_assert(default_rate % 4000 || default_rate % 11025);
-        pa_assert(alternate_rate % 4000 || alternate_rate % 11025);
+        pa_assert((default_rate % 4000 == 0) || (default_rate % 11025 == 0));
+        pa_assert((alternate_rate % 4000 == 0) || (alternate_rate % 11025 == 0));
 
-        if (default_rate % 4000) {
-            /* default is a 11025 multiple */
+        if (default_rate % 11025 == 0) {
             if ((alternate_rate % 4000 == 0) && (desired_rate % 4000 == 0))
                 use_alternate=true;
         } else {
@@ -1434,15 +1495,15 @@ bool pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) {
     }
 
     if (desired_rate == s->sample_spec.rate)
-        return false;
+        return -1;
 
     if (!passthrough && pa_sink_used_by(s) > 0)
-        return false;
+        return -1;
 
     pa_log_debug("Suspending sink %s due to changing the sample rate.", s->name);
     pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL);
 
-    if (s->update_rate(s, desired_rate) == true) {
+    if (s->update_rate(s, desired_rate) >= 0) {
         /* update monitor source as well */
         if (s->monitor_source && !passthrough)
             pa_source_update_rate(s->monitor_source, desired_rate, false);
@@ -1453,7 +1514,7 @@ bool pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) {
                 pa_sink_input_update_rate(i);
         }
 
-        ret = true;
+        ret = 0;
     }
 
     pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL);
@@ -2074,6 +2135,32 @@ void pa_sink_set_volume(
         pa_assert_se(pa_asyncmsgq_send(root_sink->asyncmsgq, PA_MSGOBJECT(root_sink), PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0);
 }
 
+/* Called from main thread */
+void pa_sink_set_volume_ramp(
+        pa_sink *s,
+        const pa_cvolume_ramp *ramp,
+        bool send_msg,
+        bool save) {
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(ramp);
+
+    /* make sure we don't change the volume when a PASSTHROUGH input is connected ...
+     * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
+    if (pa_sink_is_passthrough(s)) {
+        pa_log_warn("Cannot do volume ramp, Sink is connected to PASSTHROUGH input");
+        return;
+    }
+
+    pa_cvolume_ramp_convert(ramp, &s->ramp, s->sample_spec.rate);
+
+    /* This tells the sink that volume ramp changed */
+    if (send_msg)
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0);
+}
+
 /* Called from the io thread if sync volume is used, otherwise from the main thread.
  * Only to be called by sink implementor */
 void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
@@ -2726,13 +2813,19 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
                 s->thread_info.soft_volume = s->soft_volume;
                 pa_sink_request_rewind(s, (size_t) -1);
             }
-
             /* Fall through ... */
 
         case PA_SINK_MESSAGE_SYNC_VOLUMES:
             sync_input_volumes_within_thread(s);
             return 0;
 
+        case PA_SINK_MESSAGE_SET_VOLUME_RAMP:
+            /* if we have ongoing ramp where we take current start values */
+            pa_cvolume_ramp_start_from(&s->thread_info.ramp, &s->ramp);
+            s->thread_info.ramp = s->ramp;
+            pa_sink_request_rewind(s, (size_t) -1);
+            return 0;
+
         case PA_SINK_MESSAGE_GET_VOLUME:
 
             if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) {
@@ -2793,18 +2886,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             return 0;
         }
 
-        case PA_SINK_MESSAGE_DETACH:
-
-            /* Detach all streams */
-            pa_sink_detach_within_thread(s);
-            return 0;
-
-        case PA_SINK_MESSAGE_ATTACH:
-
-            /* Reattach all streams */
-            pa_sink_attach_within_thread(s);
-            return 0;
-
         case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
 
             pa_usec_t *usec = userdata;
@@ -2919,24 +3000,6 @@ int pa_sink_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause) {
     return ret;
 }
 
-/* Called from main thread */
-void pa_sink_detach(pa_sink *s) {
-    pa_sink_assert_ref(s);
-    pa_assert_ctl_context();
-    pa_assert(PA_SINK_IS_LINKED(s->state));
-
-    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
-}
-
-/* Called from main thread */
-void pa_sink_attach(pa_sink *s) {
-    pa_sink_assert_ref(s);
-    pa_assert_ctl_context();
-    pa_assert(PA_SINK_IS_LINKED(s->state));
-
-    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
-}
-
 /* Called from IO thread */
 void pa_sink_detach_within_thread(pa_sink *s) {
     pa_sink_input *i;