fix mingw warnings
[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 "winquake.h"
22
23 #define iDirectSoundCreate(a,b,c)       pDirectSoundCreate(a,b,c)
24
25 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
26
27 // 64K is > 1 second at 16-bit, 22050 Hz
28 #define WAV_BUFFERS                             64
29 #define WAV_MASK                                0x3F
30 #define WAV_BUFFER_SIZE                 0x0400
31 #define SECONDARY_BUFFER_SIZE   0x10000
32
33 typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
34
35 static qboolean wavonly;
36 static qboolean dsound_init;
37 static qboolean wav_init;
38 static qboolean snd_firsttime = true, snd_isdirect, snd_iswave;
39 static qboolean primary_format_set;
40
41 static int      sample16;
42 static int      snd_sent, snd_completed;
43
44
45 /* 
46  * Global variables. Must be visible to window-procedure function 
47  *  so it can unlock and free the data block after it has been played. 
48  */ 
49
50 HANDLE          hData;
51 HPSTR           lpData, lpData2;
52
53 HGLOBAL         hWaveHdr;
54 LPWAVEHDR       lpWaveHdr;
55
56 HWAVEOUT    hWaveOut; 
57
58 WAVEOUTCAPS     wavecaps;
59
60 DWORD   gSndBufSize;
61
62 MMTIME          mmstarttime;
63
64 LPDIRECTSOUND pDS;
65 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
66
67 HINSTANCE hInstDS;
68
69 qboolean SNDDMA_InitWav (void);
70 sndinitstat SNDDMA_InitDirect (void);
71
72 /*
73 ==================
74 S_BlockSound
75 ==================
76 */
77 void S_BlockSound (void)
78 {
79
80 // DirectSound takes care of blocking itself
81         if (snd_iswave)
82         {
83                 snd_blocked++;
84
85                 if (snd_blocked == 1)
86                 {
87                         waveOutReset (hWaveOut);
88                 }
89         }
90 }
91
92
93 /*
94 ==================
95 S_UnblockSound
96 ==================
97 */
98 void S_UnblockSound (void)
99 {
100
101 // DirectSound takes care of blocking itself
102         if (snd_iswave)
103         {
104                 snd_blocked--;
105         }
106 }
107
108
109 /*
110 ==================
111 FreeSound
112 ==================
113 */
114 void FreeSound (void)
115 {
116         int             i;
117
118         if (pDSBuf)
119         {
120                 pDSBuf->lpVtbl->Stop(pDSBuf);
121                 pDSBuf->lpVtbl->Release(pDSBuf);
122         }
123
124 // only release primary buffer if it's not also the mixing buffer we just released
125         if (pDSPBuf && (pDSBuf != pDSPBuf))
126         {
127                 pDSPBuf->lpVtbl->Release(pDSPBuf);
128         }
129
130         if (pDS)
131         {
132                 pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
133                 pDS->lpVtbl->Release(pDS);
134         }
135
136         if (hWaveOut)
137         {
138                 waveOutReset (hWaveOut);
139
140                 if (lpWaveHdr)
141                 {
142                         for (i=0 ; i< WAV_BUFFERS ; i++)
143                                 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
144                 }
145
146                 waveOutClose (hWaveOut);
147
148                 if (hWaveHdr)
149                 {
150                         GlobalUnlock(hWaveHdr); 
151                         GlobalFree(hWaveHdr);
152                 }
153
154                 if (hData)
155                 {
156                         GlobalUnlock(hData);
157                         GlobalFree(hData);
158                 }
159
160         }
161
162         pDS = NULL;
163         pDSBuf = NULL;
164         pDSPBuf = NULL;
165         hWaveOut = 0;
166         hData = 0;
167         hWaveHdr = 0;
168         lpData = NULL;
169         lpWaveHdr = NULL;
170         dsound_init = false;
171         wav_init = false;
172 }
173
174
175 /*
176 ==================
177 SNDDMA_InitDirect
178
179 Direct-Sound support
180 ==================
181 */
182 sndinitstat SNDDMA_InitDirect (void)
183 {
184         DSBUFFERDESC    dsbuf;
185         DSBCAPS                 dsbcaps;
186         DWORD                   dwSize, dwWrite;
187         DSCAPS                  dscaps;
188         WAVEFORMATEX    format, pformat; 
189         HRESULT                 hresult;
190         int                             reps;
191         int i;
192
193         memset ((void *)&sn, 0, sizeof (sn));
194
195         shm = &sn;
196
197         shm->channels = 2;
198         shm->samplebits = 16;
199         i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
200         if (i && i != (com_argc - 1))
201                 shm->speed = atoi(com_argv[i+1]);
202         else
203                 shm->speed = 11025;
204
205         memset (&format, 0, sizeof(format));
206         format.wFormatTag = WAVE_FORMAT_PCM;
207     format.nChannels = shm->channels;
208     format.wBitsPerSample = shm->samplebits;
209     format.nSamplesPerSec = shm->speed;
210     format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
211     format.cbSize = 0;
212     format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; 
213
214         if (!hInstDS)
215         {
216                 hInstDS = LoadLibrary("dsound.dll");
217                 
218                 if (hInstDS == NULL)
219                 {
220                         Con_SafePrintf ("Couldn't load dsound.dll\n");
221                         return SIS_FAILURE;
222                 }
223
224                 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
225
226                 if (!pDirectSoundCreate)
227                 {
228                         Con_SafePrintf ("Couldn't get DS proc addr\n");
229                         return SIS_FAILURE;
230                 }
231         }
232
233         while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
234         {
235                 if (hresult != DSERR_ALLOCATED)
236                 {
237                         Con_SafePrintf ("DirectSound create failed\n");
238                         return SIS_FAILURE;
239                 }
240
241                 if (MessageBox (NULL,
242                                                 "The sound hardware is in use by another app.\n\n"
243                                             "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
244                                                 "Sound not available",
245                                                 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
246                 {
247                         Con_SafePrintf ("DirectSoundCreate failure\n"
248                                                         "  hardware already in use\n");
249                         return SIS_NOTAVAIL;
250                 }
251         }
252
253         dscaps.dwSize = sizeof(dscaps);
254
255         if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
256         {
257                 Con_SafePrintf ("Couldn't get DS caps\n");
258         }
259
260         if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
261         {
262                 Con_SafePrintf ("No DirectSound driver installed\n");
263                 FreeSound ();
264                 return SIS_FAILURE;
265         }
266
267         if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
268         {
269                 Con_SafePrintf ("Set coop level failed\n");
270                 FreeSound ();
271                 return SIS_FAILURE;
272         }
273
274 // get access to the primary buffer, if possible, so we can set the
275 // sound hardware format
276         memset (&dsbuf, 0, sizeof(dsbuf));
277         dsbuf.dwSize = sizeof(DSBUFFERDESC);
278         dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
279         dsbuf.dwBufferBytes = 0;
280         dsbuf.lpwfxFormat = NULL;
281
282         memset(&dsbcaps, 0, sizeof(dsbcaps));
283         dsbcaps.dwSize = sizeof(dsbcaps);
284         primary_format_set = false;
285
286         if (!COM_CheckParm ("-snoforceformat"))
287         {
288                 if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
289                 {
290                         pformat = format;
291
292                         if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
293                         {
294                                 if (snd_firsttime)
295                                         Con_SafePrintf ("Set primary sound buffer format: no\n");
296                         }
297                         else
298                         {
299                                 if (snd_firsttime)
300                                         Con_SafePrintf ("Set primary sound buffer format: yes\n");
301
302                                 primary_format_set = true;
303                         }
304                 }
305         }
306
307         if (!primary_format_set || !COM_CheckParm ("-primarysound"))
308         {
309         // create the secondary buffer we'll actually work with
310                 memset (&dsbuf, 0, sizeof(dsbuf));
311                 dsbuf.dwSize = sizeof(DSBUFFERDESC);
312                 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
313                 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
314                 dsbuf.lpwfxFormat = &format;
315
316                 memset(&dsbcaps, 0, sizeof(dsbcaps));
317                 dsbcaps.dwSize = sizeof(dsbcaps);
318
319                 if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
320                 {
321                         Con_SafePrintf ("DS:CreateSoundBuffer Failed");
322                         FreeSound ();
323                         return SIS_FAILURE;
324                 }
325
326                 shm->channels = format.nChannels;
327                 shm->samplebits = format.wBitsPerSample;
328                 shm->speed = format.nSamplesPerSec;
329
330                 if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
331                 {
332                         Con_SafePrintf ("DS:GetCaps failed\n");
333                         FreeSound ();
334                         return SIS_FAILURE;
335                 }
336
337                 if (snd_firsttime)
338                         Con_SafePrintf ("Using secondary sound buffer\n");
339         }
340         else
341         {
342                 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
343                 {
344                         Con_SafePrintf ("Set coop level failed\n");
345                         FreeSound ();
346                         return SIS_FAILURE;
347                 }
348
349                 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
350                 {
351                         Con_Printf ("DS:GetCaps failed\n");
352                         return SIS_FAILURE;
353                 }
354
355                 pDSBuf = pDSPBuf;
356                 Con_SafePrintf ("Using primary sound buffer\n");
357         }
358
359         // Make sure mixer is active
360         pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
361
362         if (snd_firsttime)
363                 Con_SafePrintf("   %d channel(s)\n"
364                                "   %d bits/sample\n"
365                                            "   %d samples/sec\n",
366                                            shm->channels, shm->samplebits, shm->speed);
367         
368         gSndBufSize = dsbcaps.dwBufferBytes;
369
370 // initialize the buffer
371         reps = 0;
372
373         while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
374         {
375                 if (hresult != DSERR_BUFFERLOST)
376                 {
377                         Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
378                         FreeSound ();
379                         return SIS_FAILURE;
380                 }
381
382                 if (++reps > 10000)
383                 {
384                         Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
385                         FreeSound ();
386                         return SIS_FAILURE;
387                 }
388
389         }
390
391         memset(lpData, 0, dwSize);
392 //              lpData[4] = lpData[5] = 0x7f;   // force a pop for debugging
393
394         pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
395
396         /* we don't want anyone to access the buffer directly w/o locking it first. */
397         lpData = NULL; 
398
399         pDSBuf->lpVtbl->Stop(pDSBuf);
400         pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
401         pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
402
403         shm->soundalive = true;
404         shm->splitbuffer = false;
405         shm->samples = gSndBufSize/(shm->samplebits/8);
406         shm->samplepos = 0;
407         shm->submission_chunk = 1;
408         shm->buffer = (unsigned char *) lpData;
409         sample16 = (shm->samplebits/8) - 1;
410
411         dsound_init = true;
412
413         return SIS_SUCCESS;
414 }
415
416
417 /*
418 ==================
419 SNDDM_InitWav
420
421 Crappy windows multimedia base
422 ==================
423 */
424 qboolean SNDDMA_InitWav (void)
425 {
426         WAVEFORMATEX  format; 
427         int                             i;
428         HRESULT                 hr;
429         
430         snd_sent = 0;
431         snd_completed = 0;
432
433         shm = &sn;
434
435         shm->channels = 2;
436         shm->samplebits = 16;
437         i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
438         if (i && i != (com_argc - 1))
439                 shm->speed = atoi(com_argv[i+1]);
440         else
441                 shm->speed = 11025;
442
443         memset (&format, 0, sizeof(format));
444         format.wFormatTag = WAVE_FORMAT_PCM;
445         format.nChannels = shm->channels;
446         format.wBitsPerSample = shm->samplebits;
447         format.nSamplesPerSec = shm->speed;
448         format.nBlockAlign = format.nChannels
449                 *format.wBitsPerSample / 8;
450         format.cbSize = 0;
451         format.nAvgBytesPerSec = format.nSamplesPerSec
452                 *format.nBlockAlign; 
453         
454         /* Open a waveform device for output using window callback. */ 
455         while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, 
456                                         &format, 
457                                         0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
458         {
459                 if (hr != MMSYSERR_ALLOCATED)
460                 {
461                         Con_SafePrintf ("waveOutOpen failed\n");
462                         return false;
463                 }
464
465                 if (MessageBox (NULL,
466                                                 "The sound hardware is in use by another app.\n\n"
467                                             "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
468                                                 "Sound not available",
469                                                 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
470                 {
471                         Con_SafePrintf ("waveOutOpen failure;\n"
472                                                         "  hardware already in use\n");
473                         return false;
474                 }
475         } 
476
477         /* 
478          * Allocate and lock memory for the waveform data. The memory 
479          * for waveform data must be globally allocated with 
480          * GMEM_MOVEABLE and GMEM_SHARE flags. 
481
482         */ 
483         gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
484         hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); 
485         if (!hData) 
486         { 
487                 Con_SafePrintf ("Sound: Out of memory.\n");
488                 FreeSound ();
489                 return false; 
490         }
491         lpData = GlobalLock(hData);
492         if (!lpData)
493         { 
494                 Con_SafePrintf ("Sound: Failed to lock.\n");
495                 FreeSound ();
496                 return false; 
497         } 
498         memset (lpData, 0, gSndBufSize);
499
500         /* 
501          * Allocate and lock memory for the header. This memory must 
502          * also be globally allocated with GMEM_MOVEABLE and 
503          * GMEM_SHARE flags. 
504          */ 
505         hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, 
506                 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); 
507
508         if (hWaveHdr == NULL)
509         { 
510                 Con_SafePrintf ("Sound: Failed to Alloc header.\n");
511                 FreeSound ();
512                 return false; 
513         } 
514
515         lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); 
516
517         if (lpWaveHdr == NULL)
518         { 
519                 Con_SafePrintf ("Sound: Failed to lock header.\n");
520                 FreeSound ();
521                 return false; 
522         }
523
524         memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
525
526         /* After allocation, set up and prepare headers. */ 
527         for (i=0 ; i<WAV_BUFFERS ; i++)
528         {
529                 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE; 
530                 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
531
532                 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
533                                 MMSYSERR_NOERROR)
534                 {
535                         Con_SafePrintf ("Sound: failed to prepare wave headers\n");
536                         FreeSound ();
537                         return false;
538                 }
539         }
540
541         shm->soundalive = true;
542         shm->splitbuffer = false;
543         shm->samples = gSndBufSize/(shm->samplebits/8);
544         shm->samplepos = 0;
545         shm->submission_chunk = 1;
546         shm->buffer = (unsigned char *) lpData;
547         sample16 = (shm->samplebits/8) - 1;
548
549         wav_init = true;
550
551         return true;
552 }
553
554 /*
555 ==================
556 SNDDMA_Init
557
558 Try to find a sound device to mix for.
559 Returns false if nothing is found.
560 ==================
561 */
562
563 qboolean SNDDMA_Init(void)
564 {
565         sndinitstat     stat;
566
567         if (COM_CheckParm ("-wavonly"))
568                 wavonly = true;
569
570         dsound_init = wav_init = 0;
571
572         stat = SIS_FAILURE;     // assume DirectSound won't initialize
573
574         /* Init DirectSound */
575         if (!wavonly)
576         {
577                 if (snd_firsttime || snd_isdirect)
578                 {
579                         stat = SNDDMA_InitDirect ();;
580
581                         if (stat == SIS_SUCCESS)
582                         {
583                                 snd_isdirect = true;
584
585                                 if (snd_firsttime)
586                                         Con_SafePrintf ("DirectSound initialized\n");
587                         }
588                         else
589                         {
590                                 snd_isdirect = false;
591                                 Con_SafePrintf ("DirectSound failed to init\n");
592                         }
593                 }
594         }
595
596 // if DirectSound didn't succeed in initializing, try to initialize
597 // waveOut sound, unless DirectSound failed because the hardware is
598 // already allocated (in which case the user has already chosen not
599 // to have sound)
600         if (!dsound_init && (stat != SIS_NOTAVAIL))
601         {
602                 if (snd_firsttime || snd_iswave)
603                 {
604
605                         snd_iswave = SNDDMA_InitWav ();
606
607                         if (snd_iswave)
608                         {
609                                 if (snd_firsttime)
610                                         Con_SafePrintf ("Wave sound initialized\n");
611                         }
612                         else
613                         {
614                                 Con_SafePrintf ("Wave sound failed to init\n");
615                         }
616                 }
617         }
618
619         snd_firsttime = false;
620
621         if (!dsound_init && !wav_init)
622         {
623 //              if (snd_firsttime)
624 //                      Con_SafePrintf ("No sound device initialized\n");
625
626                 return 0;
627         }
628
629         return 1;
630 }
631
632 /*
633 ==============
634 SNDDMA_GetDMAPos
635
636 return the current sample position (in mono samples read)
637 inside the recirculating dma buffer, so the mixing code will know
638 how many sample are required to fill it up.
639 ===============
640 */
641 int SNDDMA_GetDMAPos(void)
642 {
643         MMTIME  mmtime;
644         int             s;
645         DWORD   dwWrite;
646
647         if (dsound_init) 
648         {
649                 mmtime.wType = TIME_SAMPLES;
650                 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
651                 s = mmtime.u.sample - mmstarttime.u.sample;
652         }
653         else if (wav_init)
654         {
655                 s = snd_sent * WAV_BUFFER_SIZE;
656         }
657         else
658                 s = 0;
659
660
661         s >>= sample16;
662
663         s &= (shm->samples-1);
664
665         return s;
666 }
667
668 /*
669 ==============
670 SNDDMA_Submit
671
672 Send sound to device if buffer isn't really the dma buffer
673 ===============
674 */
675 void SNDDMA_Submit(void)
676 {
677         LPWAVEHDR       h;
678         int                     wResult;
679
680         if (!wav_init)
681                 return;
682
683         //
684         // find which sound blocks have completed
685         //
686         while (1)
687         {
688                 if ( snd_completed == snd_sent )
689                 {
690                         Con_DPrintf ("Sound overrun\n");
691                         break;
692                 }
693
694                 if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
695                 {
696                         break;
697                 }
698
699                 snd_completed++;        // this buffer has been played
700         }
701
702         //
703         // submit two new sound blocks
704         //
705         while (((snd_sent - snd_completed) >> sample16) < 4)
706         {
707                 h = lpWaveHdr + ( snd_sent&WAV_MASK );
708
709                 snd_sent++;
710                 /* 
711                  * Now the data block can be sent to the output device. The 
712                  * waveOutWrite function returns immediately and waveform 
713                  * data is sent to the output device in the background. 
714                  */ 
715                 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); 
716
717                 if (wResult != MMSYSERR_NOERROR)
718                 { 
719                         Con_SafePrintf ("Failed to write block to device\n");
720                         FreeSound ();
721                         return; 
722                 } 
723         }
724 }
725
726 /*
727 ==============
728 SNDDMA_Shutdown
729
730 Reset the sound device for exiting
731 ===============
732 */
733 void SNDDMA_Shutdown(void)
734 {
735         FreeSound ();
736 }
737