From 27f56cd81d01c3e2d0d7169eaa7ec8ca1dbf3423 Mon Sep 17 00:00:00 2001 From: Jaska Uimonen Date: Fri, 1 Jun 2012 14:42:36 +0300 Subject: [PATCH] volume ramp: add volume ramping to sink --- 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); -- 2.7.4