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 // 64K is > 1 second at 16-bit, 22050 Hz
32 #define WAV_BUFFERS 64
34 #define WAV_BUFFER_SIZE 0x0400
35 #define SECONDARY_BUFFER_SIZE 0x10000
37 typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
39 static qboolean wavonly;
40 static qboolean dsound_init;
41 static qboolean wav_init;
42 static qboolean snd_firsttime = true, snd_isdirect, snd_iswave;
43 static qboolean primary_format_set;
46 static int snd_sent, snd_completed;
50 * Global variables. Must be visible to window-procedure function
51 * so it can unlock and free the data block after it has been played.
55 HPSTR lpData, lpData2;
69 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
73 qboolean SNDDMA_InitWav (void);
74 sndinitstat SNDDMA_InitDirect (void);
81 void S_BlockSound (void)
84 // DirectSound takes care of blocking itself
91 waveOutReset (hWaveOut);
102 void S_UnblockSound (void)
105 // DirectSound takes care of blocking itself
118 void FreeSound (void)
124 pDSBuf->lpVtbl->Stop(pDSBuf);
125 pDSBuf->lpVtbl->Release(pDSBuf);
128 // only release primary buffer if it's not also the mixing buffer we just released
129 if (pDSPBuf && (pDSBuf != pDSPBuf))
131 pDSPBuf->lpVtbl->Release(pDSPBuf);
136 pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
137 pDS->lpVtbl->Release(pDS);
142 waveOutReset (hWaveOut);
146 for (i=0 ; i< WAV_BUFFERS ; i++)
147 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
150 waveOutClose (hWaveOut);
154 GlobalUnlock(hWaveHdr);
155 GlobalFree(hWaveHdr);
186 sndinitstat SNDDMA_InitDirect (void)
190 DWORD dwSize, dwWrite;
192 WAVEFORMATEX format, pformat;
197 memset((void *)shm, 0, sizeof(*shm));
198 shm->format.channels = 2;
199 shm->format.width = 2;
200 // COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
201 i = COM_CheckParm ("-sndspeed");
202 if (i && i != (com_argc - 1))
203 shm->format.speed = atoi(com_argv[i+1]);
205 shm->format.speed = 44100;
207 memset (&format, 0, sizeof(format));
208 format.wFormatTag = WAVE_FORMAT_PCM;
209 format.nChannels = shm->format.channels;
210 format.wBitsPerSample = shm->format.width * 8;
211 format.nSamplesPerSec = shm->format.speed;
212 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
214 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
218 hInstDS = LoadLibrary("dsound.dll");
222 Con_SafePrint("Couldn't load dsound.dll\n");
226 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
228 if (!pDirectSoundCreate)
230 Con_SafePrint("Couldn't get DS proc addr\n");
235 while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
237 if (hresult != DSERR_ALLOCATED)
239 Con_SafePrint("DirectSound create failed\n");
243 if (MessageBox (NULL,
244 "The sound hardware is in use by another app.\n\n"
245 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
246 "Sound not available",
247 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
249 Con_SafePrint("DirectSoundCreate failure\n hardware already in use\n");
254 dscaps.dwSize = sizeof(dscaps);
256 if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
258 Con_SafePrint("Couldn't get DS caps\n");
261 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
263 Con_SafePrint("No DirectSound driver installed\n");
268 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
270 Con_SafePrint("Set coop level failed\n");
275 // get access to the primary buffer, if possible, so we can set the
276 // sound hardware format
277 memset (&dsbuf, 0, sizeof(dsbuf));
278 dsbuf.dwSize = sizeof(DSBUFFERDESC);
279 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
280 dsbuf.dwBufferBytes = 0;
281 dsbuf.lpwfxFormat = NULL;
283 memset(&dsbcaps, 0, sizeof(dsbcaps));
284 dsbcaps.dwSize = sizeof(dsbcaps);
285 primary_format_set = false;
287 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
288 if (!COM_CheckParm ("-snoforceformat"))
290 if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
294 if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
297 Con_SafePrint("Set primary sound buffer format: no\n");
302 Con_SafePrint("Set primary sound buffer format: yes\n");
304 primary_format_set = true;
309 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
310 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
312 // create the secondary buffer we'll actually work with
313 memset (&dsbuf, 0, sizeof(dsbuf));
314 dsbuf.dwSize = sizeof(DSBUFFERDESC);
315 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
316 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
317 dsbuf.lpwfxFormat = &format;
319 memset(&dsbcaps, 0, sizeof(dsbcaps));
320 dsbcaps.dwSize = sizeof(dsbcaps);
322 if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
324 Con_SafePrint("DS:CreateSoundBuffer Failed\n");
329 shm->format.channels = format.nChannels;
330 shm->format.width = format.wBitsPerSample / 8;
331 shm->format.speed = format.nSamplesPerSec;
333 if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
335 Con_SafePrint("DS:GetCaps failed\n");
341 Con_SafePrint("Using secondary sound buffer\n");
345 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
347 Con_SafePrint("Set coop level failed\n");
352 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
354 Con_Print("DS:GetCaps failed\n");
359 Con_SafePrint("Using primary sound buffer\n");
362 // Make sure mixer is active
363 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
366 Con_SafePrintf(" %d channel(s)\n"
369 shm->format.channels, shm->format.width * 8, shm->format.speed);
371 gSndBufSize = dsbcaps.dwBufferBytes;
373 // initialize the buffer
376 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
378 if (hresult != DSERR_BUFFERLOST)
380 Con_SafePrint("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
387 Con_SafePrint("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
394 memset(lpData, 0, dwSize);
396 pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
398 /* we don't want anyone to access the buffer directly w/o locking it first. */
401 pDSBuf->lpVtbl->Stop(pDSBuf);
402 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
403 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
405 shm->samples = gSndBufSize / shm->format.width;
407 shm->buffer = (unsigned char *) lpData;
408 sample16 = shm->format.width - 1;
420 Crappy windows multimedia base
423 qboolean SNDDMA_InitWav (void)
432 memset((void *)shm, 0, sizeof(*shm));
433 shm->format.channels = 2;
434 shm->format.width = 2;
435 // COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
436 i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
437 if (i && i != (com_argc - 1))
438 shm->format.speed = atoi(com_argv[i+1]);
440 shm->format.speed = 44100;
442 memset (&format, 0, sizeof(format));
443 format.wFormatTag = WAVE_FORMAT_PCM;
444 format.nChannels = shm->format.channels;
445 format.wBitsPerSample = shm->format.width * 8;
446 format.nSamplesPerSec = shm->format.speed;
447 format.nBlockAlign = format.nChannels
448 *format.wBitsPerSample / 8;
450 format.nAvgBytesPerSec = format.nSamplesPerSec
453 /* Open a waveform device for output using window callback. */
454 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
456 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
458 if (hr != MMSYSERR_ALLOCATED)
460 Con_SafePrint("waveOutOpen failed\n");
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)
470 Con_SafePrint("waveOutOpen failure;\n hardware already in use\n");
476 * Allocate and lock memory for the waveform data. The memory
477 * for waveform data must be globally allocated with
478 * GMEM_MOVEABLE and GMEM_SHARE flags.
481 gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
482 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
485 Con_SafePrint("Sound: Out of memory.\n");
489 lpData = GlobalLock(hData);
492 Con_SafePrint("Sound: Failed to lock.\n");
496 memset (lpData, 0, gSndBufSize);
499 * Allocate and lock memory for the header. This memory must
500 * also be globally allocated with GMEM_MOVEABLE and
503 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
504 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
506 if (hWaveHdr == NULL)
508 Con_SafePrint("Sound: Failed to Alloc header.\n");
513 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
515 if (lpWaveHdr == NULL)
517 Con_SafePrint("Sound: Failed to lock header.\n");
522 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
524 /* After allocation, set up and prepare headers. */
525 for (i=0 ; i<WAV_BUFFERS ; i++)
527 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
528 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
530 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
533 Con_SafePrint("Sound: failed to prepare wave headers\n");
539 shm->samples = gSndBufSize / shm->format.width;
541 shm->buffer = (unsigned char *) lpData;
542 sample16 = shm->format.width - 1;
553 Try to find a sound device to mix for.
554 Returns false if nothing is found.
558 qboolean SNDDMA_Init(void)
562 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
563 if (COM_CheckParm ("-wavonly"))
566 dsound_init = wav_init = 0;
568 stat = SIS_FAILURE; // assume DirectSound won't initialize
570 /* Init DirectSound */
573 if (snd_firsttime || snd_isdirect)
575 stat = SNDDMA_InitDirect ();
577 if (stat == SIS_SUCCESS)
582 Con_SafePrint("DirectSound initialized\n");
586 snd_isdirect = false;
587 Con_SafePrint("DirectSound failed to init\n");
592 // if DirectSound didn't succeed in initializing, try to initialize
593 // waveOut sound, unless DirectSound failed because the hardware is
594 // already allocated (in which case the user has already chosen not
596 if (!dsound_init && (stat != SIS_NOTAVAIL))
598 if (snd_firsttime || snd_iswave)
601 snd_iswave = SNDDMA_InitWav ();
606 Con_SafePrint("Wave sound initialized\n");
610 Con_SafePrint("Wave sound failed to init\n");
615 snd_firsttime = false;
617 if (!dsound_init && !wav_init)
627 return the current sample position (in mono samples read)
628 inside the recirculating dma buffer, so the mixing code will know
629 how many sample are required to fill it up.
632 int SNDDMA_GetDMAPos(void)
640 mmtime.wType = TIME_SAMPLES;
641 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
642 s = mmtime.u.sample - mmstarttime.u.sample;
646 s = snd_sent * WAV_BUFFER_SIZE;
654 s &= (shm->samples-1);
663 Send sound to device if buffer isn't really the dma buffer
666 void SNDDMA_Submit(void)
675 // find which sound blocks have completed
679 if ( snd_completed == snd_sent )
681 Con_DPrint("Sound overrun\n");
685 if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
690 snd_completed++; // this buffer has been played
694 // submit two new sound blocks
696 while (((snd_sent - snd_completed) >> sample16) < 4)
698 h = lpWaveHdr + ( snd_sent&WAV_MASK );
702 * Now the data block can be sent to the output device. The
703 * waveOutWrite function returns immediately and waveform
704 * data is sent to the output device in the background.
706 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
708 if (wResult != MMSYSERR_NOERROR)
710 Con_SafePrint("Failed to write block to device\n");
721 Reset the sound device for exiting
724 void SNDDMA_Shutdown(void)
730 DWORD dsound_dwSize2;
733 void *S_LockBuffer(void)
742 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
744 if (hresult != DSERR_BUFFERLOST)
746 Con_Print("S_LockBuffer: DS::Lock Sound Buffer Failed\n");
754 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
766 void S_UnlockBuffer(void)
769 pDSBuf->lpVtbl->Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);