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 if (requested->channels <= 2)
157 pfmtex->wFormatTag = WAVE_FORMAT_PCM;
162 pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
163 pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
164 fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
165 fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
167 // Build the channel mask
168 fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
169 switch (requested->channels)
172 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
175 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
178 fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
182 Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
193 SndSys_InitDirectSound
195 DirectSound 5 support
198 static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
204 WAVEFORMATEXTENSIBLE format, pformat;
208 if (! SndSys_BuildWaveFormat(requested, &format))
213 hInstDS = LoadLibrary("dsound.dll");
217 Con_Print("Couldn't load dsound.dll\n");
221 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
223 if (!pDirectSoundCreate)
225 Con_Print("Couldn't get DS proc addr\n");
230 while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
232 if (hresult != DSERR_ALLOCATED)
234 Con_Print("DirectSound create failed\n");
238 if (MessageBox (NULL,
239 "The sound hardware is in use by another app.\n\n"
240 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
241 "Sound not available",
242 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
244 Con_Print("DirectSoundCreate failure\n hardware already in use\n");
249 dscaps.dwSize = sizeof(dscaps);
251 if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
253 Con_Print("Couldn't get DS caps\n");
256 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
258 Con_Print("No DirectSound driver installed\n");
263 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
265 Con_Print("Set coop level failed\n");
270 // get access to the primary buffer, if possible, so we can set the
271 // sound hardware format
272 memset (&dsbuf, 0, sizeof(dsbuf));
273 dsbuf.dwSize = sizeof(DSBUFFERDESC);
274 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
275 dsbuf.dwBufferBytes = 0;
276 dsbuf.lpwfxFormat = NULL;
278 memset(&dsbcaps, 0, sizeof(dsbcaps));
279 dsbcaps.dwSize = sizeof(dsbcaps);
280 primary_format_set = false;
282 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
283 if (!COM_CheckParm ("-snoforceformat"))
285 if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
289 if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
291 Con_Print("Set primary sound buffer format: no\n");
295 Con_Print("Set primary sound buffer format: yes\n");
297 primary_format_set = true;
302 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
303 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
307 // create the secondary buffer we'll actually work with
308 memset (&dsbuf, 0, sizeof(dsbuf));
309 dsbuf.dwSize = sizeof(DSBUFFERDESC);
310 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
311 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
312 dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
314 memset(&dsbcaps, 0, sizeof(dsbcaps));
315 dsbcaps.dwSize = sizeof(dsbcaps);
317 result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
318 if (result != DS_OK ||
319 requested->channels != format.Format.nChannels ||
320 requested->width != format.Format.wBitsPerSample / 8 ||
321 requested->speed != format.Format.nSamplesPerSec)
323 Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
324 result, format.Format.nChannels, format.Format.wBitsPerSample / 8, format.Format.nSamplesPerSec);
329 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
331 Con_Print("DS:GetCaps failed\n");
336 Con_Print("Using secondary sound buffer\n");
340 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
342 Con_Print("Set coop level failed\n");
347 if (DS_OK != IDirectSoundBuffer_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 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
360 Con_DPrintf(" %d channel(s)\n"
363 requested->channels, requested->width * 8, requested->speed);
365 gSndBufSize = dsbcaps.dwBufferBytes;
367 // initialize the buffer
370 while ((hresult = IDirectSoundBuffer_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);
389 IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
391 IDirectSoundBuffer_Stop(pDSBuf);
392 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
396 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
408 Crappy windows multimedia base
411 static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
413 WAVEFORMATEXTENSIBLE format;
417 if (! SndSys_BuildWaveFormat(requested, &format))
420 // Open a waveform device for output using window callback
421 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
422 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
424 if (hr != MMSYSERR_ALLOCATED)
426 Con_Print("waveOutOpen failed\n");
430 if (MessageBox (NULL,
431 "The sound hardware is in use by another app.\n\n"
432 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
433 "Sound not available",
434 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
436 Con_Print("waveOutOpen failure;\n hardware already in use\n");
441 wav_buffer_size = (requested->speed / 2 / WAV_BUFFERS) * requested->channels * requested->width;
444 * Allocate and lock memory for the waveform data. The memory
445 * for waveform data must be globally allocated with
446 * GMEM_MOVEABLE and GMEM_SHARE flags.
448 gSndBufSize = WAV_BUFFERS * wav_buffer_size;
449 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
452 Con_Print("Sound: Out of memory.\n");
456 lpData = GlobalLock(hData);
459 Con_Print("Sound: Failed to lock.\n");
463 memset (lpData, 0, gSndBufSize);
466 * Allocate and lock memory for the header. This memory must
467 * also be globally allocated with GMEM_MOVEABLE and
470 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
472 if (hWaveHdr == NULL)
474 Con_Print("Sound: Failed to Alloc header.\n");
479 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
481 if (lpWaveHdr == NULL)
483 Con_Print("Sound: Failed to lock header.\n");
488 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
490 // After allocation, set up and prepare headers
491 for (i=0 ; i<WAV_BUFFERS ; i++)
493 lpWaveHdr[i].dwBufferLength = wav_buffer_size;
494 lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
496 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
498 Con_Print("Sound: failed to prepare wave headers\n");
504 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
522 Create "snd_renderbuffer" with the proper sound format if the call is successful
523 May return a suggested format if the requested format isn't available
526 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
531 Con_Print ("SndSys_Init: using the Win32 module\n");
533 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
534 wavonly = (COM_CheckParm ("-wavonly") != 0);
538 stat = SIS_FAILURE; // assume DirectSound won't initialize
543 stat = SndSys_InitDirectSound (requested);
545 if (stat == SIS_SUCCESS)
546 Con_Print("DirectSound initialized\n");
548 Con_Print("DirectSound failed to init\n");
551 // if DirectSound didn't succeed in initializing, try to initialize
552 // waveOut sound, unless DirectSound failed because the hardware is
553 // already allocated (in which case the user has already chosen not
555 if (!dsound_init && (stat != SIS_NOTAVAIL))
557 if (SndSys_InitMmsystem (requested))
558 Con_Print("Wave sound (MMSYSTEM) initialized\n");
560 Con_Print("Wave sound failed to init\n");
563 return (dsound_init || wav_init);
571 Stop the sound card, delete "snd_renderbuffer" and free its other resources
574 void SndSys_Shutdown (void)
578 IDirectSoundBuffer_Stop(pDSBuf);
579 IDirectSoundBuffer_Release(pDSBuf);
582 // only release primary buffer if it's not also the mixing buffer we just released
583 if (pDSPBuf && (pDSBuf != pDSPBuf))
585 IDirectSoundBuffer_Release(pDSPBuf);
590 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
591 IDirectSound_Release(pDS);
596 waveOutReset (hWaveOut);
602 for (i=0 ; i< WAV_BUFFERS ; i++)
603 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
606 waveOutClose (hWaveOut);
610 GlobalUnlock(hWaveHdr);
611 GlobalFree(hWaveHdr);
621 if (snd_renderbuffer != NULL)
623 Mem_Free(snd_renderbuffer);
624 snd_renderbuffer = NULL;
644 Submit the contents of "snd_renderbuffer" to the sound card
647 void SndSys_Submit (void)
652 // DirectSound doesn't need this
656 paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
657 if (paintpot > WAV_BUFFERS * wav_buffer_size)
658 paintpot = WAV_BUFFERS * wav_buffer_size;
659 prev_painted = snd_renderbuffer->endframe;
661 // submit new sound blocks
662 while (paintpot > wav_buffer_size)
664 h = lpWaveHdr + (snd_sent & WAV_MASK);
668 * Now the data block can be sent to the output device. The
669 * waveOutWrite function returns immediately and waveform
670 * data is sent to the output device in the background.
672 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
674 if (wResult != MMSYSERR_NOERROR)
676 Con_Print("Failed to write block to device\n");
681 paintpot -= wav_buffer_size;
691 Returns the number of sample frames consumed since the sound started
694 unsigned int SndSys_GetSoundTime (void)
698 factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
705 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
706 if (dwTime > dwStartTime)
707 diff = dwTime - dwStartTime;
709 diff = gSndBufSize - dwStartTime + dwTime;
710 dwStartTime = dwTime;
712 dsound_time += diff / factor;
718 // Find which sound blocks have completed
721 if (snd_completed == snd_sent)
723 Con_DPrint("Sound overrun\n");
727 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
730 snd_completed++; // this buffer has been played
733 return (snd_completed * wav_buffer_size) / factor;
740 static DWORD dsound_dwSize;
741 static DWORD dsound_dwSize2;
742 static DWORD *dsound_pbuf;
743 static DWORD *dsound_pbuf2;
747 SndSys_LockRenderBuffer
749 Get the exclusive lock on "snd_renderbuffer"
752 qboolean SndSys_LockRenderBuffer (void)
760 // if the buffer was lost or stopped, restore it and/or restart it
761 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
762 Con_Print("Couldn't get sound buffer status\n");
764 if (dwStatus & DSBSTATUS_BUFFERLOST)
766 Con_Print("DSound buffer is lost!!\n");
767 IDirectSoundBuffer_Restore (pDSBuf);
770 if (!(dwStatus & DSBSTATUS_PLAYING))
771 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
775 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
777 if (hresult != DSERR_BUFFERLOST)
779 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
787 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
794 if ((void*)dsound_pbuf != snd_renderbuffer->ring)
795 Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
805 SndSys_UnlockRenderBuffer
807 Release the exclusive lock on "snd_renderbuffer"
810 void SndSys_UnlockRenderBuffer (void)
813 IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);