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.
26 extern HWND mainwindow;
28 #define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c)
30 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
32 // Wave output: 64KB in 64 buffers of 1KB
33 // (64KB is > 1 sec at 16-bit 22050 Hz mono, and is 1/3 sec at 16-bit 44100 Hz stereo)
34 #define WAV_BUFFERS 64
35 #define WAV_MASK (WAV_BUFFERS - 1)
36 #define WAV_BUFFER_SIZE 1024
38 // DirectSound output: 64KB in 1 buffer
39 #define SECONDARY_BUFFER_SIZE (64 * 1024)
41 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
43 static qboolean wavonly;
44 static qboolean dsound_init;
45 static qboolean wav_init;
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_Print("Couldn't load dsound.dll\n");
225 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
227 if (!pDirectSoundCreate)
229 Con_Print("Couldn't get DS proc addr\n");
234 while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
236 if (hresult != DSERR_ALLOCATED)
238 Con_Print("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_Print("DirectSoundCreate failure\n hardware already in use\n");
253 dscaps.dwSize = sizeof(dscaps);
255 if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
257 Con_Print("Couldn't get DS caps\n");
260 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
262 Con_Print("No DirectSound driver installed\n");
267 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
269 Con_Print("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))
295 Con_Print("Set primary sound buffer format: no\n");
299 Con_Print("Set primary sound buffer format: yes\n");
301 primary_format_set = true;
306 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
307 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
309 // create the secondary buffer we'll actually work with
310 memset (&dsbuf, 0, sizeof(dsbuf));
311 dsbuf.dwSize = sizeof(DSBUFFERDESC);
312 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
313 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
314 dsbuf.lpwfxFormat = &format;
316 memset(&dsbcaps, 0, sizeof(dsbcaps));
317 dsbcaps.dwSize = sizeof(dsbcaps);
319 if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
321 Con_Print("DS:CreateSoundBuffer Failed\n");
326 shm->format.channels = format.nChannels;
327 shm->format.width = format.wBitsPerSample / 8;
328 shm->format.speed = format.nSamplesPerSec;
330 if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
332 Con_Print("DS:GetCaps failed\n");
337 Con_Print("Using secondary sound buffer\n");
341 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
343 Con_Print("Set coop level failed\n");
348 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
350 Con_Print("DS:GetCaps failed\n");
355 Con_Print("Using primary sound buffer\n");
358 // Make sure mixer is active
359 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
361 Con_Printf(" %d channel(s)\n"
364 shm->format.channels, shm->format.width * 8, shm->format.speed);
366 gSndBufSize = dsbcaps.dwBufferBytes;
368 // initialize the buffer
371 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
373 if (hresult != DSERR_BUFFERLOST)
375 Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
382 Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
389 memset(lpData, 0, dwSize);
391 pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
393 // we don't want anyone to access the buffer directly w/o locking it first.
396 pDSBuf->lpVtbl->Stop(pDSBuf);
397 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwStartTime, NULL);
398 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
400 shm->samples = gSndBufSize / shm->format.width;
401 shm->sampleframes = shm->samples / shm->format.channels;
403 shm->buffer = (unsigned char *) lpData;
415 Crappy windows multimedia base
418 qboolean SNDDMA_InitWav (void)
427 memset((void *)shm, 0, sizeof(*shm));
428 shm->format.channels = 2;
429 shm->format.width = 2;
430 // COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
431 i = COM_CheckParm ("-sndspeed");
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 * format.wBitsPerSample / 8;
444 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
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_Print("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_Print("waveOutOpen failure;\n hardware already in use\n");
469 * Allocate and lock memory for the waveform data. The memory
470 * for waveform data must be globally allocated with
471 * GMEM_MOVEABLE and GMEM_SHARE flags.
473 gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
474 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
477 Con_Print("Sound: Out of memory.\n");
481 lpData = GlobalLock(hData);
484 Con_Print("Sound: Failed to lock.\n");
488 memset (lpData, 0, gSndBufSize);
491 * Allocate and lock memory for the header. This memory must
492 * also be globally allocated with GMEM_MOVEABLE and
495 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
496 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
498 if (hWaveHdr == NULL)
500 Con_Print("Sound: Failed to Alloc header.\n");
505 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
507 if (lpWaveHdr == NULL)
509 Con_Print("Sound: Failed to lock header.\n");
514 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
516 // After allocation, set up and prepare headers
517 for (i=0 ; i<WAV_BUFFERS ; i++)
519 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
520 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
522 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
525 Con_Print("Sound: failed to prepare wave headers\n");
531 shm->samples = gSndBufSize / shm->format.width;
532 shm->sampleframes = shm->samples / shm->format.channels;
534 shm->buffer = (unsigned char *) lpData;
548 Try to find a sound device to mix for.
549 Returns false if nothing is found.
552 qboolean SNDDMA_Init(void)
556 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
557 if (COM_CheckParm ("-wavonly"))
563 stat = SIS_FAILURE; // assume DirectSound won't initialize
568 stat = SNDDMA_InitDirect ();
570 if (stat == SIS_SUCCESS)
571 Con_Print("DirectSound initialized\n");
573 Con_Print("DirectSound failed to init\n");
576 // if DirectSound didn't succeed in initializing, try to initialize
577 // waveOut sound, unless DirectSound failed because the hardware is
578 // already allocated (in which case the user has already chosen not
580 if (!dsound_init && (stat != SIS_NOTAVAIL))
582 if (SNDDMA_InitWav ())
583 Con_Print("Wave sound initialized\n");
585 Con_Print("Wave sound failed to init\n");
588 if (!dsound_init && !wav_init)
598 return the current sample position (in mono samples read)
599 inside the recirculating dma buffer, so the mixing code will know
600 how many sample are required to fill it up.
603 int SNDDMA_GetDMAPos(void)
609 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwTime, NULL);
610 s = dwTime - dwStartTime;
614 // Find which sound blocks have completed
617 if (snd_completed == snd_sent)
619 Con_DPrint("Sound overrun\n");
623 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
626 snd_completed++; // this buffer has been played
629 s = snd_completed * WAV_BUFFER_SIZE;
634 return (s >> (shm->format.width - 1)) & (shm->samples - 1);
641 Send sound to device if buffer isn't really the dma buffer
644 void SNDDMA_Submit(void)
649 // DirectSound doesn't need this
653 paintpot += (paintedtime - prev_painted) * shm->format.channels * shm->format.width;
654 if (paintpot > WAV_BUFFERS * WAV_BUFFER_SIZE)
655 paintpot = WAV_BUFFERS * WAV_BUFFER_SIZE;
656 prev_painted = paintedtime;
658 // submit new sound blocks
659 while (paintpot > WAV_BUFFER_SIZE)
661 h = lpWaveHdr + (snd_sent & WAV_MASK);
665 * Now the data block can be sent to the output device. The
666 * waveOutWrite function returns immediately and waveform
667 * data is sent to the output device in the background.
669 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
671 if (wResult != MMSYSERR_NOERROR)
673 Con_Print("Failed to write block to device\n");
678 paintpot -= WAV_BUFFER_SIZE;
686 Reset the sound device for exiting
689 void SNDDMA_Shutdown(void)
695 static DWORD dsound_dwSize;
696 static DWORD dsound_dwSize2;
697 static DWORD *dsound_pbuf;
698 static DWORD *dsound_pbuf2;
700 void *S_LockBuffer(void)
708 // if the buffer was lost or stopped, restore it and/or restart it
709 if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK)
710 Con_Print("Couldn't get sound buffer status\n");
712 if (dwStatus & DSBSTATUS_BUFFERLOST)
713 pDSBuf->lpVtbl->Restore (pDSBuf);
715 if (!(dwStatus & DSBSTATUS_PLAYING))
716 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
720 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
722 if (hresult != DSERR_BUFFERLOST)
724 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
732 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
746 void S_UnlockBuffer(void)
749 pDSBuf->lpVtbl->Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);