X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=snd_win.c;h=080122487a821d438a0e25eaad3070e4139294f0;hp=2aa42d61ed315f37013f37ce76e7ec7658659587;hb=8c6898ab1f01b8efd78fea454b3f8654c4265ebb;hpb=8dcce44300385b12c46d494c06aadcfa35a8bc14 diff --git a/snd_win.c b/snd_win.c index 2aa42d61..08012248 100644 --- a/snd_win.c +++ b/snd_win.c @@ -8,7 +8,7 @@ of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -17,35 +17,111 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef SUPPORTDIRECTX +#ifndef DIRECTSOUND_VERSION +# define DIRECTSOUND_VERSION 0x0500 /* Version 5.0 */ +#endif +#endif +#include +#include +#ifdef SUPPORTDIRECTX +#include +#endif + +#include "qtypes.h" #include "quakedef.h" -#include "winquake.h" +#include "snd_main.h" + +// ============================================================================== + +#ifndef _WAVEFORMATEXTENSIBLE_ +#define _WAVEFORMATEXTENSIBLE_ +typedef struct +{ + WAVEFORMATEX Format; + union + { + WORD wValidBitsPerSample; // bits of precision + WORD wSamplesPerBlock; // valid if wBitsPerSample==0 + WORD wReserved; // If neither applies, set to zero + } Samples; + DWORD dwChannelMask; // which channels are present in stream + GUID SubFormat; +} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE; +#endif + +#if !defined(WAVE_FORMAT_EXTENSIBLE) +# define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#endif + +// Some speaker positions +#ifndef SPEAKER_FRONT_LEFT +# define SPEAKER_FRONT_LEFT 0x1 +# define SPEAKER_FRONT_RIGHT 0x2 +# define SPEAKER_FRONT_CENTER 0x4 +# define SPEAKER_LOW_FREQUENCY 0x8 +# define SPEAKER_BACK_LEFT 0x10 +# define SPEAKER_BACK_RIGHT 0x20 +# define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 +# define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 +// ... we never use the other values +#endif + +// KSDATAFORMAT_SUBTYPE_PCM = GUID "00000001-0000-0010-8000-00aa00389b71" +static const GUID MY_KSDATAFORMAT_SUBTYPE_PCM = +{ + 0x00000001, + 0x0000, + 0x0010, + { + 0x80, 0x00, + 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + } +}; -#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) +// ============================================================================== + +extern HWND mainwindow; +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"}; +static qboolean sndsys_registeredcvars = false; + +#ifdef SUPPORTDIRECTX HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter); +#endif -// 64K is > 1 second at 16-bit, 22050 Hz -#define WAV_BUFFERS 64 -#define WAV_MASK 0x3F -#define WAV_BUFFER_SIZE 0x0400 -#define SECONDARY_BUFFER_SIZE 0x10000 +// Wave output: queue of this many sound buffers to play, reused cyclically +#define WAV_BUFFERS 16 +#define WAV_MASK (WAV_BUFFERS - 1) +static unsigned int wav_buffer_size; -typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; +// DirectSound output: 64KB in 1 buffer +//#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->width * (fmt_ptr)->channels * (fmt_ptr)->speed / 2) +// LordHavoc: changed this to be a multiple of 32768 +#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->channels * 32768) -static qboolean wavonly; +typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; + +#ifdef SUPPORTDIRECTX static qboolean dsound_init; -static qboolean wav_init; -static qboolean snd_firsttime = true, snd_isdirect, snd_iswave; +static unsigned int dsound_time; static qboolean primary_format_set; +#endif + +static qboolean wav_init; -static int sample16; static int snd_sent, snd_completed; +static int prev_painted; +static unsigned int paintpot; + -/* - * Global variables. Must be visible to window-procedure function - * so it can unlock and free the data block after it has been played. - */ + +/* + * Global variables. Must be visible to window-procedure function + * so it can unlock and free the data block after it has been played. + */ HANDLE hData; HPSTR lpData, lpData2; @@ -53,186 +129,131 @@ HPSTR lpData, lpData2; HGLOBAL hWaveHdr; LPWAVEHDR lpWaveHdr; -HWAVEOUT hWaveOut; +HWAVEOUT hWaveOut; WAVEOUTCAPS wavecaps; DWORD gSndBufSize; -MMTIME mmstarttime; +DWORD dwStartTime; +#ifdef SUPPORTDIRECTX LPDIRECTSOUND pDS; LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; HINSTANCE hInstDS; +#endif -qboolean SNDDMA_InitDirect (void); qboolean SNDDMA_InitWav (void); +#ifdef SUPPORTDIRECTX +sndinitstat SNDDMA_InitDirect (void); +#endif /* ================== -S_BlockSound -================== -*/ -void S_BlockSound (void) -{ - -// DirectSound takes care of blocking itself - if (snd_iswave) - { - snd_blocked++; - - if (snd_blocked == 1) - { - waveOutReset (hWaveOut); - } - } -} - - -/* -================== -S_UnblockSound +SndSys_BuildWaveFormat ================== */ -void S_UnblockSound (void) +static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr) { + WAVEFORMATEX* pfmtex; -// DirectSound takes care of blocking itself - if (snd_iswave) - { - snd_blocked--; - } -} - - -/* -================== -FreeSound -================== -*/ -void FreeSound (void) -{ - int i; - - if (pDSBuf) - { - pDSBuf->lpVtbl->Stop(pDSBuf); - pDSBuf->lpVtbl->Release(pDSBuf); - } + memset (fmt_ptr, 0, sizeof(*fmt_ptr)); -// only release primary buffer if it's not also the mixing buffer we just released - if (pDSPBuf && (pDSBuf != pDSPBuf)) - { - pDSPBuf->lpVtbl->Release(pDSPBuf); - } + pfmtex = &fmt_ptr->Format; + pfmtex->nChannels = requested->channels; + pfmtex->wBitsPerSample = requested->width * 8; + pfmtex->nSamplesPerSec = requested->speed; + pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8; + pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign; - if (pDS) + // LordHavoc: disabled this WAVE_FORMAT_EXTENSIBLE support because it does not seem to be working +#if 0 + if (requested->channels <= 2) { - pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); - pDS->lpVtbl->Release(pDS); +#endif + pfmtex->wFormatTag = WAVE_FORMAT_PCM; + pfmtex->cbSize = 0; +#if 0 } - - if (hWaveOut) + else { - waveOutReset (hWaveOut); - - if (lpWaveHdr) - { - for (i=0 ; i< WAV_BUFFERS ; i++) - waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)); - } - - waveOutClose (hWaveOut); - - if (hWaveHdr) + pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE; + pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format); + fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample; + fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM; + + // Build the channel mask + fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + switch (requested->channels) { - GlobalUnlock(hWaveHdr); - GlobalFree(hWaveHdr); + case 8: + fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; + // no break + case 6: + fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY; + // no break + case 4: + fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; + break; + + default: + Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels); + return false; } - - if (hData) - { - GlobalUnlock(hData); - GlobalFree(hData); - } - } +#endif - pDS = NULL; - pDSBuf = NULL; - pDSPBuf = NULL; - hWaveOut = 0; - hData = 0; - hWaveHdr = 0; - lpData = NULL; - lpWaveHdr = NULL; - dsound_init = false; - wav_init = false; + return true; } +#ifdef SUPPORTDIRECTX /* ================== -SNDDMA_InitDirect +SndSys_InitDirectSound -Direct-Sound support +DirectSound 5 support ================== */ -sndinitstat SNDDMA_InitDirect (void) +static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested) { - DSBUFFERDESC dsbuf; - DSBCAPS dsbcaps; - DWORD dwSize, dwWrite; - DSCAPS dscaps; - WAVEFORMATEX format, pformat; - HRESULT hresult; - int reps; - - memset ((void *)&sn, 0, sizeof (sn)); - - shm = &sn; - - shm->channels = 2; - shm->samplebits = 16; - shm->speed = 11025; - - memset (&format, 0, sizeof(format)); - format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = shm->channels; - format.wBitsPerSample = shm->samplebits; - format.nSamplesPerSec = shm->speed; - format.nBlockAlign = format.nChannels - *format.wBitsPerSample / 8; - format.cbSize = 0; - format.nAvgBytesPerSec = format.nSamplesPerSec - *format.nBlockAlign; + DSBUFFERDESC dsbuf; + DSBCAPS dsbcaps; + DWORD dwSize; + DSCAPS dscaps; + WAVEFORMATEXTENSIBLE format, pformat; + HRESULT hresult; + int reps; + + if (! SndSys_BuildWaveFormat(requested, &format)) + return SIS_FAILURE; if (!hInstDS) { hInstDS = LoadLibrary("dsound.dll"); - + if (hInstDS == NULL) { - Con_SafePrintf ("Couldn't load dsound.dll\n"); + Con_Print("Couldn't load dsound.dll\n"); return SIS_FAILURE; } - pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate"); + pDirectSoundCreate = (HRESULT (__stdcall *)(GUID *, LPDIRECTSOUND *,IUnknown *))GetProcAddress(hInstDS,"DirectSoundCreate"); if (!pDirectSoundCreate) { - Con_SafePrintf ("Couldn't get DS proc addr\n"); + Con_Print("Couldn't get DS proc addr\n"); return SIS_FAILURE; } } - while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK) + while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK) { if (hresult != DSERR_ALLOCATED) { - Con_SafePrintf ("DirectSound create failed\n"); + Con_Print("DirectSound create failed\n"); return SIS_FAILURE; } @@ -242,35 +263,34 @@ sndinitstat SNDDMA_InitDirect (void) "Sound not available", MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) { - Con_SafePrintf ("DirectSoundCreate failure\n" - " hardware already in use\n"); + Con_Print("DirectSoundCreate failure\n hardware already in use\n"); return SIS_NOTAVAIL; } } dscaps.dwSize = sizeof(dscaps); - if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps)) + if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps)) { - Con_SafePrintf ("Couldn't get DS caps\n"); + Con_Print("Couldn't get DS caps\n"); } if (dscaps.dwFlags & DSCAPS_EMULDRIVER) { - Con_SafePrintf ("No DirectSound driver installed\n"); - FreeSound (); + Con_Print("No DirectSound driver installed\n"); + SndSys_Shutdown (); return SIS_FAILURE; } - if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) + if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) { - Con_SafePrintf ("Set coop level failed\n"); - FreeSound (); + Con_Print("Set coop level failed\n"); + SndSys_Shutdown (); return SIS_FAILURE; } -// get access to the primary buffer, if possible, so we can set the -// sound hardware format + // get access to the primary buffer, if possible, so we can set the + // sound hardware format memset (&dsbuf, 0, sizeof(dsbuf)); dsbuf.dwSize = sizeof(DSBUFFERDESC); dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER; @@ -281,178 +301,152 @@ sndinitstat SNDDMA_InitDirect (void) dsbcaps.dwSize = sizeof(dsbcaps); primary_format_set = false; +// COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it if (!COM_CheckParm ("-snoforceformat")) { - if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL)) + if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL)) { pformat = format; - if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat)) + if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat)) { - if (snd_firsttime) - Con_SafePrintf ("Set primary sound buffer format: no\n"); + Con_Print("Set primary sound buffer format: no\n"); } else { - if (snd_firsttime) - Con_SafePrintf ("Set primary sound buffer format: yes\n"); + Con_Print("Set primary sound buffer format: yes\n"); primary_format_set = true; } } } +// COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use if (!primary_format_set || !COM_CheckParm ("-primarysound")) { - // create the secondary buffer we'll actually work with + HRESULT result; + + // create the secondary buffer we'll actually work with memset (&dsbuf, 0, sizeof(dsbuf)); dsbuf.dwSize = sizeof(DSBUFFERDESC); dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE; - dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE; - dsbuf.lpwfxFormat = &format; + dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested); + dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format; memset(&dsbcaps, 0, sizeof(dsbcaps)); dsbcaps.dwSize = sizeof(dsbcaps); - if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) + result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL); + if (result != DS_OK || + requested->channels != format.Format.nChannels || + requested->width != format.Format.wBitsPerSample / 8 || + requested->speed != format.Format.nSamplesPerSec) { - Con_SafePrintf ("DS:CreateSoundBuffer Failed"); - FreeSound (); + Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n", + (int)result, (unsigned)format.Format.nChannels, (unsigned)format.Format.wBitsPerSample / 8, (unsigned)format.Format.nSamplesPerSec); + SndSys_Shutdown (); return SIS_FAILURE; } - shm->channels = format.nChannels; - shm->samplebits = format.wBitsPerSample; - shm->speed = format.nSamplesPerSec; - - if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps)) + if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps)) { - Con_SafePrintf ("DS:GetCaps failed\n"); - FreeSound (); + Con_Print("DS:GetCaps failed\n"); + SndSys_Shutdown (); return SIS_FAILURE; } - if (snd_firsttime) - Con_SafePrintf ("Using secondary sound buffer\n"); + Con_Print("Using secondary sound buffer\n"); } else { - if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY)) + if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY)) { - Con_SafePrintf ("Set coop level failed\n"); - FreeSound (); + Con_Print("Set coop level failed\n"); + SndSys_Shutdown (); return SIS_FAILURE; } - if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps)) + if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps)) { - Con_Printf ("DS:GetCaps failed\n"); + Con_Print("DS:GetCaps failed\n"); return SIS_FAILURE; } pDSBuf = pDSPBuf; - Con_SafePrintf ("Using primary sound buffer\n"); + Con_Print("Using primary sound buffer\n"); } // Make sure mixer is active - pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); - - if (snd_firsttime) - Con_SafePrintf(" %d channel(s)\n" - " %d bits/sample\n" - " %d bytes/sec\n", - shm->channels, shm->samplebits, shm->speed); - + IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + + Con_Printf(" %d channel(s)\n" + " %d bits/sample\n" + " %d samples/sec\n", + requested->channels, requested->width * 8, requested->speed); + gSndBufSize = dsbcaps.dwBufferBytes; -// initialize the buffer + // initialize the buffer reps = 0; - while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &lpData, &dwSize, NULL, NULL, 0)) != DS_OK) + while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { - Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n"); - FreeSound (); + Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n"); + SndSys_Shutdown (); return SIS_FAILURE; } if (++reps > 10000) { - Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n"); - FreeSound (); + Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n"); + SndSys_Shutdown (); return SIS_FAILURE; } } memset(lpData, 0, dwSize); -// lpData[4] = lpData[5] = 0x7f; // force a pop for debugging - - pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0); - - /* we don't want anyone to access the buffer directly w/o locking it first. */ - lpData = NULL; + IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0); - pDSBuf->lpVtbl->Stop(pDSBuf); - pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite); - pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + IDirectSoundBuffer_Stop(pDSBuf); + IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); - shm->soundalive = true; - shm->splitbuffer = false; - shm->samples = gSndBufSize/(shm->samplebits/8); - shm->samplepos = 0; - shm->submission_chunk = 1; - shm->buffer = (unsigned char *) lpData; - sample16 = (shm->samplebits/8) - 1; + dwStartTime = 0; + dsound_time = 0; + snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData); dsound_init = true; return SIS_SUCCESS; } +#endif /* ================== -SNDDM_InitWav +SndSys_InitMmsystem Crappy windows multimedia base ================== */ -qboolean SNDDMA_InitWav (void) +static qboolean SndSys_InitMmsystem (const snd_format_t* requested) { - WAVEFORMATEX format; + WAVEFORMATEXTENSIBLE format; int i; HRESULT hr; - - snd_sent = 0; - snd_completed = 0; - shm = &sn; - - shm->channels = 2; - shm->samplebits = 16; - shm->speed = 11025; - - memset (&format, 0, sizeof(format)); - format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = shm->channels; - format.wBitsPerSample = shm->samplebits; - format.nSamplesPerSec = shm->speed; - format.nBlockAlign = format.nChannels - *format.wBitsPerSample / 8; - format.cbSize = 0; - format.nAvgBytesPerSec = format.nSamplesPerSec - *format.nBlockAlign; - - /* Open a waveform device for output using window callback. */ - while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, - &format, + if (! SndSys_BuildWaveFormat(requested, &format)) + return false; + + // Open a waveform device for output using window callback + while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format, 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR) { if (hr != MMSYSERR_ALLOCATED) { - Con_SafePrintf ("waveOutOpen failed\n"); + Con_Print("waveOutOpen failed\n"); return false; } @@ -462,268 +456,442 @@ qboolean SNDDMA_InitWav (void) "Sound not available", MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) { - Con_SafePrintf ("waveOutOpen failure;\n" - " hardware already in use\n"); + Con_Print("waveOutOpen failure;\n hardware already in use\n"); return false; } - } - - /* - * Allocate and lock memory for the waveform data. The memory - * for waveform data must be globally allocated with - * GMEM_MOVEABLE and GMEM_SHARE flags. - - */ - gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE; - hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); - if (!hData) - { - Con_SafePrintf ("Sound: Out of memory.\n"); - FreeSound (); - return false; } - lpData = GlobalLock(hData); + + wav_buffer_size = bound(128, snd_wav_partitionsize.integer, 8192) * requested->channels * requested->width; + + /* + * Allocate and lock memory for the waveform data. The memory + * for waveform data must be globally allocated with + * GMEM_MOVEABLE and GMEM_SHARE flags. + */ + gSndBufSize = WAV_BUFFERS * wav_buffer_size; + hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); + if (!hData) + { + Con_Print("Sound: Out of memory.\n"); + SndSys_Shutdown (); + return false; + } + lpData = (HPSTR)GlobalLock(hData); if (!lpData) - { - Con_SafePrintf ("Sound: Failed to lock.\n"); - FreeSound (); - return false; - } + { + Con_Print("Sound: Failed to lock.\n"); + SndSys_Shutdown (); + return false; + } memset (lpData, 0, gSndBufSize); - /* - * Allocate and lock memory for the header. This memory must - * also be globally allocated with GMEM_MOVEABLE and - * GMEM_SHARE flags. - */ - hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, - (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); + /* + * Allocate and lock memory for the header. This memory must + * also be globally allocated with GMEM_MOVEABLE and + * GMEM_SHARE flags. + */ + hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); if (hWaveHdr == NULL) - { - Con_SafePrintf ("Sound: Failed to Alloc header.\n"); - FreeSound (); - return false; - } + { + Con_Print("Sound: Failed to Alloc header.\n"); + SndSys_Shutdown (); + return false; + } - lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); + lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); if (lpWaveHdr == NULL) - { - Con_SafePrintf ("Sound: Failed to lock header.\n"); - FreeSound (); - return false; + { + Con_Print("Sound: Failed to lock header.\n"); + SndSys_Shutdown (); + return false; } memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS); - /* After allocation, set up and prepare headers. */ + // After allocation, set up and prepare headers for (i=0 ; isoundalive = true; - shm->splitbuffer = false; - shm->samples = gSndBufSize/(shm->samplebits/8); - shm->samplepos = 0; - shm->submission_chunk = 1; - shm->buffer = (unsigned char *) lpData; - sample16 = (shm->samplebits/8) - 1; + snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData); + + prev_painted = 0; + paintpot = 0; + + snd_sent = 0; + snd_completed = 0; wav_init = true; return true; } + /* -================== -SNDDMA_Init +==================== +SndSys_Init -Try to find a sound device to mix for. -Returns false if nothing is found. -================== +Create "snd_renderbuffer" with the proper sound format if the call is successful +May return a suggested format if the requested format isn't available +==================== */ - -int SNDDMA_Init(void) +qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) { +#ifdef SUPPORTDIRECTX + qboolean wavonly; +#endif sndinitstat stat; - if (COM_CheckParm ("-wavonly")) - wavonly = true; + if (!sndsys_registeredcvars) + { + sndsys_registeredcvars = true; + Cvar_RegisterVariable(&snd_wav_partitionsize); + } - dsound_init = wav_init = 0; + Con_Print ("SndSys_Init: using the Win32 module\n"); + +#ifdef SUPPORTDIRECTX +// COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound + wavonly = (COM_CheckParm ("-wavonly") != 0); + dsound_init = false; +#endif + wav_init = false; stat = SIS_FAILURE; // assume DirectSound won't initialize - /* Init DirectSound */ +#ifdef SUPPORTDIRECTX + // Init DirectSound if (!wavonly) { - if (snd_firsttime || snd_isdirect) - { - stat = SNDDMA_InitDirect ();; + stat = SndSys_InitDirectSound (requested); - if (stat == SIS_SUCCESS) - { - snd_isdirect = true; - - if (snd_firsttime) - Con_SafePrintf ("DirectSound initialized\n"); - } - else - { - snd_isdirect = false; - Con_SafePrintf ("DirectSound failed to init\n"); - } - } + if (stat == SIS_SUCCESS) + Con_Print("DirectSound initialized\n"); + else + Con_Print("DirectSound failed to init\n"); } +#endif -// if DirectSound didn't succeed in initializing, try to initialize -// waveOut sound, unless DirectSound failed because the hardware is -// already allocated (in which case the user has already chosen not -// to have sound) + // if DirectSound didn't succeed in initializing, try to initialize + // waveOut sound, unless DirectSound failed because the hardware is + // already allocated (in which case the user has already chosen not + // to have sound) +#ifdef SUPPORTDIRECTX if (!dsound_init && (stat != SIS_NOTAVAIL)) +#endif + { + if (SndSys_InitMmsystem (requested)) + Con_Print("Wave sound (MMSYSTEM) initialized\n"); + else + Con_Print("Wave sound failed to init\n"); + } + +#ifdef SUPPORTDIRECTX + return (dsound_init || wav_init); +#else + return wav_init; +#endif +} + + +/* +==================== +SndSys_Shutdown + +Stop the sound card, delete "snd_renderbuffer" and free its other resources +==================== +*/ +void SndSys_Shutdown (void) +{ +#ifdef SUPPORTDIRECTX + if (pDSBuf) + { + IDirectSoundBuffer_Stop(pDSBuf); + IDirectSoundBuffer_Release(pDSBuf); + } + + // only release primary buffer if it's not also the mixing buffer we just released + if (pDSPBuf && (pDSBuf != pDSPBuf)) + { + IDirectSoundBuffer_Release(pDSPBuf); + } + + if (pDS) { - if (snd_firsttime || snd_iswave) + IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); + IDirectSound_Release(pDS); + } +#endif + + if (hWaveOut) + { + waveOutReset (hWaveOut); + + if (lpWaveHdr) { + unsigned int i; + + for (i=0 ; i< WAV_BUFFERS ; i++) + waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)); + } - snd_iswave = SNDDMA_InitWav (); + waveOutClose (hWaveOut); - if (snd_iswave) - { - if (snd_firsttime) - Con_SafePrintf ("Wave sound initialized\n"); - } - else - { - Con_SafePrintf ("Wave sound failed to init\n"); - } + if (hWaveHdr) + { + GlobalUnlock(hWaveHdr); + GlobalFree(hWaveHdr); + } + + if (hData) + { + GlobalUnlock(hData); + GlobalFree(hData); } } - snd_firsttime = false; + if (snd_renderbuffer != NULL) + { + Mem_Free(snd_renderbuffer); + snd_renderbuffer = NULL; + } + +#ifdef SUPPORTDIRECTX + pDS = NULL; + pDSBuf = NULL; + pDSPBuf = NULL; + dsound_init = false; +#endif + hWaveOut = 0; + hData = 0; + hWaveHdr = 0; + lpData = NULL; + lpWaveHdr = NULL; + wav_init = false; +} + + +/* +==================== +SndSys_Submit - if (!dsound_init && !wav_init) +Submit the contents of "snd_renderbuffer" to the sound card +==================== +*/ +void SndSys_Submit (void) +{ + LPWAVEHDR h; + int wResult; + + // DirectSound doesn't need this + if (!wav_init) + return; + + paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width; + if (paintpot > WAV_BUFFERS * wav_buffer_size) + paintpot = WAV_BUFFERS * wav_buffer_size; + prev_painted = snd_renderbuffer->endframe; + + // submit new sound blocks + while (paintpot > wav_buffer_size) { -// if (snd_firsttime) -// Con_SafePrintf ("No sound device initialized\n"); + h = lpWaveHdr + (snd_sent & WAV_MASK); + + /* + * Now the data block can be sent to the output device. The + * waveOutWrite function returns immediately and waveform + * data is sent to the output device in the background. + */ + wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); + if (wResult == MMSYSERR_NOERROR) + snd_sent++; + else if (wResult == WAVERR_STILLPLAYING) + { + if(developer_insane.integer) + Con_DPrint("waveOutWrite failed (too much sound data)\n"); + //h->dwFlags |= WHDR_DONE; + //snd_sent++; + } + else + { + Con_Printf("waveOutWrite failed, error code %d\n", (int) wResult); + SndSys_Shutdown (); + return; + } - return 0; + paintpot -= wav_buffer_size; } - return 1; } + /* -============== -SNDDMA_GetDMAPos +==================== +SndSys_GetSoundTime -return the current sample position (in mono samples read) -inside the recirculating dma buffer, so the mixing code will know -how many sample are required to fill it up. -=============== +Returns the number of sample frames consumed since the sound started +==================== */ -int SNDDMA_GetDMAPos(void) +unsigned int SndSys_GetSoundTime (void) { - MMTIME mmtime; - int s; - DWORD dwWrite; + unsigned int factor; - if (dsound_init) + factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels; + +#ifdef SUPPORTDIRECTX + if (dsound_init) { - mmtime.wType = TIME_SAMPLES; - pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite); - s = mmtime.u.sample - mmstarttime.u.sample; + DWORD dwTime; + unsigned int diff; + + IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL); + diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize; + dwStartTime = dwTime; + + dsound_time += diff / factor; + return dsound_time; } - else if (wav_init) +#endif + + if (wav_init) { - s = snd_sent * WAV_BUFFER_SIZE; - } + // Find which sound blocks have completed + for (;;) + { + if (snd_completed == snd_sent) + { + // Con_DPrint("Sound overrun\n"); + break; + } + + if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE)) + break; + + snd_completed++; // this buffer has been played + } + return (snd_completed * wav_buffer_size) / factor; - s >>= sample16; + /* + * S_PaintAndSubmit: WARNING: newsoundtime (soundtime (275 < 134217707) + * apparently this sound time wraps quite early? + { + MMRESULT res; + MMTIME mmtime; - s &= (shm->samples-1); + mmtime.wType = TIME_SAMPLES; + res = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime)); + if(res == MMSYSERR_NOERROR) + return mmtime.u.sample; + } + */ + } - return s; + return 0; } + +#ifdef SUPPORTDIRECTX +static DWORD dsound_dwSize; +static DWORD dsound_dwSize2; +static DWORD *dsound_pbuf; +static DWORD *dsound_pbuf2; +#endif + /* -============== -SNDDMA_Submit +==================== +SndSys_LockRenderBuffer -Send sound to device if buffer isn't really the dma buffer -=============== +Get the exclusive lock on "snd_renderbuffer" +==================== */ -void SNDDMA_Submit(void) +qboolean SndSys_LockRenderBuffer (void) { - LPWAVEHDR h; - int wResult; - - if (!wav_init) - return; +#ifdef SUPPORTDIRECTX + int reps; + HRESULT hresult; + DWORD dwStatus; - // - // find which sound blocks have completed - // - while (1) + if (pDSBuf) { - if ( snd_completed == snd_sent ) + // if the buffer was lost or stopped, restore it and/or restart it + if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK) + Con_Print("Couldn't get sound buffer status\n"); + + if (dwStatus & DSBSTATUS_BUFFERLOST) { - Con_DPrintf ("Sound overrun\n"); - break; + Con_Print("DSound buffer is lost!!\n"); + IDirectSoundBuffer_Restore (pDSBuf); } - if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) ) + if (!(dwStatus & DSBSTATUS_PLAYING)) + IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + + reps = 0; + + while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK) { - break; + if (hresult != DSERR_BUFFERLOST) + { + Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n"); + S_Shutdown (); + S_Startup (); + return false; + } + + if (++reps > 10000) + { + Con_Print("S_LockBuffer: DS: couldn't restore buffer\n"); + S_Shutdown (); + S_Startup (); + return false; + } } - snd_completed++; // this buffer has been played + if ((void*)dsound_pbuf != snd_renderbuffer->ring) + Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n"); + return true; } +#endif - // - // submit two new sound blocks - // - while (((snd_sent - snd_completed) >> sample16) < 4) - { - h = lpWaveHdr + ( snd_sent&WAV_MASK ); - - snd_sent++; - /* - * Now the data block can be sent to the output device. The - * waveOutWrite function returns immediately and waveform - * data is sent to the output device in the background. - */ - wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); - - if (wResult != MMSYSERR_NOERROR) - { - Con_SafePrintf ("Failed to write block to device\n"); - FreeSound (); - return; - } - } + return wav_init; } + /* -============== -SNDDMA_Shutdown +==================== +SndSys_UnlockRenderBuffer -Reset the sound device for exiting -=============== +Release the exclusive lock on "snd_renderbuffer" +==================== */ -void SNDDMA_Shutdown(void) +void SndSys_UnlockRenderBuffer (void) { - FreeSound (); +#ifdef SUPPORTDIRECTX + if (pDSBuf) + IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2); +#endif } +/* +==================== +SndSys_SendKeyEvents + +Send keyboard events originating from the sound system (e.g. MIDI) +==================== +*/ +void SndSys_SendKeyEvents(void) +{ + // not supported +}