]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - snd_win.c
fix view blends so they are alpha, not additive
[xonotic/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
393         pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
394
395         /* we don't want anyone to access the buffer directly w/o locking it first. */
396         lpData = NULL; 
397
398         pDSBuf->lpVtbl->Stop(pDSBuf);
399         pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
400         pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
401
402         shm->soundalive = true;
403         shm->splitbuffer = false;
404         shm->samples = gSndBufSize/(shm->samplebits/8);
405         shm->samplepos = 0;
406         shm->submission_chunk = 1;
407         shm->buffer = (unsigned char *) lpData;
408         sample16 = (shm->samplebits/8) - 1;
409
410         dsound_init = true;
411
412         return SIS_SUCCESS;
413 }
414
415
416 /*
417 ==================
418 SNDDM_InitWav
419
420 Crappy windows multimedia base
421 ==================
422 */
423 qboolean SNDDMA_InitWav (void)
424 {
425         WAVEFORMATEX  format; 
426         int                             i;
427         HRESULT                 hr;
428         
429         snd_sent = 0;
430         snd_completed = 0;
431
432         shm = &sn;
433
434         shm->channels = 2;
435         shm->samplebits = 16;
436         i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
437         if (i && i != (com_argc - 1))
438                 shm->speed = atoi(com_argv[i+1]);
439         else
440                 shm->speed = 11025;
441
442         memset (&format, 0, sizeof(format));
443         format.wFormatTag = WAVE_FORMAT_PCM;
444         format.nChannels = shm->channels;
445         format.wBitsPerSample = shm->samplebits;
446         format.nSamplesPerSec = shm->speed;
447         format.nBlockAlign = format.nChannels
448                 *format.wBitsPerSample / 8;
449         format.cbSize = 0;
450         format.nAvgBytesPerSec = format.nSamplesPerSec
451                 *format.nBlockAlign; 
452         
453         /* Open a waveform device for output using window callback. */ 
454         while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, 
455                                         &format, 
456                                         0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
457         {
458                 if (hr != MMSYSERR_ALLOCATED)
459                 {
460                         Con_SafePrintf ("waveOutOpen failed\n");
461                         return false;
462                 }
463
464                 if (MessageBox (NULL,
465                                                 "The sound hardware is in use by another app.\n\n"
466                                             "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
467                                                 "Sound not available",
468                                                 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
469                 {
470                         Con_SafePrintf ("waveOutOpen failure;\n"
471                                                         "  hardware already in use\n");
472                         return false;
473                 }
474         } 
475
476         /* 
477          * Allocate and lock memory for the waveform data. The memory 
478          * for waveform data must be globally allocated with 
479          * GMEM_MOVEABLE and GMEM_SHARE flags. 
480
481         */ 
482         gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
483         hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); 
484         if (!hData) 
485         { 
486                 Con_SafePrintf ("Sound: Out of memory.\n");
487                 FreeSound ();
488                 return false; 
489         }
490         lpData = GlobalLock(hData);
491         if (!lpData)
492         { 
493                 Con_SafePrintf ("Sound: Failed to lock.\n");
494                 FreeSound ();
495                 return false; 
496         } 
497         memset (lpData, 0, gSndBufSize);
498
499         /* 
500          * Allocate and lock memory for the header. This memory must 
501          * also be globally allocated with GMEM_MOVEABLE and 
502          * GMEM_SHARE flags. 
503          */ 
504         hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, 
505                 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); 
506
507         if (hWaveHdr == NULL)
508         { 
509                 Con_SafePrintf ("Sound: Failed to Alloc header.\n");
510                 FreeSound ();
511                 return false; 
512         } 
513
514         lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); 
515
516         if (lpWaveHdr == NULL)
517         { 
518                 Con_SafePrintf ("Sound: Failed to lock header.\n");
519                 FreeSound ();
520                 return false; 
521         }
522
523         memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
524
525         /* After allocation, set up and prepare headers. */ 
526         for (i=0 ; i<WAV_BUFFERS ; i++)
527         {
528                 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE; 
529                 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
530
531                 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
532                                 MMSYSERR_NOERROR)
533                 {
534                         Con_SafePrintf ("Sound: failed to prepare wave headers\n");
535                         FreeSound ();
536                         return false;
537                 }
538         }
539
540         shm->soundalive = true;
541         shm->splitbuffer = false;
542         shm->samples = gSndBufSize/(shm->samplebits/8);
543         shm->samplepos = 0;
544         shm->submission_chunk = 1;
545         shm->buffer = (unsigned char *) lpData;
546         sample16 = (shm->samplebits/8) - 1;
547
548         wav_init = true;
549
550         return true;
551 }
552
553 /*
554 ==================
555 SNDDMA_Init
556
557 Try to find a sound device to mix for.
558 Returns false if nothing is found.
559 ==================
560 */
561
562 qboolean SNDDMA_Init(void)
563 {
564         sndinitstat     stat;
565
566         if (COM_CheckParm ("-wavonly"))
567                 wavonly = true;
568
569         dsound_init = wav_init = 0;
570
571         stat = SIS_FAILURE;     // assume DirectSound won't initialize
572
573         /* Init DirectSound */
574         if (!wavonly)
575         {
576                 if (snd_firsttime || snd_isdirect)
577                 {
578                         stat = SNDDMA_InitDirect ();;
579
580                         if (stat == SIS_SUCCESS)
581                         {
582                                 snd_isdirect = true;
583
584                                 if (snd_firsttime)
585                                         Con_SafePrintf ("DirectSound initialized\n");
586                         }
587                         else
588                         {
589                                 snd_isdirect = false;
590                                 Con_SafePrintf ("DirectSound failed to init\n");
591                         }
592                 }
593         }
594
595 // if DirectSound didn't succeed in initializing, try to initialize
596 // waveOut sound, unless DirectSound failed because the hardware is
597 // already allocated (in which case the user has already chosen not
598 // to have sound)
599         if (!dsound_init && (stat != SIS_NOTAVAIL))
600         {
601                 if (snd_firsttime || snd_iswave)
602                 {
603
604                         snd_iswave = SNDDMA_InitWav ();
605
606                         if (snd_iswave)
607                         {
608                                 if (snd_firsttime)
609                                         Con_SafePrintf ("Wave sound initialized\n");
610                         }
611                         else
612                         {
613                                 Con_SafePrintf ("Wave sound failed to init\n");
614                         }
615                 }
616         }
617
618         snd_firsttime = false;
619
620         if (!dsound_init && !wav_init)
621                 return 0;
622
623         return 1;
624 }
625
626 /*
627 ==============
628 SNDDMA_GetDMAPos
629
630 return the current sample position (in mono samples read)
631 inside the recirculating dma buffer, so the mixing code will know
632 how many sample are required to fill it up.
633 ===============
634 */
635 int SNDDMA_GetDMAPos(void)
636 {
637         MMTIME  mmtime;
638         int             s;
639         DWORD   dwWrite;
640
641         if (dsound_init) 
642         {
643                 mmtime.wType = TIME_SAMPLES;
644                 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
645                 s = mmtime.u.sample - mmstarttime.u.sample;
646         }
647         else if (wav_init)
648         {
649                 s = snd_sent * WAV_BUFFER_SIZE;
650         }
651         else
652                 s = 0;
653
654
655         s >>= sample16;
656
657         s &= (shm->samples-1);
658
659         return s;
660 }
661
662 /*
663 ==============
664 SNDDMA_Submit
665
666 Send sound to device if buffer isn't really the dma buffer
667 ===============
668 */
669 void SNDDMA_Submit(void)
670 {
671         LPWAVEHDR       h;
672         int                     wResult;
673
674         if (!wav_init)
675                 return;
676
677         //
678         // find which sound blocks have completed
679         //
680         while (1)
681         {
682                 if ( snd_completed == snd_sent )
683                 {
684                         Con_DPrintf ("Sound overrun\n");
685                         break;
686                 }
687
688                 if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
689                 {
690                         break;
691                 }
692
693                 snd_completed++;        // this buffer has been played
694         }
695
696         //
697         // submit two new sound blocks
698         //
699         while (((snd_sent - snd_completed) >> sample16) < 4)
700         {
701                 h = lpWaveHdr + ( snd_sent&WAV_MASK );
702
703                 snd_sent++;
704                 /* 
705                  * Now the data block can be sent to the output device. The 
706                  * waveOutWrite function returns immediately and waveform 
707                  * data is sent to the output device in the background. 
708                  */ 
709                 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); 
710
711                 if (wResult != MMSYSERR_NOERROR)
712                 { 
713                         Con_SafePrintf ("Failed to write block to device\n");
714                         FreeSound ();
715                         return; 
716                 } 
717         }
718 }
719
720 /*
721 ==============
722 SNDDMA_Shutdown
723
724 Reset the sound device for exiting
725 ===============
726 */
727 void SNDDMA_Shutdown(void)
728 {
729         FreeSound ();
730 }
731