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 // Wave output: 64KB in 64 buffers of 1KB
32 // (64KB is > 1 sec at 16-bit 22050 Hz mono, and is 1/3 sec at 16-bit 44100 Hz stereo)
33 #define WAV_BUFFERS 64
34 #define WAV_MASK (WAV_BUFFERS - 1)
35 #define WAV_BUFFER_SIZE 1024
37 // DirectSound output: 64KB in 1 buffer
38 #define SECONDARY_BUFFER_SIZE (64 * 1024)
40 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
42 static qboolean wavonly;
43 static qboolean dsound_init;
44 static qboolean wav_init;
45 static qboolean primary_format_set;
47 static int snd_sent, snd_completed;
49 static int prev_painted;
50 static unsigned int paintpot;
54 * Global variables. Must be visible to window-procedure function
55 * so it can unlock and free the data block after it has been played.
59 HPSTR lpData, lpData2;
73 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
77 qboolean SNDDMA_InitWav (void);
78 sndinitstat SNDDMA_InitDirect (void);
85 void S_BlockSound (void)
87 // DirectSound takes care of blocking itself
93 waveOutReset (hWaveOut);
103 void S_UnblockSound (void)
105 // DirectSound takes care of blocking itself
116 void FreeSound (void)
122 pDSBuf->lpVtbl->Stop(pDSBuf);
123 pDSBuf->lpVtbl->Release(pDSBuf);
126 // only release primary buffer if it's not also the mixing buffer we just released
127 if (pDSPBuf && (pDSBuf != pDSPBuf))
129 pDSPBuf->lpVtbl->Release(pDSPBuf);
134 pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
135 pDS->lpVtbl->Release(pDS);
140 waveOutReset (hWaveOut);
144 for (i=0 ; i< WAV_BUFFERS ; i++)
145 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
148 waveOutClose (hWaveOut);
152 GlobalUnlock(hWaveHdr);
153 GlobalFree(hWaveHdr);
184 sndinitstat SNDDMA_InitDirect (void)
190 WAVEFORMATEX format, pformat;
195 memset((void *)shm, 0, sizeof(*shm));
196 shm->format.channels = 2;
197 shm->format.width = 2;
198 // COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
199 i = COM_CheckParm ("-sndspeed");
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_Print("Couldn't load dsound.dll\n");
224 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
226 if (!pDirectSoundCreate)
228 Con_Print("Couldn't get DS proc addr\n");
233 while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
235 if (hresult != DSERR_ALLOCATED)
237 Con_Print("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_Print("DirectSoundCreate failure\n hardware already in use\n");
252 dscaps.dwSize = sizeof(dscaps);
254 if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
256 Con_Print("Couldn't get DS caps\n");
259 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
261 Con_Print("No DirectSound driver installed\n");
266 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
268 Con_Print("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 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
286 if (!COM_CheckParm ("-snoforceformat"))
288 if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
292 if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
294 Con_Print("Set primary sound buffer format: no\n");
298 Con_Print("Set primary sound buffer format: yes\n");
300 primary_format_set = true;
305 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
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_Print("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_Print("DS:GetCaps failed\n");
336 Con_Print("Using secondary sound buffer\n");
340 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
342 Con_Print("Set coop level failed\n");
347 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
349 Con_Print("DS:GetCaps failed\n");
354 Con_Print("Using primary sound buffer\n");
357 // Make sure mixer is active
358 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
360 Con_Printf(" %d channel(s)\n"
363 shm->format.channels, shm->format.width * 8, shm->format.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_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
381 Con_Print("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, &dwStartTime, NULL);
397 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
399 shm->samples = gSndBufSize / shm->format.width;
400 shm->sampleframes = shm->samples / shm->format.channels;
402 shm->buffer = (unsigned char *) lpData;
414 Crappy windows multimedia base
417 qboolean SNDDMA_InitWav (void)
426 memset((void *)shm, 0, sizeof(*shm));
427 shm->format.channels = 2;
428 shm->format.width = 2;
429 // COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
430 i = COM_CheckParm ("-sndspeed");
431 if (i && i != (com_argc - 1))
432 shm->format.speed = atoi(com_argv[i+1]);
434 shm->format.speed = 44100;
436 memset (&format, 0, sizeof(format));
437 format.wFormatTag = WAVE_FORMAT_PCM;
438 format.nChannels = shm->format.channels;
439 format.wBitsPerSample = shm->format.width * 8;
440 format.nSamplesPerSec = shm->format.speed;
441 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
443 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
445 // Open a waveform device for output using window callback
446 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
448 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
450 if (hr != MMSYSERR_ALLOCATED)
452 Con_Print("waveOutOpen failed\n");
456 if (MessageBox (NULL,
457 "The sound hardware is in use by another app.\n\n"
458 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
459 "Sound not available",
460 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
462 Con_Print("waveOutOpen failure;\n hardware already in use\n");
468 * Allocate and lock memory for the waveform data. The memory
469 * for waveform data must be globally allocated with
470 * GMEM_MOVEABLE and GMEM_SHARE flags.
472 gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
473 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
476 Con_Print("Sound: Out of memory.\n");
480 lpData = GlobalLock(hData);
483 Con_Print("Sound: Failed to lock.\n");
487 memset (lpData, 0, gSndBufSize);
490 * Allocate and lock memory for the header. This memory must
491 * also be globally allocated with GMEM_MOVEABLE and
494 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
495 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
497 if (hWaveHdr == NULL)
499 Con_Print("Sound: Failed to Alloc header.\n");
504 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
506 if (lpWaveHdr == NULL)
508 Con_Print("Sound: Failed to lock header.\n");
513 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
515 // After allocation, set up and prepare headers
516 for (i=0 ; i<WAV_BUFFERS ; i++)
518 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
519 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
521 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
524 Con_Print("Sound: failed to prepare wave headers\n");
530 shm->samples = gSndBufSize / shm->format.width;
531 shm->sampleframes = shm->samples / shm->format.channels;
533 shm->buffer = (unsigned char *) lpData;
547 Try to find a sound device to mix for.
548 Returns false if nothing is found.
551 qboolean SNDDMA_Init(void)
555 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
556 if (COM_CheckParm ("-wavonly"))
562 stat = SIS_FAILURE; // assume DirectSound won't initialize
567 stat = SNDDMA_InitDirect ();
569 if (stat == SIS_SUCCESS)
570 Con_Print("DirectSound initialized\n");
572 Con_Print("DirectSound failed to init\n");
575 // if DirectSound didn't succeed in initializing, try to initialize
576 // waveOut sound, unless DirectSound failed because the hardware is
577 // already allocated (in which case the user has already chosen not
579 if (!dsound_init && (stat != SIS_NOTAVAIL))
581 if (SNDDMA_InitWav ())
582 Con_Print("Wave sound initialized\n");
584 Con_Print("Wave sound failed to init\n");
587 if (!dsound_init && !wav_init)
597 return the current sample position (in mono samples read)
598 inside the recirculating dma buffer, so the mixing code will know
599 how many sample are required to fill it up.
602 int SNDDMA_GetDMAPos(void)
608 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwTime, NULL);
609 s = dwTime - dwStartTime;
613 // Find which sound blocks have completed
616 if (snd_completed == snd_sent)
618 Con_DPrint("Sound overrun\n");
622 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
625 snd_completed++; // this buffer has been played
628 s = snd_completed * WAV_BUFFER_SIZE;
633 return (s >> (shm->format.width - 1)) & (shm->samples - 1);
640 Send sound to device if buffer isn't really the dma buffer
643 void SNDDMA_Submit(void)
648 // DirectSound doesn't need this
652 paintpot += (paintedtime - prev_painted) * shm->format.channels * shm->format.width;
653 if (paintpot > WAV_BUFFERS * WAV_BUFFER_SIZE)
654 paintpot = WAV_BUFFERS * WAV_BUFFER_SIZE;
655 prev_painted = paintedtime;
657 // submit new sound blocks
658 while (paintpot > WAV_BUFFER_SIZE)
660 h = lpWaveHdr + (snd_sent & WAV_MASK);
664 * Now the data block can be sent to the output device. The
665 * waveOutWrite function returns immediately and waveform
666 * data is sent to the output device in the background.
668 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
670 if (wResult != MMSYSERR_NOERROR)
672 Con_Print("Failed to write block to device\n");
677 paintpot -= WAV_BUFFER_SIZE;
685 Reset the sound device for exiting
688 void SNDDMA_Shutdown(void)
694 static DWORD dsound_dwSize;
695 static DWORD dsound_dwSize2;
696 static DWORD *dsound_pbuf;
697 static DWORD *dsound_pbuf2;
699 void *S_LockBuffer(void)
707 // if the buffer was lost or stopped, restore it and/or restart it
708 if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK)
709 Con_Print("Couldn't get sound buffer status\n");
711 if (dwStatus & DSBSTATUS_BUFFERLOST)
712 pDSBuf->lpVtbl->Restore (pDSBuf);
714 if (!(dwStatus & DSBSTATUS_PLAYING))
715 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
719 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
721 if (hresult != DSERR_BUFFERLOST)
723 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
731 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
745 void S_UnlockBuffer(void)
748 pDSBuf->lpVtbl->Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);