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 #ifndef DIRECTSOUND_VERSION
25 # define DIRECTSOUND_VERSION 0x0500 /* Version 5.0 */
34 // ==============================================================================
36 #ifndef _WAVEFORMATEXTENSIBLE_
37 #define _WAVEFORMATEXTENSIBLE_
43 WORD wValidBitsPerSample; // bits of precision
44 WORD wSamplesPerBlock; // valid if wBitsPerSample==0
45 WORD wReserved; // If neither applies, set to zero
47 DWORD dwChannelMask; // which channels are present in stream
49 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
52 #if !defined(WAVE_FORMAT_EXTENSIBLE)
53 # define WAVE_FORMAT_EXTENSIBLE 0xFFFE
56 // Some speaker positions
57 #ifndef SPEAKER_FRONT_LEFT
58 # define SPEAKER_FRONT_LEFT 0x1
59 # define SPEAKER_FRONT_RIGHT 0x2
60 # define SPEAKER_FRONT_CENTER 0x4
61 # define SPEAKER_LOW_FREQUENCY 0x8
62 # define SPEAKER_BACK_LEFT 0x10
63 # define SPEAKER_BACK_RIGHT 0x20
64 # define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
65 # define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
66 // ... we never use the other values
69 // KSDATAFORMAT_SUBTYPE_PCM = GUID "00000001-0000-0010-8000-00aa00389b71"
70 static const GUID MY_KSDATAFORMAT_SUBTYPE_PCM =
77 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
82 // ==============================================================================
84 extern HWND mainwindow;
85 static cvar_t snd_wav_partitionsize = {CVAR_SAVE, "snd_wav_partitionsize", "1024", "controls sound delay in samples, values too low will cause crackling, too high will cause delayed sounds"};
86 static qboolean sndsys_registeredcvars = false;
89 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
92 // Wave output: queue of this many sound buffers to play, reused cyclically
93 #define WAV_BUFFERS 16
94 #define WAV_MASK (WAV_BUFFERS - 1)
95 static unsigned int wav_buffer_size;
97 // DirectSound output: 64KB in 1 buffer
98 //#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->width * (fmt_ptr)->channels * (fmt_ptr)->speed / 2)
99 // LordHavoc: changed this to be a multiple of 32768
100 #define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->channels * 32768)
102 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
104 #ifdef SUPPORTDIRECTX
105 static qboolean dsound_init;
106 static unsigned int dsound_time;
107 static qboolean primary_format_set;
110 static qboolean wav_init;
112 static int snd_sent, snd_completed;
114 static int prev_painted;
115 static unsigned int paintpot;
120 * Global variables. Must be visible to window-procedure function
121 * so it can unlock and free the data block after it has been played.
125 HPSTR lpData, lpData2;
132 WAVEOUTCAPS wavecaps;
138 #ifdef SUPPORTDIRECTX
140 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
145 qboolean SNDDMA_InitWav (void);
146 #ifdef SUPPORTDIRECTX
147 sndinitstat SNDDMA_InitDirect (void);
153 SndSys_BuildWaveFormat
156 static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr)
158 WAVEFORMATEX* pfmtex;
160 memset (fmt_ptr, 0, sizeof(*fmt_ptr));
162 pfmtex = &fmt_ptr->Format;
163 pfmtex->nChannels = requested->channels;
164 pfmtex->wBitsPerSample = requested->width * 8;
165 pfmtex->nSamplesPerSec = requested->speed;
166 pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8;
167 pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign;
169 // LordHavoc: disabled this WAVE_FORMAT_EXTENSIBLE support because it does not seem to be working
171 if (requested->channels <= 2)
174 pfmtex->wFormatTag = WAVE_FORMAT_PCM;
180 pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
181 pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
182 fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
183 fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
185 // Build the channel mask
186 fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
187 switch (requested->channels)
190 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
193 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
196 fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
200 Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
210 #ifdef SUPPORTDIRECTX
213 SndSys_InitDirectSound
215 DirectSound 5 support
218 static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
224 WAVEFORMATEXTENSIBLE format, pformat;
228 if (! SndSys_BuildWaveFormat(requested, &format))
233 hInstDS = LoadLibrary("dsound.dll");
237 Con_Print("Couldn't load dsound.dll\n");
241 pDirectSoundCreate = (HRESULT (__stdcall *)(GUID *, LPDIRECTSOUND *,IUnknown *))GetProcAddress(hInstDS,"DirectSoundCreate");
243 if (!pDirectSoundCreate)
245 Con_Print("Couldn't get DS proc addr\n");
250 while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
252 if (hresult != DSERR_ALLOCATED)
254 Con_Print("DirectSound create failed\n");
258 if (MessageBox (NULL,
259 "The sound hardware is in use by another app.\n\n"
260 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
261 "Sound not available",
262 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
264 Con_Print("DirectSoundCreate failure\n hardware already in use\n");
269 dscaps.dwSize = sizeof(dscaps);
271 if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
273 Con_Print("Couldn't get DS caps\n");
276 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
278 Con_Print("No DirectSound driver installed\n");
283 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
285 Con_Print("Set coop level failed\n");
290 // get access to the primary buffer, if possible, so we can set the
291 // sound hardware format
292 memset (&dsbuf, 0, sizeof(dsbuf));
293 dsbuf.dwSize = sizeof(DSBUFFERDESC);
294 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
295 dsbuf.dwBufferBytes = 0;
296 dsbuf.lpwfxFormat = NULL;
298 memset(&dsbcaps, 0, sizeof(dsbcaps));
299 dsbcaps.dwSize = sizeof(dsbcaps);
300 primary_format_set = false;
302 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
303 if (!COM_CheckParm ("-snoforceformat"))
305 if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
309 if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
311 Con_Print("Set primary sound buffer format: no\n");
315 Con_Print("Set primary sound buffer format: yes\n");
317 primary_format_set = true;
322 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
323 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
327 // create the secondary buffer we'll actually work with
328 memset (&dsbuf, 0, sizeof(dsbuf));
329 dsbuf.dwSize = sizeof(DSBUFFERDESC);
330 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
331 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
332 dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
334 memset(&dsbcaps, 0, sizeof(dsbcaps));
335 dsbcaps.dwSize = sizeof(dsbcaps);
337 result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
338 if (result != DS_OK ||
339 requested->channels != format.Format.nChannels ||
340 requested->width != format.Format.wBitsPerSample / 8 ||
341 requested->speed != format.Format.nSamplesPerSec)
343 Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
344 (int)result, (unsigned)format.Format.nChannels, (unsigned)format.Format.wBitsPerSample / 8, (unsigned)format.Format.nSamplesPerSec);
349 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
351 Con_Print("DS:GetCaps failed\n");
356 Con_Print("Using secondary sound buffer\n");
360 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
362 Con_Print("Set coop level failed\n");
367 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
369 Con_Print("DS:GetCaps failed\n");
374 Con_Print("Using primary sound buffer\n");
377 // Make sure mixer is active
378 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
380 Con_Printf(" %d channel(s)\n"
383 requested->channels, requested->width * 8, requested->speed);
385 gSndBufSize = dsbcaps.dwBufferBytes;
387 // initialize the buffer
390 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
392 if (hresult != DSERR_BUFFERLOST)
394 Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
401 Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
408 memset(lpData, 0, dwSize);
409 IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
411 IDirectSoundBuffer_Stop(pDSBuf);
412 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
416 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
429 Crappy windows multimedia base
432 static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
434 WAVEFORMATEXTENSIBLE format;
438 if (! SndSys_BuildWaveFormat(requested, &format))
441 // Open a waveform device for output using window callback
442 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
443 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
445 if (hr != MMSYSERR_ALLOCATED)
447 Con_Print("waveOutOpen failed\n");
451 if (MessageBox (NULL,
452 "The sound hardware is in use by another app.\n\n"
453 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
454 "Sound not available",
455 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
457 Con_Print("waveOutOpen failure;\n hardware already in use\n");
462 wav_buffer_size = bound(128, snd_wav_partitionsize.integer, 8192) * requested->channels * requested->width;
465 * Allocate and lock memory for the waveform data. The memory
466 * for waveform data must be globally allocated with
467 * GMEM_MOVEABLE and GMEM_SHARE flags.
469 gSndBufSize = WAV_BUFFERS * wav_buffer_size;
470 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
473 Con_Print("Sound: Out of memory.\n");
477 lpData = (HPSTR)GlobalLock(hData);
480 Con_Print("Sound: Failed to lock.\n");
484 memset (lpData, 0, gSndBufSize);
487 * Allocate and lock memory for the header. This memory must
488 * also be globally allocated with GMEM_MOVEABLE and
491 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
493 if (hWaveHdr == NULL)
495 Con_Print("Sound: Failed to Alloc header.\n");
500 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
502 if (lpWaveHdr == NULL)
504 Con_Print("Sound: Failed to lock header.\n");
509 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
511 // After allocation, set up and prepare headers
512 for (i=0 ; i<WAV_BUFFERS ; i++)
514 lpWaveHdr[i].dwBufferLength = wav_buffer_size;
515 lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
517 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
519 Con_Print("Sound: failed to prepare wave headers\n");
525 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
543 Create "snd_renderbuffer" with the proper sound format if the call is successful
544 May return a suggested format if the requested format isn't available
547 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
549 #ifdef SUPPORTDIRECTX
554 if (!sndsys_registeredcvars)
556 sndsys_registeredcvars = true;
557 Cvar_RegisterVariable(&snd_wav_partitionsize);
560 Con_Print ("SndSys_Init: using the Win32 module\n");
562 #ifdef SUPPORTDIRECTX
563 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
564 wavonly = (COM_CheckParm ("-wavonly") != 0);
569 stat = SIS_FAILURE; // assume DirectSound won't initialize
571 #ifdef SUPPORTDIRECTX
575 stat = SndSys_InitDirectSound (requested);
577 if (stat == SIS_SUCCESS)
578 Con_Print("DirectSound initialized\n");
580 Con_Print("DirectSound failed to init\n");
584 // if DirectSound didn't succeed in initializing, try to initialize
585 // waveOut sound, unless DirectSound failed because the hardware is
586 // already allocated (in which case the user has already chosen not
588 #ifdef SUPPORTDIRECTX
589 if (!dsound_init && (stat != SIS_NOTAVAIL))
592 if (SndSys_InitMmsystem (requested))
593 Con_Print("Wave sound (MMSYSTEM) initialized\n");
595 Con_Print("Wave sound failed to init\n");
598 #ifdef SUPPORTDIRECTX
599 return (dsound_init || wav_init);
610 Stop the sound card, delete "snd_renderbuffer" and free its other resources
613 void SndSys_Shutdown (void)
615 #ifdef SUPPORTDIRECTX
618 IDirectSoundBuffer_Stop(pDSBuf);
619 IDirectSoundBuffer_Release(pDSBuf);
622 // only release primary buffer if it's not also the mixing buffer we just released
623 if (pDSPBuf && (pDSBuf != pDSPBuf))
625 IDirectSoundBuffer_Release(pDSPBuf);
630 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
631 IDirectSound_Release(pDS);
637 waveOutReset (hWaveOut);
643 for (i=0 ; i< WAV_BUFFERS ; i++)
644 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
647 waveOutClose (hWaveOut);
651 GlobalUnlock(hWaveHdr);
652 GlobalFree(hWaveHdr);
662 if (snd_renderbuffer != NULL)
664 Mem_Free(snd_renderbuffer);
665 snd_renderbuffer = NULL;
668 #ifdef SUPPORTDIRECTX
687 Submit the contents of "snd_renderbuffer" to the sound card
690 void SndSys_Submit (void)
695 // DirectSound doesn't need this
699 paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
700 if (paintpot > WAV_BUFFERS * wav_buffer_size)
701 paintpot = WAV_BUFFERS * wav_buffer_size;
702 prev_painted = snd_renderbuffer->endframe;
704 // submit new sound blocks
705 while (paintpot > wav_buffer_size)
707 h = lpWaveHdr + (snd_sent & WAV_MASK);
710 * Now the data block can be sent to the output device. The
711 * waveOutWrite function returns immediately and waveform
712 * data is sent to the output device in the background.
714 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
715 if (wResult == MMSYSERR_NOERROR)
717 else if (wResult == WAVERR_STILLPLAYING)
719 if(developer_insane.integer)
720 Con_DPrint("waveOutWrite failed (too much sound data)\n");
721 //h->dwFlags |= WHDR_DONE;
726 Con_Printf("waveOutWrite failed, error code %d\n", (int) wResult);
731 paintpot -= wav_buffer_size;
741 Returns the number of sample frames consumed since the sound started
744 unsigned int SndSys_GetSoundTime (void)
748 factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
750 #ifdef SUPPORTDIRECTX
756 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
757 diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize;
758 dwStartTime = dwTime;
760 dsound_time += diff / factor;
767 // Find which sound blocks have completed
770 if (snd_completed == snd_sent)
772 Con_DPrint("Sound overrun\n");
776 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
779 snd_completed++; // this buffer has been played
782 return (snd_completed * wav_buffer_size) / factor;
785 * S_PaintAndSubmit: WARNING: newsoundtime (soundtime (275 < 134217707)
786 * apparently this sound time wraps quite early?
791 mmtime.wType = TIME_SAMPLES;
792 res = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime));
793 if(res == MMSYSERR_NOERROR)
794 return mmtime.u.sample;
803 #ifdef SUPPORTDIRECTX
804 static DWORD dsound_dwSize;
805 static DWORD dsound_dwSize2;
806 static DWORD *dsound_pbuf;
807 static DWORD *dsound_pbuf2;
812 SndSys_LockRenderBuffer
814 Get the exclusive lock on "snd_renderbuffer"
817 qboolean SndSys_LockRenderBuffer (void)
819 #ifdef SUPPORTDIRECTX
826 // if the buffer was lost or stopped, restore it and/or restart it
827 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
828 Con_Print("Couldn't get sound buffer status\n");
830 if (dwStatus & DSBSTATUS_BUFFERLOST)
832 Con_Print("DSound buffer is lost!!\n");
833 IDirectSoundBuffer_Restore (pDSBuf);
836 if (!(dwStatus & DSBSTATUS_PLAYING))
837 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
841 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
843 if (hresult != DSERR_BUFFERLOST)
845 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
853 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
860 if ((void*)dsound_pbuf != snd_renderbuffer->ring)
861 Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
872 SndSys_UnlockRenderBuffer
874 Release the exclusive lock on "snd_renderbuffer"
877 void SndSys_UnlockRenderBuffer (void)
879 #ifdef SUPPORTDIRECTX
881 IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
889 Send keyboard events originating from the sound system (e.g. MIDI)
892 void SndSys_SendKeyEvents(void)