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
run_volume_test(pa_do_volume_func_t func,pa_do_volume_func_t orig_func,int align,int channels,bool correct,bool perf)39 static 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__)
START_TEST(svolume_mmx_test)104 START_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 }
129 END_TEST
130
START_TEST(svolume_sse_test)131 START_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 }
156 END_TEST
157 #endif /* defined (__i386__) || defined (__amd64__) */
158
159 #if defined (__arm__) && defined (__linux__)
START_TEST(svolume_arm_test)160 START_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 }
185 END_TEST
186 #endif /* defined (__arm__) && defined (__linux__) */
187
START_TEST(svolume_orc_test)188 START_TEST (svolume_orc_test) {
189 pa_do_volume_func_t orig_func, orc_func;
190 pa_cpu_info cpu_info;
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 }
216 END_TEST
217
main(int argc,char * argv[])218 int 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