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.
24 extern HWND mainwindow;
26 #define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c)
28 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
30 // 64K is > 1 second at 16-bit, 22050 Hz
31 #define WAV_BUFFERS 64
33 #define WAV_BUFFER_SIZE 0x0400
34 #define SECONDARY_BUFFER_SIZE 0x10000
36 typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
38 static qboolean wavonly;
39 static qboolean dsound_init;
40 static qboolean wav_init;
41 static qboolean snd_firsttime = true, snd_isdirect, snd_iswave;
42 static qboolean primary_format_set;
45 static int snd_sent, snd_completed;
49 * Global variables. Must be visible to window-procedure function
50 * so it can unlock and free the data block after it has been played.
54 HPSTR lpData, lpData2;
68 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
72 qboolean SNDDMA_InitWav (void);
73 sndinitstat SNDDMA_InitDirect (void);
80 void S_BlockSound (void)
83 // DirectSound takes care of blocking itself
90 waveOutReset (hWaveOut);
101 void S_UnblockSound (void)
104 // 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)
189 DWORD dwSize, dwWrite;
191 WAVEFORMATEX format, pformat;
196 memset((void *)shm, 0, sizeof(*shm));
197 shm->format.channels = 2;
198 shm->format.width = 2;
199 i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
200 if (i && i != (com_argc - 1))
201 shm->format.speed = atoi(com_argv[i+1]);
203 shm->format.speed = 44100;
205 memset (&format, 0, sizeof(format));
206 format.wFormatTag = WAVE_FORMAT_PCM;
207 format.nChannels = shm->format.channels;
208 format.wBitsPerSample = shm->format.width * 8;
209 format.nSamplesPerSec = shm->format.speed;
210 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
212 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
216 hInstDS = LoadLibrary("dsound.dll");
220 Con_SafePrint("Couldn't load dsound.dll\n");
224 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
226 if (!pDirectSoundCreate)
228 Con_SafePrint("Couldn't get DS proc addr\n");
233 while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
235 if (hresult != DSERR_ALLOCATED)
237 Con_SafePrint("DirectSound create failed\n");
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)
247 Con_SafePrint("DirectSoundCreate failure\n hardware already in use\n");
252 dscaps.dwSize = sizeof(dscaps);
254 if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
256 Con_SafePrint("Couldn't get DS caps\n");
259 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
261 Con_SafePrint("No DirectSound driver installed\n");
266 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
268 Con_SafePrint("Set coop level failed\n");
273 // get access to the primary buffer, if possible, so we can set the
274 // sound hardware format
275 memset (&dsbuf, 0, sizeof(dsbuf));
276 dsbuf.dwSize = sizeof(DSBUFFERDESC);
277 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
278 dsbuf.dwBufferBytes = 0;
279 dsbuf.lpwfxFormat = NULL;
281 memset(&dsbcaps, 0, sizeof(dsbcaps));
282 dsbcaps.dwSize = sizeof(dsbcaps);
283 primary_format_set = false;
285 if (!COM_CheckParm ("-snoforceformat"))
287 if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
291 if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
294 Con_SafePrint("Set primary sound buffer format: no\n");
299 Con_SafePrint("Set primary sound buffer format: yes\n");
301 primary_format_set = true;
306 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
308 // create the secondary buffer we'll actually work with
309 memset (&dsbuf, 0, sizeof(dsbuf));
310 dsbuf.dwSize = sizeof(DSBUFFERDESC);
311 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
312 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
313 dsbuf.lpwfxFormat = &format;
315 memset(&dsbcaps, 0, sizeof(dsbcaps));
316 dsbcaps.dwSize = sizeof(dsbcaps);
318 if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
320 Con_SafePrint("DS:CreateSoundBuffer Failed\n");
325 shm->format.channels = format.nChannels;
326 shm->format.width = format.wBitsPerSample / 8;
327 shm->format.speed = format.nSamplesPerSec;
329 if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
331 Con_SafePrint("DS:GetCaps failed\n");
337 Con_SafePrint("Using secondary sound buffer\n");
341 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
343 Con_SafePrint("Set coop level failed\n");
348 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
350 Con_Print("DS:GetCaps failed\n");
355 Con_SafePrint("Using primary sound buffer\n");
358 // Make sure mixer is active
359 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
362 Con_SafePrintf(" %d channel(s)\n"
365 shm->format.channels, shm->format.width * 8, shm->format.speed);
367 gSndBufSize = dsbcaps.dwBufferBytes;
369 // initialize the buffer
372 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
374 if (hresult != DSERR_BUFFERLOST)
376 Con_SafePrint("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
383 Con_SafePrint("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
390 memset(lpData, 0, dwSize);
392 pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
394 /* we don't want anyone to access the buffer directly w/o locking it first. */
397 pDSBuf->lpVtbl->Stop(pDSBuf);
398 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
399 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
401 shm->samples = gSndBufSize / shm->format.width;
403 shm->buffer = (unsigned char *) lpData;
404 sample16 = shm->format.width - 1;
416 Crappy windows multimedia base
419 qboolean SNDDMA_InitWav (void)
428 memset((void *)shm, 0, sizeof(*shm));
429 shm->format.channels = 2;
430 shm->format.width = 2;
431 i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
432 if (i && i != (com_argc - 1))
433 shm->format.speed = atoi(com_argv[i+1]);
435 shm->format.speed = 44100;
437 memset (&format, 0, sizeof(format));
438 format.wFormatTag = WAVE_FORMAT_PCM;
439 format.nChannels = shm->format.channels;
440 format.wBitsPerSample = shm->format.width * 8;
441 format.nSamplesPerSec = shm->format.speed;
442 format.nBlockAlign = format.nChannels
443 *format.wBitsPerSample / 8;
445 format.nAvgBytesPerSec = format.nSamplesPerSec
448 /* Open a waveform device for output using window callback. */
449 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
451 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
453 if (hr != MMSYSERR_ALLOCATED)
455 Con_SafePrint("waveOutOpen failed\n");
459 if (MessageBox (NULL,
460 "The sound hardware is in use by another app.\n\n"
461 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
462 "Sound not available",
463 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
465 Con_SafePrint("waveOutOpen failure;\n hardware already in use\n");
471 * Allocate and lock memory for the waveform data. The memory
472 * for waveform data must be globally allocated with
473 * 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;
537 sample16 = shm->format.width - 1;
548 Try to find a sound device to mix for.
549 Returns false if nothing is found.
553 qboolean SNDDMA_Init(void)
557 if (COM_CheckParm ("-wavonly"))
560 dsound_init = wav_init = 0;
562 stat = SIS_FAILURE; // assume DirectSound won't initialize
564 /* Init DirectSound */
567 if (snd_firsttime || snd_isdirect)
569 stat = SNDDMA_InitDirect ();;
571 if (stat == SIS_SUCCESS)
576 Con_SafePrint("DirectSound initialized\n");
580 snd_isdirect = false;
581 Con_SafePrint("DirectSound failed to init\n");
586 // if DirectSound didn't succeed in initializing, try to initialize
587 // waveOut sound, unless DirectSound failed because the hardware is
588 // already allocated (in which case the user has already chosen not
590 if (!dsound_init && (stat != SIS_NOTAVAIL))
592 if (snd_firsttime || snd_iswave)
595 snd_iswave = SNDDMA_InitWav ();
600 Con_SafePrint("Wave sound initialized\n");
604 Con_SafePrint("Wave sound failed to init\n");
609 snd_firsttime = false;
611 if (!dsound_init && !wav_init)
621 return the current sample position (in mono samples read)
622 inside the recirculating dma buffer, so the mixing code will know
623 how many sample are required to fill it up.
626 int SNDDMA_GetDMAPos(void)
634 mmtime.wType = TIME_SAMPLES;
635 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
636 s = mmtime.u.sample - mmstarttime.u.sample;
640 s = snd_sent * WAV_BUFFER_SIZE;
648 s &= (shm->samples-1);
657 Send sound to device if buffer isn't really the dma buffer
660 void SNDDMA_Submit(void)
669 // find which sound blocks have completed
673 if ( snd_completed == snd_sent )
675 Con_DPrint("Sound overrun\n");
679 if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
684 snd_completed++; // this buffer has been played
688 // submit two new sound blocks
690 while (((snd_sent - snd_completed) >> sample16) < 4)
692 h = lpWaveHdr + ( snd_sent&WAV_MASK );
696 * Now the data block can be sent to the output device. The
697 * waveOutWrite function returns immediately and waveform
698 * data is sent to the output device in the background.
700 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
702 if (wResult != MMSYSERR_NOERROR)
704 Con_SafePrint("Failed to write block to device\n");
715 Reset the sound device for exiting
718 void SNDDMA_Shutdown(void)
724 DWORD dsound_dwSize2;
727 void *S_LockBuffer(void)
736 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
738 if (hresult != DSERR_BUFFERLOST)
740 Con_Print("S_LockBuffer: DS::Lock Sound Buffer Failed\n");
748 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
760 void S_UnlockBuffer(void)
763 pDSBuf->lpVtbl->Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);