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.
23 #ifndef DIRECTSOUND_VERSION
24 # define DIRECTSOUND_VERSION 0x0500 /* Version 5.0 */
30 // ==============================================================================
32 #ifndef _WAVEFORMATEXTENSIBLE_
33 #define _WAVEFORMATEXTENSIBLE_
39 WORD wValidBitsPerSample; // bits of precision
40 WORD wSamplesPerBlock; // valid if wBitsPerSample==0
41 WORD wReserved; // If neither applies, set to zero
43 DWORD dwChannelMask; // which channels are present in stream
45 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
48 #if !defined(WAVE_FORMAT_EXTENSIBLE)
49 # define WAVE_FORMAT_EXTENSIBLE 0xFFFE
52 // Some speaker positions
53 #ifndef SPEAKER_FRONT_LEFT
54 # define SPEAKER_FRONT_LEFT 0x1
55 # define SPEAKER_FRONT_RIGHT 0x2
56 # define SPEAKER_FRONT_CENTER 0x4
57 # define SPEAKER_LOW_FREQUENCY 0x8
58 # define SPEAKER_BACK_LEFT 0x10
59 # define SPEAKER_BACK_RIGHT 0x20
60 # define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
61 # define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
62 // ... we never use the other values
65 // KSDATAFORMAT_SUBTYPE_PCM = GUID "00000001-0000-0010-8000-00aa00389b71"
66 static const GUID MY_KSDATAFORMAT_SUBTYPE_PCM =
73 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
78 // ==============================================================================
80 extern HWND mainwindow;
82 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
84 // Wave output: 64KB in 64 buffers of 1KB
85 // (64KB is > 1 sec at 16-bit 22050 Hz mono, and is 1/3 sec at 16-bit 44100 Hz stereo)
86 #define WAV_BUFFERS 64
87 #define WAV_MASK (WAV_BUFFERS - 1)
88 static unsigned int wav_buffer_size;
90 // DirectSound output: 64KB in 1 buffer
91 //#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->width * (fmt_ptr)->channels * (fmt_ptr)->speed / 2)
92 // LordHavoc: changed this to be a multiple of 32768
93 #define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->channels * 32768)
95 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
97 static qboolean dsound_init;
98 static qboolean wav_init;
99 static qboolean primary_format_set;
101 static int snd_sent, snd_completed;
103 static int prev_painted;
104 static unsigned int paintpot;
106 static unsigned int dsound_time;
110 * Global variables. Must be visible to window-procedure function
111 * so it can unlock and free the data block after it has been played.
115 HPSTR lpData, lpData2;
122 WAVEOUTCAPS wavecaps;
129 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
133 qboolean SNDDMA_InitWav (void);
134 sndinitstat SNDDMA_InitDirect (void);
139 SndSys_BuildWaveFormat
142 static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr)
144 WAVEFORMATEX* pfmtex;
146 memset (fmt_ptr, 0, sizeof(*fmt_ptr));
148 pfmtex = &fmt_ptr->Format;
149 pfmtex->nChannels = requested->channels;
150 pfmtex->wBitsPerSample = requested->width * 8;
151 pfmtex->nSamplesPerSec = requested->speed;
152 pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8;
153 pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign;
155 // LordHavoc: disabled this WAVE_FORMAT_EXTENSIBLE support because it does not seem to be working
157 if (requested->channels <= 2)
160 pfmtex->wFormatTag = WAVE_FORMAT_PCM;
166 pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
167 pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
168 fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
169 fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
171 // Build the channel mask
172 fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
173 switch (requested->channels)
176 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
179 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
182 fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
186 Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
198 SndSys_InitDirectSound
200 DirectSound 5 support
203 static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
209 WAVEFORMATEXTENSIBLE format, pformat;
213 if (! SndSys_BuildWaveFormat(requested, &format))
218 hInstDS = LoadLibrary("dsound.dll");
222 Con_Print("Couldn't load dsound.dll\n");
226 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
228 if (!pDirectSoundCreate)
230 Con_Print("Couldn't get DS proc addr\n");
235 while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
237 if (hresult != DSERR_ALLOCATED)
239 Con_Print("DirectSound create failed\n");
243 if (MessageBox (NULL,
244 "The sound hardware is in use by another app.\n\n"
245 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
246 "Sound not available",
247 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
249 Con_Print("DirectSoundCreate failure\n hardware already in use\n");
254 dscaps.dwSize = sizeof(dscaps);
256 if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
258 Con_Print("Couldn't get DS caps\n");
261 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
263 Con_Print("No DirectSound driver installed\n");
268 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
270 Con_Print("Set coop level failed\n");
275 // get access to the primary buffer, if possible, so we can set the
276 // sound hardware format
277 memset (&dsbuf, 0, sizeof(dsbuf));
278 dsbuf.dwSize = sizeof(DSBUFFERDESC);
279 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
280 dsbuf.dwBufferBytes = 0;
281 dsbuf.lpwfxFormat = NULL;
283 memset(&dsbcaps, 0, sizeof(dsbcaps));
284 dsbcaps.dwSize = sizeof(dsbcaps);
285 primary_format_set = false;
287 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
288 if (!COM_CheckParm ("-snoforceformat"))
290 if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
294 if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
296 Con_Print("Set primary sound buffer format: no\n");
300 Con_Print("Set primary sound buffer format: yes\n");
302 primary_format_set = true;
307 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
308 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
312 // create the secondary buffer we'll actually work with
313 memset (&dsbuf, 0, sizeof(dsbuf));
314 dsbuf.dwSize = sizeof(DSBUFFERDESC);
315 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
316 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
317 dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
319 memset(&dsbcaps, 0, sizeof(dsbcaps));
320 dsbcaps.dwSize = sizeof(dsbcaps);
322 result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
323 if (result != DS_OK ||
324 requested->channels != format.Format.nChannels ||
325 requested->width != format.Format.wBitsPerSample / 8 ||
326 requested->speed != format.Format.nSamplesPerSec)
328 Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
329 result, format.Format.nChannels, format.Format.wBitsPerSample / 8, format.Format.nSamplesPerSec);
334 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
336 Con_Print("DS:GetCaps failed\n");
341 Con_Print("Using secondary sound buffer\n");
345 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
347 Con_Print("Set coop level failed\n");
352 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
354 Con_Print("DS:GetCaps failed\n");
359 Con_Print("Using primary sound buffer\n");
362 // Make sure mixer is active
363 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
365 Con_DPrintf(" %d channel(s)\n"
368 requested->channels, requested->width * 8, requested->speed);
370 gSndBufSize = dsbcaps.dwBufferBytes;
372 // initialize the buffer
375 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
377 if (hresult != DSERR_BUFFERLOST)
379 Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
386 Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
393 memset(lpData, 0, dwSize);
394 IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
396 IDirectSoundBuffer_Stop(pDSBuf);
397 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
401 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
413 Crappy windows multimedia base
416 static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
418 WAVEFORMATEXTENSIBLE format;
422 if (! SndSys_BuildWaveFormat(requested, &format))
425 // Open a waveform device for output using window callback
426 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
427 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
429 if (hr != MMSYSERR_ALLOCATED)
431 Con_Print("waveOutOpen failed\n");
435 if (MessageBox (NULL,
436 "The sound hardware is in use by another app.\n\n"
437 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
438 "Sound not available",
439 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
441 Con_Print("waveOutOpen failure;\n hardware already in use\n");
446 wav_buffer_size = (requested->speed / 2 / WAV_BUFFERS) * requested->channels * requested->width;
449 * Allocate and lock memory for the waveform data. The memory
450 * for waveform data must be globally allocated with
451 * GMEM_MOVEABLE and GMEM_SHARE flags.
453 gSndBufSize = WAV_BUFFERS * wav_buffer_size;
454 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
457 Con_Print("Sound: Out of memory.\n");
461 lpData = GlobalLock(hData);
464 Con_Print("Sound: Failed to lock.\n");
468 memset (lpData, 0, gSndBufSize);
471 * Allocate and lock memory for the header. This memory must
472 * also be globally allocated with GMEM_MOVEABLE and
475 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
477 if (hWaveHdr == NULL)
479 Con_Print("Sound: Failed to Alloc header.\n");
484 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
486 if (lpWaveHdr == NULL)
488 Con_Print("Sound: Failed to lock header.\n");
493 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
495 // After allocation, set up and prepare headers
496 for (i=0 ; i<WAV_BUFFERS ; i++)
498 lpWaveHdr[i].dwBufferLength = wav_buffer_size;
499 lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
501 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
503 Con_Print("Sound: failed to prepare wave headers\n");
509 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
527 Create "snd_renderbuffer" with the proper sound format if the call is successful
528 May return a suggested format if the requested format isn't available
531 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
536 Con_Print ("SndSys_Init: using the Win32 module\n");
538 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
539 wavonly = (COM_CheckParm ("-wavonly") != 0);
543 stat = SIS_FAILURE; // assume DirectSound won't initialize
548 stat = SndSys_InitDirectSound (requested);
550 if (stat == SIS_SUCCESS)
551 Con_Print("DirectSound initialized\n");
553 Con_Print("DirectSound failed to init\n");
556 // if DirectSound didn't succeed in initializing, try to initialize
557 // waveOut sound, unless DirectSound failed because the hardware is
558 // already allocated (in which case the user has already chosen not
560 if (!dsound_init && (stat != SIS_NOTAVAIL))
562 if (SndSys_InitMmsystem (requested))
563 Con_Print("Wave sound (MMSYSTEM) initialized\n");
565 Con_Print("Wave sound failed to init\n");
568 return (dsound_init || wav_init);
576 Stop the sound card, delete "snd_renderbuffer" and free its other resources
579 void SndSys_Shutdown (void)
583 IDirectSoundBuffer_Stop(pDSBuf);
584 IDirectSoundBuffer_Release(pDSBuf);
587 // only release primary buffer if it's not also the mixing buffer we just released
588 if (pDSPBuf && (pDSBuf != pDSPBuf))
590 IDirectSoundBuffer_Release(pDSPBuf);
595 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
596 IDirectSound_Release(pDS);
601 waveOutReset (hWaveOut);
607 for (i=0 ; i< WAV_BUFFERS ; i++)
608 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
611 waveOutClose (hWaveOut);
615 GlobalUnlock(hWaveHdr);
616 GlobalFree(hWaveHdr);
626 if (snd_renderbuffer != NULL)
628 Mem_Free(snd_renderbuffer);
629 snd_renderbuffer = NULL;
649 Submit the contents of "snd_renderbuffer" to the sound card
652 void SndSys_Submit (void)
657 // DirectSound doesn't need this
661 paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
662 if (paintpot > WAV_BUFFERS * wav_buffer_size)
663 paintpot = WAV_BUFFERS * wav_buffer_size;
664 prev_painted = snd_renderbuffer->endframe;
666 // submit new sound blocks
667 while (paintpot > wav_buffer_size)
669 h = lpWaveHdr + (snd_sent & WAV_MASK);
673 * Now the data block can be sent to the output device. The
674 * waveOutWrite function returns immediately and waveform
675 * data is sent to the output device in the background.
677 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
679 if (wResult != MMSYSERR_NOERROR)
681 Con_Print("Failed to write block to device\n");
686 paintpot -= wav_buffer_size;
696 Returns the number of sample frames consumed since the sound started
699 unsigned int SndSys_GetSoundTime (void)
703 factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
710 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
711 diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize;
712 dwStartTime = dwTime;
714 dsound_time += diff / factor;
720 // Find which sound blocks have completed
723 if (snd_completed == snd_sent)
725 Con_DPrint("Sound overrun\n");
729 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
732 snd_completed++; // this buffer has been played
735 return (snd_completed * wav_buffer_size) / factor;
742 static DWORD dsound_dwSize;
743 static DWORD dsound_dwSize2;
744 static DWORD *dsound_pbuf;
745 static DWORD *dsound_pbuf2;
749 SndSys_LockRenderBuffer
751 Get the exclusive lock on "snd_renderbuffer"
754 qboolean SndSys_LockRenderBuffer (void)
762 // if the buffer was lost or stopped, restore it and/or restart it
763 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
764 Con_Print("Couldn't get sound buffer status\n");
766 if (dwStatus & DSBSTATUS_BUFFERLOST)
768 Con_Print("DSound buffer is lost!!\n");
769 IDirectSoundBuffer_Restore (pDSBuf);
772 if (!(dwStatus & DSBSTATUS_PLAYING))
773 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
777 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
779 if (hresult != DSERR_BUFFERLOST)
781 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
789 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
796 if ((void*)dsound_pbuf != snd_renderbuffer->ring)
797 Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
807 SndSys_UnlockRenderBuffer
809 Release the exclusive lock on "snd_renderbuffer"
812 void SndSys_UnlockRenderBuffer (void)
815 IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);