1 /*
2 snd_dx.c
3
4 (description)
5
6 Copyright (C) 1996-1997 Id Software, Inc.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to:
21
22 Free Software Foundation, Inc.
23 59 Temple Place - Suite 330
24 Boston, MA 02111-1307, USA
25
26 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #define CINTERFACE
32
33 #include "winquake.h"
34 #include "QF/cvar.h"
35 #include "QF/qargs.h"
36 #include "QF/sys.h"
37
38 #include "snd_internal.h"
39
40 #define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c)
41
42 HRESULT (WINAPI * pDirectSoundCreate) (GUID FAR * lpGUID,
43 LPDIRECTSOUND FAR * lplpDS,
44 IUnknown FAR * pUnkOuter);
45
46 // 64K is > 1 second at 16-bit, 22050 Hz
47 #define WAV_BUFFERS 64
48 #define WAV_MASK 0x3F
49 #define WAV_BUFFER_SIZE 0x0400
50 #define SECONDARY_BUFFER_SIZE 0x10000
51
52 typedef enum { SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL } sndinitstat;
53
54 static qboolean dsound_init;
55 static qboolean snd_firsttime = true;
56 static qboolean primary_format_set;
57
58 static int sample16;
59 static volatile dma_t sn;
60
61 /*
62 Global variables. Must be visible to window-procedure function
63 so it can unlock and free the data block after it has been played.
64 */
65
66 static HANDLE hData;
67 static HPSTR lpData;//, lpData2;
68
69 static HGLOBAL hWaveHdr;
70 static LPWAVEHDR lpWaveHdr;
71
72 static HWAVEOUT hWaveOut;
73
74 //static WAVEOUTCAPS wavecaps;
75
76 static DWORD gSndBufSize;
77
78 static MMTIME mmstarttime;
79
80 static LPDIRECTSOUND pDS;
81 static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
82
83 static HINSTANCE hInstDS;
84
85 static sndinitstat SNDDMA_InitDirect (void);
86
87 static cvar_t *snd_stereo;
88 static cvar_t *snd_rate;
89 static cvar_t *snd_bits;
90
91 static plugin_t plugin_info;
92 static plugin_data_t plugin_info_data;
93 static plugin_funcs_t plugin_info_funcs;
94 static general_data_t plugin_info_general_data;
95 static general_funcs_t plugin_info_general_funcs;
96 static snd_output_data_t plugin_info_snd_output_data;
97 static snd_output_funcs_t plugin_info_snd_output_funcs;
98
99
100 static void
SNDDMA_Init_Cvars(void)101 SNDDMA_Init_Cvars (void)
102 {
103 snd_stereo = Cvar_Get ("snd_stereo", "1", CVAR_ROM, NULL,
104 "sound stereo output");
105 snd_rate = Cvar_Get ("snd_rate", "11025", CVAR_ROM, NULL,
106 "sound playback rate. 0 is system default");
107 snd_bits = Cvar_Get ("snd_bits", "16", CVAR_ROM, NULL,
108 "sound sample depth. 0 is system default");
109 }
110
111 static void
SNDDMA_BlockSound(void)112 SNDDMA_BlockSound (void)
113 {
114 }
115
116 static void
SNDDMA_UnblockSound(void)117 SNDDMA_UnblockSound (void)
118 {
119 }
120
121 static void
FreeSound(void)122 FreeSound (void)
123 {
124 if (pDSBuf) {
125 IDirectSoundBuffer_Stop (pDSBuf);
126 IDirectSound_Release (pDSBuf);
127 }
128 // release primary buffer only if it's not also the mixing buffer we just released
129 if (pDSPBuf && (pDSBuf != pDSPBuf)) {
130 IDirectSound_Release (pDSPBuf);
131 }
132
133 if (pDS) {
134 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
135 IDirectSound_Release (pDS);
136 }
137 pDS = NULL;
138 pDSBuf = NULL;
139 pDSPBuf = NULL;
140 hWaveOut = 0;
141 hData = 0;
142 hWaveHdr = 0;
143 lpData = NULL;
144 lpWaveHdr = NULL;
145 }
146
147 /*
148 SNDDMA_InitDirect
149
150 Direct-Sound support
151 */
152 static sndinitstat
SNDDMA_InitDirect(void)153 SNDDMA_InitDirect (void)
154 {
155 int reps;
156 DSBUFFERDESC dsbuf;
157 DSBCAPS dsbcaps;
158 DSCAPS dscaps;
159 DWORD dwSize, dwWrite;
160 HRESULT hresult;
161 WAVEFORMATEX format, pformat;
162
163 memset ((void *) &sn, 0, sizeof (sn));
164
165 if (!snd_stereo->int_val) {
166 sn.channels = 1;
167 } else {
168 sn.channels = 2;
169 }
170
171 sn.samplebits = snd_bits->int_val;
172 sn.speed = snd_rate->int_val;
173
174 memset (&format, 0, sizeof (format));
175 format.wFormatTag = WAVE_FORMAT_PCM;
176 format.nChannels = sn.channels;
177 format.wBitsPerSample = sn.samplebits;
178 format.nSamplesPerSec = sn.speed;
179 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
180 format.cbSize = 0;
181 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
182
183 if (!hInstDS) {
184 hInstDS = LoadLibrary ("dsound.dll");
185
186 if (hInstDS == NULL) {
187 Sys_Printf ("Couldn't load dsound.dll\n");
188 return SIS_FAILURE;
189 }
190
191 pDirectSoundCreate =
192 (void *) GetProcAddress (hInstDS, "DirectSoundCreate");
193
194 if (!pDirectSoundCreate) {
195 Sys_Printf ("Couldn't get DS proc addr\n");
196 return SIS_FAILURE;
197 }
198 }
199
200 while ((hresult = iDirectSoundCreate (NULL, &pDS, NULL)) != DS_OK) {
201 if (hresult != DSERR_ALLOCATED) {
202 Sys_Printf ("DirectSound create failed\n");
203 return SIS_FAILURE;
204 }
205 Sys_Printf ("DirectSoundCreate failure\n"
206 " hardware already in use\n");
207 return SIS_NOTAVAIL;
208 }
209
210 dscaps.dwSize = sizeof (dscaps);
211 if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps)) {
212 Sys_Printf ("Couldn't get DS caps\n");
213 }
214
215 if (dscaps.dwFlags & DSCAPS_EMULDRIVER) {
216 Sys_Printf ("No DirectSound driver installed\n");
217 FreeSound ();
218 return SIS_FAILURE;
219 }
220
221 if (DS_OK !=
222 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) {
223 Sys_Printf ("Set coop level failed\n");
224 FreeSound ();
225 return SIS_FAILURE;
226 }
227 // get access to the primary buffer, if possible, so we can set the
228 // sound hardware format
229 memset (&dsbuf, 0, sizeof (dsbuf));
230 dsbuf.dwSize = sizeof (DSBUFFERDESC);
231 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
232 dsbuf.dwBufferBytes = 0;
233 dsbuf.lpwfxFormat = NULL;
234
235 memset (&dsbcaps, 0, sizeof (dsbcaps));
236 dsbcaps.dwSize = sizeof (dsbcaps);
237 primary_format_set = false;
238
239 if (!COM_CheckParm ("-snoforceformat")) {
240 if (DS_OK ==
241 IDirectSound_CreateSoundBuffer (pDS, &dsbuf, &pDSPBuf, NULL)) {
242 pformat = format;
243
244 if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, &pformat)) {
245 } else
246 primary_format_set = true;
247 }
248 }
249
250 if (!primary_format_set || !COM_CheckParm ("-primarysound")) {
251 // create the secondary buffer we'll actually work with
252 memset (&dsbuf, 0, sizeof (dsbuf));
253 dsbuf.dwSize = sizeof (DSBUFFERDESC);
254 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
255 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
256 dsbuf.lpwfxFormat = &format;
257
258 memset (&dsbcaps, 0, sizeof (dsbcaps));
259 dsbcaps.dwSize = sizeof (dsbcaps);
260
261 if (DS_OK !=
262 IDirectSound_CreateSoundBuffer (pDS, &dsbuf, &pDSBuf, NULL)) {
263 Sys_Printf ("DS:CreateSoundBuffer Failed");
264 FreeSound ();
265 return SIS_FAILURE;
266 }
267
268 sn.channels = format.nChannels;
269 sn.samplebits = format.wBitsPerSample;
270 sn.speed = format.nSamplesPerSec;
271
272 if (DS_OK != IDirectSound_GetCaps (pDSBuf, &dsbcaps)) {
273 Sys_Printf ("DS:GetCaps failed\n");
274 FreeSound ();
275 return SIS_FAILURE;
276 }
277 } else {
278 if (DS_OK !=
279 IDirectSound_SetCooperativeLevel (pDS, mainwindow,
280 DSSCL_WRITEPRIMARY)) {
281 Sys_Printf ("Set coop level failed\n");
282 FreeSound ();
283 return SIS_FAILURE;
284 }
285
286 if (DS_OK != IDirectSound_GetCaps (pDSPBuf, &dsbcaps)) {
287 Sys_Printf ("DS:GetCaps failed\n");
288 return SIS_FAILURE;
289 }
290
291 pDSBuf = pDSPBuf;
292 }
293
294 // Make sure mixer is active
295 IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING);
296
297 gSndBufSize = dsbcaps.dwBufferBytes;
298
299 // initialize the buffer
300 reps = 0;
301
302 while ((hresult = IDirectSoundBuffer_Lock (pDSBuf, 0, gSndBufSize,
303 (LPVOID *)(char *)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) {
304 if (hresult != DSERR_BUFFERLOST) {
305 Sys_Printf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
306 FreeSound ();
307 return SIS_FAILURE;
308 }
309
310 if (++reps > 10000) {
311 Sys_Printf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
312 FreeSound ();
313 return SIS_FAILURE;
314 }
315
316 }
317
318 memset (lpData, 0, dwSize);
319 // lpData[4] = lpData[5] = 0x7f; // force a pop for debugging
320
321 IDirectSoundBuffer_Unlock (pDSBuf, lpData, dwSize, NULL, 0);
322
323 /* we don't want anyone to access the buffer directly w/o locking it
324 first. */
325 // lpData = NULL;
326
327 IDirectSoundBuffer_Stop (pDSBuf);
328 IDirectSoundBuffer_GetCurrentPosition (pDSBuf, &mmstarttime.u.sample,
329 &dwWrite);
330 IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING);
331
332 sn.frames = gSndBufSize / (sn.samplebits / 8) / sn.channels;
333 sn.framepos = 0;
334 sn.submission_chunk = 1;
335 sn.buffer = (byte *) lpData;
336 sample16 = (sn.samplebits / 8) - 1;
337
338 dsound_init = true;
339
340 return SIS_SUCCESS;
341 }
342
343
344 /*
345 SNDDMA_Init
346
347 Try to find a sound device to mix for.
348 Returns false if nothing is found.
349 */
350 static volatile dma_t *
SNDDMA_Init(void)351 SNDDMA_Init (void)
352 {
353 sndinitstat stat;
354
355 stat = SIS_FAILURE; // assume DirectSound won't
356 // initialize
357
358 /* Init DirectSound */
359 if (snd_firsttime) {
360 snd_firsttime = false;
361 stat = SNDDMA_InitDirect ();
362
363 if (stat == SIS_SUCCESS) {
364 Sys_Printf ("DirectSound initialized\n");
365 } else {
366 Sys_Printf ("DirectSound failed to init\n");
367 return 0;
368 }
369 }
370
371 return &sn;
372 }
373
374 /*
375 SNDDMA_GetDMAPos
376
377 return the current sample position (in mono samples read)
378 inside the recirculating dma buffer, so the mixing code will know
379 how many sample are required to fill it up.
380 */
381 static int
SNDDMA_GetDMAPos(void)382 SNDDMA_GetDMAPos (void)
383 {
384 int s = 0;
385 DWORD dwWrite;
386 MMTIME mmtime;
387 unsigned long *pbuf;
388
389 pbuf = DSOUND_LockBuffer (true);
390 if (!pbuf) {
391 Sys_Printf ("DSOUND_LockBuffer fails!\n");
392 return -1;
393 }
394 sn.buffer = (unsigned char *) pbuf;
395 mmtime.wType = TIME_SAMPLES;
396 IDirectSoundBuffer_GetCurrentPosition (pDSBuf, &mmtime.u.sample,
397 &dwWrite);
398 s = mmtime.u.sample - mmstarttime.u.sample;
399
400 s >>= sample16;
401 s /= sn.channels;
402
403 s %= sn.frames;
404 sn.framepos = s;
405
406 return sn.framepos;
407 }
408
409 /*
410 SNDDMA_Submit
411
412 Send sound to device if buffer isn't really the dma buffer
413 */
414 static void
SNDDMA_Submit(void)415 SNDDMA_Submit (void)
416 {
417 DSOUND_LockBuffer (false);
418 }
419
420 /*
421 SNDDMA_Shutdown
422
423 Reset the sound device for exiting
424 */
425 static void
SNDDMA_Shutdown(void)426 SNDDMA_Shutdown (void)
427 {
428 FreeSound ();
429 }
430
431 DWORD *
DSOUND_LockBuffer(qboolean lockit)432 DSOUND_LockBuffer (qboolean lockit)
433 {
434 int reps;
435
436 static DWORD dwSize;
437 static DWORD dwSize2;
438 static DWORD *pbuf1;
439 static DWORD *pbuf2;
440 HRESULT hresult;
441
442 if (!pDSBuf)
443 return NULL;
444
445 if (lockit) {
446 reps = 0;
447 while ((hresult = IDirectSoundBuffer_Lock
448 (pDSBuf, 0, gSndBufSize, (LPVOID *)(char *) &pbuf1, &dwSize,
449 (LPVOID *)(char *) &pbuf2, &dwSize2, 0)) != DS_OK) {
450 if (hresult != DSERR_BUFFERLOST) {
451 Sys_Printf
452 ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n");
453 SNDDMA_Shutdown ();
454 SNDDMA_Init ();
455 return NULL;
456 }
457
458 if (++reps > 10000) {
459 Sys_Printf
460 ("S_TransferStereo16: DS: couldn't restore buffer\n");
461 SNDDMA_Shutdown ();
462 SNDDMA_Init ();
463 return NULL;
464 }
465 }
466 } else {
467 IDirectSoundBuffer_Unlock (pDSBuf, pbuf1, dwSize, NULL, 0);
468 pbuf1 = NULL;
469 pbuf2 = NULL;
470 dwSize = 0;
471 dwSize2 = 0;
472 }
473 return (pbuf1);
474 }
475
476 void
DSOUND_ClearBuffer(int clear)477 DSOUND_ClearBuffer (int clear)
478 {
479 DWORD *pData;
480
481 // FIXME: this should be called with 2nd pbuf2 = NULL, dwsize =0
482 pData = DSOUND_LockBuffer (true);
483 memset (pData, clear, sn.frames * sn.channels * sn.samplebits / 8);
484 DSOUND_LockBuffer (false);
485 }
486
487 void
DSOUND_Restore(void)488 DSOUND_Restore (void)
489 {
490 // if the buffer was lost or stopped, restore it and/or restart it
491 DWORD dwStatus;
492
493 if (!pDSBuf)
494 return;
495
496 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
497 Sys_Printf ("Couldn't get sound buffer status\n");
498
499 if (dwStatus & DSBSTATUS_BUFFERLOST)
500 IDirectSoundBuffer_Restore (pDSBuf);
501
502 if (!(dwStatus & DSBSTATUS_PLAYING))
503 IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING);
504
505 return;
506 }
507
PLUGIN_INFO(snd_output,dx)508 PLUGIN_INFO(snd_output, dx)
509 {
510 plugin_info.type = qfp_snd_output;
511 plugin_info.api_version = QFPLUGIN_VERSION;
512 plugin_info.plugin_version = "0.1";
513 plugin_info.description = "Windows DirectX output";
514 plugin_info.copyright = "Copyright (C) 1996-1997 id Software, Inc.\n"
515 "Copyright (C) 1999,2000,2001,2002,2003 contributors of the QuakeForge "
516 "project\n"
517 "Please see the file \"AUTHORS\" for a list of contributors";
518 plugin_info.functions = &plugin_info_funcs;
519 plugin_info.data = &plugin_info_data;
520
521 plugin_info_data.general = &plugin_info_general_data;
522 plugin_info_data.input = NULL;
523 plugin_info_data.snd_output = &plugin_info_snd_output_data;
524
525 plugin_info_funcs.general = &plugin_info_general_funcs;
526 plugin_info_funcs.input = NULL;
527 plugin_info_funcs.snd_output = &plugin_info_snd_output_funcs;
528
529 plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars;
530 plugin_info_general_funcs.p_Shutdown = NULL;
531 plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init;
532 plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown;
533 plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos;
534 plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit;
535 plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound;
536 plugin_info_snd_output_funcs.pS_O_UnblockSound = SNDDMA_UnblockSound;
537
538 return &plugin_info;
539 }
540