1 /* Mednafen - Multi-system Emulator
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 #include "../sexyal.h"
19
20 #include <windows.h>
21 #include <windowsx.h>
22
23 #undef WINNT
24 #define NONAMELESSUNION
25
26 #define DIRECTSOUND_VERSION 0x0300
27
28 #include <dsound.h>
29
30 namespace Mednafen
31 {
32
33 struct SexyAL_DSOUND
34 {
35 LPDIRECTSOUND ppDS; /* DirectSound interface object. */
36 LPDIRECTSOUNDBUFFER ppbuf; /* Primary buffer. */
37 LPDIRECTSOUNDBUFFER ppbufsec; /* Secondary buffer. */
38 LPDIRECTSOUNDBUFFER ppbufw; /* Buffer to do writes to. */
39 WAVEFORMATEX wf; /* Format of the primary and secondary buffers. */
40 long DSBufferSize; /* The size of the buffer that we can write to, in bytes. */
41
42 long BufHowMuch; /* How many bytes of buffering we sync/wait to. */
43 long BufHowMuchOBM; /* How many bytes of buffering we actually do(at least temporarily). */
44
45 DWORD ToWritePos; /* Position which the next write to the buffer
46 should write to.
47 */
48 };
49
50
51 static int Close(SexyAL_device *device);
52 static int RawCanWrite(SexyAL_device *device, uint32 *can_write);
53 static int RawWrite(SexyAL_device *device, const void *data, uint32 len);
54
CheckStatus(SexyAL_DSOUND * tmp)55 static int CheckStatus(SexyAL_DSOUND *tmp)
56 {
57 DWORD status = 0;
58
59 if(IDirectSoundBuffer_GetStatus(tmp->ppbufw, &status) != DS_OK)
60 return(0);
61
62 if(status & DSBSTATUS_BUFFERLOST)
63 IDirectSoundBuffer_Restore(tmp->ppbufw);
64
65 if(!(status&DSBSTATUS_PLAYING))
66 {
67 tmp->ToWritePos = 0;
68 IDirectSoundBuffer_SetCurrentPosition(tmp->ppbufsec, 0);
69 IDirectSoundBuffer_SetFormat(tmp->ppbufw, &tmp->wf);
70 IDirectSoundBuffer_Play(tmp->ppbufw, 0, 0, DSBPLAY_LOOPING);
71 }
72
73 return(1);
74 }
75
Pause(SexyAL_device * device,int state)76 static int Pause(SexyAL_device *device, int state)
77 {
78 return(0);
79 }
80
81
82
SexyALI_DSound_Open(const char * id,SexyAL_format * format,SexyAL_buffering * buffering)83 SexyAL_device *SexyALI_DSound_Open(const char *id, SexyAL_format *format, SexyAL_buffering *buffering)
84 {
85 SexyAL_device *dev;
86 SexyAL_DSOUND *fobby;
87
88 DSBUFFERDESC DSBufferDesc;
89 DSCAPS dscaps;
90 //DSBCAPS dsbcaps;
91
92 dev = (SexyAL_device *)calloc(1, sizeof(SexyAL_device));
93 fobby = (SexyAL_DSOUND *)calloc(1, sizeof(SexyAL_DSOUND));
94
95 memset(&fobby->wf,0,sizeof(WAVEFORMATEX));
96 fobby->wf.wFormatTag = WAVE_FORMAT_PCM;
97 fobby->wf.nChannels = format->channels;
98 fobby->wf.nSamplesPerSec = format->rate;
99
100 if(DirectSoundCreate(0,&fobby->ppDS,0) != DS_OK)
101 {
102 free(dev);
103 free(fobby);
104 return(0);
105 }
106
107 {
108 //HWND hWnd = GetForegroundWindow(); // Ugly.
109 //if(!hWnd)
110 //{ hWnd=GetDesktopWindow(); exit(1); }
111 HWND hWnd;
112 hWnd = GetDesktopWindow();
113 IDirectSound_SetCooperativeLevel(fobby->ppDS, hWnd, DSSCL_PRIORITY);
114 }
115 memset(&dscaps,0x00,sizeof(dscaps));
116 dscaps.dwSize=sizeof(dscaps);
117 IDirectSound_GetCaps(fobby->ppDS,&dscaps);
118 IDirectSound_Compact(fobby->ppDS);
119
120 /* Create primary buffer */
121 memset(&DSBufferDesc,0x00,sizeof(DSBUFFERDESC));
122 DSBufferDesc.dwSize=sizeof(DSBufferDesc);
123 DSBufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
124
125 if(IDirectSound_CreateSoundBuffer(fobby->ppDS,&DSBufferDesc,&fobby->ppbuf,0) != DS_OK)
126 {
127 IDirectSound_Release(fobby->ppDS);
128 free(dev);
129 free(fobby);
130 return(0);
131 }
132
133 /* Set primary buffer format. */
134 if(format->sampformat == SEXYAL_FMT_PCMU8)
135 fobby->wf.wBitsPerSample=8;
136 else // if(format->sampformat == SEXYAL_FMT_PCMS16)
137 {
138 fobby->wf.wBitsPerSample=16;
139 format->sampformat=SEXYAL_FMT_PCMS16;
140 }
141
142 fobby->wf.nBlockAlign = fobby->wf.nChannels * (fobby->wf.wBitsPerSample / 8);
143 fobby->wf.nAvgBytesPerSec=fobby->wf.nSamplesPerSec*fobby->wf.nBlockAlign;
144 if(IDirectSoundBuffer_SetFormat(fobby->ppbuf,&fobby->wf) != DS_OK)
145 {
146 IDirectSound_Release(fobby->ppbuf);
147 IDirectSound_Release(fobby->ppDS);
148 free(dev);
149 free(fobby);
150 return(0);
151 }
152
153 //
154 // Buffers yay!
155 //
156 if(!buffering->ms)
157 {
158 buffering->ms = 52;
159 }
160 else if(buffering->overhead_kludge)
161 {
162 buffering->ms += 20;
163 }
164
165 buffering->buffer_size = (int64)format->rate * buffering->ms / 1000;
166
167 fobby->BufHowMuch = buffering->buffer_size * format->channels * SAMPFORMAT_BYTES(format->sampformat);
168 fobby->BufHowMuchOBM = fobby->BufHowMuch + ((30 * format->rate + 999) / 1000) * format->channels * SAMPFORMAT_BYTES(format->sampformat);
169
170 buffering->latency = buffering->buffer_size; // TODO: Add estimated WaveOut latency when using an emulated DirectSound device.
171 buffering->period_size = 0;
172
173 /* Create secondary sound buffer */
174 {
175 int64 padding_extra = (((int64)format->rate * 200 + 999) / 1000) * format->channels * SAMPFORMAT_BYTES(format->sampformat);
176
177 if(padding_extra < (fobby->BufHowMuchOBM * 2))
178 padding_extra = fobby->BufHowMuchOBM * 2;
179
180 fobby->DSBufferSize = round_up_pow2(fobby->BufHowMuchOBM + padding_extra);
181
182 //printf("Bufferbytesizenomnom: %u --- BufHowMuch: %u, BufHowMuchOBM: %u\n", fobby->DSBufferSize, fobby->BufHowMuch, fobby->BufHowMuchOBM);
183
184 if(fobby->DSBufferSize < 65536)
185 fobby->DSBufferSize = 65536;
186 }
187 IDirectSoundBuffer_GetFormat(fobby->ppbuf,&fobby->wf,sizeof(WAVEFORMATEX),0);
188 memset(&DSBufferDesc,0x00,sizeof(DSBUFFERDESC));
189 DSBufferDesc.dwSize=sizeof(DSBufferDesc);
190 DSBufferDesc.dwFlags=DSBCAPS_GETCURRENTPOSITION2;
191 DSBufferDesc.dwFlags|=DSBCAPS_GLOBALFOCUS;
192 DSBufferDesc.dwBufferBytes = fobby->DSBufferSize;
193 DSBufferDesc.lpwfxFormat=&fobby->wf;
194 if(IDirectSound_CreateSoundBuffer(fobby->ppDS, &DSBufferDesc, &fobby->ppbufsec, 0) != DS_OK)
195 {
196 IDirectSound_Release(fobby->ppbuf);
197 IDirectSound_Release(fobby->ppDS);
198 free(dev);
199 free(fobby);
200 return(0);
201 }
202
203 IDirectSoundBuffer_SetCurrentPosition(fobby->ppbufsec,0);
204 fobby->ppbufw=fobby->ppbufsec;
205
206 memcpy(&dev->format, format, sizeof(SexyAL_format));
207
208 //printf("%d\n",fobby->BufHowMuch);
209 //fflush(stdout);
210
211 dev->private_data=fobby;
212 timeBeginPeriod(1);
213
214 dev->RawWrite = RawWrite;
215 dev->RawCanWrite = RawCanWrite;
216 dev->RawClose = Close;
217 dev->Pause = Pause;
218
219 return(dev);
220 }
221
RawCanWriteInternal(SexyAL_device * device,uint32 * can_write,const bool OBM,const bool signal_nega=false)222 static int RawCanWriteInternal(SexyAL_device *device, uint32 *can_write, const bool OBM, const bool signal_nega = false)
223 {
224 SexyAL_DSOUND *tmp = (SexyAL_DSOUND *)device->private_data;
225 DWORD CurWritePos, CurPlayPos = 0;
226
227 if(!CheckStatus(tmp))
228 return(0);
229
230 CurWritePos=0;
231
232 if(IDirectSoundBuffer_GetCurrentPosition(tmp->ppbufw,&CurPlayPos,&CurWritePos)==DS_OK)
233 {
234 //MDFN_DispMessage("%d",CurWritePos-CurPlayPos);
235 }
236
237 CurWritePos = (CurPlayPos + tmp->BufHowMuchOBM) % tmp->DSBufferSize;
238
239 /* If the current write pos is >= half the buffer size less than the to write pos,
240 assume DirectSound has wrapped around.
241 */
242 if(((int32)tmp->ToWritePos-(int32)CurWritePos) >= (tmp->DSBufferSize/2))
243 {
244 CurWritePos += tmp->DSBufferSize;
245 //printf("Fixit: %d,%d,%d\n",tmp->ToWritePos,CurWritePos,CurWritePos-tmp->DSBufferSize);
246 }
247
248 if(tmp->ToWritePos < CurWritePos)
249 {
250 int32 howmuch = (int32)CurWritePos - (int32)tmp->ToWritePos;
251
252 //printf(" HM: %u\n", howmuch);
253
254 // Handle (probably severe) buffer-underflow condition.
255 if(howmuch > tmp->BufHowMuchOBM)
256 {
257 //printf("Underrun: %d %d --- %d --- CWP=%d, TWP=%d\n", howmuch, tmp->BufHowMuchOBM, OBM, CurWritePos, tmp->ToWritePos);
258
259 howmuch = tmp->BufHowMuchOBM;
260 tmp->ToWritePos = (CurWritePos + tmp->DSBufferSize - tmp->BufHowMuchOBM) % tmp->DSBufferSize;
261 }
262
263 if(false == OBM)
264 howmuch -= tmp->BufHowMuchOBM - tmp->BufHowMuch;
265
266 if(howmuch < 0)
267 {
268 if(signal_nega)
269 howmuch = ~0U;
270 else
271 howmuch = 0;
272 }
273
274 *can_write = howmuch;
275 }
276 else
277 *can_write = 0;
278
279 return(1);
280 }
281
RawCanWrite(SexyAL_device * device,uint32 * can_write)282 static int RawCanWrite(SexyAL_device *device, uint32 *can_write)
283 {
284 return RawCanWriteInternal(device, can_write, false);
285 }
286
RawWrite(SexyAL_device * device,const void * data,uint32 len)287 static int RawWrite(SexyAL_device *device, const void *data, uint32 len)
288 {
289 SexyAL_DSOUND *tmp = (SexyAL_DSOUND *)device->private_data;
290
291 //printf("Pre: %d\n",SexyALI_DSound_RawCanWrite(device));
292 //fflush(stdout);
293
294 if(!CheckStatus(tmp))
295 return(0);
296
297 /* In this block, we write as much data as we can, then we write
298 the rest of it in >=1ms chunks.
299 */
300 while(len)
301 {
302 VOID *LockPtr[2]={0,0};
303 DWORD LockLen[2]={0,0};
304 uint32 curlen;
305 int rcw_rv;
306
307 while((rcw_rv = RawCanWriteInternal(device, &curlen, true)) && !curlen)
308 {
309 //puts("WAITER1");
310 Sleep(1);
311 }
312
313 // If RawCanWrite() failed, RawWrite must fail~
314 if(!rcw_rv)
315 return(0);
316
317 if(curlen > len)
318 curlen = len;
319
320 if(IDirectSoundBuffer_Lock(tmp->ppbufw, tmp->ToWritePos, curlen, &LockPtr[0], &LockLen[0], &LockPtr[1], &LockLen[1], 0) != DS_OK)
321 {
322 return(0);
323 }
324
325 if(LockPtr[1] != 0 && LockPtr[1] != LockPtr[0])
326 {
327 memcpy(LockPtr[0], data, LockLen[0]);
328 memcpy(LockPtr[1], (uint8 *)data + LockLen[0], len - LockLen[0]);
329 }
330 else if(LockPtr[0])
331 {
332 memcpy(LockPtr[0], data, curlen);
333 }
334
335 IDirectSoundBuffer_Unlock(tmp->ppbufw, LockPtr[0], LockLen[0], LockPtr[1], LockLen[1]);
336
337 tmp->ToWritePos = (tmp->ToWritePos + curlen) % tmp->DSBufferSize;
338
339 len -= curlen;
340 data = (uint8 *)data + curlen;
341
342 if(len)
343 Sleep(1);
344 } // end while(len) loop
345
346
347 // Synchronize to effective buffer size.
348 {
349 uint32 curlen;
350 int rcw_rv;
351
352 while((rcw_rv = RawCanWriteInternal(device, &curlen, false, true)) && (curlen == ~0U))
353 {
354 //puts("WAITER2");
355 Sleep(1);
356 }
357 // If RawCanWrite() failed, RawWrite must fail~
358 if(!rcw_rv)
359 return(0);
360 }
361
362
363 return(1);
364 }
365
366
367
Close(SexyAL_device * device)368 static int Close(SexyAL_device *device)
369 {
370 if(device)
371 {
372 if(device->private_data)
373 {
374 SexyAL_DSOUND *tmp = (SexyAL_DSOUND *)device->private_data;
375 if(tmp->ppbufsec)
376 {
377 IDirectSoundBuffer_Stop(tmp->ppbufsec);
378 IDirectSoundBuffer_Release(tmp->ppbufsec);
379 }
380 if(tmp->ppbuf)
381 {
382 IDirectSoundBuffer_Stop(tmp->ppbuf);
383 IDirectSoundBuffer_Release(tmp->ppbuf);
384 }
385 if(tmp->ppDS)
386 {
387 IDirectSound_Release(tmp->ppDS);
388 }
389 free(device->private_data);
390 }
391 free(device);
392 timeEndPeriod(1);
393 return(1);
394 }
395 return(0);
396 }
397
398 }
399