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;
87 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
90 // Wave output: 64KB in 64 buffers of 1KB
91 // (64KB is > 1 sec at 16-bit 22050 Hz mono, and is 1/3 sec at 16-bit 44100 Hz stereo)
92 #define WAV_BUFFERS 64
93 #define WAV_MASK (WAV_BUFFERS - 1)
94 static unsigned int wav_buffer_size;
96 // DirectSound output: 64KB in 1 buffer
97 //#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->width * (fmt_ptr)->channels * (fmt_ptr)->speed / 2)
98 // LordHavoc: changed this to be a multiple of 32768
99 #define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->channels * 32768)
101 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
103 #ifdef SUPPORTDIRECTX
104 static qboolean dsound_init;
105 static unsigned int dsound_time;
106 static qboolean primary_format_set;
109 static qboolean wav_init;
111 static int snd_sent, snd_completed;
113 static int prev_painted;
114 static unsigned int paintpot;
119 * Global variables. Must be visible to window-procedure function
120 * so it can unlock and free the data block after it has been played.
124 HPSTR lpData, lpData2;
131 WAVEOUTCAPS wavecaps;
137 #ifdef SUPPORTDIRECTX
139 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
144 qboolean SNDDMA_InitWav (void);
145 #ifdef SUPPORTDIRECTX
146 sndinitstat SNDDMA_InitDirect (void);
152 SndSys_BuildWaveFormat
155 static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr)
157 WAVEFORMATEX* pfmtex;
159 memset (fmt_ptr, 0, sizeof(*fmt_ptr));
161 pfmtex = &fmt_ptr->Format;
162 pfmtex->nChannels = requested->channels;
163 pfmtex->wBitsPerSample = requested->width * 8;
164 pfmtex->nSamplesPerSec = requested->speed;
165 pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8;
166 pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign;
168 // LordHavoc: disabled this WAVE_FORMAT_EXTENSIBLE support because it does not seem to be working
170 if (requested->channels <= 2)
173 pfmtex->wFormatTag = WAVE_FORMAT_PCM;
179 pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
180 pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
181 fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
182 fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
184 // Build the channel mask
185 fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
186 switch (requested->channels)
189 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
192 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
195 fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
199 Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
209 #ifdef SUPPORTDIRECTX
212 SndSys_InitDirectSound
214 DirectSound 5 support
217 static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
223 WAVEFORMATEXTENSIBLE format, pformat;
227 if (! SndSys_BuildWaveFormat(requested, &format))
232 hInstDS = LoadLibrary("dsound.dll");
236 Con_Print("Couldn't load dsound.dll\n");
240 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
242 if (!pDirectSoundCreate)
244 Con_Print("Couldn't get DS proc addr\n");
249 while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
251 if (hresult != DSERR_ALLOCATED)
253 Con_Print("DirectSound create failed\n");
257 if (MessageBox (NULL,
258 "The sound hardware is in use by another app.\n\n"
259 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
260 "Sound not available",
261 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
263 Con_Print("DirectSoundCreate failure\n hardware already in use\n");
268 dscaps.dwSize = sizeof(dscaps);
270 if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
272 Con_Print("Couldn't get DS caps\n");
275 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
277 Con_Print("No DirectSound driver installed\n");
282 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
284 Con_Print("Set coop level failed\n");
289 // get access to the primary buffer, if possible, so we can set the
290 // sound hardware format
291 memset (&dsbuf, 0, sizeof(dsbuf));
292 dsbuf.dwSize = sizeof(DSBUFFERDESC);
293 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
294 dsbuf.dwBufferBytes = 0;
295 dsbuf.lpwfxFormat = NULL;
297 memset(&dsbcaps, 0, sizeof(dsbcaps));
298 dsbcaps.dwSize = sizeof(dsbcaps);
299 primary_format_set = false;
301 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
302 if (!COM_CheckParm ("-snoforceformat"))
304 if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
308 if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
310 Con_Print("Set primary sound buffer format: no\n");
314 Con_Print("Set primary sound buffer format: yes\n");
316 primary_format_set = true;
321 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
322 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
326 // create the secondary buffer we'll actually work with
327 memset (&dsbuf, 0, sizeof(dsbuf));
328 dsbuf.dwSize = sizeof(DSBUFFERDESC);
329 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
330 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
331 dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
333 memset(&dsbcaps, 0, sizeof(dsbcaps));
334 dsbcaps.dwSize = sizeof(dsbcaps);
336 result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
337 if (result != DS_OK ||
338 requested->channels != format.Format.nChannels ||
339 requested->width != format.Format.wBitsPerSample / 8 ||
340 requested->speed != format.Format.nSamplesPerSec)
342 Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
343 (int)result, (unsigned)format.Format.nChannels, (unsigned)format.Format.wBitsPerSample / 8, (unsigned)format.Format.nSamplesPerSec);
348 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
350 Con_Print("DS:GetCaps failed\n");
355 Con_Print("Using secondary sound buffer\n");
359 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
361 Con_Print("Set coop level failed\n");
366 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
368 Con_Print("DS:GetCaps failed\n");
373 Con_Print("Using primary sound buffer\n");
376 // Make sure mixer is active
377 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
379 Con_Printf(" %d channel(s)\n"
382 requested->channels, requested->width * 8, requested->speed);
384 gSndBufSize = dsbcaps.dwBufferBytes;
386 // initialize the buffer
389 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
391 if (hresult != DSERR_BUFFERLOST)
393 Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
400 Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
407 memset(lpData, 0, dwSize);
408 IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
410 IDirectSoundBuffer_Stop(pDSBuf);
411 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
415 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
428 Crappy windows multimedia base
431 static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
433 WAVEFORMATEXTENSIBLE format;
437 if (! SndSys_BuildWaveFormat(requested, &format))
440 // Open a waveform device for output using window callback
441 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
442 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
444 if (hr != MMSYSERR_ALLOCATED)
446 Con_Print("waveOutOpen failed\n");
450 if (MessageBox (NULL,
451 "The sound hardware is in use by another app.\n\n"
452 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
453 "Sound not available",
454 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
456 Con_Print("waveOutOpen failure;\n hardware already in use\n");
461 wav_buffer_size = (requested->speed / 2 / WAV_BUFFERS) * requested->channels * requested->width;
464 * Allocate and lock memory for the waveform data. The memory
465 * for waveform data must be globally allocated with
466 * GMEM_MOVEABLE and GMEM_SHARE flags.
468 gSndBufSize = WAV_BUFFERS * wav_buffer_size;
469 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
472 Con_Print("Sound: Out of memory.\n");
476 lpData = GlobalLock(hData);
479 Con_Print("Sound: Failed to lock.\n");
483 memset (lpData, 0, gSndBufSize);
486 * Allocate and lock memory for the header. This memory must
487 * also be globally allocated with GMEM_MOVEABLE and
490 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
492 if (hWaveHdr == NULL)
494 Con_Print("Sound: Failed to Alloc header.\n");
499 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
501 if (lpWaveHdr == NULL)
503 Con_Print("Sound: Failed to lock header.\n");
508 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
510 // After allocation, set up and prepare headers
511 for (i=0 ; i<WAV_BUFFERS ; i++)
513 lpWaveHdr[i].dwBufferLength = wav_buffer_size;
514 lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
516 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
518 Con_Print("Sound: failed to prepare wave headers\n");
524 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
542 Create "snd_renderbuffer" with the proper sound format if the call is successful
543 May return a suggested format if the requested format isn't available
546 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
548 #ifdef SUPPORTDIRECTX
553 Con_Print ("SndSys_Init: using the Win32 module\n");
555 #ifdef SUPPORTDIRECTX
556 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
557 wavonly = (COM_CheckParm ("-wavonly") != 0);
562 stat = SIS_FAILURE; // assume DirectSound won't initialize
564 #ifdef SUPPORTDIRECTX
568 stat = SndSys_InitDirectSound (requested);
570 if (stat == SIS_SUCCESS)
571 Con_Print("DirectSound initialized\n");
573 Con_Print("DirectSound failed to init\n");
577 // if DirectSound didn't succeed in initializing, try to initialize
578 // waveOut sound, unless DirectSound failed because the hardware is
579 // already allocated (in which case the user has already chosen not
581 #ifdef SUPPORTDIRECTX
582 if (!dsound_init && (stat != SIS_NOTAVAIL))
585 if (SndSys_InitMmsystem (requested))
586 Con_Print("Wave sound (MMSYSTEM) initialized\n");
588 Con_Print("Wave sound failed to init\n");
591 #ifdef SUPPORTDIRECTX
592 return (dsound_init || wav_init);
603 Stop the sound card, delete "snd_renderbuffer" and free its other resources
606 void SndSys_Shutdown (void)
608 #ifdef SUPPORTDIRECTX
611 IDirectSoundBuffer_Stop(pDSBuf);
612 IDirectSoundBuffer_Release(pDSBuf);
615 // only release primary buffer if it's not also the mixing buffer we just released
616 if (pDSPBuf && (pDSBuf != pDSPBuf))
618 IDirectSoundBuffer_Release(pDSPBuf);
623 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
624 IDirectSound_Release(pDS);
630 waveOutReset (hWaveOut);
636 for (i=0 ; i< WAV_BUFFERS ; i++)
637 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
640 waveOutClose (hWaveOut);
644 GlobalUnlock(hWaveHdr);
645 GlobalFree(hWaveHdr);
655 if (snd_renderbuffer != NULL)
657 Mem_Free(snd_renderbuffer);
658 snd_renderbuffer = NULL;
661 #ifdef SUPPORTDIRECTX
680 Submit the contents of "snd_renderbuffer" to the sound card
683 void SndSys_Submit (void)
688 // DirectSound doesn't need this
692 paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
693 if (paintpot > WAV_BUFFERS * wav_buffer_size)
694 paintpot = WAV_BUFFERS * wav_buffer_size;
695 prev_painted = snd_renderbuffer->endframe;
697 // submit new sound blocks
698 while (paintpot > wav_buffer_size)
700 h = lpWaveHdr + (snd_sent & WAV_MASK);
704 * Now the data block can be sent to the output device. The
705 * waveOutWrite function returns immediately and waveform
706 * data is sent to the output device in the background.
708 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
710 if (wResult != MMSYSERR_NOERROR)
712 if (developer.integer >= 1000)
713 Con_Print("waveOutWrite failed (too much sound data)\n");
714 //SndSys_Shutdown ();
718 paintpot -= wav_buffer_size;
728 Returns the number of sample frames consumed since the sound started
731 unsigned int SndSys_GetSoundTime (void)
735 factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
737 #ifdef SUPPORTDIRECTX
743 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
744 diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize;
745 dwStartTime = dwTime;
747 dsound_time += diff / factor;
754 // Find which sound blocks have completed
757 if (snd_completed == snd_sent)
759 Con_DPrint("Sound overrun\n");
763 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
766 snd_completed++; // this buffer has been played
769 return (snd_completed * wav_buffer_size) / factor;
776 #ifdef SUPPORTDIRECTX
777 static DWORD dsound_dwSize;
778 static DWORD dsound_dwSize2;
779 static DWORD *dsound_pbuf;
780 static DWORD *dsound_pbuf2;
785 SndSys_LockRenderBuffer
787 Get the exclusive lock on "snd_renderbuffer"
790 qboolean SndSys_LockRenderBuffer (void)
792 #ifdef SUPPORTDIRECTX
799 // if the buffer was lost or stopped, restore it and/or restart it
800 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
801 Con_Print("Couldn't get sound buffer status\n");
803 if (dwStatus & DSBSTATUS_BUFFERLOST)
805 Con_Print("DSound buffer is lost!!\n");
806 IDirectSoundBuffer_Restore (pDSBuf);
809 if (!(dwStatus & DSBSTATUS_PLAYING))
810 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
814 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
816 if (hresult != DSERR_BUFFERLOST)
818 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
826 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
833 if ((void*)dsound_pbuf != snd_renderbuffer->ring)
834 Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
845 SndSys_UnlockRenderBuffer
847 Release the exclusive lock on "snd_renderbuffer"
850 void SndSys_UnlockRenderBuffer (void)
852 #ifdef SUPPORTDIRECTX
854 IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);