xref: /third_party/pulseaudio/src/pulsecore/mix.c (revision 53a5a1b3)
1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6  Copyright 2013 Peter Meerwald <pmeerw@pmeerw.net>
7
8  PulseAudio is free software; you can redistribute it and/or modify
9  it under the terms of the GNU Lesser General Public License as published
10  by the Free Software Foundation; either version 2.1 of the License,
11  or (at your option) any later version.
12
13  PulseAudio is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU Lesser General Public License
19  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20***/
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <math.h>
27
28#include <pulsecore/sample-util.h>
29#include <pulsecore/macro.h>
30#include <pulsecore/g711.h>
31#include <pulsecore/endianmacros.h>
32
33#include "cpu.h"
34#include "mix.h"
35
36#define VOLUME_PADDING 32
37
38static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) {
39    unsigned channel, nchannels, padding;
40
41    pa_assert(linear);
42    pa_assert(volume);
43
44    nchannels = volume->channels;
45
46    for (channel = 0; channel < nchannels; channel++)
47        linear[channel] = (int32_t) lrint(pa_sw_volume_to_linear(volume->values[channel]) * 0x10000);
48
49    for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
50        linear[channel] = linear[padding];
51}
52
53static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) {
54    unsigned channel, nchannels, padding;
55
56    pa_assert(linear);
57    pa_assert(volume);
58
59    nchannels = volume->channels;
60
61    for (channel = 0; channel < nchannels; channel++)
62        linear[channel] = (float) pa_sw_volume_to_linear(volume->values[channel]);
63
64    for (padding = 0; padding < VOLUME_PADDING; padding++, channel++)
65        linear[channel] = linear[padding];
66}
67
68static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) {
69    unsigned k, channel;
70    float linear[PA_CHANNELS_MAX + VOLUME_PADDING];
71
72    pa_assert(streams);
73    pa_assert(spec);
74    pa_assert(volume);
75
76    calc_linear_float_volume(linear, volume);
77
78    for (k = 0; k < nstreams; k++) {
79
80        for (channel = 0; channel < spec->channels; channel++) {
81            pa_mix_info *m = streams + k;
82            m->linear[channel].i = (int32_t) lrint(pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel] * 0x10000);
83        }
84    }
85}
86
87static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) {
88    unsigned k, channel;
89    float linear[PA_CHANNELS_MAX + VOLUME_PADDING];
90
91    pa_assert(streams);
92    pa_assert(spec);
93    pa_assert(volume);
94
95    calc_linear_float_volume(linear, volume);
96
97    for (k = 0; k < nstreams; k++) {
98
99        for (channel = 0; channel < spec->channels; channel++) {
100            pa_mix_info *m = streams + k;
101            m->linear[channel].f = (float) (pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel]);
102        }
103    }
104}
105
106typedef void (*pa_calc_stream_volumes_func_t) (pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec);
107
108static const pa_calc_stream_volumes_func_t calc_stream_volumes_table[] = {
109  [PA_SAMPLE_U8]        = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
110  [PA_SAMPLE_ALAW]      = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
111  [PA_SAMPLE_ULAW]      = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
112  [PA_SAMPLE_S16LE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
113  [PA_SAMPLE_S16BE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
114  [PA_SAMPLE_FLOAT32LE] = (pa_calc_stream_volumes_func_t) calc_linear_float_stream_volumes,
115  [PA_SAMPLE_FLOAT32BE] = (pa_calc_stream_volumes_func_t) calc_linear_float_stream_volumes,
116  [PA_SAMPLE_S32LE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
117  [PA_SAMPLE_S32BE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
118  [PA_SAMPLE_S24LE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
119  [PA_SAMPLE_S24BE]     = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
120  [PA_SAMPLE_S24_32LE]  = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes,
121  [PA_SAMPLE_S24_32BE]  = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes
122};
123
124/* special case: mix 2 s16ne streams, 1 channel each */
125static void pa_mix2_ch1_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) {
126    const int16_t *ptr0 = streams[0].ptr;
127    const int16_t *ptr1 = streams[1].ptr;
128
129    const int32_t cv0 = streams[0].linear[0].i;
130    const int32_t cv1 = streams[1].linear[0].i;
131
132    length /= sizeof(int16_t);
133
134    for (; length > 0; length--) {
135        int32_t sum;
136
137        sum = pa_mult_s16_volume(*ptr0++, cv0);
138        sum += pa_mult_s16_volume(*ptr1++, cv1);
139
140        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
141        *data++ = sum;
142    }
143}
144
145/* special case: mix 2 s16ne streams, 2 channels each */
146static void pa_mix2_ch2_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) {
147    const int16_t *ptr0 = streams[0].ptr;
148    const int16_t *ptr1 = streams[1].ptr;
149
150    length /= sizeof(int16_t) * 2;
151
152    for (; length > 0; length--) {
153        int32_t sum;
154
155        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[0].i);
156        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[0].i);
157
158        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
159        *data++ = sum;
160
161        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[1].i);
162        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[1].i);
163
164        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
165        *data++ = sum;
166    }
167}
168
169/* special case: mix 2 s16ne streams */
170static void pa_mix2_s16ne(pa_mix_info streams[], unsigned channels, int16_t *data, unsigned length) {
171    const int16_t *ptr0 = streams[0].ptr;
172    const int16_t *ptr1 = streams[1].ptr;
173    unsigned channel = 0;
174
175    length /= sizeof(int16_t);
176
177    for (; length > 0; length--) {
178        int32_t sum;
179
180        sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[channel].i);
181        sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[channel].i);
182
183        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
184        *data++ = sum;
185
186        if (PA_UNLIKELY(++channel >= channels))
187            channel = 0;
188    }
189}
190
191/* special case: mix s16ne streams, 2 channels each */
192static void pa_mix_ch2_s16ne(pa_mix_info streams[], unsigned nstreams, int16_t *data, unsigned length) {
193
194    length /= sizeof(int16_t) * 2;
195
196    for (; length > 0; length--) {
197        int32_t sum0 = 0, sum1 = 0;
198        unsigned i;
199
200        for (i = 0; i < nstreams; i++) {
201            pa_mix_info *m = streams + i;
202            int32_t cv0 = m->linear[0].i;
203            int32_t cv1 = m->linear[1].i;
204
205            sum0 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv0);
206            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
207
208            sum1 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv1);
209            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
210        }
211
212        *data++ = PA_CLAMP_UNLIKELY(sum0, -0x8000, 0x7FFF);
213        *data++ = PA_CLAMP_UNLIKELY(sum1, -0x8000, 0x7FFF);
214    }
215}
216
217static void pa_mix_generic_s16ne(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) {
218    unsigned channel = 0;
219
220    length /= sizeof(int16_t);
221
222    for (; length > 0; length--) {
223        int32_t sum = 0;
224        unsigned i;
225
226        for (i = 0; i < nstreams; i++) {
227            pa_mix_info *m = streams + i;
228            int32_t cv = m->linear[channel].i;
229
230            if (PA_LIKELY(cv > 0))
231                sum += pa_mult_s16_volume(*((int16_t*) m->ptr), cv);
232            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
233        }
234
235        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
236        *data++ = sum;
237
238        if (PA_UNLIKELY(++channel >= channels))
239            channel = 0;
240    }
241}
242
243static void pa_mix_s16ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) {
244    if (nstreams == 2 && channels == 1)
245        pa_mix2_ch1_s16ne(streams, data, length);
246    else if (nstreams == 2 && channels == 2)
247        pa_mix2_ch2_s16ne(streams, data, length);
248    else if (nstreams == 2)
249        pa_mix2_s16ne(streams, channels, data, length);
250    else if (channels == 2)
251        pa_mix_ch2_s16ne(streams, nstreams, data, length);
252    else
253        pa_mix_generic_s16ne(streams, nstreams, channels, data, length);
254}
255
256static void pa_mix_s16re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) {
257    unsigned channel = 0;
258
259    length /= sizeof(int16_t);
260
261    for (; length > 0; length--, data++) {
262        int32_t sum = 0;
263        unsigned i;
264
265        for (i = 0; i < nstreams; i++) {
266            pa_mix_info *m = streams + i;
267            int32_t cv = m->linear[channel].i;
268
269            if (PA_LIKELY(cv > 0))
270                sum += pa_mult_s16_volume(PA_INT16_SWAP(*((int16_t*) m->ptr)), cv);
271            m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
272        }
273
274        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
275        *data = PA_INT16_SWAP((int16_t) sum);
276
277        if (PA_UNLIKELY(++channel >= channels))
278            channel = 0;
279    }
280}
281
282static void pa_mix_s32ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int32_t *data, unsigned length) {
283    unsigned channel = 0;
284
285    length /= sizeof(int32_t);
286
287    for (; length > 0; length--, data++) {
288        int64_t sum = 0;
289        unsigned i;
290
291        for (i = 0; i < nstreams; i++) {
292            pa_mix_info *m = streams + i;
293            int32_t cv = m->linear[channel].i;
294            int64_t v;
295
296            if (PA_LIKELY(cv > 0)) {
297                v = *((int32_t*) m->ptr);
298                v = (v * cv) >> 16;
299                sum += v;
300            }
301            m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
302        }
303
304        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
305        *data = (int32_t) sum;
306
307        if (PA_UNLIKELY(++channel >= channels))
308            channel = 0;
309    }
310}
311
312static void pa_mix_s32re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int32_t *data, unsigned length) {
313    unsigned channel = 0;
314
315    length /= sizeof(int32_t);
316
317    for (; length > 0; length--, data++) {
318        int64_t sum = 0;
319        unsigned i;
320
321        for (i = 0; i < nstreams; i++) {
322            pa_mix_info *m = streams + i;
323            int32_t cv = m->linear[channel].i;
324            int64_t v;
325
326            if (PA_LIKELY(cv > 0)) {
327                v = PA_INT32_SWAP(*((int32_t*) m->ptr));
328                v = (v * cv) >> 16;
329                sum += v;
330            }
331            m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
332        }
333
334        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
335        *data = PA_INT32_SWAP((int32_t) sum);
336
337        if (PA_UNLIKELY(++channel >= channels))
338            channel = 0;
339    }
340}
341
342static void pa_mix_s24ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
343    unsigned channel = 0;
344
345    for (; length > 0; length -= 3, data += 3) {
346        int64_t sum = 0;
347        unsigned i;
348
349        for (i = 0; i < nstreams; i++) {
350            pa_mix_info *m = streams + i;
351            int32_t cv = m->linear[channel].i;
352            int64_t v;
353
354            if (PA_LIKELY(cv > 0)) {
355                v = (int32_t) (PA_READ24NE(m->ptr) << 8);
356                v = (v * cv) >> 16;
357                sum += v;
358            }
359            m->ptr = (uint8_t*) m->ptr + 3;
360        }
361
362        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
363        PA_WRITE24NE(data, ((uint32_t) sum) >> 8);
364
365        if (PA_UNLIKELY(++channel >= channels))
366            channel = 0;
367    }
368}
369
370static void pa_mix_s24re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
371    unsigned channel = 0;
372
373    for (; length > 0; length -= 3, data += 3) {
374        int64_t sum = 0;
375        unsigned i;
376
377        for (i = 0; i < nstreams; i++) {
378            pa_mix_info *m = streams + i;
379            int32_t cv = m->linear[channel].i;
380            int64_t v;
381
382            if (PA_LIKELY(cv > 0)) {
383                v = (int32_t) (PA_READ24RE(m->ptr) << 8);
384                v = (v * cv) >> 16;
385                sum += v;
386            }
387            m->ptr = (uint8_t*) m->ptr + 3;
388        }
389
390        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
391        PA_WRITE24RE(data, ((uint32_t) sum) >> 8);
392
393        if (PA_UNLIKELY(++channel >= channels))
394            channel = 0;
395    }
396}
397
398static void pa_mix_s24_32ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint32_t *data, unsigned length) {
399    unsigned channel = 0;
400
401    length /= sizeof(uint32_t);
402
403    for (; length > 0; length--, data++) {
404        int64_t sum = 0;
405        unsigned i;
406
407        for (i = 0; i < nstreams; i++) {
408            pa_mix_info *m = streams + i;
409            int32_t cv = m->linear[channel].i;
410            int64_t v;
411
412            if (PA_LIKELY(cv > 0)) {
413                v = (int32_t) (*((uint32_t*)m->ptr) << 8);
414                v = (v * cv) >> 16;
415                sum += v;
416            }
417            m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
418        }
419
420        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
421        *data = ((uint32_t) (int32_t) sum) >> 8;
422
423        if (PA_UNLIKELY(++channel >= channels))
424            channel = 0;
425    }
426}
427
428static void pa_mix_s24_32re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint32_t *data, unsigned length) {
429    unsigned channel = 0;
430
431    length /= sizeof(uint32_t);
432
433    for (; length > 0; length--, data++) {
434        int64_t sum = 0;
435        unsigned i;
436
437        for (i = 0; i < nstreams; i++) {
438            pa_mix_info *m = streams + i;
439            int32_t cv = m->linear[channel].i;
440            int64_t v;
441
442            if (PA_LIKELY(cv > 0)) {
443                v = (int32_t) (PA_UINT32_SWAP(*((uint32_t*) m->ptr)) << 8);
444                v = (v * cv) >> 16;
445                sum += v;
446            }
447            m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
448        }
449
450        sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
451        *data = PA_INT32_SWAP(((uint32_t) (int32_t) sum) >> 8);
452
453        if (PA_UNLIKELY(++channel >= channels))
454            channel = 0;
455    }
456}
457
458static void pa_mix_u8_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
459    unsigned channel = 0;
460
461    length /= sizeof(uint8_t);
462
463    for (; length > 0; length--, data++) {
464        int32_t sum = 0;
465        unsigned i;
466
467        for (i = 0; i < nstreams; i++) {
468            pa_mix_info *m = streams + i;
469            int32_t v, cv = m->linear[channel].i;
470
471            if (PA_LIKELY(cv > 0)) {
472                v = (int32_t) *((uint8_t*) m->ptr) - 0x80;
473                v = (v * cv) >> 16;
474                sum += v;
475            }
476            m->ptr = (uint8_t*) m->ptr + 1;
477        }
478
479        sum = PA_CLAMP_UNLIKELY(sum, -0x80, 0x7F);
480        *data = (uint8_t) (sum + 0x80);
481
482        if (PA_UNLIKELY(++channel >= channels))
483            channel = 0;
484    }
485}
486
487static void pa_mix_ulaw_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
488    unsigned channel = 0;
489
490    length /= sizeof(uint8_t);
491
492    for (; length > 0; length--, data++) {
493        int32_t sum = 0;
494        unsigned i;
495
496        for (i = 0; i < nstreams; i++) {
497            pa_mix_info *m = streams + i;
498            int32_t cv = m->linear[channel].i;
499
500            if (PA_LIKELY(cv > 0))
501                sum += pa_mult_s16_volume(st_ulaw2linear16(*((uint8_t*) m->ptr)), cv);
502            m->ptr = (uint8_t*) m->ptr + 1;
503        }
504
505        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
506        *data = (uint8_t) st_14linear2ulaw((int16_t) sum >> 2);
507
508        if (PA_UNLIKELY(++channel >= channels))
509            channel = 0;
510    }
511}
512
513static void pa_mix_alaw_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) {
514    unsigned channel = 0;
515
516    length /= sizeof(uint8_t);
517
518    for (; length > 0; length--, data++) {
519        int32_t sum = 0;
520        unsigned i;
521
522        for (i = 0; i < nstreams; i++) {
523            pa_mix_info *m = streams + i;
524            int32_t cv = m->linear[channel].i;
525
526            if (PA_LIKELY(cv > 0))
527                sum += pa_mult_s16_volume(st_alaw2linear16(*((uint8_t*) m->ptr)), cv);
528            m->ptr = (uint8_t*) m->ptr + 1;
529        }
530
531        sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
532        *data = (uint8_t) st_13linear2alaw((int16_t) sum >> 3);
533
534        if (PA_UNLIKELY(++channel >= channels))
535            channel = 0;
536    }
537}
538
539static void pa_mix_float32ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, float *data, unsigned length) {
540    unsigned channel = 0;
541
542    length /= sizeof(float);
543
544    for (; length > 0; length--, data++) {
545        float sum = 0;
546        unsigned i;
547
548        for (i = 0; i < nstreams; i++) {
549            pa_mix_info *m = streams + i;
550            float v, cv = m->linear[channel].f;
551
552            if (PA_LIKELY(cv > 0)) {
553                v = *((float*) m->ptr);
554                v *= cv;
555                sum += v;
556            }
557            m->ptr = (uint8_t*) m->ptr + sizeof(float);
558        }
559
560        *data = sum;
561
562        if (PA_UNLIKELY(++channel >= channels))
563            channel = 0;
564    }
565}
566
567static void pa_mix_float32re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, float *data, unsigned length) {
568    unsigned channel = 0;
569
570    length /= sizeof(float);
571
572    for (; length > 0; length--, data++) {
573        float sum = 0;
574        unsigned i;
575
576        for (i = 0; i < nstreams; i++) {
577            pa_mix_info *m = streams + i;
578            float cv = m->linear[channel].f;
579
580            if (PA_LIKELY(cv > 0))
581                sum += PA_READ_FLOAT32RE(m->ptr) * cv;
582            m->ptr = (uint8_t*) m->ptr + sizeof(float);
583        }
584
585        PA_WRITE_FLOAT32RE(data, sum);
586
587        if (PA_UNLIKELY(++channel >= channels))
588            channel = 0;
589    }
590}
591
592static pa_do_mix_func_t do_mix_table[] = {
593    [PA_SAMPLE_U8]        = (pa_do_mix_func_t) pa_mix_u8_c,
594    [PA_SAMPLE_ALAW]      = (pa_do_mix_func_t) pa_mix_alaw_c,
595    [PA_SAMPLE_ULAW]      = (pa_do_mix_func_t) pa_mix_ulaw_c,
596    [PA_SAMPLE_S16NE]     = (pa_do_mix_func_t) pa_mix_s16ne_c,
597    [PA_SAMPLE_S16RE]     = (pa_do_mix_func_t) pa_mix_s16re_c,
598    [PA_SAMPLE_FLOAT32NE] = (pa_do_mix_func_t) pa_mix_float32ne_c,
599    [PA_SAMPLE_FLOAT32RE] = (pa_do_mix_func_t) pa_mix_float32re_c,
600    [PA_SAMPLE_S32NE]     = (pa_do_mix_func_t) pa_mix_s32ne_c,
601    [PA_SAMPLE_S32RE]     = (pa_do_mix_func_t) pa_mix_s32re_c,
602    [PA_SAMPLE_S24NE]     = (pa_do_mix_func_t) pa_mix_s24ne_c,
603    [PA_SAMPLE_S24RE]     = (pa_do_mix_func_t) pa_mix_s24re_c,
604    [PA_SAMPLE_S24_32NE]  = (pa_do_mix_func_t) pa_mix_s24_32ne_c,
605    [PA_SAMPLE_S24_32RE]  = (pa_do_mix_func_t) pa_mix_s24_32re_c
606};
607
608void pa_mix_func_init(const pa_cpu_info *cpu_info) {
609    if (cpu_info->force_generic_code)
610        do_mix_table[PA_SAMPLE_S16NE] = (pa_do_mix_func_t) pa_mix_generic_s16ne;
611    else
612        do_mix_table[PA_SAMPLE_S16NE] = (pa_do_mix_func_t) pa_mix_s16ne_c;
613}
614
615size_t pa_mix(
616        pa_mix_info streams[],
617        unsigned nstreams,
618        void *data,
619        size_t length,
620        const pa_sample_spec *spec,
621        const pa_cvolume *volume,
622        bool mute) {
623
624    pa_cvolume full_volume;
625    unsigned k;
626
627    pa_assert(streams);
628    pa_assert(data);
629    pa_assert(length);
630    pa_assert(spec);
631    pa_assert(nstreams > 1);
632
633    if (!volume)
634        volume = pa_cvolume_reset(&full_volume, spec->channels);
635
636    if (mute || pa_cvolume_is_muted(volume)) {
637        pa_silence_memory(data, length, spec);
638        return length;
639    }
640
641    for (k = 0; k < nstreams; k++) {
642        pa_assert(length <= streams[k].chunk.length);
643        streams[k].ptr = pa_memblock_acquire_chunk(&streams[k].chunk);
644    }
645
646    calc_stream_volumes_table[spec->format](streams, nstreams, volume, spec);
647    do_mix_table[spec->format](streams, nstreams, spec->channels, data, length);
648
649    for (k = 0; k < nstreams; k++)
650        pa_memblock_release(streams[k].chunk.memblock);
651
652    return length;
653}
654
655pa_do_mix_func_t pa_get_mix_func(pa_sample_format_t f) {
656    pa_assert(pa_sample_format_valid(f));
657
658    return do_mix_table[f];
659}
660
661void pa_set_mix_func(pa_sample_format_t f, pa_do_mix_func_t func) {
662    pa_assert(pa_sample_format_valid(f));
663
664    do_mix_table[f] = func;
665}
666
667typedef union {
668  float f;
669  uint32_t i;
670} volume_val;
671
672typedef void (*pa_calc_volume_func_t) (void *volumes, const pa_cvolume *volume);
673
674static const pa_calc_volume_func_t calc_volume_table[] = {
675  [PA_SAMPLE_U8]        = (pa_calc_volume_func_t) calc_linear_integer_volume,
676  [PA_SAMPLE_ALAW]      = (pa_calc_volume_func_t) calc_linear_integer_volume,
677  [PA_SAMPLE_ULAW]      = (pa_calc_volume_func_t) calc_linear_integer_volume,
678  [PA_SAMPLE_S16LE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
679  [PA_SAMPLE_S16BE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
680  [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_func_t) calc_linear_float_volume,
681  [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_func_t) calc_linear_float_volume,
682  [PA_SAMPLE_S32LE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
683  [PA_SAMPLE_S32BE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
684  [PA_SAMPLE_S24LE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
685  [PA_SAMPLE_S24BE]     = (pa_calc_volume_func_t) calc_linear_integer_volume,
686  [PA_SAMPLE_S24_32LE]  = (pa_calc_volume_func_t) calc_linear_integer_volume,
687  [PA_SAMPLE_S24_32BE]  = (pa_calc_volume_func_t) calc_linear_integer_volume
688};
689
690void pa_volume_memchunk(
691        pa_memchunk*c,
692        const pa_sample_spec *spec,
693        const pa_cvolume *volume) {
694
695    void *ptr;
696    volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING];
697    pa_do_volume_func_t do_volume;
698
699    pa_assert(c);
700    pa_assert(spec);
701    pa_assert(pa_sample_spec_valid(spec));
702    pa_assert(pa_frame_aligned(c->length, spec));
703    pa_assert(volume);
704
705    if (pa_memblock_is_silence(c->memblock))
706        return;
707
708    if (pa_cvolume_is_norm(volume))
709        return;
710
711    if (pa_cvolume_is_muted(volume)) {
712        pa_silence_memchunk(c, spec);
713        return;
714    }
715
716    do_volume = pa_get_volume_func(spec->format);
717    pa_assert(do_volume);
718
719    calc_volume_table[spec->format] ((void *)linear, volume);
720
721    ptr = pa_memblock_acquire_chunk(c);
722
723    do_volume(ptr, (void *)linear, spec->channels, c->length);
724
725    pa_memblock_release(c->memblock);
726}
727