1 /*
2 * snd_dsound.c
3 * $Id: snd_dsound.c 4767 2012-06-16 20:48:51Z sezero $
4 *
5 * Copyright (C) 1996-1997 Id Software, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
11 *
12 * This program 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.
15 *
16 * See the GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "quakedef.h"
24 #include "snd_sys.h"
25
26 #if HAVE_WIN_DX_SOUND
27
28 #include "winquake.h"
29 #include "snd_dsound.h"
30 #include <mmsystem.h>
31 #include <dsound.h>
32
33 static char s_ds_driver[] = "DirectSound";
34
35 /* DirectSound : */
36 #ifndef DSBSIZE_MIN
37 #define DSBSIZE_MIN 4
38 #endif
39 #ifndef DSBSIZE_MAX
40 #define DSBSIZE_MAX 0x0FFFFFFF
41 #endif
42
43 static LPDIRECTSOUND pDS;
44 static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
45
46 #if defined(DX_DLSYM) /* dynamic loading of dsound symbols */
47 static HINSTANCE hInstDS;
48 static HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
49 #else /* ! DX_DLSYM : we're linked to dsound */
50 #define pDirectSoundCreate DirectSoundCreate
51 #endif /* DX_DLSYM */
52
53 static qboolean primary_format_set;
54
55 static int sample16;
56 static int ds_sbuf_size;
57
58 static HPSTR lpData;
59
60 static DWORD gSndBufSize;
61 static MMTIME mmstarttime;
62
63
64 /*
65 ==================
66 FreeSound
67 ==================
68 */
FreeSound(void)69 static void FreeSound (void)
70 {
71 if (pDSBuf)
72 {
73 IDirectSoundBuffer_Stop(pDSBuf);
74 IDirectSound_Release(pDSBuf);
75 }
76
77 // only release primary buffer if it's not also the mixing buffer we just released
78 if (pDSPBuf && (pDSBuf != pDSPBuf))
79 {
80 IDirectSound_Release(pDSPBuf);
81 }
82
83 if (pDS)
84 {
85 IDirectSound_SetCooperativeLevel(pDS, mainwindow, DSSCL_NORMAL);
86 IDirectSound_Release(pDS);
87 }
88
89 pDS = NULL;
90 pDSBuf = NULL;
91 pDSPBuf = NULL;
92 lpData = NULL;
93 }
94
95
96 /*
97 ==================
98 SNDDMA_InitDirect
99
100 Direct-Sound support
101 ==================
102 */
S_DS_Init(dma_t * dma)103 static qboolean S_DS_Init (dma_t *dma)
104 {
105 DSBUFFERDESC dsbuf;
106 DSBCAPS dsbcaps;
107 DWORD dwSize, dwWrite;
108 DSCAPS dscaps;
109 WAVEFORMATEX format, pformat;
110 HRESULT hresult;
111 int reps;
112
113 memset((void *) dma, 0, sizeof(dma_t));
114 shm = dma;
115
116 shm->channels = desired_channels;
117 shm->samplebits = desired_bits;
118 shm->speed = desired_speed;
119 /* Calculate the DS buffer size to store 2 secs
120 * of data, round up to the next power of 2. */
121 ds_sbuf_size = 1 << (Q_log2((desired_bits >> 3) * (desired_speed << 1)) + 1);
122
123 memset (&format, 0, sizeof(format));
124 format.wFormatTag = WAVE_FORMAT_PCM;
125 format.nChannels = shm->channels;
126 format.wBitsPerSample = shm->samplebits;
127 format.nSamplesPerSec = shm->speed;
128 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
129 format.cbSize = 0;
130 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
131
132 #if defined(DX_DLSYM)
133 if (!hInstDS)
134 {
135 hInstDS = LoadLibrary("dsound.dll");
136
137 if (hInstDS == NULL)
138 {
139 Con_SafePrintf ("Couldn't load dsound.dll\n");
140 return false;
141 }
142
143 pDirectSoundCreate = (HRESULT (WINAPI *)(GUID FAR *, LPDIRECTSOUND FAR *, IUnknown FAR *))
144 GetProcAddress(hInstDS,"DirectSoundCreate");
145
146 if (!pDirectSoundCreate)
147 {
148 Con_SafePrintf ("Couldn't get DS proc addr\n");
149 return false;
150 }
151 }
152 #endif /* DX_DLSYM */
153
154 hresult = pDirectSoundCreate(NULL, &pDS, NULL);
155 if (hresult != DS_OK)
156 {
157 if (hresult != DSERR_ALLOCATED)
158 {
159 Con_SafePrintf ("DirectSound create failed\n");
160 return false;
161 }
162
163 Con_SafePrintf ("DirectSoundCreate failure, hardware already in use\n");
164 return false;
165 }
166
167 dscaps.dwSize = sizeof(dscaps);
168
169 if (DS_OK != IDirectSound_GetCaps(pDS, &dscaps))
170 {
171 Con_SafePrintf ("Couldn't get DS caps\n");
172 }
173
174 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
175 {
176 Con_SafePrintf ("No DirectSound driver installed\n");
177 goto fail;
178 }
179
180 // if (DS_OK != IDirectSound_SetCooperativeLevel(pDS, mainwindow, DSSCL_EXCLUSIVE))
181 /* Pa3PyX: Some MIDI synthesizers are software and require access to
182 waveOut; so if we set the coop level to exclusive, MIDI will fail
183 to init because the device is locked. We use priority level instead.
184 That way we don't lock out software synths and other apps, but can
185 still set the sound buffer format. */
186 if (DS_OK != IDirectSound_SetCooperativeLevel(pDS, mainwindow, DSSCL_PRIORITY))
187 {
188 Con_SafePrintf ("Set coop level failed\n");
189 goto fail;
190 }
191
192 // get access to the primary buffer, if possible, so we can set the
193 // sound hardware format
194 memset (&dsbuf, 0, sizeof(dsbuf));
195 dsbuf.dwSize = sizeof(DSBUFFERDESC);
196 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
197 dsbuf.dwBufferBytes = 0;
198 dsbuf.lpwfxFormat = NULL;
199
200 memset(&dsbcaps, 0, sizeof(dsbcaps));
201 dsbcaps.dwSize = sizeof(dsbcaps);
202 primary_format_set = false;
203
204 if (!COM_CheckParm ("-snoforceformat"))
205 {
206 if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
207 {
208 pformat = format;
209
210 if (DS_OK != IDirectSoundBuffer_SetFormat(pDSPBuf, &pformat))
211 {
212 Con_SafePrintf ("Set primary sound buffer format: no\n");
213 }
214 else
215 {
216 Con_SafePrintf ("Set primary sound buffer format: yes\n");
217 primary_format_set = true;
218 }
219 }
220 }
221
222 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
223 {
224 // create the secondary buffer we'll actually work with
225 memset (&dsbuf, 0, sizeof(dsbuf));
226 dsbuf.dwSize = sizeof(DSBUFFERDESC);
227 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
228 if (ds_sbuf_size < DSBSIZE_MIN)
229 ds_sbuf_size = 1 << (Q_log2(DSBSIZE_MIN) + 1);
230 if (ds_sbuf_size > DSBSIZE_MAX)
231 ds_sbuf_size = 1 << Q_log2(DSBSIZE_MAX);
232 dsbuf.dwBufferBytes = ds_sbuf_size;
233 dsbuf.lpwfxFormat = &format;
234
235 memset(&dsbcaps, 0, sizeof(dsbcaps));
236 dsbcaps.dwSize = sizeof(dsbcaps);
237
238 if (DS_OK != IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
239 {
240 Con_SafePrintf ("DS:CreateSoundBuffer Failed");
241 goto fail;
242 }
243
244 shm->channels = format.nChannels;
245 shm->samplebits = format.wBitsPerSample;
246 shm->speed = format.nSamplesPerSec;
247
248 if (DS_OK != IDirectSound_GetCaps(pDSBuf, &dsbcaps))
249 {
250 Con_SafePrintf ("DS:GetCaps failed\n");
251 goto fail;
252 }
253
254 Con_SafePrintf ("Using secondary sound buffer\n");
255 }
256 else
257 {
258 if (DS_OK != IDirectSound_SetCooperativeLevel(pDS, mainwindow, DSSCL_WRITEPRIMARY))
259 {
260 Con_SafePrintf ("Set coop level failed\n");
261 goto fail;
262 }
263
264 if (DS_OK != IDirectSound_GetCaps(pDSPBuf, &dsbcaps))
265 {
266 Con_Printf ("DS:GetCaps failed\n");
267 return false;
268 }
269
270 pDSBuf = pDSPBuf;
271 Con_SafePrintf ("Using primary sound buffer\n");
272 }
273
274 // Make sure mixer is active
275 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
276
277 Con_SafePrintf ("%lu bytes in sound buffer\n", (unsigned long)dsbcaps.dwBufferBytes);
278
279 gSndBufSize = dsbcaps.dwBufferBytes;
280
281 // initialize the buffer
282 reps = 0;
283
284 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID *) (HPSTR) &lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
285 {
286 if (hresult != DSERR_BUFFERLOST)
287 {
288 Con_SafePrintf ("%s: DS::Lock Sound Buffer Failed\n", __thisfunc__);
289 goto fail;
290 }
291
292 if (++reps > 10000)
293 {
294 Con_SafePrintf ("%s: DS: couldn't restore buffer\n", __thisfunc__);
295 goto fail;
296 }
297 }
298
299 memset(lpData, 0, dwSize);
300 // lpData[4] = lpData[5] = 0x7f; // force a pop for debugging
301
302 IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
303
304 /* we don't want anyone to access the buffer directly w/o locking it first. */
305 lpData = NULL;
306
307 IDirectSoundBuffer_Stop(pDSBuf);
308 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
309 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
310
311 shm->samples = gSndBufSize / (shm->samplebits / 8);
312 shm->samplepos = 0;
313 shm->submission_chunk = 1;
314 shm->buffer = (unsigned char *) lpData;
315 sample16 = (shm->samplebits / 8) - 1;
316
317 Con_SafePrintf ("DirectSound initialized\n");
318 return true;
319
320 fail:
321 FreeSound ();
322 Con_SafePrintf ("DirectSound failed to init\n");
323 return false;
324 }
325
326
327 /*
328 ==============
329 SNDDMA_GetDMAPos
330
331 return the current sample position (in mono samples read)
332 inside the recirculating dma buffer, so the mixing code will know
333 how many sample are required to fill it up.
334 ===============
335 */
S_DS_GetDMAPos(void)336 static int S_DS_GetDMAPos (void)
337 {
338 MMTIME mmtime;
339 int s;
340 DWORD dwWrite;
341
342 mmtime.wType = TIME_SAMPLES;
343 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
344 s = mmtime.u.sample - mmstarttime.u.sample;
345 s >>= sample16;
346 s &= (shm->samples - 1);
347
348 return s;
349 }
350
351 /*
352 ==============
353 SNDDMA_LockBuffer
354
355 Makes sure dma buffer is valid
356 ===============
357 */
358 static DWORD locksize;
S_DS_LockBuffer(void)359 static void S_DS_LockBuffer (void)
360 {
361 if (pDSBuf)
362 {
363 void *pData;
364 int reps;
365 HRESULT hresult;
366 DWORD dwStatus;
367
368 reps = 0;
369 shm->buffer = NULL;
370
371 if (IDirectSoundBuffer_GetStatus(pDSBuf, &dwStatus) != DS_OK)
372 Con_Printf ("Couldn't get sound buffer status\n");
373
374 if (dwStatus & DSBSTATUS_BUFFERLOST)
375 IDirectSoundBuffer_Restore(pDSBuf);
376
377 if (!(dwStatus & DSBSTATUS_PLAYING))
378 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
379
380 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (void **) &pData, &locksize, NULL, NULL, 0)) != DS_OK)
381 {
382 if (hresult != DSERR_BUFFERLOST)
383 {
384 Con_Printf ("%s: DS::Lock Sound Buffer Failed\n", __thisfunc__);
385 S_Shutdown ();
386 return;
387 }
388
389 if (++reps > 10000)
390 {
391 Con_Printf ("%s: DS: couldn't restore buffer\n", __thisfunc__);
392 S_Shutdown ();
393 return;
394 }
395 }
396
397 shm->buffer = (unsigned char *) pData;
398 }
399 }
400
401 /*
402 ==============
403 SNDDMA_Submit
404
405 Unlock the dma buffer /
406 Send sound to the device
407 ===============
408 */
S_DS_Submit(void)409 static void S_DS_Submit (void)
410 {
411 if (pDSBuf)
412 IDirectSoundBuffer_Unlock(pDSBuf, shm->buffer, locksize, NULL, 0);
413 }
414
415 /*
416 ==================
417 SNDDMA_BlockSound
418 ==================
419 */
S_DS_BlockSound(void)420 static void S_DS_BlockSound (void)
421 {
422 // DirectSound takes care of blocking itself
423 }
424
425
426 /*
427 ==================
428 SNDDMA_UnblockSound
429 ==================
430 */
S_DS_UnblockSound(void)431 static void S_DS_UnblockSound (void)
432 {
433 }
434
435
436 /*
437 ==============
438 SNDDMA_Shutdown
439
440 Reset the sound device for exiting
441 ===============
442 */
S_DS_Shutdown(void)443 static void S_DS_Shutdown (void)
444 {
445 FreeSound ();
446 #if defined(DX_DLSYM)
447 if (hInstDS)
448 {
449 FreeLibrary(hInstDS);
450 hInstDS = NULL;
451 }
452 #endif /* DX_DLSYM */
453 }
454
455 snd_driver_t snddrv_dsound =
456 {
457 S_DS_Init,
458 S_DS_Shutdown,
459 S_DS_GetDMAPos,
460 S_DS_LockBuffer,
461 S_DS_Submit,
462 S_DS_BlockSound,
463 S_DS_UnblockSound,
464 s_ds_driver,
465 SNDDRV_ID_DSOUND,
466 false,
467 NULL
468 };
469
470 #endif /* HAVE_WIN_DX_SOUND */
471
472