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.
23 #define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c)
25 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
27 // 64K is > 1 second at 16-bit, 22050 Hz
28 #define WAV_BUFFERS 64
30 #define WAV_BUFFER_SIZE 0x0400
31 #define SECONDARY_BUFFER_SIZE 0x10000
33 typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
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;
42 static int snd_sent, snd_completed;
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.
51 HPSTR lpData, lpData2;
65 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
69 qboolean SNDDMA_InitWav (void);
70 sndinitstat SNDDMA_InitDirect (void);
77 void S_BlockSound (void)
80 // DirectSound takes care of blocking itself
87 waveOutReset (hWaveOut);
98 void S_UnblockSound (void)
101 // DirectSound takes care of blocking itself
114 void FreeSound (void)
120 pDSBuf->lpVtbl->Stop(pDSBuf);
121 pDSBuf->lpVtbl->Release(pDSBuf);
124 // only release primary buffer if it's not also the mixing buffer we just released
125 if (pDSPBuf && (pDSBuf != pDSPBuf))
127 pDSPBuf->lpVtbl->Release(pDSPBuf);
132 pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
133 pDS->lpVtbl->Release(pDS);
138 waveOutReset (hWaveOut);
142 for (i=0 ; i< WAV_BUFFERS ; i++)
143 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
146 waveOutClose (hWaveOut);
150 GlobalUnlock(hWaveHdr);
151 GlobalFree(hWaveHdr);
182 sndinitstat SNDDMA_InitDirect (void)
186 DWORD dwSize, dwWrite;
188 WAVEFORMATEX format, pformat;
193 memset ((void *)&sn, 0, sizeof (sn));
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]);
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
211 *format.wBitsPerSample / 8;
213 format.nAvgBytesPerSec = format.nSamplesPerSec
218 hInstDS = LoadLibrary("dsound.dll");
222 Con_SafePrintf ("Couldn't load dsound.dll\n");
226 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
228 if (!pDirectSoundCreate)
230 Con_SafePrintf ("Couldn't get DS proc addr\n");
235 while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
237 if (hresult != DSERR_ALLOCATED)
239 Con_SafePrintf ("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_SafePrintf ("DirectSoundCreate failure\n"
250 " hardware already in use\n");
255 dscaps.dwSize = sizeof(dscaps);
257 if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
259 Con_SafePrintf ("Couldn't get DS caps\n");
262 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
264 Con_SafePrintf ("No DirectSound driver installed\n");
269 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
271 Con_SafePrintf ("Set coop level failed\n");
276 // get access to the primary buffer, if possible, so we can set the
277 // sound hardware format
278 memset (&dsbuf, 0, sizeof(dsbuf));
279 dsbuf.dwSize = sizeof(DSBUFFERDESC);
280 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
281 dsbuf.dwBufferBytes = 0;
282 dsbuf.lpwfxFormat = NULL;
284 memset(&dsbcaps, 0, sizeof(dsbcaps));
285 dsbcaps.dwSize = sizeof(dsbcaps);
286 primary_format_set = false;
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_SafePrintf ("Set primary sound buffer format: no\n");
302 Con_SafePrintf ("Set primary sound buffer format: yes\n");
304 primary_format_set = true;
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_SafePrintf ("DS:CreateSoundBuffer Failed");
328 shm->channels = format.nChannels;
329 shm->samplebits = format.wBitsPerSample;
330 shm->speed = format.nSamplesPerSec;
332 if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
334 Con_SafePrintf ("DS:GetCaps failed\n");
340 Con_SafePrintf ("Using secondary sound buffer\n");
344 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
346 Con_SafePrintf ("Set coop level failed\n");
351 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
353 Con_Printf ("DS:GetCaps failed\n");
358 Con_SafePrintf ("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->channels, shm->samplebits, shm->speed);
370 gSndBufSize = dsbcaps.dwBufferBytes;
372 // initialize the buffer
375 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
377 if (hresult != DSERR_BUFFERLOST)
379 Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
386 Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
393 memset(lpData, 0, dwSize);
394 // lpData[4] = lpData[5] = 0x7f; // force a pop for debugging
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->soundalive = true;
406 shm->splitbuffer = false;
407 shm->samples = gSndBufSize/(shm->samplebits/8);
409 shm->submission_chunk = 1;
410 shm->buffer = (unsigned char *) lpData;
411 sample16 = (shm->samplebits/8) - 1;
423 Crappy windows multimedia base
426 qboolean SNDDMA_InitWav (void)
438 shm->samplebits = 16;
439 i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
440 if (i && i != (com_argc - 1))
441 shm->speed = atoi(com_argv[i+1]);
445 memset (&format, 0, sizeof(format));
446 format.wFormatTag = WAVE_FORMAT_PCM;
447 format.nChannels = shm->channels;
448 format.wBitsPerSample = shm->samplebits;
449 format.nSamplesPerSec = shm->speed;
450 format.nBlockAlign = format.nChannels
451 *format.wBitsPerSample / 8;
453 format.nAvgBytesPerSec = format.nSamplesPerSec
456 /* Open a waveform device for output using window callback. */
457 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
459 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
461 if (hr != MMSYSERR_ALLOCATED)
463 Con_SafePrintf ("waveOutOpen failed\n");
467 if (MessageBox (NULL,
468 "The sound hardware is in use by another app.\n\n"
469 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
470 "Sound not available",
471 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
473 Con_SafePrintf ("waveOutOpen failure;\n"
474 " hardware already in use\n");
480 * Allocate and lock memory for the waveform data. The memory
481 * for waveform data must be globally allocated with
482 * GMEM_MOVEABLE and GMEM_SHARE flags.
485 gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
486 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
489 Con_SafePrintf ("Sound: Out of memory.\n");
493 lpData = GlobalLock(hData);
496 Con_SafePrintf ("Sound: Failed to lock.\n");
500 memset (lpData, 0, gSndBufSize);
503 * Allocate and lock memory for the header. This memory must
504 * also be globally allocated with GMEM_MOVEABLE and
507 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
508 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
510 if (hWaveHdr == NULL)
512 Con_SafePrintf ("Sound: Failed to Alloc header.\n");
517 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
519 if (lpWaveHdr == NULL)
521 Con_SafePrintf ("Sound: Failed to lock header.\n");
526 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
528 /* After allocation, set up and prepare headers. */
529 for (i=0 ; i<WAV_BUFFERS ; i++)
531 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
532 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
534 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
537 Con_SafePrintf ("Sound: failed to prepare wave headers\n");
543 shm->soundalive = true;
544 shm->splitbuffer = false;
545 shm->samples = gSndBufSize/(shm->samplebits/8);
547 shm->submission_chunk = 1;
548 shm->buffer = (unsigned char *) lpData;
549 sample16 = (shm->samplebits/8) - 1;
560 Try to find a sound device to mix for.
561 Returns false if nothing is found.
565 qboolean SNDDMA_Init(void)
569 if (COM_CheckParm ("-wavonly"))
572 dsound_init = wav_init = 0;
574 stat = SIS_FAILURE; // assume DirectSound won't initialize
576 /* Init DirectSound */
579 if (snd_firsttime || snd_isdirect)
581 stat = SNDDMA_InitDirect ();;
583 if (stat == SIS_SUCCESS)
588 Con_SafePrintf ("DirectSound initialized\n");
592 snd_isdirect = false;
593 Con_SafePrintf ("DirectSound failed to init\n");
598 // if DirectSound didn't succeed in initializing, try to initialize
599 // waveOut sound, unless DirectSound failed because the hardware is
600 // already allocated (in which case the user has already chosen not
602 if (!dsound_init && (stat != SIS_NOTAVAIL))
604 if (snd_firsttime || snd_iswave)
607 snd_iswave = SNDDMA_InitWav ();
612 Con_SafePrintf ("Wave sound initialized\n");
616 Con_SafePrintf ("Wave sound failed to init\n");
621 snd_firsttime = false;
623 if (!dsound_init && !wav_init)
625 // if (snd_firsttime)
626 // Con_SafePrintf ("No sound device initialized\n");
638 return the current sample position (in mono samples read)
639 inside the recirculating dma buffer, so the mixing code will know
640 how many sample are required to fill it up.
643 int SNDDMA_GetDMAPos(void)
651 mmtime.wType = TIME_SAMPLES;
652 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
653 s = mmtime.u.sample - mmstarttime.u.sample;
657 s = snd_sent * WAV_BUFFER_SIZE;
663 s &= (shm->samples-1);
672 Send sound to device if buffer isn't really the dma buffer
675 void SNDDMA_Submit(void)
684 // find which sound blocks have completed
688 if ( snd_completed == snd_sent )
690 Con_DPrintf ("Sound overrun\n");
694 if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
699 snd_completed++; // this buffer has been played
703 // submit two new sound blocks
705 while (((snd_sent - snd_completed) >> sample16) < 4)
707 h = lpWaveHdr + ( snd_sent&WAV_MASK );
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.
715 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
717 if (wResult != MMSYSERR_NOERROR)
719 Con_SafePrintf ("Failed to write block to device\n");
730 Reset the sound device for exiting
733 void SNDDMA_Shutdown(void)