1/***
2  This file is part of PulseAudio.
3
4  PulseAudio is free software; you can redistribute it and/or modify
5  it under the terms of the GNU Lesser General Public License as published
6  by the Free Software Foundation; either version 2.1 of the License,
7  or (at your option) any later version.
8
9  PulseAudio is distributed in the hope that it will be useful, but
10  WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  General Public License for more details.
13
14  You should have received a copy of the GNU Lesser General Public License
15  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
16***/
17
18#ifdef HAVE_CONFIG_H
19#include <config.h>
20#endif
21
22#include <check.h>
23#include <unistd.h>
24#include <stdlib.h>
25#include <math.h>
26
27#include <pulse/rtclock.h>
28#include <pulsecore/random.h>
29#include <pulsecore/macro.h>
30
31#include "runtime-test-util.h"
32
33static inline int32_t pa_mult_s16_volume_32(int16_t v, int32_t cv) {
34    /* Multiplying the 32 bit volume factor with the
35     * 16 bit sample might result in an 48 bit value. We
36     * want to do without 64 bit integers and hence do
37     * the multiplication independently for the HI and
38     * LO part of the volume. */
39    int32_t hi = cv >> 16;
40    int32_t lo = cv & 0xFFFF;
41    return ((v * lo) >> 16) + (v * hi);
42}
43
44static inline int32_t pa_mult_s16_volume_64(int16_t v, int32_t cv) {
45    /* Multiply with 64 bit integers on 64 bit platforms */
46    return (v * (int64_t) cv) >> 16;
47}
48
49#define SAMPLES 1028
50#define TIMES 10000
51#define TIMES2 100
52
53START_TEST (mult_s16_test) {
54    int16_t samples[SAMPLES];
55    int32_t volumes[SAMPLES];
56    int32_t sum1 = 0, sum2 = 0;
57    int i;
58
59    pa_random(samples, sizeof(samples));
60    pa_random(volumes, sizeof(volumes));
61
62    for (i = 0; i < SAMPLES; i++) {
63        int32_t a = pa_mult_s16_volume_32(samples[i], volumes[i]);
64        int32_t b = pa_mult_s16_volume_64(samples[i], volumes[i]);
65
66        if (a != b) {
67            pa_log_debug("%d: %d != %d", i, a, b);
68            ck_abort();
69        }
70    }
71
72    PA_RUNTIME_TEST_RUN_START("32 bit mult", TIMES, TIMES2) {
73        for (i = 0; i < SAMPLES; i++) {
74            sum1 += pa_mult_s16_volume_32(samples[i], volumes[i]);
75        }
76    } PA_RUNTIME_TEST_RUN_STOP
77
78    PA_RUNTIME_TEST_RUN_START("64 bit mult", TIMES, TIMES2) {
79        for (i = 0; i < SAMPLES; i++)
80            sum2 += pa_mult_s16_volume_64(samples[i], volumes[i]);
81    } PA_RUNTIME_TEST_RUN_STOP
82
83    fail_unless(sum1 == sum2);
84}
85END_TEST
86
87int main(int argc, char *argv[]) {
88    int failed = 0;
89    Suite *s;
90    TCase *tc;
91    SRunner *sr;
92
93    if (!getenv("MAKE_CHECK"))
94        pa_log_set_level(PA_LOG_DEBUG);
95
96#ifdef HAVE_FAST_64BIT_OPERATIONS
97    pa_log_debug("Detected CPU with fast 64-bit operations.");
98#else
99    pa_log_debug("Not detected CPU with fast 64-bit operations.");
100#endif
101
102    s = suite_create("Mult-s16");
103    tc = tcase_create("mult-s16");
104    tcase_add_test(tc, mult_s16_test);
105    tcase_set_timeout(tc, 120);
106    suite_add_tcase(s, tc);
107
108    sr = srunner_create(s);
109    srunner_run_all(sr, CK_NORMAL);
110    failed = srunner_ntests_failed(sr);
111    srunner_free(sr);
112
113    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
114}
115