Branch data Line data Source code
1 : : /***
2 : : This file is part of PulseAudio.
3 : :
4 : : Copyright 2004-2006 Lennart Poettering
5 : : Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
6 : :
7 : : PulseAudio is free software; you can redistribute it and/or modify
8 : : it under the terms of the GNU Lesser General Public License as published
9 : : by the Free Software Foundation; either version 2.1 of the License,
10 : : or (at your option) any later version.
11 : :
12 : : PulseAudio is distributed in the hope that it will be useful, but
13 : : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : General Public License for more details.
16 : :
17 : : You should have received a copy of the GNU Lesser General Public License
18 : : along with PulseAudio; if not, write to the Free Software
19 : : Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 : : USA.
21 : : ***/
22 : :
23 : : #ifdef HAVE_CONFIG_H
24 : : #include <config.h>
25 : : #endif
26 : :
27 : : #include <pulse/rtclock.h>
28 : :
29 : : #include <pulsecore/random.h>
30 : : #include <pulsecore/macro.h>
31 : : #include <pulsecore/endianmacros.h>
32 : :
33 : : #include "cpu-x86.h"
34 : :
35 : : #include "sample-util.h"
36 : :
37 : : #if defined (__i386__) || defined (__amd64__)
38 : : /* in s: 2 int16_t samples
39 : : * in v: 2 int32_t volumes, fixed point 16:16
40 : : * out s: contains scaled and clamped int16_t samples.
41 : : *
42 : : * We calculate the high 32 bits of a 32x16 multiply which we then
43 : : * clamp to 16 bits. The calculation is:
44 : : *
45 : : * vl = (v & 0xffff)
46 : : * vh = (v >> 16)
47 : : * s = ((s * vl) >> 16) + (s * vh);
48 : : *
49 : : * For the first multiply we have to do a sign correction as we need to
50 : : * multiply a signed int with an unsigned int. Hacker's delight 8-3 gives a
51 : : * simple formula to correct the sign of the high word after the signed
52 : : * multiply.
53 : : */
54 : : #define VOLUME_32x16(s,v) /* .. | vh | vl | */ \
55 : : " pxor %%mm4, %%mm4 \n\t" /* .. | 0 | 0 | */ \
56 : : " punpcklwd %%mm4, "#s" \n\t" /* .. | 0 | p0 | */ \
57 : : " pcmpgtw "#v", %%mm4 \n\t" /* .. | 0 | s(vl) | */ \
58 : : " pand "#s", %%mm4 \n\t" /* .. | 0 | (p0) | (vl >> 15) & p */ \
59 : : " movq "#s", %%mm5 \n\t" \
60 : : " pmulhw "#v", "#s" \n\t" /* .. | 0 | vl*p0 | */ \
61 : : " paddw %%mm4, "#s" \n\t" /* .. | 0 | vl*p0 | + sign correct */ \
62 : : " pslld $16, "#s" \n\t" /* .. | vl*p0 | 0 | */ \
63 : : " psrld $16, "#v" \n\t" /* .. | 0 | vh | */ \
64 : : " psrad $16, "#s" \n\t" /* .. | vl*p0 | sign extend */ \
65 : : " pmaddwd %%mm5, "#v" \n\t" /* .. | p0 * vh | */ \
66 : : " paddd "#s", "#v" \n\t" /* .. | p0 * v0 | */ \
67 : : " packssdw "#v", "#v" \n\t" /* .. | p1*v1 | p0*v0 | */
68 : :
69 : : /* approximately advances %3 = (%3 + a) % b. This function requires that
70 : : * a <= b. */
71 : : #define MOD_ADD(a,b) \
72 : : " add "#a", %3 \n\t" \
73 : : " mov %3, %4 \n\t" \
74 : : " sub "#b", %4 \n\t" \
75 : : " cmovae %4, %3 \n\t"
76 : :
77 : : /* swap 16 bits */
78 : : #define SWAP_16(s) \
79 : : " movq "#s", %%mm4 \n\t" /* .. | h l | */ \
80 : : " psrlw $8, %%mm4 \n\t" /* .. | 0 h | */ \
81 : : " psllw $8, "#s" \n\t" /* .. | l 0 | */ \
82 : : " por %%mm4, "#s" \n\t" /* .. | l h | */
83 : :
84 : : /* swap 2 registers 16 bits for better pairing */
85 : : #define SWAP_16_2(s1,s2) \
86 : : " movq "#s1", %%mm4 \n\t" /* .. | h l | */ \
87 : : " movq "#s2", %%mm5 \n\t" \
88 : : " psrlw $8, %%mm4 \n\t" /* .. | 0 h | */ \
89 : : " psrlw $8, %%mm5 \n\t" \
90 : : " psllw $8, "#s1" \n\t" /* .. | l 0 | */ \
91 : : " psllw $8, "#s2" \n\t" \
92 : : " por %%mm4, "#s1" \n\t" /* .. | l h | */ \
93 : : " por %%mm5, "#s2" \n\t"
94 : :
95 : 0 : static void pa_volume_s16ne_mmx(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
96 : : pa_reg_x86 channel, temp;
97 : :
98 : : /* Channels must be at least 4, and always a multiple of the original number.
99 : : * This is also the max amount we overread the volume array, which should
100 : : * have enough padding. */
101 [ # # ]: 0 : channels = channels == 3 ? 6 : PA_MAX (4U, channels);
102 : :
103 : 0 : __asm__ __volatile__ (
104 : : " xor %3, %3 \n\t"
105 : : " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
106 : :
107 : : " test $1, %2 \n\t" /* check for odd samples */
108 : : " je 2f \n\t"
109 : :
110 : : " movd (%q1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
111 : : " movw (%0), %w4 \n\t" /* .. | p0 | */
112 : : " movd %4, %%mm1 \n\t"
113 : : VOLUME_32x16 (%%mm1, %%mm0)
114 : : " movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
115 : : " movw %w4, (%0) \n\t"
116 : : " add $2, %0 \n\t"
117 : : MOD_ADD ($1, %5)
118 : :
119 : : "2: \n\t"
120 : : " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
121 : : " test $1, %2 \n\t" /* check for odd samples */
122 : : " je 4f \n\t"
123 : :
124 : : "3: \n\t" /* do samples in groups of 2 */
125 : : " movq (%q1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
126 : : " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
127 : : VOLUME_32x16 (%%mm1, %%mm0)
128 : : " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
129 : : " add $4, %0 \n\t"
130 : : MOD_ADD ($2, %5)
131 : :
132 : : "4: \n\t"
133 : : " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
134 : : " cmp $0, %2 \n\t"
135 : : " je 6f \n\t"
136 : :
137 : : "5: \n\t" /* do samples in groups of 4 */
138 : : " movq (%q1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
139 : : " movq 8(%q1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
140 : : " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
141 : : " movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
142 : : VOLUME_32x16 (%%mm1, %%mm0)
143 : : VOLUME_32x16 (%%mm3, %%mm2)
144 : : " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
145 : : " movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
146 : : " add $8, %0 \n\t"
147 : : MOD_ADD ($4, %5)
148 : : " dec %2 \n\t"
149 : : " jne 5b \n\t"
150 : :
151 : : "6: \n\t"
152 : : " emms \n\t"
153 : :
154 : : : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
155 : : #if defined (__i386__)
156 : : : "m" (channels)
157 : : #else
158 : 0 : : "r" ((pa_reg_x86)channels)
159 : : #endif
160 : : : "cc"
161 : : );
162 : 0 : }
163 : :
164 : 0 : static void pa_volume_s16re_mmx(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
165 : : pa_reg_x86 channel, temp;
166 : :
167 : : /* Channels must be at least 4, and always a multiple of the original number.
168 : : * This is also the max amount we overread the volume array, which should
169 : : * have enough padding. */
170 [ # # ]: 0 : channels = channels == 3 ? 6 : PA_MAX (4U, channels);
171 : :
172 : 0 : __asm__ __volatile__ (
173 : : " xor %3, %3 \n\t"
174 : : " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
175 : : " pcmpeqw %%mm6, %%mm6 \n\t" /* .. | ffff | ffff | */
176 : : " pcmpeqw %%mm7, %%mm7 \n\t" /* .. | ffff | ffff | */
177 : : " pslld $16, %%mm6 \n\t" /* .. | ffff | 0 | */
178 : : " psrld $31, %%mm7 \n\t" /* .. | 0 | 1 | */
179 : :
180 : : " test $1, %2 \n\t" /* check for odd samples */
181 : : " je 2f \n\t"
182 : :
183 : : " movd (%q1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
184 : : " movw (%0), %w4 \n\t" /* .. | p0 | */
185 : : " rorw $8, %w4 \n\t"
186 : : " movd %4, %%mm1 \n\t"
187 : : VOLUME_32x16 (%%mm1, %%mm0)
188 : : " movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
189 : : " rorw $8, %w4 \n\t"
190 : : " movw %w4, (%0) \n\t"
191 : : " add $2, %0 \n\t"
192 : : MOD_ADD ($1, %5)
193 : :
194 : : "2: \n\t"
195 : : " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
196 : : " test $1, %2 \n\t" /* check for odd samples */
197 : : " je 4f \n\t"
198 : :
199 : : "3: \n\t" /* do samples in groups of 2 */
200 : : " movq (%q1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
201 : : " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
202 : : SWAP_16 (%%mm1)
203 : : VOLUME_32x16 (%%mm1, %%mm0)
204 : : SWAP_16 (%%mm0)
205 : : " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
206 : : " add $4, %0 \n\t"
207 : : MOD_ADD ($2, %5)
208 : :
209 : : "4: \n\t"
210 : : " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
211 : : " cmp $0, %2 \n\t"
212 : : " je 6f \n\t"
213 : :
214 : : "5: \n\t" /* do samples in groups of 4 */
215 : : " movq (%q1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
216 : : " movq 8(%q1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
217 : : " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
218 : : " movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
219 : : SWAP_16_2 (%%mm1, %%mm3)
220 : : VOLUME_32x16 (%%mm1, %%mm0)
221 : : VOLUME_32x16 (%%mm3, %%mm2)
222 : : SWAP_16_2 (%%mm0, %%mm2)
223 : : " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
224 : : " movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
225 : : " add $8, %0 \n\t"
226 : : MOD_ADD ($4, %5)
227 : : " dec %2 \n\t"
228 : : " jne 5b \n\t"
229 : :
230 : : "6: \n\t"
231 : : " emms \n\t"
232 : :
233 : : : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
234 : : #if defined (__i386__)
235 : : : "m" (channels)
236 : : #else
237 : 0 : : "r" ((pa_reg_x86)channels)
238 : : #endif
239 : : : "cc"
240 : : );
241 : 0 : }
242 : :
243 : : #undef RUN_TEST
244 : :
245 : : #ifdef RUN_TEST
246 : : #define CHANNELS 2
247 : : #define SAMPLES 1022
248 : : #define TIMES 1000
249 : : #define TIMES2 100
250 : : #define PADDING 16
251 : :
252 : : static void run_test(void) {
253 : : int16_t samples[SAMPLES];
254 : : int16_t samples_ref[SAMPLES];
255 : : int16_t samples_orig[SAMPLES];
256 : : int32_t volumes[CHANNELS + PADDING];
257 : : int i, j, padding;
258 : : pa_do_volume_func_t func;
259 : : pa_usec_t start, stop;
260 : : int k;
261 : : pa_usec_t min = INT_MAX, max = 0;
262 : : double s1 = 0, s2 = 0;
263 : :
264 : : func = pa_get_volume_func(PA_SAMPLE_S16NE);
265 : :
266 : : printf("checking MMX %zd\n", sizeof(samples));
267 : :
268 : : pa_random(samples, sizeof(samples));
269 : : /* for (i = 0; i < SAMPLES; i++)
270 : : samples[i] = -1; */
271 : : memcpy(samples_ref, samples, sizeof(samples));
272 : : memcpy(samples_orig, samples, sizeof(samples));
273 : :
274 : : for (i = 0; i < CHANNELS; i++)
275 : : volumes[i] = PA_CLAMP_VOLUME(rand() >> 15);
276 : : /* volumes[i] = 0x0000ffff; */
277 : : for (padding = 0; padding < PADDING; padding++, i++)
278 : : volumes[i] = volumes[padding];
279 : :
280 : : func(samples_ref, volumes, CHANNELS, sizeof(samples));
281 : : pa_volume_s16ne_mmx(samples, volumes, CHANNELS, sizeof(samples));
282 : : for (i = 0; i < SAMPLES; i++) {
283 : : if (samples[i] != samples_ref[i]) {
284 : : printf("%d: %04x != %04x (%04x * %08x)\n", i, samples[i], samples_ref[i],
285 : : samples_orig[i], volumes[i % CHANNELS]);
286 : : }
287 : : }
288 : :
289 : : for (k = 0; k < TIMES2; k++) {
290 : : start = pa_rtclock_now();
291 : : for (j = 0; j < TIMES; j++) {
292 : : memcpy(samples, samples_orig, sizeof(samples));
293 : : pa_volume_s16ne_mmx(samples, volumes, CHANNELS, sizeof(samples));
294 : : }
295 : : stop = pa_rtclock_now();
296 : :
297 : : if (min > (stop - start)) min = stop - start;
298 : : if (max < (stop - start)) max = stop - start;
299 : : s1 += stop - start;
300 : : s2 += (stop - start) * (stop - start);
301 : : }
302 : : pa_log_info("MMX: %llu usec (min = %llu, max = %llu, stddev = %g).", (long long unsigned int)s1,
303 : : (long long unsigned int)min, (long long unsigned int)max, sqrt(TIMES2 * s2 - s1 * s1) / TIMES2);
304 : :
305 : : min = INT_MAX; max = 0;
306 : : s1 = s2 = 0;
307 : : for (k = 0; k < TIMES2; k++) {
308 : : start = pa_rtclock_now();
309 : : for (j = 0; j < TIMES; j++) {
310 : : memcpy(samples_ref, samples_orig, sizeof(samples));
311 : : func(samples_ref, volumes, CHANNELS, sizeof(samples));
312 : : }
313 : : stop = pa_rtclock_now();
314 : :
315 : : if (min > (stop - start)) min = stop - start;
316 : : if (max < (stop - start)) max = stop - start;
317 : : s1 += stop - start;
318 : : s2 += (stop - start) * (stop - start);
319 : : }
320 : : pa_log_info("ref: %llu usec (min = %llu, max = %llu, stddev = %g).", (long long unsigned int)s1,
321 : : (long long unsigned int)min, (long long unsigned int)max, sqrt(TIMES2 * s2 - s1 * s1) / TIMES2);
322 : :
323 : : pa_assert_se(memcmp(samples_ref, samples, sizeof(samples)) == 0);
324 : : }
325 : : #endif
326 : :
327 : : #endif /* defined (__i386__) || defined (__amd64__) */
328 : :
329 : :
330 : 0 : void pa_volume_func_init_mmx(pa_cpu_x86_flag_t flags) {
331 : : #if defined (__i386__) || defined (__amd64__)
332 : :
333 : : #ifdef RUN_TEST
334 : : run_test();
335 : : #endif
336 : :
337 [ # # ]: 0 : if ((flags & PA_CPU_X86_MMX) && (flags & PA_CPU_X86_CMOV)) {
338 : 0 : pa_log_info("Initialising MMX optimized volume functions.");
339 : :
340 : 0 : pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_mmx);
341 : 0 : pa_set_volume_func(PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_mmx);
342 : : }
343 : : #endif /* defined (__i386__) || defined (__amd64__) */
344 : 0 : }
|