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