[pulseaudio-discuss] [PATCH 3/4] volume ramp: add volume ramping to sink

Jaska Uimonen jaska.uimonen at helsinki.fi
Wed Aug 8 01:14:39 PDT 2012


From: Jaska Uimonen <jaska.uimonen at helsinki.fi>

---
 src/pulsecore/sink.c |  109 +++++++++++++++++++++++++++++++++++++++++++++----
 src/pulsecore/sink.h |    8 ++++
 2 files changed, 108 insertions(+), 9 deletions(-)

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index e4c343d..4bf6a83 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -318,6 +318,15 @@ pa_sink* pa_sink_new(
             &s->sample_spec,
             0);
 
+    s->ramp.type = PA_VOLUME_RAMP_TYPE_LINEAR;
+    s->ramp.length = 0;
+    s->ramp.left = 0;
+    s->ramp.start = 1.0;
+    s->ramp.end = 1.0;
+    s->ramp.curr = 1.0;
+    pa_cvolume_reset(&s->ramp.end_mapped, data->sample_spec.channels);
+    pa_cvolume_set(&s->ramp.end_mapped, data->sample_spec.channels, PA_VOLUME_NORM);
+
     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.soft_volume =  s->soft_volume;
@@ -339,6 +348,8 @@ pa_sink* pa_sink_new(
     s->thread_info.volume_change_safety_margin = core->deferred_volume_safety_margin_usec;
     s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
 
+    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);
 
@@ -1146,10 +1157,17 @@ 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)) {
-            pa_memchunk_make_writable(result, 0);
-            pa_volume_memchunk(result, &s->sample_spec, &volume);
-        }
+        } else if (!pa_cvolume_is_norm(&volume) || !pa_cvolume_is_norm(&(s->thread_info.ramp.end_mapped)) || s->thread_info.ramp.left > 0) {
+	    pa_memchunk_make_writable(result, 0);
+	    if (s->thread_info.ramp.left > 0)
+		pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp), &volume);
+	    else {
+		if (!pa_cvolume_is_norm(&(s->thread_info.ramp.end_mapped)))
+		    pa_sw_cvolume_multiply(&volume, &volume, &(s->thread_info.ramp.end_mapped));
+
+		pa_volume_memchunk(result, &s->sample_spec, &volume);
+	    }
+	}
     } else {
         void *ptr;
         result->memblock = pa_memblock_new(s->core->mempool, length);
@@ -1160,6 +1178,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_is_norm(&(s->thread_info.ramp.end_mapped)) || s->thread_info.ramp.left > 0) {
+	    pa_cvolume volume;
+	    pa_cvolume_set(&volume, s->sample_spec.channels, PA_VOLUME_NORM);
+	    if (s->thread_info.ramp.left > 0)
+		pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp), &volume);
+	    else
+		pa_volume_memchunk(result, &s->sample_spec, &(s->thread_info.ramp.end_mapped));
+	}
+
         pa_memblock_release(result->memblock);
 
         result->index = 0;
@@ -1227,10 +1255,17 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
             if (vchunk.length > length)
                 vchunk.length = length;
 
-            if (!pa_cvolume_is_norm(&volume)) {
-                pa_memchunk_make_writable(&vchunk, 0);
-                pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
-            }
+	    if (!pa_cvolume_is_norm(&volume) || !pa_cvolume_is_norm(&(s->thread_info.ramp.end_mapped)) || s->thread_info.ramp.left > 0) {
+		pa_memchunk_make_writable(&vchunk, 0);
+		if (s->thread_info.ramp.left > 0)
+		    pa_volume_ramp_memchunk(&vchunk, &s->sample_spec, &(s->thread_info.ramp), &volume);
+		else {
+		    if (!pa_cvolume_is_norm(&(s->thread_info.ramp.end_mapped)))
+			pa_sw_cvolume_multiply(&volume, &volume, &(s->thread_info.ramp.end_mapped));
+
+		    pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
+		}
+	    }
 
             pa_memchunk_memcpy(target, &vchunk);
             pa_memblock_unref(vchunk.memblock);
@@ -1247,6 +1282,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_is_norm(&(s->thread_info.ramp.end_mapped)) || s->thread_info.ramp.left > 0) {
+	    pa_cvolume volume;
+	    pa_cvolume_set(&volume, s->sample_spec.channels, PA_VOLUME_NORM);
+	    if (s->thread_info.ramp.left > 0)
+		pa_volume_ramp_memchunk(target, &s->sample_spec, &(s->thread_info.ramp), &volume);
+	    else
+		pa_volume_memchunk(target, &s->sample_spec, &(s->thread_info.ramp.end_mapped));
+	}
+
         pa_memblock_release(target->memblock);
     }
 
@@ -1999,6 +2043,47 @@ 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 *volume,
+	uint32_t time,
+	uint8_t type,
+        pa_bool_t send_msg,
+        pa_bool_t save) {
+
+    float temp;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(!volume || pa_cvolume_valid(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 ...
+     * ... *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;
+    }
+
+    s->ramp.type = type;
+    /* ms to samples */
+    s->ramp.length = time * s->default_sample_rate / 1000;
+    s->ramp.left = s->ramp.length;
+    s->ramp.start = s->ramp.end;
+    s->ramp.end_mapped = *volume;
+    /* scale to pulse internal mapping so that when ramp is over there's no glitch in volume */
+    temp = volume->values[0] / (float)0x10000U;
+    s->ramp.end = temp * temp * temp;
+
+    pa_log_debug("ramp length is %d ms, in samples %ld", time, s->ramp.length);
+
+    /* 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) {
@@ -2651,13 +2736,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:
+	    /* we have ongoing ramp where we take current start values */
+	    if (s->thread_info.ramp.left > 0)
+		    s->ramp.start = s->thread_info.ramp.curr;
+	    s->thread_info.ramp = s->ramp;
+	    return 0;
+
         case PA_SINK_MESSAGE_GET_VOLUME:
 
             if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) {
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 0b5048a..c7116ee 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -105,6 +105,9 @@ struct pa_sink {
     pa_cvolume saved_volume;
     pa_bool_t saved_save_volume:1;
 
+    /* for volume ramps */
+    pa_volume_ramp ramp;
+
     pa_asyncmsgq *asyncmsgq;
 
     pa_memchunk silence;
@@ -282,6 +285,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_volume_ramp ramp;
     } thread_info;
 
     void *userdata;
@@ -317,6 +322,7 @@ typedef enum pa_sink_message {
     PA_SINK_MESSAGE_SET_MAX_REQUEST,
     PA_SINK_MESSAGE_SET_PORT,
     PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE,
+    PA_SINK_MESSAGE_SET_VOLUME_RAMP,
     PA_SINK_MESSAGE_MAX
 } pa_sink_message_t;
 
@@ -436,6 +442,8 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
 void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute, pa_bool_t save);
 pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);
 
+void pa_sink_set_volume_ramp(pa_sink *s, const pa_cvolume *volume, uint32_t time, uint8_t type, pa_bool_t send_msg, pa_bool_t save);
+
 pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
 
 int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save);
-- 
1.7.7.6



More information about the pulseaudio-discuss mailing list