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.
22 #ifndef DIRECTSOUND_VERSION
23 # define DIRECTSOUND_VERSION 0x0500 /* Version 5.0 */
36 // ==============================================================================
38 #ifndef _WAVEFORMATEXTENSIBLE_
39 #define _WAVEFORMATEXTENSIBLE_
45 WORD wValidBitsPerSample; // bits of precision
46 WORD wSamplesPerBlock; // valid if wBitsPerSample==0
47 WORD wReserved; // If neither applies, set to zero
49 DWORD dwChannelMask; // which channels are present in stream
51 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
54 #if !defined(WAVE_FORMAT_EXTENSIBLE)
55 # define WAVE_FORMAT_EXTENSIBLE 0xFFFE
58 // Some speaker positions
59 #ifndef SPEAKER_FRONT_LEFT
60 # define SPEAKER_FRONT_LEFT 0x1
61 # define SPEAKER_FRONT_RIGHT 0x2
62 # define SPEAKER_FRONT_CENTER 0x4
63 # define SPEAKER_LOW_FREQUENCY 0x8
64 # define SPEAKER_BACK_LEFT 0x10
65 # define SPEAKER_BACK_RIGHT 0x20
66 # define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
67 # define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
68 // ... we never use the other values
71 // KSDATAFORMAT_SUBTYPE_PCM = GUID "00000001-0000-0010-8000-00aa00389b71"
72 static const GUID MY_KSDATAFORMAT_SUBTYPE_PCM =
79 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
84 // ==============================================================================
86 extern HWND mainwindow;
87 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"};
88 static qboolean sndsys_registeredcvars = false;
91 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
94 // Wave output: queue of this many sound buffers to play, reused cyclically
95 #define WAV_BUFFERS 16
96 #define WAV_MASK (WAV_BUFFERS - 1)
97 static unsigned int wav_buffer_size;
99 // DirectSound output: 64KB in 1 buffer
100 //#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->width * (fmt_ptr)->channels * (fmt_ptr)->speed / 2)
101 // LordHavoc: changed this to be a multiple of 32768
102 #define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->channels * 32768)
104 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
106 #ifdef SUPPORTDIRECTX
107 static qboolean dsound_init;
108 static unsigned int dsound_time;
109 static qboolean primary_format_set;
112 static qboolean wav_init;
114 static int snd_sent, snd_completed;
116 static int prev_painted;
117 static unsigned int paintpot;
122 * Global variables. Must be visible to window-procedure function
123 * so it can unlock and free the data block after it has been played.
127 HPSTR lpData, lpData2;
134 WAVEOUTCAPS wavecaps;
140 #ifdef SUPPORTDIRECTX
142 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
147 qboolean SNDDMA_InitWav (void);
148 #ifdef SUPPORTDIRECTX
149 sndinitstat SNDDMA_InitDirect (void);
155 SndSys_BuildWaveFormat
158 static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr)
160 WAVEFORMATEX* pfmtex;
162 memset (fmt_ptr, 0, sizeof(*fmt_ptr));
164 pfmtex = &fmt_ptr->Format;
165 pfmtex->nChannels = requested->channels;
166 pfmtex->wBitsPerSample = requested->width * 8;
167 pfmtex->nSamplesPerSec = requested->speed;
168 pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8;
169 pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign;
171 // LordHavoc: disabled this WAVE_FORMAT_EXTENSIBLE support because it does not seem to be working
173 if (requested->channels <= 2)
176 pfmtex->wFormatTag = WAVE_FORMAT_PCM;
182 pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
183 pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
184 fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
185 fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
187 // Build the channel mask
188 fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
189 switch (requested->channels)
192 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
195 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
198 fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
202 Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
212 #ifdef SUPPORTDIRECTX
215 SndSys_InitDirectSound
217 DirectSound 5 support
220 static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
226 WAVEFORMATEXTENSIBLE format, pformat;
230 if (! SndSys_BuildWaveFormat(requested, &format))
235 hInstDS = LoadLibrary("dsound.dll");
239 Con_Print("Couldn't load dsound.dll\n");
243 pDirectSoundCreate = (HRESULT (__stdcall *)(GUID *, LPDIRECTSOUND *,IUnknown *))GetProcAddress(hInstDS,"DirectSoundCreate");
245 if (!pDirectSoundCreate)
247 Con_Print("Couldn't get DS proc addr\n");
252 while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
254 if (hresult != DSERR_ALLOCATED)
256 Con_Print("DirectSound create failed\n");
260 if (MessageBox (NULL,
261 "The sound hardware is in use by another app.\n\n"
262 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
263 "Sound not available",
264 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
266 Con_Print("DirectSoundCreate failure\n hardware already in use\n");
271 dscaps.dwSize = sizeof(dscaps);
273 if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
275 Con_Print("Couldn't get DS caps\n");
278 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
280 Con_Print("No DirectSound driver installed\n");
285 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
287 Con_Print("Set coop level failed\n");
292 // get access to the primary buffer, if possible, so we can set the
293 // sound hardware format
294 memset (&dsbuf, 0, sizeof(dsbuf));
295 dsbuf.dwSize = sizeof(DSBUFFERDESC);
296 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
297 dsbuf.dwBufferBytes = 0;
298 dsbuf.lpwfxFormat = NULL;
300 memset(&dsbcaps, 0, sizeof(dsbcaps));
301 dsbcaps.dwSize = sizeof(dsbcaps);
302 primary_format_set = false;
304 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
305 if (!COM_CheckParm ("-snoforceformat"))
307 if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
311 if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
313 Con_Print("Set primary sound buffer format: no\n");
317 Con_Print("Set primary sound buffer format: yes\n");
319 primary_format_set = true;
324 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
325 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
329 // create the secondary buffer we'll actually work with
330 memset (&dsbuf, 0, sizeof(dsbuf));
331 dsbuf.dwSize = sizeof(DSBUFFERDESC);
332 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
333 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
334 dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
336 memset(&dsbcaps, 0, sizeof(dsbcaps));
337 dsbcaps.dwSize = sizeof(dsbcaps);
339 result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
340 if (result != DS_OK ||
341 requested->channels != format.Format.nChannels ||
342 requested->width != format.Format.wBitsPerSample / 8 ||
343 requested->speed != format.Format.nSamplesPerSec)
345 Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
346 (int)result, (unsigned)format.Format.nChannels, (unsigned)format.Format.wBitsPerSample / 8, (unsigned)format.Format.nSamplesPerSec);
351 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
353 Con_Print("DS:GetCaps failed\n");
358 Con_Print("Using secondary sound buffer\n");
362 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
364 Con_Print("Set coop level failed\n");
369 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
371 Con_Print("DS:GetCaps failed\n");
376 Con_Print("Using primary sound buffer\n");
379 // Make sure mixer is active
380 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
382 Con_Printf(" %d channel(s)\n"
385 requested->channels, requested->width * 8, requested->speed);
387 gSndBufSize = dsbcaps.dwBufferBytes;
389 // initialize the buffer
392 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
394 if (hresult != DSERR_BUFFERLOST)
396 Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
403 Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
410 memset(lpData, 0, dwSize);
411 IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
413 IDirectSoundBuffer_Stop(pDSBuf);
414 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
418 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
431 Crappy windows multimedia base
434 static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
436 WAVEFORMATEXTENSIBLE format;
440 if (! SndSys_BuildWaveFormat(requested, &format))
443 // Open a waveform device for output using window callback
444 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
445 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
447 if (hr != MMSYSERR_ALLOCATED)
449 Con_Print("waveOutOpen failed\n");
453 if (MessageBox (NULL,
454 "The sound hardware is in use by another app.\n\n"
455 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
456 "Sound not available",
457 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
459 Con_Print("waveOutOpen failure;\n hardware already in use\n");
464 wav_buffer_size = bound(128, snd_wav_partitionsize.integer, 8192) * requested->channels * requested->width;
467 * Allocate and lock memory for the waveform data. The memory
468 * for waveform data must be globally allocated with
469 * GMEM_MOVEABLE and GMEM_SHARE flags.
471 gSndBufSize = WAV_BUFFERS * wav_buffer_size;
472 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
475 Con_Print("Sound: Out of memory.\n");
479 lpData = (HPSTR)GlobalLock(hData);
482 Con_Print("Sound: Failed to lock.\n");
486 memset (lpData, 0, gSndBufSize);
489 * Allocate and lock memory for the header. This memory must
490 * also be globally allocated with GMEM_MOVEABLE and
493 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
495 if (hWaveHdr == NULL)
497 Con_Print("Sound: Failed to Alloc header.\n");
502 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
504 if (lpWaveHdr == NULL)
506 Con_Print("Sound: Failed to lock header.\n");
511 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
513 // After allocation, set up and prepare headers
514 for (i=0 ; i<WAV_BUFFERS ; i++)
516 lpWaveHdr[i].dwBufferLength = wav_buffer_size;
517 lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
519 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
521 Con_Print("Sound: failed to prepare wave headers\n");
527 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
545 Create "snd_renderbuffer" with the proper sound format if the call is successful
546 May return a suggested format if the requested format isn't available
549 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
551 #ifdef SUPPORTDIRECTX
556 if (!sndsys_registeredcvars)
558 sndsys_registeredcvars = true;
559 Cvar_RegisterVariable(&snd_wav_partitionsize);
562 Con_Print ("SndSys_Init: using the Win32 module\n");
564 #ifdef SUPPORTDIRECTX
565 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
566 wavonly = (COM_CheckParm ("-wavonly") != 0);
571 stat = SIS_FAILURE; // assume DirectSound won't initialize
573 #ifdef SUPPORTDIRECTX
577 stat = SndSys_InitDirectSound (requested);
579 if (stat == SIS_SUCCESS)
580 Con_Print("DirectSound initialized\n");
582 Con_Print("DirectSound failed to init\n");
586 // if DirectSound didn't succeed in initializing, try to initialize
587 // waveOut sound, unless DirectSound failed because the hardware is
588 // already allocated (in which case the user has already chosen not
590 #ifdef SUPPORTDIRECTX
591 if (!dsound_init && (stat != SIS_NOTAVAIL))
594 if (SndSys_InitMmsystem (requested))
595 Con_Print("Wave sound (MMSYSTEM) initialized\n");
597 Con_Print("Wave sound failed to init\n");
600 #ifdef SUPPORTDIRECTX
601 return (dsound_init || wav_init);
612 Stop the sound card, delete "snd_renderbuffer" and free its other resources
615 void SndSys_Shutdown (void)
617 #ifdef SUPPORTDIRECTX
620 IDirectSoundBuffer_Stop(pDSBuf);
621 IDirectSoundBuffer_Release(pDSBuf);
624 // only release primary buffer if it's not also the mixing buffer we just released
625 if (pDSPBuf && (pDSBuf != pDSPBuf))
627 IDirectSoundBuffer_Release(pDSPBuf);
632 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
633 IDirectSound_Release(pDS);
639 waveOutReset (hWaveOut);
645 for (i=0 ; i< WAV_BUFFERS ; i++)
646 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
649 waveOutClose (hWaveOut);
653 GlobalUnlock(hWaveHdr);
654 GlobalFree(hWaveHdr);
664 if (snd_renderbuffer != NULL)
666 Mem_Free(snd_renderbuffer);
667 snd_renderbuffer = NULL;
670 #ifdef SUPPORTDIRECTX
689 Submit the contents of "snd_renderbuffer" to the sound card
692 void SndSys_Submit (void)
697 // DirectSound doesn't need this
701 paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
702 if (paintpot > WAV_BUFFERS * wav_buffer_size)
703 paintpot = WAV_BUFFERS * wav_buffer_size;
704 prev_painted = snd_renderbuffer->endframe;
706 // submit new sound blocks
707 while (paintpot > wav_buffer_size)
709 h = lpWaveHdr + (snd_sent & WAV_MASK);
712 * Now the data block can be sent to the output device. The
713 * waveOutWrite function returns immediately and waveform
714 * data is sent to the output device in the background.
716 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
717 if (wResult == MMSYSERR_NOERROR)
719 else if (wResult == WAVERR_STILLPLAYING)
721 if(developer_insane.integer)
722 Con_DPrint("waveOutWrite failed (too much sound data)\n");
723 //h->dwFlags |= WHDR_DONE;
728 Con_Printf("waveOutWrite failed, error code %d\n", (int) wResult);
733 paintpot -= wav_buffer_size;
743 Returns the number of sample frames consumed since the sound started
746 unsigned int SndSys_GetSoundTime (void)
750 factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
752 #ifdef SUPPORTDIRECTX
758 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
759 diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize;
760 dwStartTime = dwTime;
762 dsound_time += diff / factor;
769 // Find which sound blocks have completed
772 if (snd_completed == snd_sent)
774 // Con_DPrint("Sound overrun\n");
778 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
781 snd_completed++; // this buffer has been played
784 return (snd_completed * wav_buffer_size) / factor;
787 * S_PaintAndSubmit: WARNING: newsoundtime (soundtime (275 < 134217707)
788 * apparently this sound time wraps quite early?
793 mmtime.wType = TIME_SAMPLES;
794 res = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime));
795 if(res == MMSYSERR_NOERROR)
796 return mmtime.u.sample;
805 #ifdef SUPPORTDIRECTX
806 static DWORD dsound_dwSize;
807 static DWORD dsound_dwSize2;
808 static DWORD *dsound_pbuf;
809 static DWORD *dsound_pbuf2;
814 SndSys_LockRenderBuffer
816 Get the exclusive lock on "snd_renderbuffer"
819 qboolean SndSys_LockRenderBuffer (void)
821 #ifdef SUPPORTDIRECTX
828 // if the buffer was lost or stopped, restore it and/or restart it
829 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
830 Con_Print("Couldn't get sound buffer status\n");
832 if (dwStatus & DSBSTATUS_BUFFERLOST)
834 Con_Print("DSound buffer is lost!!\n");
835 IDirectSoundBuffer_Restore (pDSBuf);
838 if (!(dwStatus & DSBSTATUS_PLAYING))
839 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
843 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
845 if (hresult != DSERR_BUFFERLOST)
847 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
855 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
862 if ((void*)dsound_pbuf != snd_renderbuffer->ring)
863 Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
874 SndSys_UnlockRenderBuffer
876 Release the exclusive lock on "snd_renderbuffer"
879 void SndSys_UnlockRenderBuffer (void)
881 #ifdef SUPPORTDIRECTX
883 IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
891 Send keyboard events originating from the sound system (e.g. MIDI)
894 void SndSys_SendKeyEvents(void)