]> code.delx.au - pulseaudio/blob - src/tests/mix-special-test.c
df86ccbb900e3ab99198a4f1c9324a707c6d8d3b
[pulseaudio] / src / tests / mix-special-test.c
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, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <check.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <math.h>
28
29 #include <pulse/rtclock.h>
30 #include <pulsecore/random.h>
31 #include <pulsecore/macro.h>
32 #include <pulsecore/mix.h>
33 #include <pulsecore/sample-util.h>
34
35 #define PA_CPU_TEST_RUN_START(l, t1, t2) \
36 { \
37 int _j, _k; \
38 int _times = (t1), _times2 = (t2); \
39 pa_usec_t _start, _stop; \
40 pa_usec_t _min = INT_MAX, _max = 0; \
41 double _s1 = 0, _s2 = 0; \
42 const char *_label = (l); \
43 \
44 for (_k = 0; _k < _times2; _k++) { \
45 _start = pa_rtclock_now(); \
46 for (_j = 0; _j < _times; _j++)
47
48 #define PA_CPU_TEST_RUN_STOP \
49 _stop = pa_rtclock_now(); \
50 \
51 if (_min > (_stop - _start)) _min = _stop - _start; \
52 if (_max < (_stop - _start)) _max = _stop - _start; \
53 _s1 += _stop - _start; \
54 _s2 += (_stop - _start) * (_stop - _start); \
55 } \
56 pa_log_debug("%s: %llu usec (avg: %g, min = %llu, max = %llu, stddev = %g).", _label, \
57 (long long unsigned int)_s1, \
58 ((double)_s1 / _times2), \
59 (long long unsigned int)_min, \
60 (long long unsigned int)_max, \
61 sqrt(_times2 * _s2 - _s1 * _s1) / _times2); \
62 }
63
64 static void acquire_mix_streams(pa_mix_info streams[], unsigned nstreams) {
65 unsigned i;
66
67 for (i = 0; i < nstreams; i++)
68 streams[i].ptr = pa_memblock_acquire_chunk(&streams[i].chunk);
69 }
70
71 static void release_mix_streams(pa_mix_info streams[], unsigned nstreams) {
72 unsigned i;
73
74 for (i = 0; i < nstreams; i++)
75 pa_memblock_release(streams[i].chunk.memblock);
76 }
77
78 /* special case: mix 2 s16ne streams, 1 channel each */
79 static void pa_mix2_ch1_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) {
80 const int16_t *ptr0 = streams[0].ptr;
81 const int16_t *ptr1 = streams[1].ptr;
82
83 const int32_t cv0 = streams[0].linear[0].i;
84 const int32_t cv1 = streams[1].linear[0].i;
85
86 length /= sizeof(int16_t);
87
88 for (; length > 0; length--) {
89 int32_t sum;
90
91 sum = pa_mult_s16_volume(*ptr0++, cv0);
92 sum += pa_mult_s16_volume(*ptr1++, cv1);
93
94 sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
95 *data++ = sum;
96 }
97 }
98
99 /* special case: mix 2 s16ne streams, 2 channels each */
100 static void pa_mix2_ch2_s16ne(pa_mix_info streams[], int16_t *data, unsigned length) {
101 const int16_t *ptr0 = streams[0].ptr;
102 const int16_t *ptr1 = streams[1].ptr;
103
104 length /= sizeof(int16_t) * 2;
105
106 for (; length > 0; length--) {
107 int32_t sum;
108
109 sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[0].i);
110 sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[0].i);
111
112 sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
113 *data++ = sum;
114
115 sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[1].i);
116 sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[1].i);
117
118 sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
119 *data++ = sum;
120 }
121 }
122
123 /* special case: mix 2 s16ne streams */
124 static void pa_mix2_s16ne(pa_mix_info streams[], unsigned channels, int16_t *data, unsigned length) {
125 const int16_t *ptr0 = streams[0].ptr;
126 const int16_t *ptr1 = streams[1].ptr;
127 unsigned channel = 0;
128
129 length /= sizeof(int16_t);
130
131 for (; length > 0; length--) {
132 int32_t sum;
133
134 sum = pa_mult_s16_volume(*ptr0++, streams[0].linear[channel].i);
135 sum += pa_mult_s16_volume(*ptr1++, streams[1].linear[channel].i);
136
137 sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
138 *data++ = sum;
139
140 if (PA_UNLIKELY(++channel >= channels))
141 channel = 0;
142 }
143 }
144
145 /* special case: mix s16ne streams, 2 channels each */
146 static void pa_mix_ch2_s16ne(pa_mix_info streams[], unsigned nstreams, int16_t *data, unsigned length) {
147
148 length /= sizeof(int16_t) * 2;
149
150 for (; length > 0; length--) {
151 int32_t sum0 = 0, sum1 = 0;
152 unsigned i;
153
154 for (i = 0; i < nstreams; i++) {
155 pa_mix_info *m = streams + i;
156 int32_t cv0 = m->linear[0].i;
157 int32_t cv1 = m->linear[1].i;
158
159 sum0 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv0);
160 m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
161
162 sum1 += pa_mult_s16_volume(*((int16_t*) m->ptr), cv1);
163 m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
164 }
165
166 *data++ = PA_CLAMP_UNLIKELY(sum0, -0x8000, 0x7FFF);
167 *data++ = PA_CLAMP_UNLIKELY(sum1, -0x8000, 0x7FFF);
168 }
169 }
170
171 static void pa_mix_generic_s16ne(pa_mix_info streams[], unsigned nstreams, unsigned channels, int16_t *data, unsigned length) {
172 unsigned channel = 0;
173
174 length /= sizeof(int16_t);
175
176 for (; length > 0; length--) {
177 int32_t sum = 0;
178 unsigned i;
179
180 for (i = 0; i < nstreams; i++) {
181 pa_mix_info *m = streams + i;
182 int32_t cv = m->linear[channel].i;
183
184 if (PA_LIKELY(cv > 0))
185 sum += pa_mult_s16_volume(*((int16_t*) m->ptr), cv);
186 m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
187 }
188
189 sum = PA_CLAMP_UNLIKELY(sum, -0x8000, 0x7FFF);
190 *data++ = sum;
191
192 if (PA_UNLIKELY(++channel >= channels))
193 channel = 0;
194 }
195 }
196
197
198 #define SAMPLES 1028
199 #define TIMES 1000
200 #define TIMES2 100
201
202 START_TEST (mix_special_1ch_test) {
203 int16_t samples0[SAMPLES];
204 int16_t samples1[SAMPLES];
205 int16_t out[SAMPLES];
206 int16_t out_ref[SAMPLES];
207 pa_mempool *pool;
208 pa_memchunk c0, c1;
209 pa_mix_info m[2];
210 unsigned nsamples = SAMPLES;
211
212 fail_unless((pool = pa_mempool_new(FALSE, 0)) != NULL, NULL);
213
214 pa_random(samples0, nsamples * sizeof(int16_t));
215 c0.memblock = pa_memblock_new_fixed(pool, samples0, nsamples * sizeof(int16_t), FALSE);
216 c0.length = pa_memblock_get_length(c0.memblock);
217 c0.index = 0;
218
219 pa_random(samples1, nsamples * sizeof(int16_t));
220 c1.memblock = pa_memblock_new_fixed(pool, samples1, nsamples * sizeof(int16_t), FALSE);
221 c1.length = pa_memblock_get_length(c1.memblock);
222 c1.index = 0;
223
224 m[0].chunk = c0;
225 m[0].volume.channels = 1;
226 m[0].volume.values[0] = PA_VOLUME_NORM;
227 m[0].linear[0].i = 0x5555;
228
229 m[1].chunk = c1;
230 m[1].volume.channels = 1;
231 m[1].volume.values[0] = PA_VOLUME_NORM;
232 m[1].linear[0].i = 0x6789;
233
234 PA_CPU_TEST_RUN_START("mix s16 generic 1 channel", TIMES, TIMES2) {
235 acquire_mix_streams(m, 2);
236 pa_mix_generic_s16ne(m, 2, 1, out_ref, nsamples * sizeof(int16_t));
237 release_mix_streams(m, 2);
238 } PA_CPU_TEST_RUN_STOP
239
240 PA_CPU_TEST_RUN_START("mix s16 2 streams 1 channel", TIMES, TIMES2) {
241 acquire_mix_streams(m, 2);
242 pa_mix2_ch1_s16ne(m, out, nsamples * sizeof(int16_t));
243 release_mix_streams(m, 2);
244 } PA_CPU_TEST_RUN_STOP
245
246 fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0);
247
248 pa_memblock_unref(c0.memblock);
249 pa_memblock_unref(c1.memblock);
250
251 pa_mempool_free(pool);
252 }
253 END_TEST
254
255 START_TEST (mix_special_2ch_test) {
256 int16_t samples0[SAMPLES*2];
257 int16_t samples1[SAMPLES*2];
258 int16_t out[SAMPLES*2];
259 int16_t out_ref[SAMPLES*2];
260 int i;
261 pa_mempool *pool;
262 pa_memchunk c0, c1;
263 pa_mix_info m[2];
264 unsigned nsamples = SAMPLES * 2;
265
266 fail_unless((pool = pa_mempool_new(FALSE, 0)) != NULL, NULL);
267
268 pa_random(samples0, nsamples * sizeof(int16_t));
269 c0.memblock = pa_memblock_new_fixed(pool, samples0, nsamples * sizeof(int16_t), FALSE);
270 c0.length = pa_memblock_get_length(c0.memblock);
271 c0.index = 0;
272
273 pa_random(samples1, nsamples * sizeof(int16_t));
274 c1.memblock = pa_memblock_new_fixed(pool, samples1, nsamples * sizeof(int16_t), FALSE);
275 c1.length = pa_memblock_get_length(c1.memblock);
276 c1.index = 0;
277
278 m[0].chunk = c0;
279 m[0].volume.channels = 2;
280 for (i = 0; i < m[0].volume.channels; i++) {
281 m[0].volume.values[i] = PA_VOLUME_NORM;
282 m[0].linear[i].i = 0x5555;
283 }
284
285 m[1].chunk = c1;
286 m[1].volume.channels = 2;
287 for (i = 0; i < m[1].volume.channels; i++) {
288 m[1].volume.values[i] = PA_VOLUME_NORM;
289 m[1].linear[i].i = 0x6789;
290 }
291
292 PA_CPU_TEST_RUN_START("mix s16 generic 2 channels", TIMES, TIMES2) {
293 acquire_mix_streams(m, 2);
294 pa_mix_generic_s16ne(m, 2, 2, out_ref, nsamples * sizeof(int16_t));
295 release_mix_streams(m, 2);
296 } PA_CPU_TEST_RUN_STOP
297
298 PA_CPU_TEST_RUN_START("mix s16 2 channels", TIMES, TIMES2) {
299 acquire_mix_streams(m, 2);
300 pa_mix_ch2_s16ne(m, 2, out, nsamples * sizeof(int16_t));
301 release_mix_streams(m, 2);
302 } PA_CPU_TEST_RUN_STOP
303
304 fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0);
305
306 PA_CPU_TEST_RUN_START("mix s16 2 streams", TIMES, TIMES2) {
307 acquire_mix_streams(m, 2);
308 pa_mix2_s16ne(m, 2, out, nsamples * sizeof(int16_t));
309 release_mix_streams(m, 2);
310 } PA_CPU_TEST_RUN_STOP
311
312 fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0);
313
314 PA_CPU_TEST_RUN_START("mix s16 2 streams 2 channels", TIMES, TIMES2) {
315 acquire_mix_streams(m, 2);
316 pa_mix2_ch2_s16ne(m, out, nsamples * sizeof(int16_t));
317 release_mix_streams(m, 2);
318 } PA_CPU_TEST_RUN_STOP
319
320 fail_unless(memcmp(out, out_ref, nsamples * sizeof(int16_t)) == 0);
321
322 pa_memblock_unref(c0.memblock);
323 pa_memblock_unref(c1.memblock);
324
325 pa_mempool_free(pool);
326 }
327 END_TEST
328
329 int main(int argc, char *argv[]) {
330 int failed = 0;
331 Suite *s;
332 TCase *tc;
333 SRunner *sr;
334
335 if (!getenv("MAKE_CHECK"))
336 pa_log_set_level(PA_LOG_DEBUG);
337
338 s = suite_create("Mix-special");
339 tc = tcase_create("mix-special 1ch");
340 tcase_add_test(tc, mix_special_1ch_test);
341 tcase_set_timeout(tc, 120);
342 suite_add_tcase(s, tc);
343 tc = tcase_create("mix-special 2ch");
344 tcase_add_test(tc, mix_special_2ch_test);
345 tcase_set_timeout(tc, 120);
346 suite_add_tcase(s, tc);
347
348 sr = srunner_create(s);
349 srunner_run_all(sr, CK_NORMAL);
350 failed = srunner_ntests_failed(sr);
351 srunner_free(sr);
352
353 return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
354 }