[DIRECTX/WINE]
[reactos.git] / reactos / dll / directx / wine / dsound / dsound_convert.c
1 /* DirectSound format conversion and mixing routines
2 *
3 * Copyright 2007 Maarten Lankhorst
4 * Copyright 2011 Owen Rudge for CodeWeavers
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 /* 8 bits is unsigned, the rest is signed.
22 * First I tried to reuse existing stuff from alsa-lib, after that
23 * didn't work, I gave up and just went for individual hacks.
24 *
25 * 24 bit is expensive to do, due to unaligned access.
26 * In dlls/winex11.drv/dib_convert.c convert_888_to_0888_asis there is a way
27 * around it, but I'm happy current code works, maybe something for later.
28 *
29 * The ^ 0x80 flips the signed bit, this is the conversion from
30 * signed (-128.. 0.. 127) to unsigned (0...255)
31 * This is only temporary: All 8 bit data should be converted to signed.
32 * then when fed to the sound card, it should be converted to unsigned again.
33 *
34 * Sound is LITTLE endian
35 */
36
37 #include <config.h>
38
39 #include <stdarg.h>
40 #include <math.h>
41
42 #define WIN32_NO_STATUS
43 #define _INC_WINDOWS
44 #define COM_NO_WINDOWS_H
45
46 #define NONAMELESSSTRUCT
47 #define NONAMELESSUNION
48 #include <windef.h>
49 #include <winbase.h>
50 #include <mmsystem.h>
51 #include <winternl.h>
52 #include <wine/debug.h>
53 #include <dsound.h>
54 #include "dsound_private.h"
55
56 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
57
58 #ifdef WORDS_BIGENDIAN
59 #define le16(x) RtlUshortByteSwap((x))
60 #define le32(x) RtlUlongByteSwap((x))
61 #else
62 #define le16(x) (x)
63 #define le32(x) (x)
64 #endif
65
66 /* This is an inlined version of lrintf. */
67 #if defined(_MSC_VER)
68 #if defined(_M_AMD64)
69 #include <xmmintrin.h>
70 #endif
71
72 FORCEINLINE
73 int
74 lrintf(float f)
75 {
76 #if defined(_M_IX86)
77 int result;
78 __asm
79 {
80 fld f;
81 fistp result;
82 }
83 return result;
84 #elif defined(_M_AMD64)
85 return _mm_cvtss_si32(_mm_load_ss(&f));
86 #endif
87 }
88 #endif
89
90 static float get8(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel)
91 {
92 const BYTE* buf = dsb->buffer->memory;
93 buf += pos + channel;
94 return (buf[0] - 0x80) / (float)0x80;
95 }
96
97 static float get16(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel)
98 {
99 const BYTE* buf = dsb->buffer->memory;
100 const SHORT *sbuf = (const SHORT*)(buf + pos + 2 * channel);
101 SHORT sample = (SHORT)le16(*sbuf);
102 return sample / (float)0x8000;
103 }
104
105 static float get24(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel)
106 {
107 LONG sample;
108 const BYTE* buf = dsb->buffer->memory;
109 buf += pos + 3 * channel;
110 /* The next expression deliberately has an overflow for buf[2] >= 0x80,
111 this is how negative values are made.
112 */
113 sample = (buf[0] << 8) | (buf[1] << 16) | (buf[2] << 24);
114 return sample / (float)0x80000000U;
115 }
116
117 static float get32(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel)
118 {
119 const BYTE* buf = dsb->buffer->memory;
120 const LONG *sbuf = (const LONG*)(buf + pos + 4 * channel);
121 LONG sample = le32(*sbuf);
122 return sample / (float)0x80000000U;
123 }
124
125 static float getieee32(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel)
126 {
127 const BYTE* buf = dsb->buffer->memory;
128 const float *sbuf = (const float*)(buf + pos + 4 * channel);
129 /* The value will be clipped later, when put into some non-float buffer */
130 return *sbuf;
131 }
132
133 const bitsgetfunc getbpp[5] = {get8, get16, get24, get32, getieee32};
134
135 float get_mono(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel)
136 {
137 DWORD channels = dsb->pwfx->nChannels;
138 DWORD c;
139 float val = 0;
140 /* XXX: does Windows include LFE into the mix? */
141 for (c = 0; c < channels; c++)
142 val += dsb->get_aux(dsb, pos, c);
143 val /= channels;
144 return val;
145 }
146
147 static inline unsigned char f_to_8(float value)
148 {
149 if(value <= -1.f)
150 return 0;
151 if(value >= 1.f * 0x7f / 0x80)
152 return 0xFF;
153 return lrintf((value + 1.f) * 0x80);
154 }
155
156 static inline SHORT f_to_16(float value)
157 {
158 if(value <= -1.f)
159 return 0x8000;
160 if(value >= 1.f * 0x7FFF / 0x8000)
161 return 0x7FFF;
162 return le16(lrintf(value * 0x8000));
163 }
164
165 static LONG f_to_24(float value)
166 {
167 if(value <= -1.f)
168 return 0x80000000;
169 if(value >= 1.f * 0x7FFFFF / 0x800000)
170 return 0x7FFFFF00;
171 return lrintf(value * 0x80000000U);
172 }
173
174 static inline LONG f_to_32(float value)
175 {
176 if(value <= -1.f)
177 return 0x80000000;
178 if(value >= 1.f * 0x7FFFFFFF / 0x80000000U) /* this rounds to 1.f */
179 return 0x7FFFFFFF;
180 return le32(lrintf(value * 0x80000000U));
181 }
182
183 void putieee32(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value)
184 {
185 BYTE *buf = (BYTE *)dsb->device->tmp_buffer;
186 float *fbuf = (float*)(buf + pos + sizeof(float) * channel);
187 *fbuf = value;
188 }
189
190 void put_mono2stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value)
191 {
192 dsb->put_aux(dsb, pos, 0, value);
193 dsb->put_aux(dsb, pos, 1, value);
194 }
195
196 void mixieee32(float *src, float *dst, unsigned samples)
197 {
198 TRACE("%p - %p %d\n", src, dst, samples);
199 while (samples--)
200 *(dst++) += *(src++);
201 }
202
203 static void norm8(float *src, unsigned char *dst, unsigned len)
204 {
205 TRACE("%p - %p %d\n", src, dst, len);
206 while (len--)
207 {
208 *dst = f_to_8(*src);
209 ++dst;
210 ++src;
211 }
212 }
213
214 static void norm16(float *src, SHORT *dst, unsigned len)
215 {
216 TRACE("%p - %p %d\n", src, dst, len);
217 len /= 2;
218 while (len--)
219 {
220 *dst = f_to_16(*src);
221 ++dst;
222 ++src;
223 }
224 }
225
226 static void norm24(float *src, BYTE *dst, unsigned len)
227 {
228 TRACE("%p - %p %d\n", src, dst, len);
229 len /= 3;
230 while (len--)
231 {
232 LONG t = f_to_24(*src);
233 dst[0] = (t >> 8) & 0xFF;
234 dst[1] = (t >> 16) & 0xFF;
235 dst[2] = t >> 24;
236 dst += 3;
237 ++src;
238 }
239 }
240
241 static void norm32(float *src, INT *dst, unsigned len)
242 {
243 TRACE("%p - %p %d\n", src, dst, len);
244 len /= 4;
245 while (len--)
246 {
247 *dst = f_to_32(*src);
248 ++dst;
249 ++src;
250 }
251 }
252
253 static void normieee32(float *src, float *dst, unsigned len)
254 {
255 TRACE("%p - %p %d\n", src, dst, len);
256 len /= 4;
257 while (len--)
258 {
259 if(*src > 1)
260 *dst = 1;
261 else if(*src < -1)
262 *dst = -1;
263 else
264 *dst = *src;
265 ++dst;
266 ++src;
267 }
268 }
269
270 const normfunc normfunctions[5] = {
271 (normfunc)norm8,
272 (normfunc)norm16,
273 (normfunc)norm24,
274 (normfunc)norm32,
275 (normfunc)normieee32
276 };