1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 #include "quakedef.h"
21 #include "snd_main.h"
22
23 #ifdef SUPPORTDIRECTX
24 #ifndef DIRECTSOUND_VERSION
25 # define DIRECTSOUND_VERSION 0x0500 /* Version 5.0 */
26 #endif
27 #endif
28 #include <windows.h>
29 #include <mmsystem.h>
30 #ifdef SUPPORTDIRECTX
31 #include <dsound.h>
32 #endif
33
34 // ==============================================================================
35
36 #ifndef _WAVEFORMATEXTENSIBLE_
37 #define _WAVEFORMATEXTENSIBLE_
38 typedef struct
39 {
40 WAVEFORMATEX Format;
41 union
42 {
43 WORD wValidBitsPerSample; // bits of precision
44 WORD wSamplesPerBlock; // valid if wBitsPerSample==0
45 WORD wReserved; // If neither applies, set to zero
46 } Samples;
47 DWORD dwChannelMask; // which channels are present in stream
48 GUID SubFormat;
49 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
50 #endif
51
52 #if !defined(WAVE_FORMAT_EXTENSIBLE)
53 # define WAVE_FORMAT_EXTENSIBLE 0xFFFE
54 #endif
55
56 // Some speaker positions
57 #ifndef SPEAKER_FRONT_LEFT
58 # define SPEAKER_FRONT_LEFT 0x1
59 # define SPEAKER_FRONT_RIGHT 0x2
60 # define SPEAKER_FRONT_CENTER 0x4
61 # define SPEAKER_LOW_FREQUENCY 0x8
62 # define SPEAKER_BACK_LEFT 0x10
63 # define SPEAKER_BACK_RIGHT 0x20
64 # define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
65 # define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
66 // ... we never use the other values
67 #endif
68
69 // KSDATAFORMAT_SUBTYPE_PCM = GUID "00000001-0000-0010-8000-00aa00389b71"
70 static const GUID MY_KSDATAFORMAT_SUBTYPE_PCM =
71 {
72 0x00000001,
73 0x0000,
74 0x0010,
75 {
76 0x80, 0x00,
77 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
78 }
79 };
80
81
82 // ==============================================================================
83
84 extern HWND mainwindow;
85
86 #ifdef SUPPORTDIRECTX
87 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
88 #endif
89
90 // Wave output: how much buffer time, and how many partitions in that time
91 #define WAV_BUFFERTIME 0.125
92 #define WAV_BUFFERS 16
93 #define WAV_MASK (WAV_BUFFERS - 1)
94 static unsigned int wav_buffer_size;
95
96 // DirectSound output: 64KB in 1 buffer
97 //#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->width * (fmt_ptr)->channels * (fmt_ptr)->speed / 2)
98 // LordHavoc: changed this to be a multiple of 32768
99 #define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->channels * 32768)
100
101 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
102
103 #ifdef SUPPORTDIRECTX
104 static qboolean dsound_init;
105 static unsigned int dsound_time;
106 static qboolean primary_format_set;
107 #endif
108
109 static qboolean wav_init;
110
111 static int snd_sent, snd_completed;
112
113 static int prev_painted;
114 static unsigned int paintpot;
115
116
117
118 /*
119 * Global variables. Must be visible to window-procedure function
120 * so it can unlock and free the data block after it has been played.
121 */
122
123 HANDLE hData;
124 HPSTR lpData, lpData2;
125
126 HGLOBAL hWaveHdr;
127 LPWAVEHDR lpWaveHdr;
128
129 HWAVEOUT hWaveOut;
130
131 WAVEOUTCAPS wavecaps;
132
133 DWORD gSndBufSize;
134
135 DWORD dwStartTime;
136
137 #ifdef SUPPORTDIRECTX
138 LPDIRECTSOUND pDS;
139 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
140
141 HINSTANCE hInstDS;
142 #endif
143
144 qboolean SNDDMA_InitWav (void);
145 #ifdef SUPPORTDIRECTX
146 sndinitstat SNDDMA_InitDirect (void);
147 #endif
148
149
150 /*
151 ==================
152 SndSys_BuildWaveFormat
153 ==================
154 */
SndSys_BuildWaveFormat(const snd_format_t * requested,WAVEFORMATEXTENSIBLE * fmt_ptr)155 static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr)
156 {
157 WAVEFORMATEX* pfmtex;
158
159 memset (fmt_ptr, 0, sizeof(*fmt_ptr));
160
161 pfmtex = &fmt_ptr->Format;
162 pfmtex->nChannels = requested->channels;
163 pfmtex->wBitsPerSample = requested->width * 8;
164 pfmtex->nSamplesPerSec = requested->speed;
165 pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8;
166 pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign;
167
168 // LordHavoc: disabled this WAVE_FORMAT_EXTENSIBLE support because it does not seem to be working
169 #if 0
170 if (requested->channels <= 2)
171 {
172 #endif
173 pfmtex->wFormatTag = WAVE_FORMAT_PCM;
174 pfmtex->cbSize = 0;
175 #if 0
176 }
177 else
178 {
179 pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
180 pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
181 fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
182 fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
183
184 // Build the channel mask
185 fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
186 switch (requested->channels)
187 {
188 case 8:
189 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
190 // no break
191 case 6:
192 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
193 // no break
194 case 4:
195 fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
196 break;
197
198 default:
199 Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
200 return false;
201 }
202 }
203 #endif
204
205 return true;
206 }
207
208
209 #ifdef SUPPORTDIRECTX
210 /*
211 ==================
212 SndSys_InitDirectSound
213
214 DirectSound 5 support
215 ==================
216 */
SndSys_InitDirectSound(const snd_format_t * requested)217 static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
218 {
219 DSBUFFERDESC dsbuf;
220 DSBCAPS dsbcaps;
221 DWORD dwSize;
222 DSCAPS dscaps;
223 WAVEFORMATEXTENSIBLE format, pformat;
224 HRESULT hresult;
225 int reps;
226
227 if (! SndSys_BuildWaveFormat(requested, &format))
228 return SIS_FAILURE;
229
230 if (!hInstDS)
231 {
232 hInstDS = LoadLibrary("dsound.dll");
233
234 if (hInstDS == NULL)
235 {
236 Con_Print("Couldn't load dsound.dll\n");
237 return SIS_FAILURE;
238 }
239
240 pDirectSoundCreate = (HRESULT (__stdcall *)(GUID *, LPDIRECTSOUND *,IUnknown *))GetProcAddress(hInstDS,"DirectSoundCreate");
241
242 if (!pDirectSoundCreate)
243 {
244 Con_Print("Couldn't get DS proc addr\n");
245 return SIS_FAILURE;
246 }
247 }
248
249 while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
250 {
251 if (hresult != DSERR_ALLOCATED)
252 {
253 Con_Print("DirectSound create failed\n");
254 return SIS_FAILURE;
255 }
256
257 if (MessageBox (NULL,
258 "The sound hardware is in use by another app.\n\n"
259 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
260 "Sound not available",
261 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
262 {
263 Con_Print("DirectSoundCreate failure\n hardware already in use\n");
264 return SIS_NOTAVAIL;
265 }
266 }
267
268 dscaps.dwSize = sizeof(dscaps);
269
270 if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
271 {
272 Con_Print("Couldn't get DS caps\n");
273 }
274
275 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
276 {
277 Con_Print("No DirectSound driver installed\n");
278 SndSys_Shutdown ();
279 return SIS_FAILURE;
280 }
281
282 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
283 {
284 Con_Print("Set coop level failed\n");
285 SndSys_Shutdown ();
286 return SIS_FAILURE;
287 }
288
289 // get access to the primary buffer, if possible, so we can set the
290 // sound hardware format
291 memset (&dsbuf, 0, sizeof(dsbuf));
292 dsbuf.dwSize = sizeof(DSBUFFERDESC);
293 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
294 dsbuf.dwBufferBytes = 0;
295 dsbuf.lpwfxFormat = NULL;
296
297 memset(&dsbcaps, 0, sizeof(dsbcaps));
298 dsbcaps.dwSize = sizeof(dsbcaps);
299 primary_format_set = false;
300
301 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
302 if (!COM_CheckParm ("-snoforceformat"))
303 {
304 if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
305 {
306 pformat = format;
307
308 if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
309 {
310 Con_Print("Set primary sound buffer format: no\n");
311 }
312 else
313 {
314 Con_Print("Set primary sound buffer format: yes\n");
315
316 primary_format_set = true;
317 }
318 }
319 }
320
321 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
322 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
323 {
324 HRESULT result;
325
326 // create the secondary buffer we'll actually work with
327 memset (&dsbuf, 0, sizeof(dsbuf));
328 dsbuf.dwSize = sizeof(DSBUFFERDESC);
329 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
330 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
331 dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
332
333 memset(&dsbcaps, 0, sizeof(dsbcaps));
334 dsbcaps.dwSize = sizeof(dsbcaps);
335
336 result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
337 if (result != DS_OK ||
338 requested->channels != format.Format.nChannels ||
339 requested->width != format.Format.wBitsPerSample / 8 ||
340 requested->speed != format.Format.nSamplesPerSec)
341 {
342 Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
343 (int)result, (unsigned)format.Format.nChannels, (unsigned)format.Format.wBitsPerSample / 8, (unsigned)format.Format.nSamplesPerSec);
344 SndSys_Shutdown ();
345 return SIS_FAILURE;
346 }
347
348 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
349 {
350 Con_Print("DS:GetCaps failed\n");
351 SndSys_Shutdown ();
352 return SIS_FAILURE;
353 }
354
355 Con_Print("Using secondary sound buffer\n");
356 }
357 else
358 {
359 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
360 {
361 Con_Print("Set coop level failed\n");
362 SndSys_Shutdown ();
363 return SIS_FAILURE;
364 }
365
366 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
367 {
368 Con_Print("DS:GetCaps failed\n");
369 return SIS_FAILURE;
370 }
371
372 pDSBuf = pDSPBuf;
373 Con_Print("Using primary sound buffer\n");
374 }
375
376 // Make sure mixer is active
377 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
378
379 Con_Printf(" %d channel(s)\n"
380 " %d bits/sample\n"
381 " %d samples/sec\n",
382 requested->channels, requested->width * 8, requested->speed);
383
384 gSndBufSize = dsbcaps.dwBufferBytes;
385
386 // initialize the buffer
387 reps = 0;
388
389 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
390 {
391 if (hresult != DSERR_BUFFERLOST)
392 {
393 Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
394 SndSys_Shutdown ();
395 return SIS_FAILURE;
396 }
397
398 if (++reps > 10000)
399 {
400 Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
401 SndSys_Shutdown ();
402 return SIS_FAILURE;
403 }
404
405 }
406
407 memset(lpData, 0, dwSize);
408 IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
409
410 IDirectSoundBuffer_Stop(pDSBuf);
411 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
412
413 dwStartTime = 0;
414 dsound_time = 0;
415 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
416
417 dsound_init = true;
418
419 return SIS_SUCCESS;
420 }
421 #endif
422
423
424 /*
425 ==================
426 SndSys_InitMmsystem
427
428 Crappy windows multimedia base
429 ==================
430 */
SndSys_InitMmsystem(const snd_format_t * requested)431 static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
432 {
433 WAVEFORMATEXTENSIBLE format;
434 int i;
435 HRESULT hr;
436
437 if (! SndSys_BuildWaveFormat(requested, &format))
438 return false;
439
440 // Open a waveform device for output using window callback
441 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
442 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
443 {
444 if (hr != MMSYSERR_ALLOCATED)
445 {
446 Con_Print("waveOutOpen failed\n");
447 return false;
448 }
449
450 if (MessageBox (NULL,
451 "The sound hardware is in use by another app.\n\n"
452 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
453 "Sound not available",
454 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
455 {
456 Con_Print("waveOutOpen failure;\n hardware already in use\n");
457 return false;
458 }
459 }
460
461 wav_buffer_size = ((int)(requested->speed * WAV_BUFFERTIME) / WAV_BUFFERS) * requested->channels * requested->width;
462
463 /*
464 * Allocate and lock memory for the waveform data. The memory
465 * for waveform data must be globally allocated with
466 * GMEM_MOVEABLE and GMEM_SHARE flags.
467 */
468 gSndBufSize = WAV_BUFFERS * wav_buffer_size;
469 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
470 if (!hData)
471 {
472 Con_Print("Sound: Out of memory.\n");
473 SndSys_Shutdown ();
474 return false;
475 }
476 lpData = (HPSTR)GlobalLock(hData);
477 if (!lpData)
478 {
479 Con_Print("Sound: Failed to lock.\n");
480 SndSys_Shutdown ();
481 return false;
482 }
483 memset (lpData, 0, gSndBufSize);
484
485 /*
486 * Allocate and lock memory for the header. This memory must
487 * also be globally allocated with GMEM_MOVEABLE and
488 * GMEM_SHARE flags.
489 */
490 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
491
492 if (hWaveHdr == NULL)
493 {
494 Con_Print("Sound: Failed to Alloc header.\n");
495 SndSys_Shutdown ();
496 return false;
497 }
498
499 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
500
501 if (lpWaveHdr == NULL)
502 {
503 Con_Print("Sound: Failed to lock header.\n");
504 SndSys_Shutdown ();
505 return false;
506 }
507
508 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
509
510 // After allocation, set up and prepare headers
511 for (i=0 ; i<WAV_BUFFERS ; i++)
512 {
513 lpWaveHdr[i].dwBufferLength = wav_buffer_size;
514 lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
515
516 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
517 {
518 Con_Print("Sound: failed to prepare wave headers\n");
519 SndSys_Shutdown ();
520 return false;
521 }
522 }
523
524 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
525
526 prev_painted = 0;
527 paintpot = 0;
528
529 snd_sent = 0;
530 snd_completed = 0;
531
532 wav_init = true;
533
534 return true;
535 }
536
537
538 /*
539 ====================
540 SndSys_Init
541
542 Create "snd_renderbuffer" with the proper sound format if the call is successful
543 May return a suggested format if the requested format isn't available
544 ====================
545 */
SndSys_Init(const snd_format_t * requested,snd_format_t * suggested)546 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
547 {
548 #ifdef SUPPORTDIRECTX
549 qboolean wavonly;
550 #endif
551 sndinitstat stat;
552
553 Con_Print ("SndSys_Init: using the Win32 module\n");
554
555 #ifdef SUPPORTDIRECTX
556 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
557 wavonly = (COM_CheckParm ("-wavonly") != 0);
558 dsound_init = false;
559 #endif
560 wav_init = false;
561
562 stat = SIS_FAILURE; // assume DirectSound won't initialize
563
564 #ifdef SUPPORTDIRECTX
565 // Init DirectSound
566 if (!wavonly)
567 {
568 stat = SndSys_InitDirectSound (requested);
569
570 if (stat == SIS_SUCCESS)
571 Con_Print("DirectSound initialized\n");
572 else
573 Con_Print("DirectSound failed to init\n");
574 }
575 #endif
576
577 // if DirectSound didn't succeed in initializing, try to initialize
578 // waveOut sound, unless DirectSound failed because the hardware is
579 // already allocated (in which case the user has already chosen not
580 // to have sound)
581 #ifdef SUPPORTDIRECTX
582 if (!dsound_init && (stat != SIS_NOTAVAIL))
583 #endif
584 {
585 if (SndSys_InitMmsystem (requested))
586 Con_Print("Wave sound (MMSYSTEM) initialized\n");
587 else
588 Con_Print("Wave sound failed to init\n");
589 }
590
591 #ifdef SUPPORTDIRECTX
592 return (dsound_init || wav_init);
593 #else
594 return wav_init;
595 #endif
596 }
597
598
599 /*
600 ====================
601 SndSys_Shutdown
602
603 Stop the sound card, delete "snd_renderbuffer" and free its other resources
604 ====================
605 */
SndSys_Shutdown(void)606 void SndSys_Shutdown (void)
607 {
608 #ifdef SUPPORTDIRECTX
609 if (pDSBuf)
610 {
611 IDirectSoundBuffer_Stop(pDSBuf);
612 IDirectSoundBuffer_Release(pDSBuf);
613 }
614
615 // only release primary buffer if it's not also the mixing buffer we just released
616 if (pDSPBuf && (pDSBuf != pDSPBuf))
617 {
618 IDirectSoundBuffer_Release(pDSPBuf);
619 }
620
621 if (pDS)
622 {
623 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
624 IDirectSound_Release(pDS);
625 }
626 #endif
627
628 if (hWaveOut)
629 {
630 waveOutReset (hWaveOut);
631
632 if (lpWaveHdr)
633 {
634 unsigned int i;
635
636 for (i=0 ; i< WAV_BUFFERS ; i++)
637 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
638 }
639
640 waveOutClose (hWaveOut);
641
642 if (hWaveHdr)
643 {
644 GlobalUnlock(hWaveHdr);
645 GlobalFree(hWaveHdr);
646 }
647
648 if (hData)
649 {
650 GlobalUnlock(hData);
651 GlobalFree(hData);
652 }
653 }
654
655 if (snd_renderbuffer != NULL)
656 {
657 Mem_Free(snd_renderbuffer);
658 snd_renderbuffer = NULL;
659 }
660
661 #ifdef SUPPORTDIRECTX
662 pDS = NULL;
663 pDSBuf = NULL;
664 pDSPBuf = NULL;
665 dsound_init = false;
666 #endif
667 hWaveOut = 0;
668 hData = 0;
669 hWaveHdr = 0;
670 lpData = NULL;
671 lpWaveHdr = NULL;
672 wav_init = false;
673 }
674
675
676 /*
677 ====================
678 SndSys_Submit
679
680 Submit the contents of "snd_renderbuffer" to the sound card
681 ====================
682 */
SndSys_Submit(void)683 void SndSys_Submit (void)
684 {
685 LPWAVEHDR h;
686 int wResult;
687
688 // DirectSound doesn't need this
689 if (!wav_init)
690 return;
691
692 paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
693 if (paintpot > WAV_BUFFERS * wav_buffer_size)
694 paintpot = WAV_BUFFERS * wav_buffer_size;
695 prev_painted = snd_renderbuffer->endframe;
696
697 // submit new sound blocks
698 while (paintpot > wav_buffer_size)
699 {
700 h = lpWaveHdr + (snd_sent & WAV_MASK);
701
702 /*
703 * Now the data block can be sent to the output device. The
704 * waveOutWrite function returns immediately and waveform
705 * data is sent to the output device in the background.
706 */
707 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
708 if (wResult == MMSYSERR_NOERROR)
709 snd_sent++;
710 else if (wResult == WAVERR_STILLPLAYING)
711 {
712 if(developer.integer >= 1000)
713 Con_Print("waveOutWrite failed (too much sound data)\n");
714 //h->dwFlags |= WHDR_DONE;
715 //snd_sent++;
716 }
717 else
718 {
719 Con_Printf("waveOutWrite failed, error code %d\n", (int) wResult);
720 SndSys_Shutdown ();
721 return;
722 }
723
724 paintpot -= wav_buffer_size;
725 }
726
727 }
728
729
730 /*
731 ====================
732 SndSys_GetSoundTime
733
734 Returns the number of sample frames consumed since the sound started
735 ====================
736 */
SndSys_GetSoundTime(void)737 unsigned int SndSys_GetSoundTime (void)
738 {
739 unsigned int factor;
740
741 factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
742
743 #ifdef SUPPORTDIRECTX
744 if (dsound_init)
745 {
746 DWORD dwTime;
747 unsigned int diff;
748
749 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
750 diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize;
751 dwStartTime = dwTime;
752
753 dsound_time += diff / factor;
754 return dsound_time;
755 }
756 #endif
757
758 if (wav_init)
759 {
760 // Find which sound blocks have completed
761 for (;;)
762 {
763 if (snd_completed == snd_sent)
764 {
765 Con_DPrint("Sound overrun\n");
766 break;
767 }
768
769 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
770 break;
771
772 snd_completed++; // this buffer has been played
773 }
774
775 return (snd_completed * wav_buffer_size) / factor;
776
777 /*
778 * S_PaintAndSubmit: WARNING: newsoundtime (soundtime (275 < 134217707)
779 * apparently this sound time wraps quite early?
780 {
781 MMRESULT res;
782 MMTIME mmtime;
783
784 mmtime.wType = TIME_SAMPLES;
785 res = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime));
786 if(res == MMSYSERR_NOERROR)
787 return mmtime.u.sample;
788 }
789 */
790 }
791
792 return 0;
793 }
794
795
796 #ifdef SUPPORTDIRECTX
797 static DWORD dsound_dwSize;
798 static DWORD dsound_dwSize2;
799 static DWORD *dsound_pbuf;
800 static DWORD *dsound_pbuf2;
801 #endif
802
803 /*
804 ====================
805 SndSys_LockRenderBuffer
806
807 Get the exclusive lock on "snd_renderbuffer"
808 ====================
809 */
SndSys_LockRenderBuffer(void)810 qboolean SndSys_LockRenderBuffer (void)
811 {
812 #ifdef SUPPORTDIRECTX
813 int reps;
814 HRESULT hresult;
815 DWORD dwStatus;
816
817 if (pDSBuf)
818 {
819 // if the buffer was lost or stopped, restore it and/or restart it
820 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
821 Con_Print("Couldn't get sound buffer status\n");
822
823 if (dwStatus & DSBSTATUS_BUFFERLOST)
824 {
825 Con_Print("DSound buffer is lost!!\n");
826 IDirectSoundBuffer_Restore (pDSBuf);
827 }
828
829 if (!(dwStatus & DSBSTATUS_PLAYING))
830 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
831
832 reps = 0;
833
834 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
835 {
836 if (hresult != DSERR_BUFFERLOST)
837 {
838 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
839 S_Shutdown ();
840 S_Startup ();
841 return false;
842 }
843
844 if (++reps > 10000)
845 {
846 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
847 S_Shutdown ();
848 S_Startup ();
849 return false;
850 }
851 }
852
853 if ((void*)dsound_pbuf != snd_renderbuffer->ring)
854 Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
855 return true;
856 }
857 #endif
858
859 return wav_init;
860 }
861
862
863 /*
864 ====================
865 SndSys_UnlockRenderBuffer
866
867 Release the exclusive lock on "snd_renderbuffer"
868 ====================
869 */
SndSys_UnlockRenderBuffer(void)870 void SndSys_UnlockRenderBuffer (void)
871 {
872 #ifdef SUPPORTDIRECTX
873 if (pDSBuf)
874 IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
875 #endif
876 }
877
878 /*
879 ====================
880 SndSys_SendKeyEvents
881
882 Send keyboard events originating from the sound system (e.g. MIDI)
883 ====================
884 */
SndSys_SendKeyEvents(void)885 void SndSys_SendKeyEvents(void)
886 {
887 // not supported
888 }
889