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
24#include <pulsecore/cpu-arm.h>
25#include <pulsecore/cpu-x86.h>
26#include <pulsecore/cpu-orc.h>
27#include <pulsecore/random.h>
28#include <pulsecore/macro.h>
29#include <pulsecore/sample-util.h>
30
31#include "runtime-test-util.h"
32
33/* Common defines for svolume tests */
34#define SAMPLES 1028
35#define TIMES 1000
36#define TIMES2 100
37#define PADDING 16
38
39static void run_volume_test(
40        pa_do_volume_func_t func,
41        pa_do_volume_func_t orig_func,
42        int align,
43        int channels,
44        bool correct,
45        bool perf) {
46
47    PA_DECLARE_ALIGNED(8, int16_t, s[SAMPLES]) = { 0 };
48    PA_DECLARE_ALIGNED(8, int16_t, s_ref[SAMPLES]) = { 0 };
49    PA_DECLARE_ALIGNED(8, int16_t, s_orig[SAMPLES]) = { 0 };
50    int32_t volumes[channels + PADDING];
51    int16_t *samples, *samples_ref, *samples_orig;
52    int i, padding, nsamples, size;
53
54    /* Force sample alignment as requested */
55    samples = s + (8 - align);
56    samples_ref = s_ref + (8 - align);
57    samples_orig = s_orig + (8 - align);
58    nsamples = SAMPLES - (8 - align);
59    if (nsamples % channels)
60        nsamples -= nsamples % channels;
61    size = nsamples * sizeof(int16_t);
62
63    pa_random(samples, size);
64    memcpy(samples_ref, samples, size);
65    memcpy(samples_orig, samples, size);
66
67    for (i = 0; i < channels; i++)
68        volumes[i] = PA_CLAMP_VOLUME((pa_volume_t)(rand() >> 15));
69    for (padding = 0; padding < PADDING; padding++, i++)
70        volumes[i] = volumes[padding];
71
72    if (correct) {
73        orig_func(samples_ref, volumes, channels, size);
74        func(samples, volumes, channels, size);
75
76        for (i = 0; i < nsamples; i++) {
77            if (samples[i] != samples_ref[i]) {
78                pa_log_debug("Correctness test failed: align=%d, channels=%d", align, channels);
79                pa_log_debug("%d: %04hx != %04hx (%04hx * %08x)", i, samples[i], samples_ref[i],
80                        samples_orig[i], volumes[i % channels]);
81                ck_abort();
82            }
83        }
84    }
85
86    if (perf) {
87        pa_log_debug("Testing svolume %dch performance with %d sample alignment", channels, align);
88
89        PA_RUNTIME_TEST_RUN_START("func", TIMES, TIMES2) {
90            memcpy(samples, samples_orig, size);
91            func(samples, volumes, channels, size);
92        } PA_RUNTIME_TEST_RUN_STOP
93
94        PA_RUNTIME_TEST_RUN_START("orig", TIMES, TIMES2) {
95            memcpy(samples_ref, samples_orig, size);
96            orig_func(samples_ref, volumes, channels, size);
97        } PA_RUNTIME_TEST_RUN_STOP
98
99        fail_unless(memcmp(samples_ref, samples, size) == 0);
100    }
101}
102
103#if defined (__i386__) || defined (__amd64__)
104START_TEST (svolume_mmx_test) {
105    pa_do_volume_func_t orig_func, mmx_func;
106    pa_cpu_x86_flag_t flags = 0;
107    int i, j;
108
109    pa_cpu_get_x86_flags(&flags);
110
111    if (!((flags & PA_CPU_X86_MMX) && (flags & PA_CPU_X86_CMOV))) {
112        pa_log_info("MMX/CMOV not supported. Skipping");
113        return;
114    }
115
116    orig_func = pa_get_volume_func(PA_SAMPLE_S16NE);
117    pa_volume_func_init_mmx(flags);
118    mmx_func = pa_get_volume_func(PA_SAMPLE_S16NE);
119
120    pa_log_debug("Checking MMX svolume");
121    for (i = 1; i <= 3; i++) {
122        for (j = 0; j < 7; j++)
123            run_volume_test(mmx_func, orig_func, j, i, true, false);
124    }
125    run_volume_test(mmx_func, orig_func, 7, 1, true, true);
126    run_volume_test(mmx_func, orig_func, 7, 2, true, true);
127    run_volume_test(mmx_func, orig_func, 7, 3, true, true);
128}
129END_TEST
130
131START_TEST (svolume_sse_test) {
132    pa_do_volume_func_t orig_func, sse_func;
133    pa_cpu_x86_flag_t flags = 0;
134    int i, j;
135
136    pa_cpu_get_x86_flags(&flags);
137
138    if (!(flags & PA_CPU_X86_SSE2)) {
139        pa_log_info("SSE2 not supported. Skipping");
140        return;
141    }
142
143    orig_func = pa_get_volume_func(PA_SAMPLE_S16NE);
144    pa_volume_func_init_sse(flags);
145    sse_func = pa_get_volume_func(PA_SAMPLE_S16NE);
146
147    pa_log_debug("Checking SSE2 svolume");
148    for (i = 1; i <= 3; i++) {
149        for (j = 0; j < 7; j++)
150            run_volume_test(sse_func, orig_func, j, i, true, false);
151    }
152    run_volume_test(sse_func, orig_func, 7, 1, true, true);
153    run_volume_test(sse_func, orig_func, 7, 2, true, true);
154    run_volume_test(sse_func, orig_func, 7, 3, true, true);
155}
156END_TEST
157#endif /* defined (__i386__) || defined (__amd64__) */
158
159#if defined (__arm__) && defined (__linux__)
160START_TEST (svolume_arm_test) {
161    pa_do_volume_func_t orig_func, arm_func;
162    pa_cpu_arm_flag_t flags = 0;
163    int i, j;
164
165    pa_cpu_get_arm_flags(&flags);
166
167    if (!(flags & PA_CPU_ARM_V6)) {
168        pa_log_info("ARMv6 instructions not supported. Skipping");
169        return;
170    }
171
172    orig_func = pa_get_volume_func(PA_SAMPLE_S16NE);
173    pa_volume_func_init_arm(flags);
174    arm_func = pa_get_volume_func(PA_SAMPLE_S16NE);
175
176    pa_log_debug("Checking ARM svolume");
177    for (i = 1; i <= 3; i++) {
178        for (j = 0; j < 7; j++)
179            run_volume_test(arm_func, orig_func, j, i, true, false);
180    }
181    run_volume_test(arm_func, orig_func, 7, 1, true, true);
182    run_volume_test(arm_func, orig_func, 7, 2, true, true);
183    run_volume_test(arm_func, orig_func, 7, 3, true, true);
184}
185END_TEST
186#endif /* defined (__arm__) && defined (__linux__) */
187
188START_TEST (svolume_orc_test) {
189    pa_do_volume_func_t orig_func, orc_func;
190    pa_cpu_info cpu_info = { PA_CPU_UNDEFINED, {}, false };
191    int i, j;
192
193#if defined (__i386__) || defined (__amd64__)
194    pa_zero(cpu_info);
195    cpu_info.cpu_type = PA_CPU_X86;
196    pa_cpu_get_x86_flags(&cpu_info.flags.x86);
197#endif
198
199    orig_func = pa_get_volume_func(PA_SAMPLE_S16NE);
200
201    if (!pa_cpu_init_orc(cpu_info)) {
202        pa_log_info("Orc not supported. Skipping");
203        return;
204    }
205
206    orc_func = pa_get_volume_func(PA_SAMPLE_S16NE);
207
208    pa_log_debug("Checking Orc svolume");
209    for (i = 1; i <= 2; i++) {
210        for (j = 0; j < 7; j++)
211            run_volume_test(orc_func, orig_func, j, i, true, false);
212    }
213    run_volume_test(orc_func, orig_func, 7, 1, true, true);
214    run_volume_test(orc_func, orig_func, 7, 2, true, true);
215}
216END_TEST
217
218int main(int argc, char *argv[]) {
219    int failed = 0;
220    Suite *s;
221    TCase *tc;
222    SRunner *sr;
223
224    if (!getenv("MAKE_CHECK"))
225        pa_log_set_level(PA_LOG_DEBUG);
226
227    s = suite_create("CPU");
228
229    tc = tcase_create("svolume");
230#if defined (__i386__) || defined (__amd64__)
231    tcase_add_test(tc, svolume_mmx_test);
232    tcase_add_test(tc, svolume_sse_test);
233#endif
234#if defined (__arm__) && defined (__linux__)
235    tcase_add_test(tc, svolume_arm_test);
236#endif
237    tcase_add_test(tc, svolume_orc_test);
238    tcase_set_timeout(tc, 120);
239    suite_add_tcase(s, tc);
240
241    sr = srunner_create(s);
242    srunner_run_all(sr, CK_NORMAL);
243    failed = srunner_ntests_failed(sr);
244    srunner_free(sr);
245
246    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
247}
248