Upgrade to thud
[AGL/meta-agl.git] / meta-agl-profile-core / recipes-multimedia / pulseaudio / pulseaudio-12.2 / 0002-volume-ramp-additions-to-the-low-level-infra.patch
1 From c28b5faa6745b525c11d1f54d8bf52386a90fcc1 Mon Sep 17 00:00:00 2001
2 From: Sangchul Lee <sangchul1011@gmail.com>
3 Date: Sat, 27 Aug 2016 21:33:16 +0900
4 Subject: [PATCH 2/6] volume ramp: additions to the low level infra
5
6 The original patch is
7  - https://review.tizen.org/git/?p=platform/upstream/pulseaudio.git;a=commit;h=df1c4275ed79e0b708c75b92f9d247e0492bc1f0
8  - by Jaska Uimonen <jaska.uimonen <at> helsinki.fi>
9
10 Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
11
12 ---
13  src/map-file        |   4 +
14  src/pulse/def.h     |  13 ++-
15  src/pulse/volume.c  |  74 ++++++++++++-
16  src/pulse/volume.h  |  33 ++++++
17  src/pulsecore/mix.c | 310 ++++++++++++++++++++++++++++++++++++++++++++++++++++
18  src/pulsecore/mix.h |  27 +++++
19  6 files changed, 459 insertions(+), 2 deletions(-)
20
21 diff --git a/src/map-file b/src/map-file
22 index 9b6cba2..946ab54 100644
23 --- a/src/map-file
24 +++ b/src/map-file
25 @@ -138,6 +138,10 @@ pa_cvolume_max_mask;
26  pa_cvolume_merge;
27  pa_cvolume_min;
28  pa_cvolume_min_mask;
29 +pa_cvolume_ramp_equal;
30 +pa_cvolume_ramp_init;
31 +pa_cvolume_ramp_set;
32 +pa_cvolume_ramp_channel_ramp_set;
33  pa_cvolume_remap;
34  pa_cvolume_scale;
35  pa_cvolume_scale_mask;
36 diff --git a/src/pulse/def.h b/src/pulse/def.h
37 index 100df5b..b13eed7 100644
38 --- a/src/pulse/def.h
39 +++ b/src/pulse/def.h
40 @@ -349,11 +349,15 @@ typedef enum pa_stream_flags {
41       * consider absolute when the sink is in flat volume mode,
42       * relative otherwise. \since 0.9.20 */
43  
44 -    PA_STREAM_PASSTHROUGH = 0x80000U
45 +    PA_STREAM_PASSTHROUGH = 0x80000U,
46      /**< Used to tag content that will be rendered by passthrough sinks.
47       * The data will be left as is and not reformatted, resampled.
48       * \since 1.0 */
49  
50 +    PA_STREAM_START_RAMP_MUTED = 0x100000U
51 +    /**< Used to tag content that the stream will be started ramp volume
52 +     * muted so that you can nicely fade it in */
53 +
54  } pa_stream_flags_t;
55  
56  /** \cond fulldocs */
57 @@ -382,6 +386,7 @@ typedef enum pa_stream_flags {
58  #define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
59  #define PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
60  #define PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
61 +#define PA_STREAM_START_RAMP_MUTED PA_STREAM_START_RAMP_MUTED
62  
63  /** \endcond */
64  
65 @@ -1070,6 +1075,12 @@ typedef enum pa_port_available {
66  /** \endcond */
67  #endif
68  
69 +/** \cond fulldocs */
70 +#define PA_VOLUMER_RAMP_TYPE_LINEAR PA_VOLUMER_RAMP_TYPE_LINEAR
71 +#define PA_VOLUMER_RAMP_TYPE_LOGARITHMIC PA_VOLUMER_RAMP_TYPE_LOGARITHMIC
72 +#define PA_VOLUMER_RAMP_TYPE_CUBIC PA_VOLUMER_RAMP_TYPE_CUBIC
73 +/** \endcond */
74 +
75  PA_C_DECL_END
76  
77  #endif
78 diff --git a/src/pulse/volume.c b/src/pulse/volume.c
79 index ead5415..83a1e05 100644
80 --- a/src/pulse/volume.c
81 +++ b/src/pulse/volume.c
82 @@ -457,7 +457,10 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
83      unsigned c;
84      pa_assert(a);
85  
86 -    pa_return_val_if_fail(pa_cvolume_valid(a), 0);
87 +    if (pa_cvolume_valid(a) == 0)
88 +        abort();
89 +
90 +    /* pa_return_val_if_fail(pa_cvolume_valid(a), 0); */
91      pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0);
92  
93      for (c = 0; c < a->channels; c++)
94 @@ -989,3 +992,72 @@ pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) {
95  
96      return pa_cvolume_scale(v, m);
97  }
98 +
99 +int pa_cvolume_ramp_equal(const pa_cvolume_ramp *a, const pa_cvolume_ramp *b) {
100 +    int i;
101 +    pa_assert(a);
102 +    pa_assert(b);
103 +
104 +    if (PA_UNLIKELY(a == b))
105 +        return 1;
106 +
107 +    if (a->channels != b->channels)
108 +        return 0;
109 +
110 +    for (i = 0; i < a->channels; i++) {
111 +        if (a->ramps[i].type != b->ramps[i].type ||
112 +            a->ramps[i].length != b->ramps[i].length ||
113 +            a->ramps[i].target != b->ramps[i].target)
114 +            return 0;
115 +    }
116 +
117 +    return 1;
118 +}
119 +
120 +pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp) {
121 +    unsigned c;
122 +
123 +    pa_assert(ramp);
124 +
125 +    ramp->channels = 0;
126 +
127 +    for (c = 0; c < PA_CHANNELS_MAX; c++) {
128 +        ramp->ramps[c].type = PA_VOLUME_RAMP_TYPE_LINEAR;
129 +        ramp->ramps[c].length = 0;
130 +        ramp->ramps[c].target = PA_VOLUME_INVALID;
131 +    }
132 +
133 +    return ramp;
134 +}
135 +
136 +pa_cvolume_ramp* pa_cvolume_ramp_set(pa_cvolume_ramp *ramp, unsigned channels, pa_volume_ramp_type_t type, long time, pa_volume_t vol) {
137 +    int i;
138 +
139 +    pa_assert(ramp);
140 +    pa_assert(channels > 0);
141 +    pa_assert(time >= 0);
142 +    pa_assert(channels <= PA_CHANNELS_MAX);
143 +
144 +    ramp->channels = (uint8_t) channels;
145 +
146 +    for (i = 0; i < ramp->channels; i++) {
147 +        ramp->ramps[i].type = type;
148 +        ramp->ramps[i].length = time;
149 +        ramp->ramps[i].target = PA_CLAMP_VOLUME(vol);
150 +    }
151 +
152 +    return ramp;
153 +}
154 +
155 +pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol) {
156 +
157 +    pa_assert(ramp);
158 +    pa_assert(channel <= ramp->channels);
159 +    pa_assert(time >= 0);
160 +
161 +    ramp->ramps[channel].type = type;
162 +    ramp->ramps[channel].length = time;
163 +    ramp->ramps[channel].target = PA_CLAMP_VOLUME(vol);
164 +
165 +    return ramp;
166 +}
167 diff --git a/src/pulse/volume.h b/src/pulse/volume.h
168 index 2503c3f..0b74833 100644
169 --- a/src/pulse/volume.h
170 +++ b/src/pulse/volume.h
171 @@ -437,6 +437,39 @@ pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc);
172   * the channels are kept. Returns \a v, or NULL on error. \since 0.9.16 */
173  pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec);
174  
175 +/** Volume ramp type
176 +*/
177 +typedef enum pa_volume_ramp_type {
178 +    PA_VOLUME_RAMP_TYPE_LINEAR = 0,        /**< linear */
179 +    PA_VOLUME_RAMP_TYPE_LOGARITHMIC = 1,   /**< logarithmic */
180 +    PA_VOLUME_RAMP_TYPE_CUBIC = 2,
181 +} pa_volume_ramp_type_t;
182 +
183 +/** A structure encapsulating a volume ramp */
184 +typedef struct pa_volume_ramp_t {
185 +    pa_volume_ramp_type_t type;
186 +    long length;
187 +    pa_volume_t target;
188 +} pa_volume_ramp_t;
189 +
190 +/** A structure encapsulating a multichannel volume ramp */
191 +typedef struct pa_cvolume_ramp {
192 +    uint8_t channels;
193 +    pa_volume_ramp_t ramps[PA_CHANNELS_MAX];
194 +} pa_cvolume_ramp;
195 +
196 +/** Return non-zero when *a == *b */
197 +int pa_cvolume_ramp_equal(const pa_cvolume_ramp *a, const pa_cvolume_ramp *b);
198 +
199 +/** Init volume ramp struct */
200 +pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp);
201 +
202 +/** Set first n channels of ramp struct to certain value */
203 +pa_cvolume_ramp* pa_cvolume_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol);
204 +
205 +/** Set individual channel in the channel struct */
206 +pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol);
207 +
208  PA_C_DECL_END
209  
210  #endif
211 diff --git a/src/pulsecore/mix.c b/src/pulsecore/mix.c
212 index 59622d7..1e4cc1e 100644
213 --- a/src/pulsecore/mix.c
214 +++ b/src/pulsecore/mix.c
215 @@ -724,3 +724,313 @@ void pa_volume_memchunk(
216  
217      pa_memblock_release(c->memblock);
218  }
219 +
220 +static void calc_linear_integer_volume_no_mapping(int32_t linear[], float volume[], unsigned nchannels) {
221 +    unsigned channel, padding;
222 +
223 +    pa_assert(linear);
224 +    pa_assert(volume);
225 +
226 +    for (channel = 0; channel < nchannels; channel++)
227 +        linear[channel] = (int32_t) lrint(volume[channel] * 0x10000U);
228 +
229 +    for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
230 +        linear[channel] = linear[padding];
231 +}
232 +
233 +static void calc_linear_float_volume_no_mapping(float linear[], float volume[], unsigned nchannels) {
234 +    unsigned channel, padding;
235 +
236 +    pa_assert(linear);
237 +    pa_assert(volume);
238 +
239 +    for (channel = 0; channel < nchannels; channel++)
240 +        linear[channel] = volume[channel];
241 +
242 +    for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
243 +        linear[channel] = linear[padding];
244 +}
245 +
246 +typedef void (*pa_calc_volume_no_mapping_func_t) (void *volumes, float *volume, int channels);
247 +
248 +static const pa_calc_volume_no_mapping_func_t calc_volume_table_no_mapping[] = {
249 +  [PA_SAMPLE_U8]        = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
250 +  [PA_SAMPLE_ALAW]      = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
251 +  [PA_SAMPLE_ULAW]      = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
252 +  [PA_SAMPLE_S16LE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
253 +  [PA_SAMPLE_S16BE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
254 +  [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping,
255 +  [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping,
256 +  [PA_SAMPLE_S32LE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
257 +  [PA_SAMPLE_S32BE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
258 +  [PA_SAMPLE_S24LE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
259 +  [PA_SAMPLE_S24BE]     = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
260 +  [PA_SAMPLE_S24_32LE]  = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
261 +  [PA_SAMPLE_S24_32BE]  = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping
262 +};
263 +
264 +static const unsigned format_sample_size_table[] = {
265 +  [PA_SAMPLE_U8]        = 1,
266 +  [PA_SAMPLE_ALAW]      = 1,
267 +  [PA_SAMPLE_ULAW]      = 1,
268 +  [PA_SAMPLE_S16LE]     = 2,
269 +  [PA_SAMPLE_S16BE]     = 2,
270 +  [PA_SAMPLE_FLOAT32LE] = 4,
271 +  [PA_SAMPLE_FLOAT32BE] = 4,
272 +  [PA_SAMPLE_S32LE]     = 4,
273 +  [PA_SAMPLE_S32BE]     = 4,
274 +  [PA_SAMPLE_S24LE]     = 3,
275 +  [PA_SAMPLE_S24BE]     = 3,
276 +  [PA_SAMPLE_S24_32LE]  = 4,
277 +  [PA_SAMPLE_S24_32BE]  = 4
278 +};
279 +
280 +static float calc_volume_ramp_linear(pa_volume_ramp_int_t *ramp) {
281 +    pa_assert(ramp);
282 +    pa_assert(ramp->length > 0);
283 +
284 +    /* basic linear interpolation */
285 +    return ramp->start + (ramp->length - ramp->left) * (ramp->end - ramp->start) / (float) ramp->length;
286 +}
287 +
288 +static float calc_volume_ramp_logarithmic(pa_volume_ramp_int_t *ramp) {
289 +    float x_val, s, e;
290 +    long temp;
291 +
292 +    pa_assert(ramp);
293 +    pa_assert(ramp->length > 0);
294 +
295 +    if (ramp->end > ramp->start) {
296 +        temp = ramp->left;
297 +        s = ramp->end;
298 +        e = ramp->start;
299 +    } else {
300 +        temp = ramp->length - ramp->left;
301 +        s = ramp->start;
302 +        e = ramp->end;
303 +    }
304 +
305 +    x_val = temp == 0 ? 0.0 : powf(temp, 10);
306 +
307 +    /* base 10 logarithmic interpolation */
308 +    return s + x_val * (e - s) / powf(ramp->length, 10);
309 +}
310 +
311 +static float calc_volume_ramp_cubic(pa_volume_ramp_int_t *ramp) {
312 +    float x_val, s, e;
313 +    long temp;
314 +
315 +    pa_assert(ramp);
316 +    pa_assert(ramp->length > 0);
317 +
318 +    if (ramp->end > ramp->start) {
319 +        temp = ramp->left;
320 +        s = ramp->end;
321 +        e = ramp->start;
322 +    } else {
323 +        temp = ramp->length - ramp->left;
324 +        s = ramp->start;
325 +        e = ramp->end;
326 +    }
327 +
328 +    x_val = temp == 0 ? 0.0 : cbrtf(temp);
329 +
330 +    /* cubic interpolation */
331 +    return s + x_val * (e - s) / cbrtf(ramp->length);
332 +}
333 +
334 +typedef float (*pa_calc_volume_ramp_func_t) (pa_volume_ramp_int_t *);
335 +
336 +static const pa_calc_volume_ramp_func_t calc_volume_ramp_table[] = {
337 +    [PA_VOLUME_RAMP_TYPE_LINEAR] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_linear,
338 +    [PA_VOLUME_RAMP_TYPE_LOGARITHMIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_logarithmic,
339 +    [PA_VOLUME_RAMP_TYPE_CUBIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_cubic
340 +};
341 +
342 +static void calc_volume_ramps(pa_cvolume_ramp_int *ram, float *vol)
343 +{
344 +    int i;
345 +
346 +    for (i = 0; i < ram->channels; i++) {
347 +        if (ram->ramps[i].left <= 0) {
348 +            if (ram->ramps[i].target == PA_VOLUME_NORM) {
349 +                vol[i] = 1.0;
350 +            }
351 +        } else {
352 +            vol[i] = ram->ramps[i].curr = calc_volume_ramp_table[ram->ramps[i].type] (&ram->ramps[i]);
353 +            ram->ramps[i].left--;
354 +        }
355 +    }
356 +}
357 +
358 +void pa_volume_ramp_memchunk(
359 +        pa_memchunk *c,
360 +        const pa_sample_spec *spec,
361 +        pa_cvolume_ramp_int *ramp) {
362 +
363 +    void *ptr;
364 +    volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING];
365 +    float vol[PA_CHANNELS_MAX + VOLUME_PADDING];
366 +    pa_do_volume_func_t do_volume;
367 +    long length_in_frames;
368 +    int i;
369 +
370 +    pa_assert(c);
371 +    pa_assert(spec);
372 +    pa_assert(pa_frame_aligned(c->length, spec));
373 +    pa_assert(ramp);
374 +
375 +    length_in_frames = c->length / format_sample_size_table[spec->format] / spec->channels;
376 +
377 +    if (pa_memblock_is_silence(c->memblock)) {
378 +        for (i = 0; i < ramp->channels; i++) {
379 +            if (ramp->ramps[i].length > 0)
380 +                ramp->ramps[i].length -= length_in_frames;
381 +        }
382 +        return;
383 +    }
384 +
385 +    if (spec->format < 0 || spec->format >= PA_SAMPLE_MAX) {
386 +      pa_log_warn("Unable to change volume of format");
387 +      return;
388 +    }
389 +
390 +    do_volume = pa_get_volume_func(spec->format);
391 +    pa_assert(do_volume);
392 +
393 +    ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
394 +
395 +    for (i = 0; i < length_in_frames; i++) {
396 +        calc_volume_ramps(ramp, vol);
397 +        calc_volume_table_no_mapping[spec->format] ((void *)linear, vol, spec->channels);
398 +
399 +        /* we only process one frame per iteration */
400 +        do_volume (ptr, (void *)linear, spec->channels, format_sample_size_table[spec->format] * spec->channels);
401 +
402 +        /* pa_log_debug("1: %d  2: %d", linear[0], linear[1]); */
403 +
404 +        ptr = (uint8_t*)ptr + format_sample_size_table[spec->format] * spec->channels;
405 +    }
406 +
407 +    pa_memblock_release(c->memblock);
408 +}
409 +
410 +pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate) {
411 +
412 +    int i, j, channels, remaining_channels;
413 +    float temp;
414 +
415 +    if (dst->channels < src->channels) {
416 +        channels = dst->channels;
417 +        remaining_channels = 0;
418 +    }
419 +    else {
420 +        channels = src->channels;
421 +        remaining_channels = dst->channels;
422 +    }
423 +
424 +    for (i = 0; i < channels; i++) {
425 +        dst->ramps[i].type = src->ramps[i].type;
426 +        /* ms to samples */
427 +        dst->ramps[i].length = src->ramps[i].length * sample_rate / 1000;
428 +        dst->ramps[i].left = dst->ramps[i].length;
429 +        dst->ramps[i].start = dst->ramps[i].end;
430 +        dst->ramps[i].target = src->ramps[i].target;
431 +        /* scale to pulse internal mapping so that when ramp is over there's no glitch in volume */
432 +        temp = src->ramps[i].target / (float)0x10000U;
433 +        dst->ramps[i].end = temp * temp * temp;
434 +    }
435 +
436 +    j = i;
437 +
438 +    for (i--; j < remaining_channels; j++) {
439 +        dst->ramps[j].type = dst->ramps[i].type;
440 +        dst->ramps[j].length = dst->ramps[i].length;
441 +        dst->ramps[j].left = dst->ramps[i].left;
442 +        dst->ramps[j].start = dst->ramps[i].start;
443 +        dst->ramps[j].target = dst->ramps[i].target;
444 +        dst->ramps[j].end = dst->ramps[i].end;
445 +    }
446 +
447 +    return dst;
448 +}
449 +
450 +bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp) {
451 +    int i;
452 +
453 +    for (i = 0; i < ramp->channels; i++) {
454 +        if (ramp->ramps[i].left > 0)
455 +            return true;
456 +    }
457 +
458 +    return false;
459 +}
460 +
461 +bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp) {
462 +    int i;
463 +
464 +    for (i = 0; i < ramp->channels; i++) {
465 +        if (ramp->ramps[i].target != PA_VOLUME_NORM)
466 +            return true;
467 +    }
468 +
469 +    return false;
470 +}
471 +
472 +pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume) {
473 +    int i = 0;
474 +
475 +    volume->channels = ramp->channels;
476 +
477 +    for (i = 0; i < ramp->channels; i++)
478 +        volume->values[i] = ramp->ramps[i].target;
479 +
480 +    return volume;
481 +}
482 +
483 +pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst) {
484 +    int i;
485 +
486 +    for (i = 0; i < src->channels; i++) {
487 +        /* if new vols are invalid, copy old ramp i.e. no effect */
488 +        if (dst->ramps[i].target == PA_VOLUME_INVALID)
489 +            dst->ramps[i] = src->ramps[i];
490 +        /* if there's some old ramp still left */
491 +        else if (src->ramps[i].left > 0)
492 +            dst->ramps[i].start = src->ramps[i].curr;
493 +    }
494 +
495 +    return dst;
496 +}
497 +
498 +pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels) {
499 +    int i;
500 +    float temp;
501 +
502 +    src->channels = channels;
503 +
504 +    for (i = 0; i < channels; i++) {
505 +        src->ramps[i].type = PA_VOLUME_RAMP_TYPE_LINEAR;
506 +        src->ramps[i].length = 0;
507 +        src->ramps[i].left = 0;
508 +        if (vol == PA_VOLUME_NORM) {
509 +            src->ramps[i].start = 1.0;
510 +            src->ramps[i].end = 1.0;
511 +            src->ramps[i].curr = 1.0;
512 +        }
513 +        else if (vol == PA_VOLUME_MUTED) {
514 +            src->ramps[i].start = 0.0;
515 +            src->ramps[i].end = 0.0;
516 +            src->ramps[i].curr = 0.0;
517 +        }
518 +        else {
519 +            temp = vol / (float)0x10000U;
520 +            src->ramps[i].start = temp * temp * temp;
521 +            src->ramps[i].end = src->ramps[i].start;
522 +            src->ramps[i].curr = src->ramps[i].start;
523 +        }
524 +        src->ramps[i].target = vol;
525 +    }
526 +
527 +    return src;
528 +}
529 diff --git a/src/pulsecore/mix.h b/src/pulsecore/mix.h
530 index 8102bcd..0f86b6f 100644
531 --- a/src/pulsecore/mix.h
532 +++ b/src/pulsecore/mix.h
533 @@ -59,4 +59,31 @@ void pa_volume_memchunk(
534      const pa_sample_spec *spec,
535      const pa_cvolume *volume);
536  
537 +typedef struct pa_volume_ramp_int_t {
538 +    pa_volume_ramp_type_t type;
539 +    long length;
540 +    long left;
541 +    float start;
542 +    float end;
543 +    float curr;
544 +    pa_volume_t target;
545 +} pa_volume_ramp_int_t;
546 +
547 +typedef struct pa_cvolume_ramp_int {
548 +    uint8_t channels;
549 +    pa_volume_ramp_int_t ramps[PA_CHANNELS_MAX];
550 +} pa_cvolume_ramp_int;
551 +
552 +pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate);
553 +bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp);
554 +bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp);
555 +pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst);
556 +pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels);
557 +pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume);
558 +
559 +void pa_volume_ramp_memchunk(
560 +        pa_memchunk *c,
561 +        const pa_sample_spec *spec,
562 +        pa_cvolume_ramp_int *ramp);
563 +
564  #endif