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)
93 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
95 static qboolean dsound_init;
96 static qboolean wav_init;
97 static qboolean primary_format_set;
99 static int snd_sent, snd_completed;
101 static int prev_painted;
102 static unsigned int paintpot;
104 static unsigned int dsound_time;
108 * Global variables. Must be visible to window-procedure function
109 * so it can unlock and free the data block after it has been played.
113 HPSTR lpData, lpData2;
120 WAVEOUTCAPS wavecaps;
127 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
131 qboolean SNDDMA_InitWav (void);
132 sndinitstat SNDDMA_InitDirect (void);
137 SndSys_BuildWaveFormat
140 static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr)
142 WAVEFORMATEX* pfmtex;
144 memset (fmt_ptr, 0, sizeof(*fmt_ptr));
146 pfmtex = &fmt_ptr->Format;
147 pfmtex->nChannels = requested->channels;
148 pfmtex->wBitsPerSample = requested->width * 8;
149 pfmtex->nSamplesPerSec = requested->speed;
150 pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8;
151 pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign;
153 if (requested->channels <= 2)
155 pfmtex->wFormatTag = WAVE_FORMAT_PCM;
160 pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
161 pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
162 fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
163 fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
165 // Build the channel mask
166 fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
167 switch (requested->channels)
170 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
173 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
176 fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
180 Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
191 SndSys_InitDirectSound
193 DirectSound 5 support
196 static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
202 WAVEFORMATEXTENSIBLE format, pformat;
206 if (! SndSys_BuildWaveFormat(requested, &format))
211 hInstDS = LoadLibrary("dsound.dll");
215 Con_Print("Couldn't load dsound.dll\n");
219 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
221 if (!pDirectSoundCreate)
223 Con_Print("Couldn't get DS proc addr\n");
228 while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
230 if (hresult != DSERR_ALLOCATED)
232 Con_Print("DirectSound create failed\n");
236 if (MessageBox (NULL,
237 "The sound hardware is in use by another app.\n\n"
238 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
239 "Sound not available",
240 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
242 Con_Print("DirectSoundCreate failure\n hardware already in use\n");
247 dscaps.dwSize = sizeof(dscaps);
249 if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
251 Con_Print("Couldn't get DS caps\n");
254 if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
256 Con_Print("No DirectSound driver installed\n");
261 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
263 Con_Print("Set coop level failed\n");
268 // get access to the primary buffer, if possible, so we can set the
269 // sound hardware format
270 memset (&dsbuf, 0, sizeof(dsbuf));
271 dsbuf.dwSize = sizeof(DSBUFFERDESC);
272 dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
273 dsbuf.dwBufferBytes = 0;
274 dsbuf.lpwfxFormat = NULL;
276 memset(&dsbcaps, 0, sizeof(dsbcaps));
277 dsbcaps.dwSize = sizeof(dsbcaps);
278 primary_format_set = false;
280 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
281 if (!COM_CheckParm ("-snoforceformat"))
283 if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
287 if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
289 Con_Print("Set primary sound buffer format: no\n");
293 Con_Print("Set primary sound buffer format: yes\n");
295 primary_format_set = true;
300 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
301 if (!primary_format_set || !COM_CheckParm ("-primarysound"))
305 // create the secondary buffer we'll actually work with
306 memset (&dsbuf, 0, sizeof(dsbuf));
307 dsbuf.dwSize = sizeof(DSBUFFERDESC);
308 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
309 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
310 dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
312 memset(&dsbcaps, 0, sizeof(dsbcaps));
313 dsbcaps.dwSize = sizeof(dsbcaps);
315 result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
316 if (result != DS_OK ||
317 requested->channels != format.Format.nChannels ||
318 requested->width != format.Format.wBitsPerSample / 8 ||
319 requested->speed != format.Format.nSamplesPerSec)
321 Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
322 result, format.Format.nChannels, format.Format.wBitsPerSample / 8, format.Format.nSamplesPerSec);
327 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
329 Con_Print("DS:GetCaps failed\n");
334 Con_Print("Using secondary sound buffer\n");
338 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
340 Con_Print("Set coop level failed\n");
345 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
347 Con_Print("DS:GetCaps failed\n");
352 Con_Print("Using primary sound buffer\n");
355 // Make sure mixer is active
356 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
358 Con_DPrintf(" %d channel(s)\n"
361 requested->channels, requested->width * 8, requested->speed);
363 gSndBufSize = dsbcaps.dwBufferBytes;
365 // initialize the buffer
368 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
370 if (hresult != DSERR_BUFFERLOST)
372 Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
379 Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
386 memset(lpData, 0, dwSize);
387 IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
389 IDirectSoundBuffer_Stop(pDSBuf);
390 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
394 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
406 Crappy windows multimedia base
409 static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
411 WAVEFORMATEXTENSIBLE format;
415 if (! SndSys_BuildWaveFormat(requested, &format))
418 // Open a waveform device for output using window callback
419 while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
420 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
422 if (hr != MMSYSERR_ALLOCATED)
424 Con_Print("waveOutOpen failed\n");
428 if (MessageBox (NULL,
429 "The sound hardware is in use by another app.\n\n"
430 "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
431 "Sound not available",
432 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
434 Con_Print("waveOutOpen failure;\n hardware already in use\n");
439 wav_buffer_size = (requested->speed / 2 / WAV_BUFFERS) * requested->channels * requested->width;
442 * Allocate and lock memory for the waveform data. The memory
443 * for waveform data must be globally allocated with
444 * GMEM_MOVEABLE and GMEM_SHARE flags.
446 gSndBufSize = WAV_BUFFERS * wav_buffer_size;
447 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
450 Con_Print("Sound: Out of memory.\n");
454 lpData = GlobalLock(hData);
457 Con_Print("Sound: Failed to lock.\n");
461 memset (lpData, 0, gSndBufSize);
464 * Allocate and lock memory for the header. This memory must
465 * also be globally allocated with GMEM_MOVEABLE and
468 hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
470 if (hWaveHdr == NULL)
472 Con_Print("Sound: Failed to Alloc header.\n");
477 lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
479 if (lpWaveHdr == NULL)
481 Con_Print("Sound: Failed to lock header.\n");
486 memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
488 // After allocation, set up and prepare headers
489 for (i=0 ; i<WAV_BUFFERS ; i++)
491 lpWaveHdr[i].dwBufferLength = wav_buffer_size;
492 lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
494 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
496 Con_Print("Sound: failed to prepare wave headers\n");
502 snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
520 Create "snd_renderbuffer" with the proper sound format if the call is successful
521 May return a suggested format if the requested format isn't available
524 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
529 Con_Print ("SndSys_Init: using the Win32 module\n");
531 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
532 wavonly = (COM_CheckParm ("-wavonly") != 0);
536 stat = SIS_FAILURE; // assume DirectSound won't initialize
541 stat = SndSys_InitDirectSound (requested);
543 if (stat == SIS_SUCCESS)
544 Con_Print("DirectSound initialized\n");
546 Con_Print("DirectSound failed to init\n");
549 // if DirectSound didn't succeed in initializing, try to initialize
550 // waveOut sound, unless DirectSound failed because the hardware is
551 // already allocated (in which case the user has already chosen not
553 if (!dsound_init && (stat != SIS_NOTAVAIL))
555 if (SndSys_InitMmsystem (requested))
556 Con_Print("Wave sound (MMSYSTEM) initialized\n");
558 Con_Print("Wave sound failed to init\n");
561 return (dsound_init || wav_init);
569 Stop the sound card, delete "snd_renderbuffer" and free its other resources
572 void SndSys_Shutdown (void)
576 IDirectSoundBuffer_Stop(pDSBuf);
577 IDirectSoundBuffer_Release(pDSBuf);
580 // only release primary buffer if it's not also the mixing buffer we just released
581 if (pDSPBuf && (pDSBuf != pDSPBuf))
583 IDirectSoundBuffer_Release(pDSPBuf);
588 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
589 IDirectSound_Release(pDS);
594 waveOutReset (hWaveOut);
600 for (i=0 ; i< WAV_BUFFERS ; i++)
601 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
604 waveOutClose (hWaveOut);
608 GlobalUnlock(hWaveHdr);
609 GlobalFree(hWaveHdr);
619 if (snd_renderbuffer != NULL)
621 Mem_Free(snd_renderbuffer);
622 snd_renderbuffer = NULL;
642 Submit the contents of "snd_renderbuffer" to the sound card
645 void SndSys_Submit (void)
650 // DirectSound doesn't need this
654 paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
655 if (paintpot > WAV_BUFFERS * wav_buffer_size)
656 paintpot = WAV_BUFFERS * wav_buffer_size;
657 prev_painted = snd_renderbuffer->endframe;
659 // submit new sound blocks
660 while (paintpot > wav_buffer_size)
662 h = lpWaveHdr + (snd_sent & WAV_MASK);
666 * Now the data block can be sent to the output device. The
667 * waveOutWrite function returns immediately and waveform
668 * data is sent to the output device in the background.
670 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
672 if (wResult != MMSYSERR_NOERROR)
674 Con_Print("Failed to write block to device\n");
679 paintpot -= wav_buffer_size;
689 Returns the number of sample frames consumed since the sound started
692 unsigned int SndSys_GetSoundTime (void)
696 factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
703 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
704 if (dwTime > dwStartTime)
705 diff = dwTime - dwStartTime;
707 diff = gSndBufSize - dwStartTime + dwTime;
708 dwStartTime = dwTime;
710 dsound_time += diff / factor;
716 // Find which sound blocks have completed
719 if (snd_completed == snd_sent)
721 Con_DPrint("Sound overrun\n");
725 if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
728 snd_completed++; // this buffer has been played
731 return (snd_completed * wav_buffer_size) / factor;
738 static DWORD dsound_dwSize;
739 static DWORD dsound_dwSize2;
740 static DWORD *dsound_pbuf;
741 static DWORD *dsound_pbuf2;
745 SndSys_LockRenderBuffer
747 Get the exclusive lock on "snd_renderbuffer"
750 qboolean SndSys_LockRenderBuffer (void)
758 // if the buffer was lost or stopped, restore it and/or restart it
759 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
760 Con_Print("Couldn't get sound buffer status\n");
762 if (dwStatus & DSBSTATUS_BUFFERLOST)
764 Con_Print("DSound buffer is lost!!\n");
765 IDirectSoundBuffer_Restore (pDSBuf);
768 if (!(dwStatus & DSBSTATUS_PLAYING))
769 IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
773 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
775 if (hresult != DSERR_BUFFERLOST)
777 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
785 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
792 if ((void*)dsound_pbuf != snd_renderbuffer->ring)
793 Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
803 SndSys_UnlockRenderBuffer
805 Release the exclusive lock on "snd_renderbuffer"
808 void SndSys_UnlockRenderBuffer (void)
811 IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);