📄 snd_win.c
字号:
/*
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
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.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <float.h>
#include "../client/client.h"
#include "../client/snd_loc.h"
#include "winquake.h"
#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c)
HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
// 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
typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
cvar_t *s_wavonly;
static qboolean dsound_init;
static qboolean wav_init;
static qboolean snd_firsttime = true, snd_isdirect, snd_iswave;
static qboolean primary_format_set;
// starts at 0 for disabled
static int snd_buffer_count = 0;
static int sample16;
static int snd_sent, snd_completed;
/*
* 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;
HGLOBAL hWaveHdr;
LPWAVEHDR lpWaveHdr;
HWAVEOUT hWaveOut;
WAVEOUTCAPS wavecaps;
DWORD gSndBufSize;
MMTIME mmstarttime;
LPDIRECTSOUND pDS;
LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
HINSTANCE hInstDS;
qboolean SNDDMA_InitDirect (void);
qboolean SNDDMA_InitWav (void);
void FreeSound( void );
static const char *DSoundError( int error )
{
switch ( error )
{
case DSERR_BUFFERLOST:
return "DSERR_BUFFERLOST";
case DSERR_INVALIDCALL:
return "DSERR_INVALIDCALLS";
case DSERR_INVALIDPARAM:
return "DSERR_INVALIDPARAM";
case DSERR_PRIOLEVELNEEDED:
return "DSERR_PRIOLEVELNEEDED";
}
return "unknown";
}
/*
** DS_CreateBuffers
*/
static qboolean DS_CreateBuffers( void )
{
DSBUFFERDESC dsbuf;
DSBCAPS dsbcaps;
WAVEFORMATEX pformat, format;
DWORD dwWrite;
memset (&format, 0, sizeof(format));
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = dma.channels;
format.wBitsPerSample = dma.samplebits;
format.nSamplesPerSec = dma.speed;
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
format.cbSize = 0;
format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign;
Com_Printf( "Creating DS buffers\n" );
Com_DPrintf("...setting EXCLUSIVE coop level: " );
if ( DS_OK != pDS->lpVtbl->SetCooperativeLevel( pDS, cl_hwnd, DSSCL_EXCLUSIVE ) )
{
Com_Printf ("failed\n");
FreeSound ();
return false;
}
Com_DPrintf("ok\n" );
// 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;
dsbuf.dwBufferBytes = 0;
dsbuf.lpwfxFormat = NULL;
memset(&dsbcaps, 0, sizeof(dsbcaps));
dsbcaps.dwSize = sizeof(dsbcaps);
primary_format_set = false;
Com_DPrintf( "...creating primary buffer: " );
if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
{
pformat = format;
Com_DPrintf( "ok\n" );
if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
{
if (snd_firsttime)
Com_DPrintf ("...setting primary sound format: failed\n");
}
else
{
if (snd_firsttime)
Com_DPrintf ("...setting primary sound format: ok\n");
primary_format_set = true;
}
}
else
Com_Printf( "failed\n" );
if ( !primary_format_set || !s_primary->value)
{
// 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;
memset(&dsbcaps, 0, sizeof(dsbcaps));
dsbcaps.dwSize = sizeof(dsbcaps);
Com_DPrintf( "...creating secondary buffer: " );
if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
{
Com_Printf( "failed\n" );
FreeSound ();
return false;
}
Com_DPrintf( "ok\n" );
dma.channels = format.nChannels;
dma.samplebits = format.wBitsPerSample;
dma.speed = format.nSamplesPerSec;
if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
{
Com_Printf ("*** GetCaps failed ***\n");
FreeSound ();
return false;
}
Com_Printf ("...using secondary sound buffer\n");
}
else
{
Com_Printf( "...using primary buffer\n" );
Com_DPrintf( "...setting WRITEPRIMARY coop level: " );
if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, cl_hwnd, DSSCL_WRITEPRIMARY))
{
Com_Printf( "failed\n" );
FreeSound ();
return false;
}
Com_DPrintf( "ok\n" );
if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
{
Com_Printf ("*** GetCaps failed ***\n");
return false;
}
pDSBuf = pDSPBuf;
}
// Make sure mixer is active
pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
if (snd_firsttime)
Com_Printf(" %d channel(s)\n"
" %d bits/sample\n"
" %d bytes/sec\n",
dma.channels, dma.samplebits, dma.speed);
gSndBufSize = dsbcaps.dwBufferBytes;
/* we don't want anyone to access the buffer directly w/o locking it first. */
lpData = NULL;
pDSBuf->lpVtbl->Stop(pDSBuf);
pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
dma.samples = gSndBufSize/(dma.samplebits/8);
dma.samplepos = 0;
dma.submission_chunk = 1;
dma.buffer = (unsigned char *) lpData;
sample16 = (dma.samplebits/8) - 1;
return true;
}
/*
** DS_DestroyBuffers
*/
static void DS_DestroyBuffers( void )
{
Com_DPrintf( "Destroying DS buffers\n" );
if ( pDS )
{
Com_DPrintf( "...setting NORMAL coop level\n" );
pDS->lpVtbl->SetCooperativeLevel( pDS, cl_hwnd, DSSCL_NORMAL );
}
if ( pDSBuf )
{
Com_DPrintf( "...stopping and releasing sound buffer\n" );
pDSBuf->lpVtbl->Stop( pDSBuf );
pDSBuf->lpVtbl->Release( pDSBuf );
}
// only release primary buffer if it's not also the mixing buffer we just released
if ( pDSPBuf && ( pDSBuf != pDSPBuf ) )
{
Com_DPrintf( "...releasing primary buffer\n" );
pDSPBuf->lpVtbl->Release( pDSPBuf );
}
pDSBuf = NULL;
pDSPBuf = NULL;
dma.buffer = NULL;
}
/*
==================
FreeSound
==================
*/
void FreeSound (void)
{
int i;
Com_DPrintf( "Shutting down sound system\n" );
if ( pDS )
DS_DestroyBuffers();
if ( hWaveOut )
{
Com_DPrintf( "...resetting waveOut\n" );
waveOutReset (hWaveOut);
if (lpWaveHdr)
{
Com_DPrintf( "...unpreparing headers\n" );
for (i=0 ; i< WAV_BUFFERS ; i++)
waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
}
Com_DPrintf( "...closing waveOut\n" );
waveOutClose (hWaveOut);
if (hWaveHdr)
{
Com_DPrintf( "...freeing WAV header\n" );
GlobalUnlock(hWaveHdr);
GlobalFree(hWaveHdr);
}
if (hData)
{
Com_DPrintf( "...freeing WAV buffer\n" );
GlobalUnlock(hData);
GlobalFree(hData);
}
}
if ( pDS )
{
Com_DPrintf( "...releasing DS object\n" );
pDS->lpVtbl->Release( pDS );
}
if ( hInstDS )
{
Com_DPrintf( "...freeing DSOUND.DLL\n" );
FreeLibrary( hInstDS );
hInstDS = NULL;
}
pDS = NULL;
pDSBuf = NULL;
pDSPBuf = NULL;
hWaveOut = 0;
hData = 0;
hWaveHdr = 0;
lpData = NULL;
lpWaveHdr = NULL;
dsound_init = false;
wav_init = false;
}
/*
==================
SNDDMA_InitDirect
Direct-Sound support
==================
*/
sndinitstat SNDDMA_InitDirect (void)
{
DSCAPS dscaps;
HRESULT hresult;
dma.channels = 2;
dma.samplebits = 16;
if (s_khz->value == 44)
dma.speed = 44100;
if (s_khz->value == 22)
dma.speed = 22050;
else
dma.speed = 11025;
Com_Printf( "Initializing DirectSound\n");
if ( !hInstDS )
{
Com_DPrintf( "...loading dsound.dll: " );
hInstDS = LoadLibrary("dsound.dll");
if (hInstDS == NULL)
{
Com_Printf ("failed\n");
return SIS_FAILURE;
}
Com_DPrintf ("ok\n");
pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
if (!pDirectSoundCreate)
{
Com_Printf ("*** couldn't get DS proc addr ***\n");
return SIS_FAILURE;
}
}
Com_DPrintf( "...creating DS object: " );
while ( ( hresult = iDirectSoundCreate( NULL, &pDS, NULL ) ) != DS_OK )
{
if (hresult != DSERR_ALLOCATED)
{
Com_Printf( "failed\n" );
return SIS_FAILURE;
}
if (MessageBox (NULL,
"The sound hardware is in use by another app.\n\n"
"Select Retry to try to start sound again or Cancel to run Quake with no sound.",
"Sound not available",
MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
{
Com_Printf ("failed, hardware already in use\n" );
return SIS_NOTAVAIL;
}
}
Com_DPrintf( "ok\n" );
dscaps.dwSize = sizeof(dscaps);
if ( DS_OK != pDS->lpVtbl->GetCaps( pDS, &dscaps ) )
{
Com_Printf ("*** couldn't get DS caps ***\n");
}
if ( dscaps.dwFlags & DSCAPS_EMULDRIVER )
{
Com_DPrintf ("...no DSound driver found\n" );
FreeSound();
return SIS_FAILURE;
}
if ( !DS_CreateBuffers() )
return SIS_FAILURE;
dsound_init = true;
Com_DPrintf("...completed successfully\n" );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -