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/random.h> 27#include <pulsecore/macro.h> 28#include <pulsecore/sconv.h> 29 30#include "runtime-test-util.h" 31 32#define SAMPLES 1028 33#define TIMES 1000 34#define TIMES2 100 35 36static void run_conv_test_float_to_s16( 37 pa_convert_func_t func, 38 pa_convert_func_t orig_func, 39 int align, 40 bool correct, 41 bool perf) { 42 43 PA_DECLARE_ALIGNED(8, int16_t, s[SAMPLES]) = { 0 }; 44 PA_DECLARE_ALIGNED(8, int16_t, s_ref[SAMPLES]) = { 0 }; 45 PA_DECLARE_ALIGNED(8, float, f[SAMPLES]); 46 int16_t *samples, *samples_ref; 47 float *floats; 48 int i, nsamples; 49 50 /* Force sample alignment as requested */ 51 samples = s + (8 - align); 52 samples_ref = s_ref + (8 - align); 53 floats = f + (8 - align); 54 nsamples = SAMPLES - (8 - align); 55 56 for (i = 0; i < nsamples; i++) { 57 floats[i] = 2.1f * (rand()/(float) RAND_MAX - 0.5f); 58 } 59 60 if (correct) { 61 orig_func(nsamples, floats, samples_ref); 62 func(nsamples, floats, samples); 63 64 for (i = 0; i < nsamples; i++) { 65 if (abs(samples[i] - samples_ref[i]) > 1) { 66 pa_log_debug("Correctness test failed: align=%d", align); 67 pa_log_debug("%d: %04hx != %04hx (%.24f)\n", i, samples[i], samples_ref[i], floats[i]); 68 ck_abort(); 69 } 70 } 71 } 72 73 if (perf) { 74 pa_log_debug("Testing sconv performance with %d sample alignment", align); 75 76 PA_RUNTIME_TEST_RUN_START("func", TIMES, TIMES2) { 77 func(nsamples, floats, samples); 78 } PA_RUNTIME_TEST_RUN_STOP 79 80 PA_RUNTIME_TEST_RUN_START("orig", TIMES, TIMES2) { 81 orig_func(nsamples, floats, samples_ref); 82 } PA_RUNTIME_TEST_RUN_STOP 83 } 84} 85 86/* This test is currently only run under NEON */ 87#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON) 88static void run_conv_test_s16_to_float( 89 pa_convert_func_t func, 90 pa_convert_func_t orig_func, 91 int align, 92 bool correct, 93 bool perf) { 94 95 PA_DECLARE_ALIGNED(8, float, f[SAMPLES]) = { 0.0f }; 96 PA_DECLARE_ALIGNED(8, float, f_ref[SAMPLES]) = { 0.0f }; 97 PA_DECLARE_ALIGNED(8, int16_t, s[SAMPLES]); 98 float *floats, *floats_ref; 99 int16_t *samples; 100 int i, nsamples; 101 102 /* Force sample alignment as requested */ 103 floats = f + (8 - align); 104 floats_ref = f_ref + (8 - align); 105 samples = s + (8 - align); 106 nsamples = SAMPLES - (8 - align); 107 108 pa_random(samples, nsamples * sizeof(int16_t)); 109 110 if (correct) { 111 orig_func(nsamples, samples, floats_ref); 112 func(nsamples, samples, floats); 113 114 for (i = 0; i < nsamples; i++) { 115 if (fabsf(floats[i] - floats_ref[i]) > 0.0001f) { 116 pa_log_debug("Correctness test failed: align=%d", align); 117 pa_log_debug("%d: %.24f != %.24f (%d)\n", i, floats[i], floats_ref[i], samples[i]); 118 ck_abort(); 119 } 120 } 121 } 122 123 if (perf) { 124 pa_log_debug("Testing sconv performance with %d sample alignment", align); 125 126 PA_RUNTIME_TEST_RUN_START("func", TIMES, TIMES2) { 127 func(nsamples, samples, floats); 128 } PA_RUNTIME_TEST_RUN_STOP 129 130 PA_RUNTIME_TEST_RUN_START("orig", TIMES, TIMES2) { 131 orig_func(nsamples, samples, floats_ref); 132 } PA_RUNTIME_TEST_RUN_STOP 133 } 134} 135#endif /* defined (__arm__) && defined (__linux__) && defined (HAVE_NEON) */ 136 137#if (defined (__i386__) || defined (__amd64__)) && defined (HAVE_SSE) 138START_TEST (sconv_sse2_test) { 139 pa_cpu_x86_flag_t flags = 0; 140 pa_convert_func_t orig_func, sse2_func; 141 142 pa_cpu_get_x86_flags(&flags); 143 144 if (!(flags & PA_CPU_X86_SSE2)) { 145 pa_log_info("SSE2 not supported. Skipping"); 146 return; 147 } 148 149 orig_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE); 150 pa_convert_func_init_sse(PA_CPU_X86_SSE2); 151 sse2_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE); 152 153 pa_log_debug("Checking SSE2 sconv (float -> s16)"); 154 run_conv_test_float_to_s16(sse2_func, orig_func, 0, true, false); 155 run_conv_test_float_to_s16(sse2_func, orig_func, 1, true, false); 156 run_conv_test_float_to_s16(sse2_func, orig_func, 2, true, false); 157 run_conv_test_float_to_s16(sse2_func, orig_func, 3, true, false); 158 run_conv_test_float_to_s16(sse2_func, orig_func, 4, true, false); 159 run_conv_test_float_to_s16(sse2_func, orig_func, 5, true, false); 160 run_conv_test_float_to_s16(sse2_func, orig_func, 6, true, false); 161 run_conv_test_float_to_s16(sse2_func, orig_func, 7, true, true); 162} 163END_TEST 164 165START_TEST (sconv_sse_test) { 166 pa_cpu_x86_flag_t flags = 0; 167 pa_convert_func_t orig_func, sse_func; 168 169 pa_cpu_get_x86_flags(&flags); 170 171 if (!(flags & PA_CPU_X86_SSE)) { 172 pa_log_info("SSE not supported. Skipping"); 173 return; 174 } 175 176 orig_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE); 177 pa_convert_func_init_sse(PA_CPU_X86_SSE); 178 sse_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE); 179 180 pa_log_debug("Checking SSE sconv (float -> s16)"); 181 run_conv_test_float_to_s16(sse_func, orig_func, 0, true, false); 182 run_conv_test_float_to_s16(sse_func, orig_func, 1, true, false); 183 run_conv_test_float_to_s16(sse_func, orig_func, 2, true, false); 184 run_conv_test_float_to_s16(sse_func, orig_func, 3, true, false); 185 run_conv_test_float_to_s16(sse_func, orig_func, 4, true, false); 186 run_conv_test_float_to_s16(sse_func, orig_func, 5, true, false); 187 run_conv_test_float_to_s16(sse_func, orig_func, 6, true, false); 188 run_conv_test_float_to_s16(sse_func, orig_func, 7, true, true); 189} 190END_TEST 191#endif /* (defined (__i386__) || defined (__amd64__)) && defined (HAVE_SSE) */ 192 193#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON) 194START_TEST (sconv_neon_test) { 195 pa_cpu_arm_flag_t flags = 0; 196 pa_convert_func_t orig_from_func, neon_from_func; 197 pa_convert_func_t orig_to_func, neon_to_func; 198 199 pa_cpu_get_arm_flags(&flags); 200 201 if (!(flags & PA_CPU_ARM_NEON)) { 202 pa_log_info("NEON not supported. Skipping"); 203 return; 204 } 205 206 orig_from_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE); 207 orig_to_func = pa_get_convert_to_float32ne_function(PA_SAMPLE_S16LE); 208 pa_convert_func_init_neon(flags); 209 neon_from_func = pa_get_convert_from_float32ne_function(PA_SAMPLE_S16LE); 210 neon_to_func = pa_get_convert_to_float32ne_function(PA_SAMPLE_S16LE); 211 212 pa_log_debug("Checking NEON sconv (float -> s16)"); 213 run_conv_test_float_to_s16(neon_from_func, orig_from_func, 0, true, false); 214 run_conv_test_float_to_s16(neon_from_func, orig_from_func, 1, true, false); 215 run_conv_test_float_to_s16(neon_from_func, orig_from_func, 2, true, false); 216 run_conv_test_float_to_s16(neon_from_func, orig_from_func, 3, true, false); 217 run_conv_test_float_to_s16(neon_from_func, orig_from_func, 4, true, false); 218 run_conv_test_float_to_s16(neon_from_func, orig_from_func, 5, true, false); 219 run_conv_test_float_to_s16(neon_from_func, orig_from_func, 6, true, false); 220 run_conv_test_float_to_s16(neon_from_func, orig_from_func, 7, true, true); 221 222 pa_log_debug("Checking NEON sconv (s16 -> float)"); 223 run_conv_test_s16_to_float(neon_to_func, orig_to_func, 0, true, false); 224 run_conv_test_s16_to_float(neon_to_func, orig_to_func, 1, true, false); 225 run_conv_test_s16_to_float(neon_to_func, orig_to_func, 2, true, false); 226 run_conv_test_s16_to_float(neon_to_func, orig_to_func, 3, true, false); 227 run_conv_test_s16_to_float(neon_to_func, orig_to_func, 4, true, false); 228 run_conv_test_s16_to_float(neon_to_func, orig_to_func, 5, true, false); 229 run_conv_test_s16_to_float(neon_to_func, orig_to_func, 6, true, false); 230 run_conv_test_s16_to_float(neon_to_func, orig_to_func, 7, true, true); 231} 232END_TEST 233#endif /* defined (__arm__) && defined (__linux__) && defined (HAVE_NEON) */ 234 235int main(int argc, char *argv[]) { 236 int failed = 0; 237 Suite *s; 238 TCase *tc; 239 SRunner *sr; 240 241 if (!getenv("MAKE_CHECK")) 242 pa_log_set_level(PA_LOG_DEBUG); 243 244 s = suite_create("CPU"); 245 246 tc = tcase_create("sconv"); 247#if (defined (__i386__) || defined (__amd64__)) && defined (HAVE_SSE) 248 tcase_add_test(tc, sconv_sse2_test); 249 tcase_add_test(tc, sconv_sse_test); 250#endif 251#if defined (__arm__) && defined (__linux__) && defined (HAVE_NEON) 252 tcase_add_test(tc, sconv_neon_test); 253#endif 254 tcase_set_timeout(tc, 120); 255 suite_add_tcase(s, tc); 256 257 sr = srunner_create(s); 258 srunner_run_all(sr, CK_NORMAL); 259 failed = srunner_ntests_failed(sr); 260 srunner_free(sr); 261 262 return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 263} 264