From: Peter Meerwald <p.meerwald@xxxxxxxxxxxxxxxxxx> x86-64, i7-870, 3 GHz gcc -O0, 100 TIMES mix s16 generic 1 channel: 286503 usec (avg: 2865.03, min = 2673, max = 6892, stddev = 425.996). mix s16 2 streams 1 channel: 136490 usec (avg: 1364.9, min = 1304, max = 1686, stddev = 73.4594). mix s16 generic 2 channels: 590472 usec (avg: 5904.72, min = 5342, max = 16027, stddev = 1079.34). mix s16 2 channels: 380369 usec (avg: 3803.69, min = 3570, max = 5041, stddev = 234.831). mix s16 2 streams: 316254 usec (avg: 3162.54, min = 3034, max = 3536, stddev = 107.716). mix s16 2 streams 2 channels: 269645 usec (avg: 2696.45, min = 2585, max = 3027, stddev = 87.9661). x86-64, i7-870, 3 GHz gcc -O2, 1000 TIMES mix s16 generic 1 channel: 371550 usec (avg: 3715.5, min = 3515, max = 10534, stddev = 760.071). mix s16 2 streams 1 channel: 207750 usec (avg: 2077.5, min = 2009, max = 2271, stddev = 58.6076). mix s16 generic 2 channels: 724294 usec (avg: 7242.94, min = 6937, max = 10350, stddev = 363.451). mix s16 2 channels: 345661 usec (avg: 3456.61, min = 3291, max = 5586, stddev = 256.309). mix s16 2 streams: 559243 usec (avg: 5592.43, min = 5349, max = 6705, stddev = 223.271). mix s16 2 streams 2 channels: 390101 usec (avg: 3901.01, min = 3696, max = 5345, stddev = 213.482). Signed-off-by: Peter Meerwald <pmeerw at pmeerw.net> --- src/Makefile.am | 8 +- src/tests/mix-special-test.c | 354 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 src/tests/mix-special-test.c diff --git a/src/Makefile.am b/src/Makefile.am index 2bc80c9..f514712 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -246,7 +246,8 @@ TESTS_default = \ proplist-test \ cpu-test \ lock-autospawn-test \ - mult-s16-test + mult-s16-test \ + mix-special-test TESTS_norun = \ mcalign-test \ @@ -509,6 +510,11 @@ mult_s16_test_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINOR@.la libpulse.la li mult_s16_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS) mult_s16_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS) +mix_special_test_SOURCES = tests/mix-special-test.c +mix_special_test_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINOR@.la libpulse.la libpulsecommon- at PA_MAJORMINOR@.la +mix_special_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS) +mix_special_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS) + rtstutter_SOURCES = tests/rtstutter.c rtstutter_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINOR@.la libpulse.la libpulsecommon- at PA_MAJORMINOR@.la rtstutter_CFLAGS = $(AM_CFLAGS) diff --git a/src/tests/mix-special-test.c b/src/tests/mix-special-test.c new file mode 100644 index 0000000..df86ccb --- /dev/null +++ b/src/tests/mix-special-test.c @@ -0,0 +1,354 @@ +/*** + This file is part of PulseAudio. + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <check.h> +#include <unistd.h> +#include <stdlib.h> +#include <math.h> + +#include <pulse/rtclock.h> +#include <pulsecore/random.h> +#include <pulsecore/macro.h> +#include <pulsecore/mix.h> +#include <pulsecore/sample-util.h> + +#define PA_CPU_TEST_RUN_START(l, t1, t2) \ +{ \ + int _j, _k; \ + int _times = (t1), _times2 = (t2); \ + pa_usec_t _start, _stop; \ + pa_usec_t _min = INT_MAX, _max = 0; \ + double _s1 = 0, _s2 = 0; \ + const char *_label = (l); \ + \ + for (_k = 0; _k < _times2; _k++) { \ + _start = pa_rtclock_now(); \ + for (_j = 0; _j < _times; _j++) + +#define PA_CPU_TEST_RUN_STOP \ + _stop = pa_rtclock_now(); \ + \ + if (_min > (_stop - _start)) _min = _stop - _start; \ + if (_max < (_stop - _start)) _max = _stop - _start; \ + _s1 += _stop - _start; \ + _s2 += (_stop - _start) * (_stop - _start); \ + } \ + pa_log_debug("%s: %llu usec (avg: %g, min = %llu, max = %llu, stddev = %g).", _label, \ + (long long unsigned int)_s1, \ + ((double)_s1 / _times2), \ + (long long unsigned int)_min, \ + (long long unsigned int)_max, \ + sqrt(_times2 * _s2 - _s1 * _s1) / _times2); \ +} + +static void acquire_mix_streams(pa_mix_info streams[], unsigned nstreams) { + unsigned i; + + for (i = 0; i < nstreams; i++) + streams[i].ptr = pa_memblock_acquire_chunk(&streams[i].chunk); +} + +static void release_mix_streams(pa_mix_info streams[], unsigned nstreams) { + unsigned i; + + for (i = 0; i < nstreams; i++) + pa_memblock_release(streams[i].chunk.memblock); +} + +/* 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; + } +} + + +#define SAMPLES 1028 +#define TIMES 1000 +#define TIMES2 100 + +START_TEST (mix_special_1ch_test) { + int16_t samples0[SAMPLES]; + int16_t samples1[SAMPLES]; + int16_t out[SAMPLES]; + int16_t out_ref[SAMPLES]; + pa_mempool *pool; + pa_memchunk c0, c1; + pa_mix_info m[2]; + unsigned nsamples = SAMPLES; + + fail_unless((pool = pa_mempool_new(FALSE, 0)) != NULL, NULL); + + pa_random(samples0, nsamples * sizeof(int16_t)); + c0.memblock = pa_memblock_new_fixed(pool, samples0, nsamples * sizeof(int16_t), FALSE); + c0.length = pa_memblock_get_length(c0.memblock); + c0.index = 0; + + pa_random(samples1, nsamples * sizeof(int16_t)); + c1.memblock = pa_memblock_new_fixed(pool, samples1, nsamples * sizeof(int16_t), FALSE); + c1.length = pa_memblock_get_length(c1.memblock); + c1.index = 0; + + m[0].chunk = c0; + m[0].volume.channels = 1; + m[0].volume.values[0] = PA_VOLUME_NORM; + m[0].linear[0].i = 0x5555; + + m[1].chunk = c1; + m[1].volume.channels = 1; + m[1].volume.values[0] = PA_VOLUME_NORM; + m[1].linear[0].i = 0x6789; + + PA_CPU_TEST_RUN_START("mix s16 generic 1 channel", TIMES, TIMES2) { + acquire_mix_streams(m, 2); + pa_mix_generic_s16ne(m, 2, 1, out_ref, nsamples * sizeof(int16_t)); + release_mix_streams(m, 2); + } PA_CPU_TEST_RUN_STOP + + PA_CPU_TEST_RUN_START("mix s16 2 streams 1 channel", TIMES, TIMES2) { + acquire_mix_streams(m, 2); + pa_mix2_ch1_s16ne(m, out, nsamples * sizeof(int16_t)); + release_mix_streams(m, 2); + } PA_CPU_TEST_RUN_STOP + + fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0); + + pa_memblock_unref(c0.memblock); + pa_memblock_unref(c1.memblock); + + pa_mempool_free(pool); +} +END_TEST + +START_TEST (mix_special_2ch_test) { + int16_t samples0[SAMPLES*2]; + int16_t samples1[SAMPLES*2]; + int16_t out[SAMPLES*2]; + int16_t out_ref[SAMPLES*2]; + int i; + pa_mempool *pool; + pa_memchunk c0, c1; + pa_mix_info m[2]; + unsigned nsamples = SAMPLES * 2; + + fail_unless((pool = pa_mempool_new(FALSE, 0)) != NULL, NULL); + + pa_random(samples0, nsamples * sizeof(int16_t)); + c0.memblock = pa_memblock_new_fixed(pool, samples0, nsamples * sizeof(int16_t), FALSE); + c0.length = pa_memblock_get_length(c0.memblock); + c0.index = 0; + + pa_random(samples1, nsamples * sizeof(int16_t)); + c1.memblock = pa_memblock_new_fixed(pool, samples1, nsamples * sizeof(int16_t), FALSE); + c1.length = pa_memblock_get_length(c1.memblock); + c1.index = 0; + + m[0].chunk = c0; + m[0].volume.channels = 2; + for (i = 0; i < m[0].volume.channels; i++) { + m[0].volume.values[i] = PA_VOLUME_NORM; + m[0].linear[i].i = 0x5555; + } + + m[1].chunk = c1; + m[1].volume.channels = 2; + for (i = 0; i < m[1].volume.channels; i++) { + m[1].volume.values[i] = PA_VOLUME_NORM; + m[1].linear[i].i = 0x6789; + } + + PA_CPU_TEST_RUN_START("mix s16 generic 2 channels", TIMES, TIMES2) { + acquire_mix_streams(m, 2); + pa_mix_generic_s16ne(m, 2, 2, out_ref, nsamples * sizeof(int16_t)); + release_mix_streams(m, 2); + } PA_CPU_TEST_RUN_STOP + + PA_CPU_TEST_RUN_START("mix s16 2 channels", TIMES, TIMES2) { + acquire_mix_streams(m, 2); + pa_mix_ch2_s16ne(m, 2, out, nsamples * sizeof(int16_t)); + release_mix_streams(m, 2); + } PA_CPU_TEST_RUN_STOP + + fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0); + + PA_CPU_TEST_RUN_START("mix s16 2 streams", TIMES, TIMES2) { + acquire_mix_streams(m, 2); + pa_mix2_s16ne(m, 2, out, nsamples * sizeof(int16_t)); + release_mix_streams(m, 2); + } PA_CPU_TEST_RUN_STOP + + fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0); + + PA_CPU_TEST_RUN_START("mix s16 2 streams 2 channels", TIMES, TIMES2) { + acquire_mix_streams(m, 2); + pa_mix2_ch2_s16ne(m, out, nsamples * sizeof(int16_t)); + release_mix_streams(m, 2); + } PA_CPU_TEST_RUN_STOP + + fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0); + + pa_memblock_unref(c0.memblock); + pa_memblock_unref(c1.memblock); + + pa_mempool_free(pool); +} +END_TEST + +int main(int argc, char *argv[]) { + int failed = 0; + Suite *s; + TCase *tc; + SRunner *sr; + + if (!getenv("MAKE_CHECK")) + pa_log_set_level(PA_LOG_DEBUG); + + s = suite_create("Mix-special"); + tc = tcase_create("mix-special 1ch"); + tcase_add_test(tc, mix_special_1ch_test); + tcase_set_timeout(tc, 120); + suite_add_tcase(s, tc); + tc = tcase_create("mix-special 2ch"); + tcase_add_test(tc, mix_special_2ch_test); + tcase_set_timeout(tc, 120); + suite_add_tcase(s, tc); + + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} -- 1.7.9.5