volume ramp: add volume ramping to sink
authorJaska Uimonen <jaska.uimonen@helsinki.fi>
Wed, 8 Aug 2012 08:14:39 +0000 (11:14 +0300)
committerJaska Uimonen <jaska.uimonen@intel.com>
Wed, 12 Mar 2014 08:55:39 +0000 (10:55 +0200)
Change-Id: I72bd64b4e4ad161ae0526a7e40d4bf9e843ae98c
Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
src/pulsecore/sink.c
src/pulsecore/sink.h

index f4647b8..61656ab 100644 (file)
@@ -333,6 +333,8 @@ 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_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
                                                 (pa_free_cb_t) pa_sink_input_unref);
@@ -356,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);
 
@@ -1189,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);
@@ -1205,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);
@@ -1219,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;
@@ -1279,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);
@@ -1286,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);
@@ -1297,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);
 
@@ -1306,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);
     }
 
@@ -2085,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) {
@@ -2737,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) {
index c7cb5f8..576f34b 100644 (file)
@@ -37,6 +37,7 @@ typedef struct pa_sink_volume_change pa_sink_volume_change;
 #include <pulsecore/core.h>
 #include <pulsecore/idxset.h>
 #include <pulsecore/memchunk.h>
+#include <pulsecore/mix.h>
 #include <pulsecore/source.h>
 #include <pulsecore/module.h>
 #include <pulsecore/asyncmsgq.h>
@@ -105,6 +106,9 @@ struct pa_sink {
     pa_cvolume saved_volume;
     bool saved_save_volume:1;
 
+    /* for volume ramps */
+    pa_cvolume_ramp_int ramp;
+
     pa_asyncmsgq *asyncmsgq;
 
     pa_memchunk silence;
@@ -288,6 +292,8 @@ struct pa_sink {
         uint32_t volume_change_safety_margin;
         /* Usec delay added to all volume change events, may be negative. */
         int32_t volume_change_extra_delay;
+
+        pa_cvolume_ramp_int ramp;
     } thread_info;
 
     void *userdata;
@@ -322,6 +328,7 @@ typedef enum pa_sink_message {
     PA_SINK_MESSAGE_SET_PORT,
     PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE,
     PA_SINK_MESSAGE_SET_LATENCY_OFFSET,
+    PA_SINK_MESSAGE_SET_VOLUME_RAMP,
     PA_SINK_MESSAGE_MAX
 } pa_sink_message_t;
 
@@ -443,6 +450,8 @@ bool pa_sink_get_mute(pa_sink *sink, bool force_refresh);
 
 bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
 
+void pa_sink_set_volume_ramp(pa_sink *s, const pa_cvolume_ramp *ramp, bool send_msg, bool save);
+
 int pa_sink_set_port(pa_sink *s, const char *name, bool save);
 void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty);