volume ramp: additions to the low level infra
authorJaska Uimonen <jaska.uimonen@helsinki.fi>
Fri, 1 Jun 2012 11:37:03 +0000 (14:37 +0300)
committerJaska Uimonen <jaska.uimonen@intel.com>
Mon, 10 Dec 2012 20:43:56 +0000 (22:43 +0200)
src/pulse/def.h
src/pulsecore/sample-util.c
src/pulsecore/sample-util.h

index b939319..cc7405a 100644 (file)
@@ -314,11 +314,15 @@ typedef enum pa_stream_flags {
      * consider absolute when the sink is in flat volume mode,
      * relative otherwise. \since 0.9.20 */
 
-    PA_STREAM_PASSTHROUGH = 0x80000U
+    PA_STREAM_PASSTHROUGH = 0x80000U,
     /**< Used to tag content that will be rendered by passthrough sinks.
      * The data will be left as is and not reformatted, resampled.
      * \since 1.0 */
 
+    PA_STREAM_START_RAMP_MUTED = 0x100000U
+    /**< Used to tag content that the stream will be started ramp volume
+     * muted so that you can nicely fade it in */
+
 } pa_stream_flags_t;
 
 /** \cond fulldocs */
@@ -347,6 +351,7 @@ typedef enum pa_stream_flags {
 #define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
 #define PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
 #define PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
+#define PA_STREAM_START_RAMP_MUTED PA_STREAM_START_RAMP_MUTED
 
 /** \endcond */
 
@@ -1006,6 +1011,21 @@ typedef enum pa_port_available {
 
 /** \endcond */
 
+
+/** Volume ramp type
+*/
+typedef enum pa_volume_ramp_type {
+    PA_VOLUME_RAMP_TYPE_LINEAR = 0,        /**< linear */
+    PA_VOLUME_RAMP_TYPE_LOGARITHMIC = 1,   /**< logarithmic */
+    PA_VOLUME_RAMP_TYPE_CUBIC = 2,
+} pa_volume_ramp_type_t;
+
+/** \cond fulldocs */
+#define PA_VOLUMER_RAMP_TYPE_LINEAR PA_VOLUMER_RAMP_TYPE_LINEAR
+#define PA_VOLUMER_RAMP_TYPE_LOGARITHMIC PA_VOLUMER_RAMP_TYPE_LOGARITHMIC
+#define PA_VOLUMER_RAMP_TYPE_CUBIC PA_VOLUMER_RAMP_TYPE_CUBIC
+/** \endcond */
+
 PA_C_DECL_END
 
 #endif
index 38201b2..fccabc1 100644 (file)
@@ -1050,3 +1050,204 @@ size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_
     usec = pa_bytes_to_usec_round_up(size, from);
     return pa_usec_to_bytes_round_up(usec, to);
 }
+
+void calc_linear_integer_volume_no_mapping(int32_t [], float [], unsigned);
+void calc_linear_float_volume_no_mapping(float [], float [], unsigned);
+
+void calc_linear_integer_volume_no_mapping(int32_t linear[], float volume[], unsigned nchannels) {
+    unsigned channel, padding;
+
+    pa_assert(linear);
+    pa_assert(volume);
+
+    for (channel = 0; channel < nchannels; channel++)
+        linear[channel] = (int32_t) lrint(volume[channel] * 0x10000U);
+
+    for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
+        linear[channel] = linear[padding];
+}
+
+void calc_linear_float_volume_no_mapping(float linear[], float volume[], unsigned nchannels) {
+    unsigned channel, padding;
+
+    pa_assert(linear);
+    pa_assert(volume);
+
+    for (channel = 0; channel < nchannels; channel++)
+        linear[channel] = volume[channel];
+
+    for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
+        linear[channel] = linear[padding];
+}
+
+typedef void (*pa_calc_volume_no_mapping_func_t) (void *volumes, float *volume, int channels);
+
+static const pa_calc_volume_no_mapping_func_t calc_volume_table_no_mapping[] = {
+  [PA_SAMPLE_U8]        = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_ALAW]      = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_ULAW]      = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_S16LE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_S16BE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping,
+  [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping,
+  [PA_SAMPLE_S32LE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_S32BE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_S24LE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_S24BE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_S24_32LE]  = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
+  [PA_SAMPLE_S24_32BE]  = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping
+};
+
+static const unsigned format_sample_size_table[] = {
+  [PA_SAMPLE_U8]        = 1,
+  [PA_SAMPLE_ALAW]      = 1,
+  [PA_SAMPLE_ULAW]      = 1,
+  [PA_SAMPLE_S16LE]     = 2,
+  [PA_SAMPLE_S16BE]     = 2,
+  [PA_SAMPLE_FLOAT32LE] = 4,
+  [PA_SAMPLE_FLOAT32BE] = 4,
+  [PA_SAMPLE_S32LE]     = 4,
+  [PA_SAMPLE_S32BE]     = 4,
+  [PA_SAMPLE_S24LE]     = 3,
+  [PA_SAMPLE_S24BE]     = 3,
+  [PA_SAMPLE_S24_32LE]  = 4,
+  [PA_SAMPLE_S24_32BE]  = 4
+};
+
+float calc_volume_ramp_linear(pa_volume_ramp *);
+float calc_volume_ramp_logarithmic(pa_volume_ramp *);
+float calc_volume_ramp_cubic(pa_volume_ramp *);
+void pa_ramp_volume_multiply(float *, const pa_cvolume *, float);
+
+typedef float (*pa_calc_volume_ramp_func_t) (pa_volume_ramp *);
+
+static const pa_calc_volume_ramp_func_t calc_volume_ramp_table[] = {
+  [PA_VOLUME_RAMP_TYPE_LINEAR] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_linear,
+  [PA_VOLUME_RAMP_TYPE_LOGARITHMIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_logarithmic,
+  [PA_VOLUME_RAMP_TYPE_CUBIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_cubic
+};
+
+float calc_volume_ramp_linear(pa_volume_ramp *ramp) {
+
+    pa_assert(ramp);
+    pa_assert(ramp->length > 0);
+
+    /* basic linear interpolation */
+    return ramp->start + (ramp->length - ramp->left) * (ramp->end - ramp->start) / ramp->length;
+}
+
+float calc_volume_ramp_logarithmic(pa_volume_ramp *ramp) {
+
+    float x_val, s, e;
+    long temp;
+
+    pa_assert(ramp);
+    pa_assert(ramp->length > 0);
+
+    if (ramp->end > ramp->start) {
+       temp = ramp->left;
+       s = ramp->end;
+       e = ramp->start;
+    }
+    else {
+       temp = ramp->length - ramp->left;
+       s = ramp->start;
+       e = ramp->end;
+    }
+
+    x_val = temp == 0 ? 0.0 : pow((float)temp, 10);
+
+    /* base 10 logarithmic interpolation */
+    return s + x_val * (e - s) / pow((float)ramp->length, 10);
+}
+
+float calc_volume_ramp_cubic(pa_volume_ramp *ramp) {
+
+    float x_val, s, e;
+    long temp;
+
+    pa_assert(ramp);
+    pa_assert(ramp->length > 0);
+
+    if (ramp->end > ramp->start) {
+       temp = ramp->left;
+       s = ramp->end;
+       e = ramp->start;
+    }
+    else {
+       temp = ramp->length - ramp->left;
+       s = ramp->start;
+       e = ramp->end;
+    }
+
+    x_val = temp == 0 ? 0.0 : cbrt((float)temp);
+
+    /* cubic interpolation */
+    return s + x_val * (e - s) / cbrt((float)ramp->length);
+}
+
+void pa_ramp_volume_multiply(float *dest, const pa_cvolume *volume, float ramp_vol) {
+    unsigned i;
+    float conv;
+
+    pa_assert(dest);
+    pa_assert(volume);
+
+    /* multiplying ramp with volume with pa internal mapping */
+    for (i = 0; i < volume->channels; i++) {
+       conv = volume->values[i] / 0x10000U;
+       conv = conv * conv * conv;
+        dest[i] = ramp_vol * conv;
+    }
+}
+
+void pa_volume_ramp_memchunk(
+        pa_memchunk*c,
+        const pa_sample_spec *spec,
+       pa_volume_ramp *ramp,
+       const pa_cvolume *volume) {
+
+    void *ptr;
+    volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING];
+    float vol_adjusted[PA_CHANNELS_MAX + VOLUME_PADDING];
+    pa_do_volume_func_t do_volume;
+    long length_in_frames;
+
+    pa_assert(c);
+    pa_assert(spec);
+    pa_assert(pa_frame_aligned(c->length, spec));
+    pa_assert(ramp);
+
+    length_in_frames = c->length / format_sample_size_table[spec->format] / spec->channels;
+
+    if (pa_memblock_is_silence(c->memblock)) {
+       ramp->length -= length_in_frames;
+        return;
+    }
+
+    if (spec->format < 0 || spec->format >= PA_SAMPLE_MAX) {
+      pa_log_warn("Unable to change volume of format");
+      return;
+    }
+
+    do_volume = pa_get_volume_func(spec->format);
+    pa_assert(do_volume);
+
+    ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
+
+    for (int i = 0; i < length_in_frames; i++) {
+       ramp->curr = calc_volume_ramp_table[ramp->type] (ramp);
+       pa_ramp_volume_multiply(vol_adjusted, volume, ramp->curr);
+       calc_volume_table_no_mapping[spec->format] ((void *)linear, vol_adjusted, spec->channels);
+
+       /* we only process one sample per iteration */
+       do_volume (ptr, (void *)linear, spec->channels, format_sample_size_table[spec->format] * spec->channels);
+
+       ptr = (uint8_t*)ptr + format_sample_size_table[spec->format] * spec->channels;
+
+       if (ramp->left > 0)
+           ramp->left--;
+    }
+
+    pa_memblock_release(c->memblock);
+}
index cf79d43..215ed3f 100644 (file)
@@ -44,6 +44,17 @@ pa_memblock* pa_silence_memblock(pa_memblock *b, const pa_sample_spec *spec);
 
 pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length);
 
+/** A structure encapsulating a volume ramp */
+typedef struct pa_volume_ramp {
+    pa_volume_ramp_type_t type;
+    long length;
+    long left;
+    float start;
+    float end;
+    float curr;
+    pa_cvolume end_mapped;
+} pa_volume_ramp;
+
 typedef struct pa_mix_info {
     pa_memchunk chunk;
     pa_cvolume volume;
@@ -72,6 +83,12 @@ void pa_volume_memchunk(
     const pa_sample_spec *spec,
     const pa_cvolume *volume);
 
+void pa_volume_ramp_memchunk(
+        pa_memchunk*c,
+        const pa_sample_spec *spec,
+       pa_volume_ramp *ramp,
+       const pa_cvolume *volume);
+
 size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
 
 pa_bool_t pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;