1 --- a/src/map-file 2016-04-12 15:03:17.009975690 +0200
2 +++ b/src/map-file 2016-04-12 15:03:52.389975631 +0200
9 +pa_cvolume_ramp_channel_ramp_set;
12 pa_cvolume_scale_mask;
13 --- a/src/pulse/def.h 2016-04-12 15:05:58.245975421 +0200
14 +++ b/src/pulse/def.h 2016-04-12 15:13:19.424974685 +0200
16 * consider absolute when the sink is in flat volume mode,
17 * relative otherwise. \since 0.9.20 */
19 - PA_STREAM_PASSTHROUGH = 0x80000U
20 + PA_STREAM_PASSTHROUGH = 0x80000U,
21 /**< Used to tag content that will be rendered by passthrough sinks.
22 * The data will be left as is and not reformatted, resampled.
25 + PA_STREAM_START_RAMP_MUTED = 0x100000U
26 + /**< Used to tag content that the stream will be started ramp volume
27 + * muted so that you can nicely fade it in */
33 #define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
34 #define PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
35 #define PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
36 +#define PA_STREAM_START_RAMP_MUTED PA_STREAM_START_RAMP_MUTED
40 @@ -1047,6 +1052,13 @@
44 +/** \cond fulldocs */
45 +#define PA_VOLUMER_RAMP_TYPE_LINEAR PA_VOLUMER_RAMP_TYPE_LINEAR
46 +#define PA_VOLUMER_RAMP_TYPE_LOGARITHMIC PA_VOLUMER_RAMP_TYPE_LOGARITHMIC
47 +#define PA_VOLUMER_RAMP_TYPE_CUBIC PA_VOLUMER_RAMP_TYPE_CUBIC
54 --- a/src/pulse/volume.c 2016-04-12 15:13:38.598974653 +0200
55 +++ b/src/pulse/volume.c 2016-04-12 15:27:57.729973219 +0200
60 - pa_return_val_if_fail(pa_cvolume_valid(a), 0);
61 + if (pa_cvolume_valid(a) == 0)
64 + /* pa_return_val_if_fail(pa_cvolume_valid(a), 0); */
65 pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0);
67 for (c = 0; c < a->channels; c++)
70 return pa_cvolume_scale(v, m);
73 +pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp) {
80 + for (c = 0; c < PA_CHANNELS_MAX; c++) {
81 + ramp->ramps[c].type = PA_VOLUME_RAMP_TYPE_LINEAR;
82 + ramp->ramps[c].length = 0;
83 + ramp->ramps[c].target = PA_VOLUME_INVALID;
89 +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) {
93 + pa_assert(channels > 0);
94 + pa_assert(time >= 0);
95 + pa_assert(channels <= PA_CHANNELS_MAX);
97 + ramp->channels = (uint8_t) channels;
99 + for (i = 0; i < ramp->channels; i++) {
100 + ramp->ramps[i].type = type;
101 + ramp->ramps[i].length = time;
102 + ramp->ramps[i].target = PA_CLAMP_VOLUME(vol);
108 +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) {
111 + pa_assert(channel <= ramp->channels);
112 + pa_assert(time >= 0);
114 + ramp->ramps[channel].type = type;
115 + ramp->ramps[channel].length = time;
116 + ramp->ramps[channel].target = PA_CLAMP_VOLUME(vol);
120 --- a/src/pulse/volume.h 2016-04-12 15:40:34.989971955 +0200
121 +++ b/src/pulse/volume.h 2016-04-12 15:38:50.708972129 +0200
123 * the channels are kept. \since 0.9.16 */
124 pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec);
126 +/** Volume ramp type
128 +typedef enum pa_volume_ramp_type {
129 + PA_VOLUME_RAMP_TYPE_LINEAR = 0, /**< linear */
130 + PA_VOLUME_RAMP_TYPE_LOGARITHMIC = 1, /**< logarithmic */
131 + PA_VOLUME_RAMP_TYPE_CUBIC = 2,
132 +} pa_volume_ramp_type_t;
134 +/** A structure encapsulating a volume ramp */
135 +typedef struct pa_volume_ramp_t {
136 + pa_volume_ramp_type_t type;
138 + pa_volume_t target;
141 +/** A structure encapsulating a multichannel volume ramp */
142 +typedef struct pam_cvolume_ramp {
144 + pa_volume_ramp_t ramps[PA_CHANNELS_MAX];
147 +/** Init volume ramp struct */
148 +pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp);
150 +/** Set first n channels of ramp struct to certain value */
151 +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);
153 +/** Set individual channel in the channel struct */
154 +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);
159 --- a/src/pulsecore/sample-util.c 2016-04-12 15:41:51.812971827 +0200
160 +++ b/src/pulsecore/sample-util.c 2016-04-12 16:31:56.795966812 +0200
163 #define PA_SILENCE_MAX (PA_PAGE_SIZE*16)
165 +#define VOLUME_PADDING 32
172 pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
175 @@ -403,3 +410,292 @@
176 usec = pa_bytes_to_usec_round_up(size, from);
177 return pa_usec_to_bytes_round_up(usec, to);
180 +static void calc_linear_integer_volume_no_mapping(int32_t linear[], float volume[], unsigned nchannels) {
181 + unsigned channel, padding;
186 + for (channel = 0; channel < nchannels; channel++)
187 + linear[channel] = (int32_t) lrint(volume[channel] * 0x10000U);
189 + for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
190 + linear[channel] = linear[padding];
193 +static void calc_linear_float_volume_no_mapping(float linear[], float volume[], unsigned nchannels) {
194 + unsigned channel, padding;
199 + for (channel = 0; channel < nchannels; channel++)
200 + linear[channel] = volume[channel];
202 + for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
203 + linear[channel] = linear[padding];
206 +typedef void (*pa_calc_volume_no_mapping_func_t) (void *volumes, float *volume, int channels);
208 +static const pa_calc_volume_no_mapping_func_t calc_volume_table_no_mapping[] = {
209 + [PA_SAMPLE_U8] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
210 + [PA_SAMPLE_ALAW] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
211 + [PA_SAMPLE_ULAW] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
212 + [PA_SAMPLE_S16LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
213 + [PA_SAMPLE_S16BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
214 + [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping,
215 + [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping,
216 + [PA_SAMPLE_S32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
217 + [PA_SAMPLE_S32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
218 + [PA_SAMPLE_S24LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
219 + [PA_SAMPLE_S24BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
220 + [PA_SAMPLE_S24_32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping,
221 + [PA_SAMPLE_S24_32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping
224 +static const unsigned format_sample_size_table[] = {
225 + [PA_SAMPLE_U8] = 1,
226 + [PA_SAMPLE_ALAW] = 1,
227 + [PA_SAMPLE_ULAW] = 1,
228 + [PA_SAMPLE_S16LE] = 2,
229 + [PA_SAMPLE_S16BE] = 2,
230 + [PA_SAMPLE_FLOAT32LE] = 4,
231 + [PA_SAMPLE_FLOAT32BE] = 4,
232 + [PA_SAMPLE_S32LE] = 4,
233 + [PA_SAMPLE_S32BE] = 4,
234 + [PA_SAMPLE_S24LE] = 3,
235 + [PA_SAMPLE_S24BE] = 3,
236 + [PA_SAMPLE_S24_32LE] = 4,
237 + [PA_SAMPLE_S24_32BE] = 4
240 +static float calc_volume_ramp_linear(pa_volume_ramp_int_t *ramp) {
242 + pa_assert(ramp->length > 0);
244 + /* basic linear interpolation */
245 + return ramp->start + (ramp->length - ramp->left) * (ramp->end - ramp->start) / (float) ramp->length;
248 +static float calc_volume_ramp_logarithmic(pa_volume_ramp_int_t *ramp) {
253 + pa_assert(ramp->length > 0);
255 + if (ramp->end > ramp->start) {
260 + temp = ramp->length - ramp->left;
265 + x_val = temp == 0 ? 0.0 : powf(temp, 10);
267 + /* base 10 logarithmic interpolation */
268 + return s + x_val * (e - s) / powf(ramp->length, 10);
271 +static float calc_volume_ramp_cubic(pa_volume_ramp_int_t *ramp) {
276 + pa_assert(ramp->length > 0);
278 + if (ramp->end > ramp->start) {
283 + temp = ramp->length - ramp->left;
288 + x_val = temp == 0 ? 0.0 : cbrtf(temp);
290 + /* cubic interpolation */
291 + return s + x_val * (e - s) / cbrtf(ramp->length);
294 +typedef float (*pa_calc_volume_ramp_func_t) (pa_volume_ramp_int_t *);
296 +static const pa_calc_volume_ramp_func_t calc_volume_ramp_table[] = {
297 + [PA_VOLUME_RAMP_TYPE_LINEAR] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_linear,
298 + [PA_VOLUME_RAMP_TYPE_LOGARITHMIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_logarithmic,
299 + [PA_VOLUME_RAMP_TYPE_CUBIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_cubic
302 +static void calc_volume_ramps(pa_cvolume_ramp_int *ram, float *vol)
306 + for (i = 0; i < ram->channels; i++) {
307 + if (ram->ramps[i].left <= 0) {
308 + if (ram->ramps[i].target == PA_VOLUME_NORM) {
312 + vol[i] = ram->ramps[i].curr = calc_volume_ramp_table[ram->ramps[i].type] (&ram->ramps[i]);
313 + ram->ramps[i].left--;
318 +void pa_volume_ramp_memchunk(
320 + const pa_sample_spec *spec,
321 + pa_cvolume_ramp_int *ramp) {
324 + volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING];
325 + float vol[PA_CHANNELS_MAX + VOLUME_PADDING];
326 + pa_do_volume_func_t do_volume;
327 + long length_in_frames;
332 + pa_assert(pa_frame_aligned(c->length, spec));
335 + length_in_frames = c->length / format_sample_size_table[spec->format] / spec->channels;
337 + if (pa_memblock_is_silence(c->memblock)) {
338 + for (i = 0; i < ramp->channels; i++) {
339 + if (ramp->ramps[i].length > 0)
340 + ramp->ramps[i].length -= length_in_frames;
345 + if (spec->format < 0 || spec->format >= PA_SAMPLE_MAX) {
346 + pa_log_warn("Unable to change volume of format");
350 + do_volume = pa_get_volume_func(spec->format);
351 + pa_assert(do_volume);
353 + ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index;
355 + for (i = 0; i < length_in_frames; i++) {
356 + calc_volume_ramps(ramp, vol);
357 + calc_volume_table_no_mapping[spec->format] ((void *)linear, vol, spec->channels);
359 + /* we only process one frame per iteration */
360 + do_volume (ptr, (void *)linear, spec->channels, format_sample_size_table[spec->format] * spec->channels);
362 + /* pa_log_debug("1: %d 2: %d", linear[0], linear[1]); */
364 + ptr = (uint8_t*)ptr + format_sample_size_table[spec->format] * spec->channels;
367 + pa_memblock_release(c->memblock);
370 +pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate) {
374 + for (i = 0; i < dst->channels; i++) {
375 + dst->ramps[i].type = src->ramps[i].type;
376 + /* ms to samples */
377 + dst->ramps[i].length = src->ramps[i].length * sample_rate / 1000;
378 + dst->ramps[i].left = dst->ramps[i].length;
379 + dst->ramps[i].start = dst->ramps[i].end;
380 + dst->ramps[i].target = src->ramps[i].target;
381 + /* scale to pulse internal mapping so that when ramp is over there's no glitch in volume */
382 + temp = src->ramps[i].target / (float)0x10000U;
383 + dst->ramps[i].end = temp * temp * temp;
389 +bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp) {
392 + for (i = 0; i < ramp->channels; i++) {
393 + if (ramp->ramps[i].left > 0)
400 +bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp) {
403 + for (i = 0; i < ramp->channels; i++) {
404 + if (ramp->ramps[i].target != PA_VOLUME_NORM)
411 +pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume) {
414 + volume->channels = ramp->channels;
416 + for (i = 0; i < ramp->channels; i++)
417 + volume->values[i] = ramp->ramps[i].target;
422 +pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst) {
425 + for (i = 0; i < src->channels; i++) {
426 + /* if new vols are invalid, copy old ramp i.e. no effect */
427 + if (dst->ramps[i].target == PA_VOLUME_INVALID)
428 + dst->ramps[i] = src->ramps[i];
429 + /* if there's some old ramp still left */
430 + else if (src->ramps[i].left > 0)
431 + dst->ramps[i].start = src->ramps[i].curr;
437 +pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels) {
441 + src->channels = channels;
443 + for (i = 0; i < channels; i++) {
444 + src->ramps[i].type = PA_VOLUME_RAMP_TYPE_LINEAR;
445 + src->ramps[i].length = 0;
446 + src->ramps[i].left = 0;
447 + if (vol == PA_VOLUME_NORM) {
448 + src->ramps[i].start = 1.0;
449 + src->ramps[i].end = 1.0;
450 + src->ramps[i].curr = 1.0;
452 + else if (vol == PA_VOLUME_MUTED) {
453 + src->ramps[i].start = 0.0;
454 + src->ramps[i].end = 0.0;
455 + src->ramps[i].curr = 0.0;
458 + temp = vol / (float)0x10000U;
459 + src->ramps[i].start = temp * temp * temp;
460 + src->ramps[i].end = src->ramps[i].start;
461 + src->ramps[i].curr = src->ramps[i].start;
463 + src->ramps[i].target = vol;
468 --- a/src/pulsecore/sample-util.h 2016-04-12 15:53:06.327970701 +0200
469 +++ b/src/pulsecore/sample-util.h 2016-04-12 16:24:16.356967580 +0200
472 pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length);
474 +typedef struct pa_volume_ramp_int_t {
475 + pa_volume_ramp_type_t type;
481 + pa_volume_t target;
482 +} pa_volume_ramp_int_t;
484 +typedef struct pa_cvolume_ramp_int {
486 + pa_volume_ramp_int_t ramps[PA_CHANNELS_MAX];
487 +} pa_cvolume_ramp_int;
489 +pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate);
490 +bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp);
491 +bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp);
492 +pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst);
493 +pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels);
494 +pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume);
496 +void pa_volume_ramp_memchunk(
498 + const pa_sample_spec *spec,
499 + pa_cvolume_ramp_int *ramp);
501 size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
503 bool pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;