2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
25 extern HWND mainwindow;
27 #define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c)
29 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
31 // Wave output: 64KB in 64 buffers of 1KB
32 // (64KB is > 1 sec at 16-bit 22050 Hz mono, and is 1/3 sec at 16-bit 44100 Hz stereo)
33 #define WAV_BUFFERS 64
34 #define WAV_MASK (WAV_BUFFERS - 1)
35 #define WAV_BUFFER_SIZE 1024
37 // DirectSound output: 64KB in 1 buffer
38 #define SECONDARY_BUFFER_SIZE (64 * 1024)
40 typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
42 static qboolean wavonly;
43 static qboolean dsound_init;
44 static qboolean wav_init;
45 static qboolean snd_firsttime = true, snd_isdirect, snd_iswave;
46 static qboolean primary_format_set;
48 static int snd_sent, snd_completed;
50 static int prev_painted;
51 static unsigned int paintpot;
55 * Global variables. Must be visible to window-procedure function
56 * so it can unlock and free the data block after it has been played.
60 HPSTR lpData, lpData2;
74 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
78 qboolean SNDDMA_InitWav (void);
79 sndinitstat SNDDMA_InitDirect (void);
86 void S_BlockSound (void)
88 // DirectSound takes care of blocking itself
94 waveOutReset (hWaveOut);
104 void S_UnblockSound (void)
106 // DirectSound takes care of blocking itself
117 void FreeSound (void)
123 pDSBuf->lpVtbl->Stop(pDSBuf);
124 pDSBuf->lpVtbl->Release(pDSBuf);
127 // only release primary buffer if it's not also the mixing buffer we just released
128 if (pDSPBuf && (pDSBuf != pDSPBuf))
130 pDSPBuf->lpVtbl->Release(pDSPBuf);
135 pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
136 pDS->lpVtbl->Release(pDS);
141 waveOutReset (hWaveOut);
145 for (i=0 ; i< WAV_BUFFERS ; i++)
146 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
149 waveOutClose (hWaveOut);
153 GlobalUnlock(hWaveHdr);
154 GlobalFree(hWaveHdr);
185 sndinitstat SNDDMA_InitDirect (void)
191 WAVEFORMATEX format, pformat;
196 memset((void *)shm, 0, sizeof(*shm));
197 shm->format.channels = 2;
198 shm->format.width = 2;
199 // COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
200 i = COM_CheckParm ("-sndspeed");
201 if (i && i != (com_argc - 1))
202 shm->format.speed = atoi(com_argv[i+1]);
204 shm->format.speed = 44100;
206 memset (&format, 0, sizeof(format));
207 format.wFormatTag = WAVE_FORMAT_PCM;
208 format.nChannels = shm->format.channels;
209 format.wBitsPerSample = shm->format.width * 8;
210 format.nSamplesPerSec = shm->format.speed;
211 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
213 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
217 hInstDS = LoadLibrary("dsound.dll");
221 Con_SafePrint("Couldn't load dsound.dll\n");
225 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
227 if (!pDirectSoundCreate)
229 Con_SafePrint("Couldn't get DS proc addr\n");
234 while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
236 if (hresult != DSERR_ALLOCATED)
238 Con_SafePrint("DirectSound create failed\n");
242 if (MessageBox (NULL,
243 "The sound hardware is in use by another app.\n\n"
244 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
245 "Sound not available",
246 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
248 Con_SafePrint("DirectSoundCreate failure\n hardware already in use\n");
253 dscaps.dwSize = sizeof(dscaps);
255 if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
257 Con_SafePrint("Couldn't get DS caps\n");
260 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
262 Con_SafePrint("No DirectSound driver installed\n");
267 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
269 Con_SafePrint("Set coop level failed\n");
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;
282 memset(&dsbcaps, 0, sizeof(dsbcaps));
283 dsbcaps.dwSize = sizeof(dsbcaps);
284 primary_format_set = false;
286 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
287 if (!COM_CheckParm ("-snoforceformat"))
289 if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
293 if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
296 Con_SafePrint("Set primary sound buffer format: no\n");
301 Con_SafePrint("Set primary sound buffer format: yes\n");
303 primary_format_set = true;
308 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
309 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
311 // create the secondary buffer we'll actually work with
312 memset (&dsbuf, 0, sizeof(dsbuf));
313 dsbuf.dwSize = sizeof(DSBUFFERDESC);
314 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
315 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
316 dsbuf.lpwfxFormat = &format;
318 memset(&dsbcaps, 0, sizeof(dsbcaps));
319 dsbcaps.dwSize = sizeof(dsbcaps);
321 if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
323 Con_SafePrint("DS:CreateSoundBuffer Failed\n");
328 shm->format.channels = format.nChannels;
329 shm->format.width = format.wBitsPerSample / 8;
330 shm->format.speed = format.nSamplesPerSec;
332 if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
334 Con_SafePrint("DS:GetCaps failed\n");
340 Con_SafePrint("Using secondary sound buffer\n");
344 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
346 Con_SafePrint("Set coop level failed\n");
351 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
353 Con_Print("DS:GetCaps failed\n");
358 Con_SafePrint("Using primary sound buffer\n");
361 // Make sure mixer is active
362 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
365 Con_SafePrintf(" %d channel(s)\n"
368 shm->format.channels, shm->format.width * 8, shm->format.speed);
370 gSndBufSize = dsbcaps.dwBufferBytes;
372 // initialize the buffer
375 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
377 if (hresult != DSERR_BUFFERLOST)
379 Con_SafePrint("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
386 Con_SafePrint("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
393 memset(lpData, 0, dwSize);
395 pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
397 // we don't want anyone to access the buffer directly w/o locking it first.
400 pDSBuf->lpVtbl->Stop(pDSBuf);
401 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwStartTime, NULL);
402 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
404 shm->samples = gSndBufSize / shm->format.width;
406 shm->buffer = (unsigned char *) lpData;
418 Crappy windows multimedia base
421 qboolean SNDDMA_InitWav (void)
430 memset((void *)shm, 0, sizeof(*shm));
431 shm->format.channels = 2;
432 shm->format.width = 2;
433 // COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
434 i = COM_CheckParm ("-sndspeed");
435 if (i && i != (com_argc - 1))
436 shm->format.speed = atoi(com_argv[i+1]);
438 shm->format.speed = 44100;
440 memset (&format, 0, sizeof(format));
441 format.wFormatTag = WAVE_FORMAT_PCM;
442 format.nChannels = shm->format.channels;
443 format.wBitsPerSample = shm->format.width * 8;
444 format.nSamplesPerSec = shm->format.speed;
445 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
447 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
449 // Open a waveform device for output using window callback
450 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
452 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
454 if (hr != MMSYSERR_ALLOCATED)
456 Con_SafePrint("waveOutOpen failed\n");
460 if (MessageBox (NULL,
461 "The sound hardware is in use by another app.\n\n"
462 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
463 "Sound not available",
464 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
466 Con_SafePrint("waveOutOpen failure;\n hardware already in use\n");
472 * Allocate and lock memory for the waveform data. The memory
473 * for waveform data must be globally allocated with
474 * GMEM_MOVEABLE and GMEM_SHARE flags.
476 gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
477 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
480 Con_SafePrint("Sound: Out of memory.\n");
484 lpData = GlobalLock(hData);
487 Con_SafePrint("Sound: Failed to lock.\n");
491 memset (lpData, 0, gSndBufSize);
494 * Allocate and lock memory for the header. This memory must
495 * also be globally allocated with GMEM_MOVEABLE and
498 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
499 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
501 if (hWaveHdr == NULL)
503 Con_SafePrint("Sound: Failed to Alloc header.\n");
508 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
510 if (lpWaveHdr == NULL)
512 Con_SafePrint("Sound: Failed to lock header.\n");
517 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
519 // After allocation, set up and prepare headers
520 for (i=0 ; i<WAV_BUFFERS ; i++)
522 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
523 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
525 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
528 Con_SafePrint("Sound: failed to prepare wave headers\n");
534 shm->samples = gSndBufSize / shm->format.width;
536 shm->buffer = (unsigned char *) lpData;
550 Try to find a sound device to mix for.
551 Returns false if nothing is found.
554 qboolean SNDDMA_Init(void)
558 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
559 if (COM_CheckParm ("-wavonly"))
562 dsound_init = wav_init = 0;
564 stat = SIS_FAILURE; // assume DirectSound won't initialize
569 if (snd_firsttime || snd_isdirect)
571 stat = SNDDMA_InitDirect ();
573 if (stat == SIS_SUCCESS)
578 Con_SafePrint("DirectSound initialized\n");
582 snd_isdirect = false;
583 Con_SafePrint("DirectSound failed to init\n");
588 // if DirectSound didn't succeed in initializing, try to initialize
589 // waveOut sound, unless DirectSound failed because the hardware is
590 // already allocated (in which case the user has already chosen not
592 if (!dsound_init && (stat != SIS_NOTAVAIL))
594 if (snd_firsttime || snd_iswave)
597 snd_iswave = SNDDMA_InitWav ();
602 Con_SafePrint("Wave sound initialized\n");
606 Con_SafePrint("Wave sound failed to init\n");
611 snd_firsttime = false;
613 if (!dsound_init && !wav_init)
623 return the current sample position (in mono samples read)
624 inside the recirculating dma buffer, so the mixing code will know
625 how many sample are required to fill it up.
628 int SNDDMA_GetDMAPos(void)
634 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwTime, NULL);
635 s = dwTime - dwStartTime;
639 // Find which sound blocks have completed
642 if (snd_completed == snd_sent)
644 Con_DPrint("Sound overrun\n");
648 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
651 snd_completed++; // this buffer has been played
654 s = snd_completed * WAV_BUFFER_SIZE;
659 return (s >> (shm->format.width - 1)) & (shm->samples - 1);
666 Send sound to device if buffer isn't really the dma buffer
669 void SNDDMA_Submit(void)
674 // DirectSound doesn't need this
678 paintpot += (paintedtime - prev_painted) * shm->format.channels * shm->format.width;
679 prev_painted = paintedtime;
681 // submit new sound blocks
682 while (paintpot > WAV_BUFFER_SIZE)
684 h = lpWaveHdr + (snd_sent & WAV_MASK);
688 * Now the data block can be sent to the output device. The
689 * waveOutWrite function returns immediately and waveform
690 * data is sent to the output device in the background.
692 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
694 if (wResult != MMSYSERR_NOERROR)
696 Con_SafePrint("Failed to write block to device\n");
701 paintpot -= WAV_BUFFER_SIZE;
709 Reset the sound device for exiting
712 void SNDDMA_Shutdown(void)
718 DWORD dsound_dwSize2;
721 void *S_LockBuffer(void)
729 // if the buffer was lost or stopped, restore it and/or restart it
730 if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK)
731 Con_Print("Couldn't get sound buffer status\n");
733 if (dwStatus & DSBSTATUS_BUFFERLOST)
734 pDSBuf->lpVtbl->Restore (pDSBuf);
736 if (!(dwStatus & DSBSTATUS_PLAYING))
737 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
741 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
743 if (hresult != DSERR_BUFFERLOST)
745 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
753 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
765 void S_UnlockBuffer(void)
768 pDSBuf->lpVtbl->Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);