| /*** | |
| This file is part of PulseAudio. | |
| Copyright 2004-2006 Lennart Poettering | |
| Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB | |
| Copyright 2013 Peter Meerwald <pmeerw@pmeerw.net> | |
| PulseAudio is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU Lesser General Public License as published | |
| by the Free Software Foundation; either version 2.1 of the License, | |
| or (at your option) any later version. | |
| PulseAudio is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public License | |
| along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. | |
| ***/ | |
| #ifdef HAVE_CONFIG_H | |
| #include <config.h> | |
| #endif | |
| #include <math.h> | |
| #include <pulsecore/sample-util.h> | |
| #include <pulsecore/macro.h> | |
| #include <pulsecore/g711.h> | |
| #include <pulsecore/endianmacros.h> | |
| #include "cpu.h" | |
| #include "mix.h" | |
| #define VOLUME_PADDING 32 | |
| static void calc_linear_integer_volume(int32_t linear[], const pa_cvolume *volume) { | |
| unsigned channel, nchannels, padding; | |
| pa_assert(linear); | |
| pa_assert(volume); | |
| nchannels = volume->channels; | |
| for (channel = 0; channel < nchannels; channel++) | |
| linear[channel] = (int32_t) lrint(pa_sw_volume_to_linear(volume->values[channel]) * 0x10000); | |
| for (padding = 0; padding < VOLUME_PADDING; padding++, channel++) | |
| linear[channel] = linear[padding]; | |
| } | |
| static void calc_linear_float_volume(float linear[], const pa_cvolume *volume) { | |
| unsigned channel, nchannels, padding; | |
| pa_assert(linear); | |
| pa_assert(volume); | |
| nchannels = volume->channels; | |
| for (channel = 0; channel < nchannels; channel++) | |
| linear[channel] = (float) pa_sw_volume_to_linear(volume->values[channel]); | |
| for (padding = 0; padding < VOLUME_PADDING; padding++, channel++) | |
| linear[channel] = linear[padding]; | |
| } | |
| static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) { | |
| unsigned k, channel; | |
| float linear[PA_CHANNELS_MAX + VOLUME_PADDING]; | |
| pa_assert(streams); | |
| pa_assert(spec); | |
| pa_assert(volume); | |
| calc_linear_float_volume(linear, volume); | |
| for (k = 0; k < nstreams; k++) { | |
| for (channel = 0; channel < spec->channels; channel++) { | |
| pa_mix_info *m = streams + k; | |
| m->linear[channel].i = (int32_t) lrint(pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel] * 0x10000); | |
| } | |
| } | |
| } | |
| static void calc_linear_float_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec) { | |
| unsigned k, channel; | |
| float linear[PA_CHANNELS_MAX + VOLUME_PADDING]; | |
| pa_assert(streams); | |
| pa_assert(spec); | |
| pa_assert(volume); | |
| calc_linear_float_volume(linear, volume); | |
| for (k = 0; k < nstreams; k++) { | |
| for (channel = 0; channel < spec->channels; channel++) { | |
| pa_mix_info *m = streams + k; | |
| m->linear[channel].f = (float) (pa_sw_volume_to_linear(m->volume.values[channel]) * linear[channel]); | |
| } | |
| } | |
| } | |
| typedef void (*pa_calc_stream_volumes_func_t) (pa_mix_info streams[], unsigned nstreams, const pa_cvolume *volume, const pa_sample_spec *spec); | |
| static const pa_calc_stream_volumes_func_t calc_stream_volumes_table[] = { | |
| [PA_SAMPLE_U8] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_ALAW] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_ULAW] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_S16LE] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_S16BE] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_FLOAT32LE] = (pa_calc_stream_volumes_func_t) calc_linear_float_stream_volumes, | |
| [PA_SAMPLE_FLOAT32BE] = (pa_calc_stream_volumes_func_t) calc_linear_float_stream_volumes, | |
| [PA_SAMPLE_S32LE] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_S32BE] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_S24LE] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_S24BE] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_S24_32LE] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes, | |
| [PA_SAMPLE_S24_32BE] = (pa_calc_stream_volumes_func_t) calc_linear_integer_stream_volumes | |
| }; | |
| /* special case: mix 2 s16ne streams, 1 channel each */ | |
| static void pa_mix2_ch1_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) { | |
| const int16_t *ptr0 = streams[0].ptr; | |
| const int16_t *ptr1 = streams[1].ptr; | |
| const int32_t cv0 = streams[0].linear[0].i; | |
| const int32_t cv1 = streams[1].linear[0].i; | |
| length /= sizeof(int16_t); | |
| for (; length > 0; length--) { | |
| int32_t sum; | |
| sum = pa_mult_s16_volume(*ptr0++, cv0); | |
| sum += pa_mult_s16_volume(*ptr1++, cv1); | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); | |
| *data++ = sum; | |
| } | |
| } | |
| /* special case: mix 2 s16ne streams, 2 channels each */ | |
| static void pa_mix2_ch2_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) { | |
| const int16_t *ptr0 = streams[0].ptr; | |
| const int16_t *ptr1 = streams[1].ptr; | |
| length /= sizeof(int16_t) * 2; | |
| for (; length > 0; length--) { | |
| int32_t sum; | |
| sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[0].i); | |
| sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[0].i); | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); | |
| *data++ = sum; | |
| sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[1].i); | |
| sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[1].i); | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); | |
| *data++ = sum; | |
| } | |
| } | |
| /* special case: mix 2 s16ne streams */ | |
| static void pa_mix2_s16ne(pa_mix_info streams[], unsigned channels, int16_t *data, unsigned length) { | |
| const int16_t *ptr0 = streams[0].ptr; | |
| const int16_t *ptr1 = streams[1].ptr; | |
| unsigned channel = 0; | |
| length /= sizeof(int16_t); | |
| for (; length > 0; length--) { | |
| int32_t sum; | |
| sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[channel].i); | |
| sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[channel].i); | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); | |
| *data++ = sum; | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| /* special case: mix s16ne streams, 2 channels each */ | |
| static void pa_mix_ch2_s16ne(pa_mix_info streams[], unsigned nstreams, int16_t *data, unsigned length) { | |
| length /= sizeof(int16_t) * 2; | |
| for (; length > 0; length--) { | |
| int32_t sum0 = 0, sum1 = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv0 = m->linear[0].i; | |
| int32_t cv1 = m->linear[1].i; | |
| sum0 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv0); | |
| m->ptr = (uint8_t*) m->ptr + sizeof(int16_t); | |
| sum1 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv1); | |
| m->ptr = (uint8_t*) m->ptr + sizeof(int16_t); | |
| } | |
| *data++ = PA_CLAMP_UNLIKELY(sum0, -0x8000, 0x7FFF); | |
| *data++ = PA_CLAMP_UNLIKELY(sum1, -0x8000, 0x7FFF); | |
| } | |
| } | |
| static void pa_mix_generic_s16ne(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(int16_t); | |
| for (; length > 0; length--) { | |
| int32_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| if (PA_LIKELY(cv > 0)) | |
| sum += pa_mult_s16_volume(*((int16_t*) m->ptr), cv); | |
| m->ptr = (uint8_t*) m->ptr + sizeof(int16_t); | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); | |
| *data++ = sum; | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_s16ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) { | |
| if (nstreams == 2 && channels == 1) | |
| pa_mix2_ch1_s16ne(streams, data, length); | |
| else if (nstreams == 2 && channels == 2) | |
| pa_mix2_ch2_s16ne(streams, data, length); | |
| else if (nstreams == 2) | |
| pa_mix2_s16ne(streams, channels, data, length); | |
| else if (channels == 2) | |
| pa_mix_ch2_s16ne(streams, nstreams, data, length); | |
| else | |
| pa_mix_generic_s16ne(streams, nstreams, channels, data, length); | |
| } | |
| static void pa_mix_s16re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(int16_t); | |
| for (; length > 0; length--, data++) { | |
| int32_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| if (PA_LIKELY(cv > 0)) | |
| sum += pa_mult_s16_volume(PA_INT16_SWAP(*((int16_t*) m->ptr)), cv); | |
| m->ptr = (uint8_t*) m->ptr + sizeof(int16_t); | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); | |
| *data = PA_INT16_SWAP((int16_t) sum); | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_s32ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int32_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(int32_t); | |
| for (; length > 0; length--, data++) { | |
| int64_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| int64_t v; | |
| if (PA_LIKELY(cv > 0)) { | |
| v = *((int32_t*) m->ptr); | |
| v = (v * cv) >> 16; | |
| sum += v; | |
| } | |
| m->ptr = (uint8_t*) m->ptr + sizeof(int32_t); | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); | |
| *data = (int32_t) sum; | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_s32re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, int32_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(int32_t); | |
| for (; length > 0; length--, data++) { | |
| int64_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| int64_t v; | |
| if (PA_LIKELY(cv > 0)) { | |
| v = PA_INT32_SWAP(*((int32_t*) m->ptr)); | |
| v = (v * cv) >> 16; | |
| sum += v; | |
| } | |
| m->ptr = (uint8_t*) m->ptr + sizeof(int32_t); | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); | |
| *data = PA_INT32_SWAP((int32_t) sum); | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_s24ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| for (; length > 0; length -= 3, data += 3) { | |
| int64_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| int64_t v; | |
| if (PA_LIKELY(cv > 0)) { | |
| v = (int32_t) (PA_READ24NE(m->ptr) << 8); | |
| v = (v * cv) >> 16; | |
| sum += v; | |
| } | |
| m->ptr = (uint8_t*) m->ptr + 3; | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); | |
| PA_WRITE24NE(data, ((uint32_t) sum) >> 8); | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_s24re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| for (; length > 0; length -= 3, data += 3) { | |
| int64_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| int64_t v; | |
| if (PA_LIKELY(cv > 0)) { | |
| v = (int32_t) (PA_READ24RE(m->ptr) << 8); | |
| v = (v * cv) >> 16; | |
| sum += v; | |
| } | |
| m->ptr = (uint8_t*) m->ptr + 3; | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); | |
| PA_WRITE24RE(data, ((uint32_t) sum) >> 8); | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_s24_32ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint32_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(uint32_t); | |
| for (; length > 0; length--, data++) { | |
| int64_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| int64_t v; | |
| if (PA_LIKELY(cv > 0)) { | |
| v = (int32_t) (*((uint32_t*)m->ptr) << 8); | |
| v = (v * cv) >> 16; | |
| sum += v; | |
| } | |
| m->ptr = (uint8_t*) m->ptr + sizeof(int32_t); | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); | |
| *data = ((uint32_t) (int32_t) sum) >> 8; | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_s24_32re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint32_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(uint32_t); | |
| for (; length > 0; length--, data++) { | |
| int64_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| int64_t v; | |
| if (PA_LIKELY(cv > 0)) { | |
| v = (int32_t) (PA_UINT32_SWAP(*((uint32_t*) m->ptr)) << 8); | |
| v = (v * cv) >> 16; | |
| sum += v; | |
| } | |
| m->ptr = (uint8_t*) m->ptr + sizeof(int32_t); | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL); | |
| *data = PA_INT32_SWAP(((uint32_t) (int32_t) sum) >> 8); | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_u8_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(uint8_t); | |
| for (; length > 0; length--, data++) { | |
| int32_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t v, cv = m->linear[channel].i; | |
| if (PA_LIKELY(cv > 0)) { | |
| v = (int32_t) *((uint8_t*) m->ptr) - 0x80; | |
| v = (v * cv) >> 16; | |
| sum += v; | |
| } | |
| m->ptr = (uint8_t*) m->ptr + 1; | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x80, 0x7F); | |
| *data = (uint8_t) (sum + 0x80); | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_ulaw_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(uint8_t); | |
| for (; length > 0; length--, data++) { | |
| int32_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| if (PA_LIKELY(cv > 0)) | |
| sum += pa_mult_s16_volume(st_ulaw2linear16(*((uint8_t*) m->ptr)), cv); | |
| m->ptr = (uint8_t*) m->ptr + 1; | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); | |
| *data = (uint8_t) st_14linear2ulaw((int16_t) sum >> 2); | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_alaw_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, uint8_t *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(uint8_t); | |
| for (; length > 0; length--, data++) { | |
| int32_t sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| int32_t cv = m->linear[channel].i; | |
| if (PA_LIKELY(cv > 0)) | |
| sum += pa_mult_s16_volume(st_alaw2linear16(*((uint8_t*) m->ptr)), cv); | |
| m->ptr = (uint8_t*) m->ptr + 1; | |
| } | |
| sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF); | |
| *data = (uint8_t) st_13linear2alaw((int16_t) sum >> 3); | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_float32ne_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, float *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(float); | |
| for (; length > 0; length--, data++) { | |
| float sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| float v, cv = m->linear[channel].f; | |
| if (PA_LIKELY(cv > 0)) { | |
| v = *((float*) m->ptr); | |
| v *= cv; | |
| sum += v; | |
| } | |
| m->ptr = (uint8_t*) m->ptr + sizeof(float); | |
| } | |
| *data = sum; | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static void pa_mix_float32re_c(pa_mix_info streams[], unsigned nstreams, unsigned channels, float *data, unsigned length) { | |
| unsigned channel = 0; | |
| length /= sizeof(float); | |
| for (; length > 0; length--, data++) { | |
| float sum = 0; | |
| unsigned i; | |
| for (i = 0; i < nstreams; i++) { | |
| pa_mix_info *m = streams + i; | |
| float cv = m->linear[channel].f; | |
| if (PA_LIKELY(cv > 0)) | |
| sum += PA_READ_FLOAT32RE(m->ptr) * cv; | |
| m->ptr = (uint8_t*) m->ptr + sizeof(float); | |
| } | |
| PA_WRITE_FLOAT32RE(data, sum); | |
| if (PA_UNLIKELY(++channel >= channels)) | |
| channel = 0; | |
| } | |
| } | |
| static pa_do_mix_func_t do_mix_table[] = { | |
| [PA_SAMPLE_U8] = (pa_do_mix_func_t) pa_mix_u8_c, | |
| [PA_SAMPLE_ALAW] = (pa_do_mix_func_t) pa_mix_alaw_c, | |
| [PA_SAMPLE_ULAW] = (pa_do_mix_func_t) pa_mix_ulaw_c, | |
| [PA_SAMPLE_S16NE] = (pa_do_mix_func_t) pa_mix_s16ne_c, | |
| [PA_SAMPLE_S16RE] = (pa_do_mix_func_t) pa_mix_s16re_c, | |
| [PA_SAMPLE_FLOAT32NE] = (pa_do_mix_func_t) pa_mix_float32ne_c, | |
| [PA_SAMPLE_FLOAT32RE] = (pa_do_mix_func_t) pa_mix_float32re_c, | |
| [PA_SAMPLE_S32NE] = (pa_do_mix_func_t) pa_mix_s32ne_c, | |
| [PA_SAMPLE_S32RE] = (pa_do_mix_func_t) pa_mix_s32re_c, | |
| [PA_SAMPLE_S24NE] = (pa_do_mix_func_t) pa_mix_s24ne_c, | |
| [PA_SAMPLE_S24RE] = (pa_do_mix_func_t) pa_mix_s24re_c, | |
| [PA_SAMPLE_S24_32NE] = (pa_do_mix_func_t) pa_mix_s24_32ne_c, | |
| [PA_SAMPLE_S24_32RE] = (pa_do_mix_func_t) pa_mix_s24_32re_c | |
| }; | |
| void pa_mix_func_init(const pa_cpu_info *cpu_info) { | |
| if (cpu_info->force_generic_code) | |
| do_mix_table[PA_SAMPLE_S16NE] = (pa_do_mix_func_t) pa_mix_generic_s16ne; | |
| else | |
| do_mix_table[PA_SAMPLE_S16NE] = (pa_do_mix_func_t) pa_mix_s16ne_c; | |
| } | |
| size_t pa_mix( | |
| pa_mix_info streams[], | |
| unsigned nstreams, | |
| void *data, | |
| size_t length, | |
| const pa_sample_spec *spec, | |
| const pa_cvolume *volume, | |
| bool mute) { | |
| pa_cvolume full_volume; | |
| unsigned k; | |
| pa_assert(streams); | |
| pa_assert(data); | |
| pa_assert(length); | |
| pa_assert(spec); | |
| pa_assert(nstreams > 1); | |
| if (!volume) | |
| volume = pa_cvolume_reset(&full_volume, spec->channels); | |
| if (mute || pa_cvolume_is_muted(volume)) { | |
| pa_silence_memory(data, length, spec); | |
| return length; | |
| } | |
| for (k = 0; k < nstreams; k++) { | |
| pa_assert(length <= streams[k].chunk.length); | |
| streams[k].ptr = pa_memblock_acquire_chunk(&streams[k].chunk); | |
| } | |
| calc_stream_volumes_table[spec->format](streams, nstreams, volume, spec); | |
| do_mix_table[spec->format](streams, nstreams, spec->channels, data, length); | |
| for (k = 0; k < nstreams; k++) | |
| pa_memblock_release(streams[k].chunk.memblock); | |
| return length; | |
| } | |
| pa_do_mix_func_t pa_get_mix_func(pa_sample_format_t f) { | |
| pa_assert(pa_sample_format_valid(f)); | |
| return do_mix_table[f]; | |
| } | |
| void pa_set_mix_func(pa_sample_format_t f, pa_do_mix_func_t func) { | |
| pa_assert(pa_sample_format_valid(f)); | |
| do_mix_table[f] = func; | |
| } | |
| typedef union { | |
| float f; | |
| uint32_t i; | |
| } volume_val; | |
| typedef void (*pa_calc_volume_func_t) (void *volumes, const pa_cvolume *volume); | |
| static const pa_calc_volume_func_t calc_volume_table[] = { | |
| [PA_SAMPLE_U8] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_ALAW] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_ULAW] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_S16LE] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_S16BE] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_func_t) calc_linear_float_volume, | |
| [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_func_t) calc_linear_float_volume, | |
| [PA_SAMPLE_S32LE] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_S32BE] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_S24LE] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_S24BE] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_S24_32LE] = (pa_calc_volume_func_t) calc_linear_integer_volume, | |
| [PA_SAMPLE_S24_32BE] = (pa_calc_volume_func_t) calc_linear_integer_volume | |
| }; | |
| void pa_volume_memchunk( | |
| pa_memchunk*c, | |
| const pa_sample_spec *spec, | |
| const pa_cvolume *volume) { | |
| void *ptr; | |
| volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING]; | |
| pa_do_volume_func_t do_volume; | |
| pa_assert(c); | |
| pa_assert(spec); | |
| pa_assert(pa_sample_spec_valid(spec)); | |
| pa_assert(pa_frame_aligned(c->length, spec)); | |
| pa_assert(volume); | |
| if (pa_memblock_is_silence(c->memblock)) | |
| return; | |
| if (pa_cvolume_is_norm(volume)) | |
| return; | |
| if (pa_cvolume_is_muted(volume)) { | |
| pa_silence_memchunk(c, spec); | |
| return; | |
| } | |
| do_volume = pa_get_volume_func(spec->format); | |
| pa_assert(do_volume); | |
| calc_volume_table[spec->format] ((void *)linear, volume); | |
| ptr = pa_memblock_acquire_chunk(c); | |
| do_volume(ptr, (void *)linear, spec->channels, c->length); | |
| pa_memblock_release(c->memblock); | |
| } |