go back to block counting, looks like we now know what's broken with waveOut
[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 #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 */
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 */
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 = (void *)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 */
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 = 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 */
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 */
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 */
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 */
737 unsigned int SndSys_GetSoundTime (void)
738 {
739         unsigned int factor;
740         MMRESULT res;
741         MMTIME mmtime;
742
743         factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
744
745 #ifdef SUPPORTDIRECTX
746         if (dsound_init)
747         {
748                 DWORD dwTime;
749                 unsigned int diff;
750
751                 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
752                 diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize;
753                 dwStartTime = dwTime;
754
755                 dsound_time += diff / factor;
756                 return dsound_time;
757         }
758 #endif
759
760         if (wav_init)
761         {
762                 // Find which sound blocks have completed
763                 for (;;)
764                 {
765                         if (snd_completed == snd_sent)
766                         {
767                                 Con_DPrint("Sound overrun\n");
768                                 break;
769                         }
770
771                         if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
772                                 break;
773
774                         snd_completed++;        // this buffer has been played
775                 }
776
777                 return (snd_completed * wav_buffer_size) / factor;
778
779                 /*
780                  * S_PaintAndSubmit: WARNING: newsoundtime (soundtime (275 < 134217707)
781                  * apparently this sound time wraps quite early?
782
783                 mmtime.wType = TIME_SAMPLES;
784                 res = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime));
785                 if(res == MMSYSERR_NOERROR)
786                         return mmtime.u.sample;
787
788                 */
789         }
790
791         return 0;
792 }
793
794
795 #ifdef SUPPORTDIRECTX
796 static DWORD dsound_dwSize;
797 static DWORD dsound_dwSize2;
798 static DWORD *dsound_pbuf;
799 static DWORD *dsound_pbuf2;
800 #endif
801
802 /*
803 ====================
804 SndSys_LockRenderBuffer
805
806 Get the exclusive lock on "snd_renderbuffer"
807 ====================
808 */
809 qboolean SndSys_LockRenderBuffer (void)
810 {
811 #ifdef SUPPORTDIRECTX
812         int reps;
813         HRESULT hresult;
814         DWORD   dwStatus;
815
816         if (pDSBuf)
817         {
818                 // if the buffer was lost or stopped, restore it and/or restart it
819                 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
820                         Con_Print("Couldn't get sound buffer status\n");
821
822                 if (dwStatus & DSBSTATUS_BUFFERLOST)
823                 {
824                         Con_Print("DSound buffer is lost!!\n");
825                         IDirectSoundBuffer_Restore (pDSBuf);
826                 }
827
828                 if (!(dwStatus & DSBSTATUS_PLAYING))
829                         IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
830
831                 reps = 0;
832
833                 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
834                 {
835                         if (hresult != DSERR_BUFFERLOST)
836                         {
837                                 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
838                                 S_Shutdown ();
839                                 S_Startup ();
840                                 return false;
841                         }
842
843                         if (++reps > 10000)
844                         {
845                                 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
846                                 S_Shutdown ();
847                                 S_Startup ();
848                                 return false;
849                         }
850                 }
851
852                 if ((void*)dsound_pbuf != snd_renderbuffer->ring)
853                         Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
854                 return true;
855         }
856 #endif
857
858         return wav_init;
859 }
860
861
862 /*
863 ====================
864 SndSys_UnlockRenderBuffer
865
866 Release the exclusive lock on "snd_renderbuffer"
867 ====================
868 */
869 void SndSys_UnlockRenderBuffer (void)
870 {
871 #ifdef SUPPORTDIRECTX
872         if (pDSBuf)
873                 IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
874 #endif
875 }