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 *)shm, 0, sizeof(*shm));
195 shm->samplebits = 16;
196 i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
197 if (i && i != (com_argc - 1))
198 shm->speed = atoi(com_argv[i+1]);
202 memset (&format, 0, sizeof(format));
203 format.wFormatTag = WAVE_FORMAT_PCM;
204 format.nChannels = shm->channels;
205 format.wBitsPerSample = shm->samplebits;
206 format.nSamplesPerSec = shm->speed;
207 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
209 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
213 hInstDS = LoadLibrary("dsound.dll");
217 Con_SafePrintf ("Couldn't load dsound.dll\n");
221 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
223 if (!pDirectSoundCreate)
225 Con_SafePrintf ("Couldn't get DS proc addr\n");
230 while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
232 if (hresult != DSERR_ALLOCATED)
234 Con_SafePrintf ("DirectSound create failed\n");
238 if (MessageBox (NULL,
239 "The sound hardware is in use by another app.\n\n"
240 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
241 "Sound not available",
242 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
244 Con_SafePrintf ("DirectSoundCreate failure\n"
245 " hardware already in use\n");
250 dscaps.dwSize = sizeof(dscaps);
252 if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
254 Con_SafePrintf ("Couldn't get DS caps\n");
257 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
259 Con_SafePrintf ("No DirectSound driver installed\n");
264 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
266 Con_SafePrintf ("Set coop level failed\n");
271 // get access to the primary buffer, if possible, so we can set the
272 // sound hardware format
273 memset (&dsbuf, 0, sizeof(dsbuf));
274 dsbuf.dwSize = sizeof(DSBUFFERDESC);
275 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
276 dsbuf.dwBufferBytes = 0;
277 dsbuf.lpwfxFormat = NULL;
279 memset(&dsbcaps, 0, sizeof(dsbcaps));
280 dsbcaps.dwSize = sizeof(dsbcaps);
281 primary_format_set = false;
283 if (!COM_CheckParm ("-snoforceformat"))
285 if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
289 if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
292 Con_SafePrintf ("Set primary sound buffer format: no\n");
297 Con_SafePrintf ("Set primary sound buffer format: yes\n");
299 primary_format_set = true;
304 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
306 // create the secondary buffer we'll actually work with
307 memset (&dsbuf, 0, sizeof(dsbuf));
308 dsbuf.dwSize = sizeof(DSBUFFERDESC);
309 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
310 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
311 dsbuf.lpwfxFormat = &format;
313 memset(&dsbcaps, 0, sizeof(dsbcaps));
314 dsbcaps.dwSize = sizeof(dsbcaps);
316 if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
318 Con_SafePrintf ("DS:CreateSoundBuffer Failed");
323 shm->channels = format.nChannels;
324 shm->samplebits = format.wBitsPerSample;
325 shm->speed = format.nSamplesPerSec;
327 if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
329 Con_SafePrintf ("DS:GetCaps failed\n");
335 Con_SafePrintf ("Using secondary sound buffer\n");
339 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
341 Con_SafePrintf ("Set coop level failed\n");
346 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
348 Con_Printf ("DS:GetCaps failed\n");
353 Con_SafePrintf ("Using primary sound buffer\n");
356 // Make sure mixer is active
357 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
360 Con_SafePrintf(" %d channel(s)\n"
363 shm->channels, shm->samplebits, shm->speed);
365 gSndBufSize = dsbcaps.dwBufferBytes;
367 // initialize the buffer
370 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
372 if (hresult != DSERR_BUFFERLOST)
374 Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
381 Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
388 memset(lpData, 0, dwSize);
390 pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
392 /* we don't want anyone to access the buffer directly w/o locking it first. */
395 pDSBuf->lpVtbl->Stop(pDSBuf);
396 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
397 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
399 shm->samples = gSndBufSize/(shm->samplebits/8);
401 shm->buffer = (unsigned char *) lpData;
402 sample16 = (shm->samplebits/8) - 1;
414 Crappy windows multimedia base
417 qboolean SNDDMA_InitWav (void)
426 memset((void *)shm, 0, sizeof(*shm));
428 shm->samplebits = 16;
429 i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
430 if (i && i != (com_argc - 1))
431 shm->speed = atoi(com_argv[i+1]);
435 memset (&format, 0, sizeof(format));
436 format.wFormatTag = WAVE_FORMAT_PCM;
437 format.nChannels = shm->channels;
438 format.wBitsPerSample = shm->samplebits;
439 format.nSamplesPerSec = shm->speed;
440 format.nBlockAlign = format.nChannels
441 *format.wBitsPerSample / 8;
443 format.nAvgBytesPerSec = format.nSamplesPerSec
446 /* Open a waveform device for output using window callback. */
447 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
449 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
451 if (hr != MMSYSERR_ALLOCATED)
453 Con_SafePrintf ("waveOutOpen failed\n");
457 if (MessageBox (NULL,
458 "The sound hardware is in use by another app.\n\n"
459 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
460 "Sound not available",
461 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
463 Con_SafePrintf ("waveOutOpen failure;\n"
464 " hardware already in use\n");
470 * Allocate and lock memory for the waveform data. The memory
471 * for waveform data must be globally allocated with
472 * GMEM_MOVEABLE and GMEM_SHARE flags.
475 gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
476 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
479 Con_SafePrintf ("Sound: Out of memory.\n");
483 lpData = GlobalLock(hData);
486 Con_SafePrintf ("Sound: Failed to lock.\n");
490 memset (lpData, 0, gSndBufSize);
493 * Allocate and lock memory for the header. This memory must
494 * also be globally allocated with GMEM_MOVEABLE and
497 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
498 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
500 if (hWaveHdr == NULL)
502 Con_SafePrintf ("Sound: Failed to Alloc header.\n");
507 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
509 if (lpWaveHdr == NULL)
511 Con_SafePrintf ("Sound: Failed to lock header.\n");
516 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
518 /* After allocation, set up and prepare headers. */
519 for (i=0 ; i<WAV_BUFFERS ; i++)
521 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
522 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
524 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
527 Con_SafePrintf ("Sound: failed to prepare wave headers\n");
533 shm->samples = gSndBufSize/(shm->samplebits/8);
535 shm->buffer = (unsigned char *) lpData;
536 sample16 = (shm->samplebits/8) - 1;
547 Try to find a sound device to mix for.
548 Returns false if nothing is found.
552 qboolean SNDDMA_Init(void)
556 if (COM_CheckParm ("-wavonly"))
559 dsound_init = wav_init = 0;
561 stat = SIS_FAILURE; // assume DirectSound won't initialize
563 /* Init DirectSound */
566 if (snd_firsttime || snd_isdirect)
568 stat = SNDDMA_InitDirect ();;
570 if (stat == SIS_SUCCESS)
575 Con_SafePrintf ("DirectSound initialized\n");
579 snd_isdirect = false;
580 Con_SafePrintf ("DirectSound failed to init\n");
585 // if DirectSound didn't succeed in initializing, try to initialize
586 // waveOut sound, unless DirectSound failed because the hardware is
587 // already allocated (in which case the user has already chosen not
589 if (!dsound_init && (stat != SIS_NOTAVAIL))
591 if (snd_firsttime || snd_iswave)
594 snd_iswave = SNDDMA_InitWav ();
599 Con_SafePrintf ("Wave sound initialized\n");
603 Con_SafePrintf ("Wave sound failed to init\n");
608 snd_firsttime = false;
610 if (!dsound_init && !wav_init)
620 return the current sample position (in mono samples read)
621 inside the recirculating dma buffer, so the mixing code will know
622 how many sample are required to fill it up.
625 int SNDDMA_GetDMAPos(void)
633 mmtime.wType = TIME_SAMPLES;
634 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
635 s = mmtime.u.sample - mmstarttime.u.sample;
639 s = snd_sent * WAV_BUFFER_SIZE;
647 s &= (shm->samples-1);
656 Send sound to device if buffer isn't really the dma buffer
659 void SNDDMA_Submit(void)
668 // find which sound blocks have completed
672 if ( snd_completed == snd_sent )
674 Con_DPrintf ("Sound overrun\n");
678 if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
683 snd_completed++; // this buffer has been played
687 // submit two new sound blocks
689 while (((snd_sent - snd_completed) >> sample16) < 4)
691 h = lpWaveHdr + ( snd_sent&WAV_MASK );
695 * Now the data block can be sent to the output device. The
696 * waveOutWrite function returns immediately and waveform
697 * data is sent to the output device in the background.
699 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
701 if (wResult != MMSYSERR_NOERROR)
703 Con_SafePrintf ("Failed to write block to device\n");
714 Reset the sound device for exiting
717 void SNDDMA_Shutdown(void)
723 DWORD dsound_dwSize2;
726 void *S_LockBuffer(void)
735 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
737 if (hresult != DSERR_BUFFERLOST)
739 Con_Printf ("S_LockBuffer: DS::Lock Sound Buffer Failed\n");
747 Con_Printf ("S_LockBuffer: DS: couldn't restore buffer\n");
759 void S_UnlockBuffer(void)
762 pDSBuf->lpVtbl->Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);