Merge remote branch 'origin/merge-queue'
authorLennart Poettering <lennart@poettering.net>
Thu, 5 Nov 2009 21:54:42 +0000 (22:54 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 5 Nov 2009 21:54:42 +0000 (22:54 +0100)
Conflicts:
src/pulsecore/sink-input.c
src/pulsecore/sink.c

1  2 
src/pulsecore/sink-input.c
src/pulsecore/sink-input.h
src/pulsecore/sink.c

  #define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
  #define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
  
 -static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
 +PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
  
  static void sink_input_free(pa_object *o);
 +static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
+ static void sink_input_set_ramping_info(pa_sink_input* i, pa_volume_t  pre_virtual_volume, pa_volume_t target_virtual_volume, pa_usec_t t);
+ static void sink_input_set_ramping_info_for_mute(pa_sink_input* i, pa_bool_t mute, pa_usec_t t);
+ static void sink_input_volume_ramping(pa_sink_input* i, pa_memchunk* chunk);
+ static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes);
+ static void sink_input_release_envelope(pa_sink_input *i);
  
  pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
      pa_assert(data);
@@@ -505,11 -494,14 +521,17 @@@ static void sink_input_free(pa_object *
  
      pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
  
 -    pa_assert(!i->thread_info.attached);
 +    /* Side note: this function must be able to destruct properly any
 +     * kind of sink input in any state, even those which are
 +     * "half-moved" or are connected to sinks that have no asyncmsgq
 +     * and are hence half-destructed themselves! */
  
+     if (i->thread_info.ramp_info.envelope) {
+         pa_log_debug ("Freeing envelope\n");
+         pa_envelope_free(i->thread_info.ramp_info.envelope);
+         i->thread_info.ramp_info.envelope = NULL;
+     }
      if (i->thread_info.render_memblockq)
          pa_memblockq_free(i->thread_info.render_memblockq);
  
@@@ -595,8 -585,9 +617,9 @@@ pa_usec_t pa_sink_input_get_latency(pa_
  
  /* Called from thread context */
  void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
 -    pa_bool_t do_volume_adj_here;
 +    pa_bool_t do_volume_adj_here, need_volume_factor_sink;
      pa_bool_t volume_is_norm;
+     pa_bool_t ramping;
      size_t block_size_max_sink, block_size_max_sink_input;
      size_t ilength;
  
       * to adjust the volume *before* we resample. Otherwise we can do
       * it after and leave it for the sink code */
  
-     do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
+     do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map) || i->thread_info.ramp_info.is_ramping;
      volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted;
 +    need_volume_factor_sink = !pa_cvolume_is_norm(&i->volume_factor_sink);
  
      while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) {
          pa_memchunk tchunk;
                  wchunk.length = block_size_max_sink_input;
  
              /* It might be necessary to adjust the volume here */
-             if (do_volume_adj_here && !volume_is_norm) {
+             if (do_volume_adj_here && !volume_is_norm && !i->thread_info.ramp_info.is_ramping) {
                  pa_memchunk_make_writable(&wchunk, 0);
  
 -                if (i->thread_info.muted)
 +                if (i->thread_info.muted) {
                      pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
 -                else
 +                    nvfs = FALSE;
 +
 +                } else if (!i->thread_info.resampler && nvfs) {
 +                    pa_cvolume v;
 +
 +                    /* If we don't need a resampler we can merge the
 +                     * post and the pre volume adjustment into one */
 +
 +                    pa_sw_cvolume_multiply(&v, &i->thread_info.soft_volume, &i->volume_factor_sink);
 +                    pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &v);
 +                    nvfs = FALSE;
 +
 +                } else
                      pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.soft_volume);
              }
  
@@@ -977,60 -934,22 +1018,9 @@@ static void set_real_ratio(pa_sink_inpu
  }
  
  /* Called from main context */
 -pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) {
 -    unsigned c;
 -
 -    pa_sink_input_assert_ref(i);
 -    pa_assert(v);
 -    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
 -
 -    /* This always returns the relative volume. Converts the float
 -     * version into a pa_cvolume */
 -
 -    v->channels = i->sample_spec.channels;
 -
 -    for (c = 0; c < v->channels; c++)
 -        v->values[c] = pa_sw_volume_from_linear(i->relative_volume[c]);
 -
 -    return v;
 +void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
-     pa_cvolume v;
-     pa_sink_input_assert_ref(i);
-     pa_assert_ctl_context();
-     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-     pa_assert(volume);
-     pa_assert(pa_cvolume_valid(volume));
-     pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
-     if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
-         v = i->sink->reference_volume;
-         pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
-         if (pa_cvolume_compatible(volume, &i->sample_spec))
-             volume = pa_sw_cvolume_multiply(&v, &v, volume);
-         else
-             volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume));
-     } else {
-         if (!pa_cvolume_compatible(volume, &i->sample_spec)) {
-             v = i->volume;
-             volume = pa_cvolume_scale(&v, pa_cvolume_max(volume));
-         }
-     }
-     if (pa_cvolume_equal(volume, &i->volume)) {
-         i->save_volume = i->save_volume || save;
-         return;
-     }
-     i->volume = *volume;
-     i->save_volume = save;
-     if (i->sink->flags & PA_SINK_FLAT_VOLUME)
-         /* We are in flat volume mode, so let's update all sink input
-          * volumes and update the flat volume of the sink */
-         pa_sink_set_volume(i->sink, NULL, TRUE, save);
-     else {
-         /* OK, we are in normal volume mode. The volume only affects
-          * ourselves */
-         set_real_ratio(i, volume);
-         /* Copy the new soft_volume to the thread_info struct */
-         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
-     }
-     /* The volume changed, let's tell people so */
-     if (i->volume_changed)
-         i->volume_changed(i);
-     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
++    /* test ramping -> return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 2000 * PA_USEC_PER_MSEC); */
++    return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 0);
  }
  
  /* Called from main context */
@@@ -1659,3 -1567,217 +1662,227 @@@ finish
      if (pl)
          pa_proplist_free(pl);
  }
 -    pa_sink_input_assert_ref(i);
+ /* Called from IO context */
+ static void sink_input_volume_ramping(pa_sink_input* i, pa_memchunk* chunk) {
+     pa_assert(i);
+     pa_assert(chunk);
+     pa_assert(chunk->memblock);
+     pa_assert(i->thread_info.ramp_info.is_ramping);
+     /* Volume is adjusted with ramping effect here */
+     pa_envelope_apply(i->thread_info.ramp_info.envelope, chunk);
+     if (pa_envelope_is_finished(i->thread_info.ramp_info.envelope)) {
+         i->thread_info.ramp_info.is_ramping = FALSE;
+         if (pa_atomic_load(&i->before_ramping_v)) {
+             i->thread_info.soft_volume = i->thread_info.future_soft_volume;
+             pa_atomic_store(&i->before_ramping_v, 0);
+         }
+         else if (pa_atomic_load(&i->before_ramping_m)) {
+             i->thread_info.muted = i->thread_info.future_muted;
+             pa_atomic_store(&i->before_ramping_m, 0);
+         }
+     }
+ }
+ /*
+  * Called from main context
+  * This function should be called inside pa_sink_input_set_volume_with_ramping
+  * should be called after soft_volume of sink_input and sink are all adjusted
+  */
+ static void sink_input_set_ramping_info(pa_sink_input* i, pa_volume_t  pre_virtual_volume, pa_volume_t target_virtual_volume, pa_usec_t t) {
+     int32_t target_abs_vol, target_apply_vol, pre_apply_vol;
+     pa_assert(i);
+     pa_log_debug("Sink input's soft volume is %d= %f ", pa_cvolume_avg(&i->soft_volume), pa_sw_volume_to_linear(pa_cvolume_avg(&i->soft_volume)));
+     /* Calculation formula are target_abs_vol := i->soft_volume
+      *                                   target_apply_vol := lrint(pa_sw_volume_to_linear(target_abs_vol) * 0x10000)
+      *                                   pre_apply_vol := ( previous_virtual_volume / target_virtual_volume ) * target_apply_vol
+      *
+      * Will do volume adjustment inside pa_sink_input_peek
+      */
+     target_abs_vol = pa_cvolume_avg(&i->soft_volume);
+     target_apply_vol = (int32_t) lrint(pa_sw_volume_to_linear(target_abs_vol) * 0x10000);
+     pre_apply_vol = (int32_t) ((pa_sw_volume_to_linear(pre_virtual_volume) / pa_sw_volume_to_linear(target_virtual_volume)) * target_apply_vol);
+     i->using_def.n_points = 2;
+     i->using_def.points_x[0] = 0;
+     i->using_def.points_x[1] = t;
+     i->using_def.points_y.i[0] = pre_apply_vol;
+     i->using_def.points_y.i[1] = target_apply_vol;
+     i->using_def.points_y.f[0] = ((float) i->using_def.points_y.i[0]) /0x10000;
+     i->using_def.points_y.f[1] = ((float) i->using_def.points_y.i[1]) /0x10000;
+     pa_log_debug("Volume Ramping: Point 1 is %d=%f, Point 2 is %d=%f\n", i->using_def.points_y.i[0], i->using_def.points_y.f[0],
+                                    i->using_def.points_y.i[1], i->using_def.points_y.f[1]);
+     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_ENVELOPE, NULL, 0, NULL) == 0);
+ }
+ /* Called from main context */
+ static void sink_input_set_ramping_info_for_mute(pa_sink_input* i, pa_bool_t mute, pa_usec_t t) {
+     int32_t cur_vol;
+     pa_assert(i);
+     i->using_def.n_points = 2;
+     i->using_def.points_x[0] = 0;
+     i->using_def.points_x[1] = t;
+     cur_vol = (int32_t) lrint( pa_sw_volume_to_linear(pa_cvolume_avg(&i->soft_volume)) * 0x10000);
+     if (mute) {
+         i->using_def.points_y.i[0] = cur_vol;
+         i->using_def.points_y.i[1] = 0;
+     } else {
+         i->using_def.points_y.i[0] = 0;
+         i->using_def.points_y.i[1] = cur_vol;
+     }
+     i->using_def.points_y.f[0] = ((float) i->using_def.points_y.i[0]) /0x10000;
+     i->using_def.points_y.f[1] = ((float) i->using_def.points_y.i[1]) /0x10000;
+     pa_log_debug("Mute Ramping: Point 1 is %d=%f, Point 2 is %d=%f\n", i->using_def.points_y.i[0], i->using_def.points_y.f[0],
+                    i->using_def.points_y.i[1], i->using_def.points_y.f[1]);
+     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_ENVELOPE, NULL, 0, NULL) == 0);
+ }
+ /* Called from IO context */
+ static void sink_input_release_envelope(pa_sink_input *i) {
+     pa_assert(i);
+     pa_assert(!i->thread_info.ramp_info.is_ramping);
+     pa_assert(i->thread_info.ramp_info.envelope_dead);
+     pa_envelope_free(i->thread_info.ramp_info.envelope);
+     i->thread_info.ramp_info.envelope = NULL;
+     i->thread_info.ramp_info.item = NULL;
+ }
+ /* Called from IO context */
+ static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes) {
+     pa_assert(i);
+     if (!i->thread_info.ramp_info.envelope_dead) {
+         pa_assert(i->thread_info.ramp_info.envelope);
+         int32_t envelope_length = pa_envelope_length(i->thread_info.ramp_info.envelope);
+         if (i->thread_info.ramp_info.envelope_dying > envelope_length) {
+             if ((i->thread_info.ramp_info.envelope_dying - nbytes) < envelope_length) {
+                 pa_log_debug("Envelope Become Alive");
+                 pa_envelope_rewind(i->thread_info.ramp_info.envelope, envelope_length - (i->thread_info.ramp_info.envelope_dying - nbytes));
+                 i->thread_info.ramp_info.is_ramping = TRUE;
+             }
+         } else if (i->thread_info.ramp_info.envelope_dying < envelope_length) {
+             if ((i->thread_info.ramp_info.envelope_dying - nbytes) <= 0) {
+                 pa_log_debug("Envelope Restart");
+                 pa_envelope_restart(i->thread_info.ramp_info.envelope);
+             }
+             else {
+                 pa_log_debug("Envelope Simple Rewind");
+                 pa_envelope_rewind(i->thread_info.ramp_info.envelope, nbytes);
+             }
+         }
+         i->thread_info.ramp_info.envelope_dying -= nbytes;
+         if (i->thread_info.ramp_info.envelope_dying <= 0)
+             i->thread_info.ramp_info.envelope_dying = 0;
+     }
+ }
+ void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute, pa_usec_t t){
+     pa_cvolume v;
+     pa_volume_t previous_virtual_volume, target_virtual_volume;
 -    pa_assert(pa_cvolume_compatible(volume, &i->sample_spec));
++    pa_sink_input_assert_ref(i);
++    pa_assert_ctl_context();
+     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+     pa_assert(volume);
+     pa_assert(pa_cvolume_valid(volume));
 -        volume = pa_sw_cvolume_multiply(&v, &v, volume);
++    pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
+     if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
+         v = i->sink->reference_volume;
+         pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
 -    if (pa_cvolume_equal(volume, &i->virtual_volume))
++
++        if (pa_cvolume_compatible(volume, &i->sample_spec))
++            volume = pa_sw_cvolume_multiply(&v, &v, volume);
++        else
++            volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume));
++    } else {
++        if (!pa_cvolume_compatible(volume, &i->sample_spec)) {
++            v = i->volume;
++            volume = pa_cvolume_scale(&v, pa_cvolume_max(volume));
++        }
+     }
 -    previous_virtual_volume = pa_cvolume_avg(&i->virtual_volume);
++    if (pa_cvolume_equal(volume, &i->volume)) {
++        i->save_volume = i->save_volume || save;
+         return;
++    }
 -    i->virtual_volume = *volume;
++    previous_virtual_volume = pa_cvolume_avg(&i->volume);
+     target_virtual_volume = pa_cvolume_avg(volume);
++
+     if (t > 0 && target_virtual_volume > 0)
+         pa_log_debug("SetVolumeWithRamping: Virtual Volume From %u=%f to %u=%f\n", previous_virtual_volume, pa_sw_volume_to_linear(previous_virtual_volume),
+                                              target_virtual_volume, pa_sw_volume_to_linear(target_virtual_volume));
 -        pa_cvolume new_volume;
 -
++    i->volume = *volume;
+     i->save_volume = save;
+     /* Set this flag before the following code modify i->thread_info.soft_volume */
+     if (t > 0 && target_virtual_volume > 0)
+         pa_atomic_store(&i->before_ramping_v, 1);
+     if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
 -        pa_sink_update_flat_volume(i->sink, &new_volume);
 -        pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE, FALSE);
+         /* We are in flat volume mode, so let's update all sink input
+          * volumes and update the flat volume of the sink */
 -
++        pa_sink_set_volume(i->sink, NULL, TRUE, save);
+     } else {
 -        pa_sink_input_set_relative_volume(i, volume);
 -
 -        /* Hooks have the ability to play games with i->soft_volume */
 -        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+         /* OK, we are in normal volume mode. The volume only affects
+          * ourselves */
++        set_real_ratio(i, volume);
+         /* Copy the new soft_volume to the thread_info struct */
+         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+     }
+     if (t > 0 && target_virtual_volume > 0)
+         sink_input_set_ramping_info(i, previous_virtual_volume, target_virtual_volume, t);
++    /* The volume changed, let's tell people so */
++    if (i->volume_changed)
++        i->volume_changed(i);
++
+     /* The virtual volume changed, let's tell people so */
+     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
+ void pa_sink_input_set_mute_with_ramping(pa_sink_input *i, pa_bool_t mute, pa_bool_t save, pa_usec_t t){
+     pa_assert(i);
+     pa_sink_input_assert_ref(i);
+     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+     if (!i->muted == !mute)
+         return;
+     i->muted = mute;
+     i->save_muted = save;
+     /* Set this flag before the following code modify i->thread_info.muted, otherwise distortion will be heard */
+     if (t > 0)
+         pa_atomic_store(&i->before_ramping_m, 1);
+     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
+     if (t > 0)
+         sink_input_set_ramping_info_for_mute(i, mute, t);
+     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
@@@ -381,7 -373,11 +398,11 @@@ pa_bool_t pa_sink_input_safe_to_remove(
  
  pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
  
 -/* To be used by sink.c only */
 -void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v);
 +#define pa_sink_input_assert_io_context(s) \
 +    pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state))
  
+ /* Volume ramping*/
+ void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute, pa_usec_t t);
+ void pa_sink_input_set_mute_with_ramping(pa_sink_input *i, pa_bool_t mute, pa_bool_t save, pa_usec_t t);
  #endif
@@@ -1728,9 -1597,11 +1728,12 @@@ static void sync_input_volumes_within_t
      void *state = NULL;
  
      pa_sink_assert_ref(s);
 +    pa_sink_assert_io_context(s);
  
 -    while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) {
 +    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;