2004-08-23 00:15:46 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 1996-1997 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 "quakedef.h"
|
|
|
|
#include "winquake.h"
|
|
|
|
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c)
|
|
|
|
#define iDirectSoundEnumerate(a,b,c) pDirectSoundEnumerate(a,b)
|
|
|
|
|
|
|
|
HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
|
|
|
|
HRESULT (WINAPI *pDirectSoundCaptureCreate)(GUID FAR *lpGUID, LPDIRECTSOUNDCAPTURE FAR *lplpDS, IUnknown FAR *pUnkOuter);
|
|
|
|
HRESULT (WINAPI *pDirectSoundEnumerate)(LPDSENUMCALLBACKA lpCallback, LPVOID lpContext );
|
|
|
|
#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
|
|
|
|
|
|
|
|
typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL, SIS_NOMORE} sndinitstat;
|
|
|
|
|
|
|
|
static qboolean wavonly;
|
|
|
|
//static qboolean dsound_init;
|
|
|
|
//static qboolean wav_init;
|
|
|
|
qboolean snd_firsttime = true;
|
|
|
|
static qboolean primary_format_set;
|
|
|
|
|
|
|
|
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 (soundcardinfo_t *sc);
|
|
|
|
qboolean SNDDMA_InitWav (soundcardinfo_t *sc);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
soundcardinfo_t *sndcardinfo;
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
S_BlockSound
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
//all devices
|
|
|
|
void S_BlockSound (void)
|
|
|
|
{
|
|
|
|
soundcardinfo_t *sc;
|
|
|
|
|
|
|
|
snd_blocked++;
|
|
|
|
|
|
|
|
for (sc = sndcardinfo; sc; sc=sc->next)
|
|
|
|
{
|
|
|
|
if (sc->snd_iswave)
|
|
|
|
if (snd_blocked == 1)
|
|
|
|
waveOutReset (sc->hWaveOut);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
S_UnblockSound
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
//all devices
|
|
|
|
void S_UnblockSound (void)
|
|
|
|
{
|
|
|
|
snd_blocked--;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
FreeSound
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
//per device
|
|
|
|
void FreeSound (soundcardinfo_t *sc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
if (sc->EaxKsPropertiesSet)
|
|
|
|
{
|
|
|
|
IKsPropertySet_Release(sc->EaxKsPropertiesSet);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->pDSBuf)
|
|
|
|
{
|
|
|
|
sc->pDSBuf->lpVtbl->Stop(sc->pDSBuf);
|
|
|
|
sc->pDSBuf->lpVtbl->Release(sc->pDSBuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
// only release primary buffer if it's not also the mixing buffer we just released
|
|
|
|
if (sc->pDSPBuf && (sc->pDSBuf != sc->pDSPBuf))
|
|
|
|
{
|
|
|
|
sc->pDSPBuf->lpVtbl->Release(sc->pDSPBuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->pDS)
|
|
|
|
{
|
|
|
|
sc->pDS->lpVtbl->SetCooperativeLevel (sc->pDS, mainwindow, DSSCL_NORMAL);
|
|
|
|
sc->pDS->lpVtbl->Release(sc->pDS);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (sc->hWaveOut)
|
|
|
|
{
|
|
|
|
waveOutReset (sc->hWaveOut);
|
|
|
|
|
|
|
|
if (sc->lpWaveHdr)
|
|
|
|
{
|
|
|
|
for (i=0 ; i< WAV_BUFFERS ; i++)
|
|
|
|
waveOutUnprepareHeader (sc->hWaveOut, sc->lpWaveHdr+i, sizeof(WAVEHDR));
|
|
|
|
}
|
|
|
|
|
|
|
|
waveOutClose (sc->hWaveOut);
|
|
|
|
|
|
|
|
if (sc->hWaveHdr)
|
|
|
|
{
|
|
|
|
GlobalUnlock(sc->hWaveHdr);
|
|
|
|
GlobalFree(sc->hWaveHdr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->hData)
|
|
|
|
{
|
|
|
|
GlobalUnlock(sc->hData);
|
|
|
|
GlobalFree(sc->hData);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
sc->pDS = NULL;
|
|
|
|
sc->pDSBuf = NULL;
|
|
|
|
sc->pDSPBuf = NULL;
|
|
|
|
sc->EaxKsPropertiesSet = NULL;
|
|
|
|
sc->dsound_init = false;
|
|
|
|
#endif
|
|
|
|
sc->hWaveOut = 0;
|
|
|
|
sc->hData = 0;
|
|
|
|
sc->hWaveHdr = 0;
|
|
|
|
sc->lpData = NULL;
|
|
|
|
sc->lpWaveHdr = NULL;
|
|
|
|
sc->wav_init = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
const char *dsndcard;
|
|
|
|
GUID FAR *dsndguid;
|
|
|
|
int dsnd_guids;
|
|
|
|
int aimedforguid;
|
|
|
|
BOOL (CALLBACK DSEnumCallback)(GUID FAR *guid, LPCSTR str1, LPCSTR str2, LPVOID parm)
|
|
|
|
{
|
|
|
|
if (guid == NULL)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
if (aimedforguid == dsnd_guids)
|
|
|
|
{
|
|
|
|
dsndcard = str1;
|
|
|
|
dsndguid = guid;
|
|
|
|
}
|
|
|
|
dsnd_guids++;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Direct Sound.
|
|
|
|
These following defs should be moved to winquake.h somewhere.
|
|
|
|
|
|
|
|
We tell DS to use a different wave format. We do this to gain extra channels. >2
|
|
|
|
We still use the old stuff too, when we can for compatability.
|
|
|
|
|
|
|
|
EAX 2 is also supported.
|
|
|
|
This is a global state. Once applied, it's applied for other programs too.
|
|
|
|
We have to do a few special things to try to ensure support in all it's different versions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* new formatTag:*/
|
|
|
|
# define WAVE_FORMAT_EXTENSIBLE (0xfffe)
|
|
|
|
|
|
|
|
/* Speaker Positions:*/
|
|
|
|
# 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
|
|
|
|
# define SPEAKER_BACK_CENTER 0x100
|
|
|
|
# define SPEAKER_SIDE_LEFT 0x200
|
|
|
|
# define SPEAKER_SIDE_RIGHT 0x400
|
|
|
|
# define SPEAKER_TOP_CENTER 0x800
|
|
|
|
# define SPEAKER_TOP_FRONT_LEFT 0x1000
|
|
|
|
# define SPEAKER_TOP_FRONT_CENTER 0x2000
|
|
|
|
# define SPEAKER_TOP_FRONT_RIGHT 0x4000
|
|
|
|
# define SPEAKER_TOP_BACK_LEFT 0x8000
|
|
|
|
# define SPEAKER_TOP_BACK_CENTER 0x10000
|
|
|
|
# define SPEAKER_TOP_BACK_RIGHT 0x20000
|
|
|
|
|
|
|
|
/* Bit mask locations reserved for future use*/
|
|
|
|
# define SPEAKER_RESERVED 0x7FFC0000
|
|
|
|
|
|
|
|
/* Used to specify that any possible permutation of speaker configurations*/
|
|
|
|
# define SPEAKER_ALL 0x80000000
|
|
|
|
|
|
|
|
/* DirectSound Speaker Config*/
|
|
|
|
# define KSAUDIO_SPEAKER_MONO (SPEAKER_FRONT_CENTER)
|
|
|
|
# define KSAUDIO_SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
|
|
|
|
# define KSAUDIO_SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
|
|
|
|
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
|
|
|
|
# define KSAUDIO_SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
|
|
|
|
SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER)
|
|
|
|
# define KSAUDIO_SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
|
|
|
|
SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \
|
|
|
|
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
|
|
|
|
# define KSAUDIO_SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
|
|
|
|
SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \
|
|
|
|
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \
|
|
|
|
SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER)
|
|
|
|
|
|
|
|
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;
|
|
|
|
} QWAVEFORMATEX;
|
|
|
|
|
|
|
|
const static GUID KSDATAFORMAT_SUBTYPE_PCM = {0x00000001,0x0000,0x0010,
|
|
|
|
{0x80,
|
|
|
|
0x00,
|
|
|
|
0x00,
|
|
|
|
0xaa,
|
|
|
|
0x00,
|
|
|
|
0x38,
|
|
|
|
0x9b,
|
|
|
|
0x71}};
|
|
|
|
|
|
|
|
#ifdef _IKsPropertySet_
|
|
|
|
const static GUID CLSID_EAXDIRECTSOUND = {0x4ff53b81, 0x1ce0, 0x11d3,
|
2004-09-15 03:09:55 +00:00
|
|
|
{0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5}};
|
2004-08-23 00:15:46 +00:00
|
|
|
const static GUID DSPROPSETID_EAX20_LISTENERPROPERTIES = {0x306a6a8, 0xb224, 0x11d2,
|
2004-09-15 03:09:55 +00:00
|
|
|
{0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22}};
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
typedef struct _EAXLISTENERPROPERTIES
|
|
|
|
{
|
|
|
|
long lRoom; // room effect level at low frequencies
|
|
|
|
long lRoomHF; // room effect high-frequency level re. low frequency level
|
|
|
|
float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect
|
|
|
|
float flDecayTime; // reverberation decay time at low frequencies
|
|
|
|
float flDecayHFRatio; // high-frequency to low-frequency decay time ratio
|
|
|
|
long lReflections; // early reflections level relative to room effect
|
|
|
|
float flReflectionsDelay; // initial reflection delay time
|
|
|
|
long lReverb; // late reverberation level relative to room effect
|
|
|
|
float flReverbDelay; // late reverberation delay time relative to initial reflection
|
|
|
|
unsigned long dwEnvironment; // sets all listener properties
|
|
|
|
float flEnvironmentSize; // environment size in meters
|
|
|
|
float flEnvironmentDiffusion; // environment diffusion
|
|
|
|
float flAirAbsorptionHF; // change in level per meter at 5 kHz
|
|
|
|
unsigned long dwFlags; // modifies the behavior of properties
|
|
|
|
} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES;
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
EAX_ENVIRONMENT_GENERIC,
|
|
|
|
EAX_ENVIRONMENT_PADDEDCELL,
|
|
|
|
EAX_ENVIRONMENT_ROOM,
|
|
|
|
EAX_ENVIRONMENT_BATHROOM,
|
|
|
|
EAX_ENVIRONMENT_LIVINGROOM,
|
|
|
|
EAX_ENVIRONMENT_STONEROOM,
|
|
|
|
EAX_ENVIRONMENT_AUDITORIUM,
|
|
|
|
EAX_ENVIRONMENT_CONCERTHALL,
|
|
|
|
EAX_ENVIRONMENT_CAVE,
|
|
|
|
EAX_ENVIRONMENT_ARENA,
|
|
|
|
EAX_ENVIRONMENT_HANGAR,
|
|
|
|
EAX_ENVIRONMENT_CARPETEDHALLWAY,
|
|
|
|
EAX_ENVIRONMENT_HALLWAY,
|
|
|
|
EAX_ENVIRONMENT_STONECORRIDOR,
|
|
|
|
EAX_ENVIRONMENT_ALLEY,
|
|
|
|
EAX_ENVIRONMENT_FOREST,
|
|
|
|
EAX_ENVIRONMENT_CITY,
|
|
|
|
EAX_ENVIRONMENT_MOUNTAINS,
|
|
|
|
EAX_ENVIRONMENT_QUARRY,
|
|
|
|
EAX_ENVIRONMENT_PLAIN,
|
|
|
|
EAX_ENVIRONMENT_PARKINGLOT,
|
|
|
|
EAX_ENVIRONMENT_SEWERPIPE,
|
|
|
|
EAX_ENVIRONMENT_UNDERWATER,
|
|
|
|
EAX_ENVIRONMENT_DRUGGED,
|
|
|
|
EAX_ENVIRONMENT_DIZZY,
|
|
|
|
EAX_ENVIRONMENT_PSYCHOTIC,
|
|
|
|
|
|
|
|
EAX_ENVIRONMENT_COUNT
|
|
|
|
};
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
DSPROPERTY_EAXLISTENER_NONE,
|
|
|
|
DSPROPERTY_EAXLISTENER_ALLPARAMETERS,
|
|
|
|
DSPROPERTY_EAXLISTENER_ROOM,
|
|
|
|
DSPROPERTY_EAXLISTENER_ROOMHF,
|
|
|
|
DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR,
|
|
|
|
DSPROPERTY_EAXLISTENER_DECAYTIME,
|
|
|
|
DSPROPERTY_EAXLISTENER_DECAYHFRATIO,
|
|
|
|
DSPROPERTY_EAXLISTENER_REFLECTIONS,
|
|
|
|
DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY,
|
|
|
|
DSPROPERTY_EAXLISTENER_REVERB,
|
|
|
|
DSPROPERTY_EAXLISTENER_REVERBDELAY,
|
|
|
|
DSPROPERTY_EAXLISTENER_ENVIRONMENT,
|
|
|
|
DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE,
|
|
|
|
DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION,
|
|
|
|
DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF,
|
|
|
|
DSPROPERTY_EAXLISTENER_FLAGS
|
|
|
|
} DSPROPERTY_EAX_LISTENERPROPERTY;
|
|
|
|
|
|
|
|
const static GUID DSPROPSETID_EAX20_BUFFERPROPERTIES ={
|
|
|
|
0x306a6a7,
|
|
|
|
0xb224,
|
|
|
|
0x11d2,
|
2004-09-15 03:09:55 +00:00
|
|
|
{0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22}};
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
const static GUID CLSID_EAXDirectSound ={
|
|
|
|
0x4ff53b81,
|
|
|
|
0x1ce0,
|
|
|
|
0x11d3,
|
2004-09-15 03:09:55 +00:00
|
|
|
{0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5}};
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
typedef struct _EAXBUFFERPROPERTIES
|
|
|
|
{
|
|
|
|
long lDirect; // direct path level
|
|
|
|
long lDirectHF; // direct path level at high frequencies
|
|
|
|
long lRoom; // room effect level
|
|
|
|
long lRoomHF; // room effect level at high frequencies
|
|
|
|
float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect
|
|
|
|
long lObstruction; // main obstruction control (attenuation at high frequencies)
|
|
|
|
float flObstructionLFRatio; // obstruction low-frequency level re. main control
|
|
|
|
long lOcclusion; // main occlusion control (attenuation at high frequencies)
|
|
|
|
float flOcclusionLFRatio; // occlusion low-frequency level re. main control
|
|
|
|
float flOcclusionRoomRatio; // occlusion room effect level re. main control
|
|
|
|
long lOutsideVolumeHF; // outside sound cone level at high frequencies
|
|
|
|
float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF
|
|
|
|
unsigned long dwFlags; // modifies the behavior of properties
|
|
|
|
} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES;
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
DSPROPERTY_EAXBUFFER_NONE,
|
|
|
|
DSPROPERTY_EAXBUFFER_ALLPARAMETERS,
|
|
|
|
DSPROPERTY_EAXBUFFER_DIRECT,
|
|
|
|
DSPROPERTY_EAXBUFFER_DIRECTHF,
|
|
|
|
DSPROPERTY_EAXBUFFER_ROOM,
|
|
|
|
DSPROPERTY_EAXBUFFER_ROOMHF,
|
|
|
|
DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR,
|
|
|
|
DSPROPERTY_EAXBUFFER_OBSTRUCTION,
|
|
|
|
DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO,
|
|
|
|
DSPROPERTY_EAXBUFFER_OCCLUSION,
|
|
|
|
DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO,
|
|
|
|
DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO,
|
|
|
|
DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF,
|
|
|
|
DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR,
|
|
|
|
DSPROPERTY_EAXBUFFER_FLAGS
|
|
|
|
} DSPROPERTY_EAX_BUFFERPROPERTY;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
SNDDMA_InitDirect
|
|
|
|
|
|
|
|
Direct-Sound support
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
sndinitstat SNDDMA_InitDirect (soundcardinfo_t *sc)
|
|
|
|
{
|
|
|
|
extern cvar_t snd_khz, snd_eax, snd_speakers;
|
|
|
|
DSBUFFERDESC dsbuf;
|
|
|
|
DSBCAPS dsbcaps;
|
|
|
|
DWORD dwSize, dwWrite;
|
|
|
|
DSCAPS dscaps;
|
|
|
|
QWAVEFORMATEX format, pformat;
|
|
|
|
HRESULT hresult;
|
|
|
|
int reps;
|
|
|
|
|
|
|
|
memset ((void *)&sc->sn, 0, sizeof (sc->sn));
|
|
|
|
|
|
|
|
sc->sn.numchannels = 2;
|
|
|
|
sc->sn.samplebits = 16;
|
|
|
|
if (!sc->sn.speed)
|
|
|
|
{
|
2004-09-20 23:25:38 +00:00
|
|
|
if (snd_khz.value >= 45)
|
2004-08-23 00:15:46 +00:00
|
|
|
sc->sn.speed = 48000;
|
2004-09-20 23:25:38 +00:00
|
|
|
else if (snd_khz.value >= 30) //set by a slider
|
2004-08-23 00:15:46 +00:00
|
|
|
sc->sn.speed = 44100;
|
2004-09-20 23:25:38 +00:00
|
|
|
else if (snd_khz.value >= 20)
|
2004-08-23 00:15:46 +00:00
|
|
|
sc->sn.speed = 22050;
|
|
|
|
else
|
|
|
|
sc->sn.speed = 11025;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset (&format, 0, sizeof(format));
|
|
|
|
|
2004-09-20 23:25:38 +00:00
|
|
|
if (snd_speakers.value >= 5) //5.1 surround
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
|
|
format.Format.cbSize = 22;
|
|
|
|
memcpy(&format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID));
|
|
|
|
|
|
|
|
format.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
|
|
|
|
sc->sn.numchannels = 6;
|
|
|
|
}
|
2004-09-20 23:25:38 +00:00
|
|
|
else if (snd_speakers.value >= 3) //4 speaker quad
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
|
|
format.Format.cbSize = 22;
|
|
|
|
memcpy(&format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID));
|
|
|
|
|
|
|
|
format.dwChannelMask = KSAUDIO_SPEAKER_QUAD;
|
|
|
|
sc->sn.numchannels = 4;
|
|
|
|
}
|
2004-09-20 23:25:38 +00:00
|
|
|
else if (snd_speakers.value >= 1.5) //stereo
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
format.Format.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
format.Format.cbSize = 0;
|
|
|
|
sc->sn.numchannels = 2;
|
|
|
|
}
|
|
|
|
else //mono time
|
|
|
|
{
|
|
|
|
format.Format.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
format.Format.cbSize = 0;
|
|
|
|
sc->sn.numchannels = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
format.Format.nChannels = sc->sn.numchannels;
|
|
|
|
format.Format.wBitsPerSample = sc->sn.samplebits;
|
|
|
|
format.Format.nSamplesPerSec = sc->sn.speed;
|
|
|
|
format.Format.nBlockAlign = format.Format.nChannels
|
|
|
|
*format.Format.wBitsPerSample / 8;
|
|
|
|
format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec
|
|
|
|
*format.Format.nBlockAlign;
|
|
|
|
|
|
|
|
if (!hInstDS)
|
|
|
|
{
|
|
|
|
hInstDS = LoadLibrary("dsound.dll");
|
|
|
|
|
|
|
|
if (hInstDS == NULL)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Couldn't load dsound.dll\n");
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
|
|
|
|
|
|
|
|
if (!pDirectSoundCreate)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Couldn't get DS proc addr\n");
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
pDirectSoundEnumerate = (void *)GetProcAddress(hInstDS,"DirectSoundEnumerateA");
|
|
|
|
}
|
|
|
|
|
|
|
|
dsnd_guids=0;
|
|
|
|
dsndguid=NULL;
|
|
|
|
dsndcard="DirectSound";
|
|
|
|
if (snd_multipledevices)
|
|
|
|
if (pDirectSoundEnumerate)
|
|
|
|
pDirectSoundEnumerate(&DSEnumCallback, NULL);
|
|
|
|
aimedforguid++;
|
|
|
|
|
|
|
|
if (!dsndguid) //no more...
|
|
|
|
if (aimedforguid != 1) //not the first device.
|
|
|
|
return SIS_NOMORE;
|
|
|
|
//EAX attempt
|
|
|
|
#ifndef MINIMAL
|
|
|
|
CoInitialize(NULL);
|
|
|
|
if (FAILED(CoCreateInstance( &CLSID_EAXDirectSound, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectSound, (void **)&sc->pDS )))
|
|
|
|
sc->pDS=NULL;
|
|
|
|
else
|
|
|
|
IDirectSound_Initialize(sc->pDS, dsndguid);
|
|
|
|
|
|
|
|
if (!sc->pDS)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
while ((hresult = iDirectSoundCreate(dsndguid, &sc->pDS, NULL)) != DS_OK)
|
|
|
|
{
|
|
|
|
if (hresult != DSERR_ALLOCATED)
|
|
|
|
{
|
|
|
|
Con_SafePrintf (": create 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)
|
|
|
|
// {
|
|
|
|
Con_SafePrintf (": failure\n"
|
|
|
|
" hardware already in use\n"
|
|
|
|
" Close the app then use snd_restart\n");
|
|
|
|
return SIS_NOTAVAIL;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Q_strncpyz(sc->name, dsndcard, sizeof(sc->name));
|
|
|
|
|
|
|
|
dscaps.dwSize = sizeof(dscaps);
|
|
|
|
|
|
|
|
if (DS_OK != sc->pDS->lpVtbl->GetCaps (sc->pDS, &dscaps))
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Couldn't get DS caps\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("No DirectSound driver installed\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DS_OK != sc->pDS->lpVtbl->SetCooperativeLevel (sc->pDS, mainwindow, DSSCL_EXCLUSIVE))
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Set coop level failed\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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|DSBCAPS_CTRLVOLUME;
|
|
|
|
dsbuf.dwBufferBytes = 0;
|
|
|
|
dsbuf.lpwfxFormat = NULL;
|
|
|
|
|
|
|
|
memset(&dsbcaps, 0, sizeof(dsbcaps));
|
|
|
|
dsbcaps.dwSize = sizeof(dsbcaps);
|
|
|
|
primary_format_set = false;
|
|
|
|
|
|
|
|
if (!COM_CheckParm ("-snoforceformat"))
|
|
|
|
{
|
|
|
|
if (DS_OK == sc->pDS->lpVtbl->CreateSoundBuffer(sc->pDS, &dsbuf, &sc->pDSPBuf, NULL))
|
|
|
|
{
|
|
|
|
pformat = format;
|
|
|
|
|
|
|
|
if (DS_OK != sc->pDSPBuf->lpVtbl->SetFormat (sc->pDSPBuf, (WAVEFORMATEX *)&pformat))
|
|
|
|
{
|
|
|
|
// if (snd_firsttime)
|
|
|
|
// Con_SafePrintf ("Set primary sound buffer format: no\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// {
|
|
|
|
// if (snd_firsttime)
|
|
|
|
// Con_SafePrintf ("Set primary sound buffer format: yes\n");
|
|
|
|
|
|
|
|
primary_format_set = true;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!primary_format_set || !COM_CheckParm ("-primarysound"))
|
|
|
|
{
|
|
|
|
// create the secondary buffer we'll actually work with
|
|
|
|
memset (&dsbuf, 0, sizeof(dsbuf));
|
|
|
|
dsbuf.dwSize = sizeof(DSBUFFERDESC);
|
|
|
|
dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | /*DSBCAPS_LOCSOFTWARE |*/ DSBCAPS_GLOBALFOCUS; //dmw 29 may, 2003 removed locsoftware
|
|
|
|
dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
|
|
|
|
dsbuf.lpwfxFormat = (WAVEFORMATEX *)&format;
|
|
|
|
|
|
|
|
memset(&dsbcaps, 0, sizeof(dsbcaps));
|
|
|
|
dsbcaps.dwSize = sizeof(dsbcaps);
|
|
|
|
|
|
|
|
if (DS_OK != sc->pDS->lpVtbl->CreateSoundBuffer(sc->pDS, &dsbuf, &sc->pDSBuf, NULL))
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("DS:CreateSoundBuffer Failed");
|
|
|
|
FreeSound (sc);
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sn.numchannels = format.Format.nChannels;
|
|
|
|
sc->sn.samplebits = format.Format.wBitsPerSample;
|
|
|
|
sc->sn.speed = format.Format.nSamplesPerSec;
|
|
|
|
|
|
|
|
if (DS_OK != sc->pDSBuf->lpVtbl->GetCaps (sc->pDSBuf, &dsbcaps))
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("DS:GetCaps failed\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if (snd_firsttime)
|
|
|
|
// Con_SafePrintf ("Using secondary sound buffer\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (DS_OK != sc->pDS->lpVtbl->SetCooperativeLevel (sc->pDS, mainwindow, DSSCL_WRITEPRIMARY))
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Set coop level failed\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DS_OK != sc->pDSPBuf->lpVtbl->GetCaps (sc->pDSPBuf, &dsbcaps))
|
|
|
|
{
|
|
|
|
Con_Printf ("DS:GetCaps failed\n");
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->pDSBuf = sc->pDSPBuf;
|
|
|
|
// Con_SafePrintf ("Using primary sound buffer\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->gSndBufSize = dsbcaps.dwBufferBytes;
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
// Make sure mixer is active
|
|
|
|
sc->pDSBuf->lpVtbl->Play(sc->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);*/
|
|
|
|
|
|
|
|
|
|
|
|
// initialize the buffer
|
|
|
|
reps = 0;
|
|
|
|
|
2004-09-15 03:09:55 +00:00
|
|
|
while ((hresult = sc->pDSBuf->lpVtbl->Lock(sc->pDSBuf, 0, sc->gSndBufSize, (void**)&sc->lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
if (hresult != DSERR_BUFFERLOST)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (++reps > 10000)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(sc->lpData, 0, dwSize);
|
|
|
|
// lpData[4] = lpData[5] = 0x7f; // force a pop for debugging
|
|
|
|
|
|
|
|
// Sleep(500);
|
|
|
|
|
|
|
|
sc->pDSBuf->lpVtbl->Unlock(sc->pDSBuf, sc->lpData, dwSize, NULL, 0);
|
|
|
|
|
|
|
|
/* we don't want anyone to access the buffer directly w/o locking it first. */
|
|
|
|
sc->lpData = NULL;
|
|
|
|
sc->pDSBuf->lpVtbl->Stop(sc->pDSBuf);
|
|
|
|
#endif
|
|
|
|
sc->pDSBuf->lpVtbl->GetCurrentPosition(sc->pDSBuf, &sc->mmstarttime, &dwWrite);
|
|
|
|
sc->pDSBuf->lpVtbl->Play(sc->pDSBuf, 0, 0, DSBPLAY_LOOPING);
|
|
|
|
|
|
|
|
sc->sn.soundalive = true;
|
|
|
|
sc->sn.splitbuffer = false;
|
|
|
|
sc->sn.samples = sc->gSndBufSize/(sc->sn.samplebits/8);
|
|
|
|
sc->sn.samplepos = 0;
|
|
|
|
sc->sn.submission_chunk = 1;
|
|
|
|
sc->sn.buffer = (unsigned char *) sc->lpData;
|
|
|
|
sample16 = (sc->sn.samplebits/8) - 1;
|
|
|
|
|
|
|
|
sc->dsound_init = true;
|
|
|
|
|
|
|
|
#ifdef _IKsPropertySet_
|
|
|
|
//attempt at eax support
|
|
|
|
if (snd_eax.value)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
DWORD support;
|
|
|
|
|
2004-09-15 03:09:55 +00:00
|
|
|
if (SUCCEEDED(IDirectSoundBuffer_QueryInterface(sc->pDSBuf, &IID_IKsPropertySet, (void*)&sc->EaxKsPropertiesSet)))
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
r = IKsPropertySet_QuerySupport(sc->EaxKsPropertiesSet, &DSPROPSETID_EAX20_LISTENERPROPERTIES, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, &support);
|
|
|
|
if(!SUCCEEDED(r) || (support&(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET))
|
|
|
|
!= (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET))
|
|
|
|
{
|
|
|
|
IKsPropertySet_Release(sc->EaxKsPropertiesSet);
|
|
|
|
sc->EaxKsPropertiesSet = NULL;
|
|
|
|
Con_SafePrintf ("EAX 2 not supported\n");
|
|
|
|
return SIS_SUCCESS;//otherwise successful. It can be used for normal sound anyway.
|
|
|
|
}
|
|
|
|
|
|
|
|
//worked. EAX is supported.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Couldn't get extended properties\n");
|
|
|
|
sc->EaxKsPropertiesSet = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return SIS_SUCCESS;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
void SNDDMA_SetUnderWater(qboolean underwater)
|
|
|
|
{
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
soundcardinfo_t *sc;
|
|
|
|
#ifdef _IKsPropertySet_
|
|
|
|
//attempt at eax support.
|
|
|
|
//EAX is a global thing. Get it going in a game and your media player will be doing it too.
|
|
|
|
|
|
|
|
for (sc = sndcardinfo; sc; sc = sc->next)
|
|
|
|
if (sc->EaxKsPropertiesSet) //only on ds cards.
|
|
|
|
{
|
|
|
|
EAXLISTENERPROPERTIES ListenerProperties = {0};
|
|
|
|
|
|
|
|
/* DWORD p;
|
|
|
|
IKsPropertySet_Get(sc->EaxKsPropertiesSet, &DSPROPSETID_EAX20_LISTENERPROPERTIES,
|
|
|
|
DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, 0, &ListenerProperties,
|
|
|
|
sizeof(ListenerProperties), &p);
|
|
|
|
*/
|
|
|
|
if (underwater)
|
|
|
|
{
|
|
|
|
#if 0 //phycotic.
|
|
|
|
ListenerProperties.flEnvironmentSize = 2.8;
|
|
|
|
ListenerProperties.flEnvironmentDiffusion = 0.240;
|
|
|
|
ListenerProperties.lRoom = -374;
|
|
|
|
ListenerProperties.lRoomHF = -150;
|
|
|
|
ListenerProperties.flRoomRolloffFactor = 0;
|
|
|
|
ListenerProperties.flAirAbsorptionHF = -5;
|
|
|
|
ListenerProperties.lReflections = -10000;
|
|
|
|
ListenerProperties.flReflectionsDelay = 0.053;
|
|
|
|
ListenerProperties.lReverb = 625;
|
|
|
|
ListenerProperties.flReverbDelay = 0.08;
|
|
|
|
ListenerProperties.flDecayTime = 5.096;
|
|
|
|
ListenerProperties.flDecayHFRatio = 0.910;
|
|
|
|
ListenerProperties.dwFlags = 0x3f;
|
|
|
|
ListenerProperties.dwEnvironment = EAX_ENVIRONMENT_PSYCHOTIC;
|
|
|
|
#else
|
|
|
|
ListenerProperties.flEnvironmentSize = 5.8;
|
|
|
|
ListenerProperties.flEnvironmentDiffusion = 0;
|
|
|
|
ListenerProperties.lRoom = -374;
|
|
|
|
ListenerProperties.lRoomHF = -2860;
|
|
|
|
ListenerProperties.flRoomRolloffFactor = 0;
|
|
|
|
ListenerProperties.flAirAbsorptionHF = -5;
|
|
|
|
ListenerProperties.lReflections = -889;
|
|
|
|
ListenerProperties.flReflectionsDelay = 0.024;
|
|
|
|
ListenerProperties.lReverb = 797;
|
|
|
|
ListenerProperties.flReverbDelay = 0.035;
|
|
|
|
ListenerProperties.flDecayTime = 5.568;
|
|
|
|
ListenerProperties.flDecayHFRatio = 0.100;
|
|
|
|
ListenerProperties.dwFlags = 0x3f;
|
|
|
|
ListenerProperties.dwEnvironment = EAX_ENVIRONMENT_UNDERWATER;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ListenerProperties.flEnvironmentSize = 3.2;
|
|
|
|
ListenerProperties.flEnvironmentDiffusion = 1;
|
|
|
|
ListenerProperties.lRoom = -374;
|
|
|
|
ListenerProperties.lRoomHF = -2300;
|
|
|
|
ListenerProperties.flRoomRolloffFactor = 0;
|
|
|
|
ListenerProperties.flAirAbsorptionHF = -5;
|
|
|
|
ListenerProperties.lReflections = 337;
|
|
|
|
ListenerProperties.flReflectionsDelay = 0.002;
|
|
|
|
ListenerProperties.lReverb = 813;
|
|
|
|
ListenerProperties.flReverbDelay = 0.03;
|
|
|
|
ListenerProperties.flDecayTime = 0.381;
|
|
|
|
ListenerProperties.flDecayHFRatio = 0.240;
|
|
|
|
ListenerProperties.dwFlags = 0x3f;
|
|
|
|
ListenerProperties.dwEnvironment = EAX_ENVIRONMENT_GENERIC;
|
|
|
|
}
|
|
|
|
|
|
|
|
// env = EAX_ENVIRONMENT_UNDERWATER;
|
|
|
|
|
|
|
|
if (FAILED(IKsPropertySet_Set(sc->EaxKsPropertiesSet, &DSPROPSETID_EAX20_LISTENERPROPERTIES,
|
|
|
|
DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, 0, &ListenerProperties,
|
|
|
|
sizeof(ListenerProperties))))
|
|
|
|
Con_SafePrintf ("EAX set failed\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
SNDDM_InitWav
|
|
|
|
|
|
|
|
Crappy windows multimedia base
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
qboolean SNDDMA_InitWav (soundcardinfo_t *sc)
|
|
|
|
{
|
|
|
|
extern cvar_t snd_khz;
|
|
|
|
WAVEFORMATEX format;
|
|
|
|
int i;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
sc->snd_sent = 0;
|
|
|
|
sc->snd_completed = 0;
|
|
|
|
|
|
|
|
sc->sn.numchannels = 2;
|
|
|
|
sc->sn.samplebits = 16;
|
|
|
|
|
|
|
|
if (!sc->sn.speed)
|
|
|
|
{
|
|
|
|
if (snd_khz.value == 48)
|
|
|
|
sc->sn.speed = 48000;
|
|
|
|
else if (snd_khz.value == 44 || snd_khz.value == 44.1)
|
|
|
|
sc->sn.speed = 44100;
|
|
|
|
else if (snd_khz.value == 22 || snd_khz.value == 22.05)
|
|
|
|
sc->sn.speed = 22050;
|
|
|
|
else
|
|
|
|
sc->sn.speed = 11025;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset (&format, 0, sizeof(format));
|
|
|
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
format.nChannels = sc->sn.numchannels;
|
|
|
|
format.wBitsPerSample = sc->sn.samplebits;
|
|
|
|
format.nSamplesPerSec = sc->sn.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)&sc->hWaveOut, WAVE_MAPPER,
|
|
|
|
&format,
|
|
|
|
0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
|
|
|
|
{
|
|
|
|
if (hr != MMSYSERR_ALLOCATED)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("waveOutOpen failed\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
// {
|
|
|
|
Con_SafePrintf ("waveOutOpen failure;\n"
|
|
|
|
" hardware already in use\nclose the app, then try using snd_restart\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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
sc->gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
|
|
|
|
sc->hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sc->gSndBufSize);
|
|
|
|
if (!sc->hData)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Sound: Out of memory.\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
sc->lpData = GlobalLock(sc->hData);
|
|
|
|
if (!sc->lpData)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Sound: Failed to lock.\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
memset (sc->lpData, 0, sc->gSndBufSize);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate and lock memory for the header. This memory must
|
|
|
|
* also be globally allocated with GMEM_MOVEABLE and
|
|
|
|
* GMEM_SHARE flags.
|
|
|
|
*/
|
|
|
|
sc->hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
|
|
|
|
(DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
|
|
|
|
|
|
|
|
if (sc->hWaveHdr == NULL)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Sound: Failed to Alloc header.\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->lpWaveHdr = (LPWAVEHDR) GlobalLock(sc->hWaveHdr);
|
|
|
|
|
|
|
|
if (sc->lpWaveHdr == NULL)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Sound: Failed to lock header.\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset (sc->lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
|
|
|
|
|
|
|
|
/* After allocation, set up and prepare headers. */
|
|
|
|
for (i=0 ; i<WAV_BUFFERS ; i++)
|
|
|
|
{
|
|
|
|
sc->lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
|
|
|
|
sc->lpWaveHdr[i].lpData = sc->lpData + i*WAV_BUFFER_SIZE;
|
|
|
|
|
|
|
|
if (waveOutPrepareHeader(sc->hWaveOut, sc->lpWaveHdr+i, sizeof(WAVEHDR)) !=
|
|
|
|
MMSYSERR_NOERROR)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Sound: failed to prepare wave headers\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sn.soundalive = true;
|
|
|
|
sc->sn.splitbuffer = false;
|
|
|
|
sc->sn.samples = sc->gSndBufSize/(sc->sn.samplebits/8);
|
|
|
|
sc->sn.samplepos = 0;
|
|
|
|
sc->sn.submission_chunk = 1;
|
|
|
|
sc->sn.buffer = (unsigned char *) sc->lpData;
|
|
|
|
sample16 = (sc->sn.samplebits/8) - 1;
|
|
|
|
|
|
|
|
sc->wav_init = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
SNDDMA_Init
|
|
|
|
|
|
|
|
Try to find a sound device to mix for.
|
|
|
|
Returns false if nothing is found.
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
|
|
|
|
int SNDDMA_Init(soundcardinfo_t *sc)
|
|
|
|
{
|
|
|
|
sndinitstat stat;
|
|
|
|
|
|
|
|
if (COM_CheckParm ("-wavonly"))
|
|
|
|
wavonly = true;
|
|
|
|
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
sc->dsound_init =
|
|
|
|
#endif
|
|
|
|
sc->wav_init = 0;
|
|
|
|
|
|
|
|
stat = SIS_FAILURE; // assume DirectSound won't initialize
|
|
|
|
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
/* Init DirectSound */
|
|
|
|
if (!wavonly)
|
|
|
|
{
|
|
|
|
if (snd_firsttime || sc->snd_isdirect)
|
|
|
|
{
|
|
|
|
stat = SNDDMA_InitDirect (sc);
|
|
|
|
|
|
|
|
if (stat == SIS_SUCCESS)
|
|
|
|
{
|
|
|
|
sc->snd_isdirect = true;
|
|
|
|
|
|
|
|
if (snd_firsttime)
|
|
|
|
Con_SafePrintf ("%s initialized\n", sc->name);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (stat == SIS_NOMORE)
|
|
|
|
return 2;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sc->snd_isdirect = false;
|
|
|
|
Con_SafePrintf ("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)
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
if (!sc->dsound_init && (stat != SIS_NOTAVAIL))
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if (snd_firsttime || sc->snd_iswave)
|
|
|
|
{
|
|
|
|
|
|
|
|
sc->snd_iswave = SNDDMA_InitWav (sc);
|
|
|
|
|
|
|
|
if (sc->snd_iswave)
|
|
|
|
{
|
|
|
|
if (snd_firsttime)
|
|
|
|
Con_SafePrintf ("Wave sound initialized\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Wave sound failed to init\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
snd_firsttime = false;
|
|
|
|
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
if (!sc->dsound_init && !sc->wav_init)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if (snd_firsttime)
|
|
|
|
Con_SafePrintf ("No sound device initialized\n");
|
|
|
|
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
SNDDMA_GetDMAPos
|
|
|
|
|
|
|
|
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.
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
int SNDDMA_GetDMAPos(soundcardinfo_t *sc)
|
|
|
|
{
|
|
|
|
DWORD mmtime;
|
|
|
|
int s;
|
|
|
|
DWORD dwWrite;
|
|
|
|
|
|
|
|
#ifndef NODIRECTX
|
|
|
|
if (sc->dsound_init)
|
|
|
|
{
|
|
|
|
sc->pDSBuf->lpVtbl->GetCurrentPosition(sc->pDSBuf, &mmtime, &dwWrite);
|
|
|
|
s = mmtime - sc->mmstarttime;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if (sc->wav_init)
|
|
|
|
{
|
|
|
|
s = sc->snd_sent * WAV_BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
s = 0;
|
|
|
|
|
|
|
|
|
|
|
|
s >>= sample16;
|
|
|
|
|
|
|
|
// s = (s/shm->numchannels % (shm->samples-1))*shm->numchannels;
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
SNDDMA_Submit
|
|
|
|
|
|
|
|
Send sound to device if buffer isn't really the dma buffer
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
void SNDDMA_Submit(soundcardinfo_t *sc)
|
|
|
|
{
|
|
|
|
LPWAVEHDR h;
|
|
|
|
int wResult;
|
|
|
|
|
|
|
|
if (!sc->wav_init)
|
|
|
|
return;
|
|
|
|
|
|
|
|
//
|
|
|
|
// find which sound blocks have completed
|
|
|
|
//
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if ( sc->snd_completed == sc->snd_sent )
|
|
|
|
{
|
|
|
|
Con_DPrintf ("Sound overrun\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! (sc->lpWaveHdr[ sc->snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->snd_completed++; // this buffer has been played
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// submit two new sound blocks
|
|
|
|
//
|
|
|
|
while (((sc->snd_sent - sc->snd_completed) >> sample16) < 4)
|
|
|
|
{
|
|
|
|
h = sc->lpWaveHdr + ( sc->snd_sent&WAV_MASK );
|
|
|
|
|
|
|
|
sc->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(sc->hWaveOut, h, sizeof(WAVEHDR));
|
|
|
|
|
|
|
|
if (wResult != MMSYSERR_NOERROR)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Failed to write block to device\n");
|
|
|
|
FreeSound (sc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
SNDDMA_Shutdown
|
|
|
|
|
|
|
|
Reset the sound device for exiting
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
void SNDDMA_Shutdown(soundcardinfo_t *sc)
|
|
|
|
{
|
|
|
|
FreeSound (sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if !defined(NODIRECTX) && defined(VOICECHAT)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LPDIRECTSOUNDCAPTURE DSCapture;
|
|
|
|
LPDIRECTSOUNDCAPTUREBUFFER DSCaptureBuffer;
|
|
|
|
long lastreadpos;
|
|
|
|
long bufferbytes = 1024*1024;
|
|
|
|
|
|
|
|
long inputwidth = 2;
|
|
|
|
|
|
|
|
static WAVEFORMATEX wfxFormat;
|
|
|
|
|
|
|
|
int SNDDMA_InitCapture (void)
|
|
|
|
{
|
|
|
|
DSCBUFFERDESC bufdesc;
|
|
|
|
|
|
|
|
wfxFormat.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
wfxFormat.nChannels = 1;
|
|
|
|
wfxFormat.nSamplesPerSec = 11025;
|
|
|
|
wfxFormat.wBitsPerSample = 8*inputwidth;
|
|
|
|
wfxFormat.nBlockAlign = wfxFormat.nChannels * (wfxFormat.wBitsPerSample / 8);
|
|
|
|
wfxFormat.nAvgBytesPerSec = wfxFormat.nSamplesPerSec * wfxFormat.nBlockAlign;
|
|
|
|
wfxFormat.cbSize = 0;
|
|
|
|
|
|
|
|
bufdesc.dwSize = sizeof(bufdesc);
|
|
|
|
bufdesc.dwBufferBytes = bufferbytes;
|
|
|
|
bufdesc.dwFlags = 0;
|
|
|
|
bufdesc.dwReserved = 0;
|
|
|
|
bufdesc.lpwfxFormat = &wfxFormat;
|
|
|
|
|
|
|
|
if (DSCaptureBuffer)
|
|
|
|
{
|
|
|
|
IDirectSoundCaptureBuffer_Stop(DSCaptureBuffer);
|
|
|
|
IDirectSoundCaptureBuffer_Release(DSCaptureBuffer);
|
|
|
|
DSCaptureBuffer=NULL;
|
|
|
|
}
|
|
|
|
if (DSCapture)
|
|
|
|
{
|
|
|
|
IDirectSoundCapture_Release(DSCapture);
|
|
|
|
DSCapture=NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!hInstDS)
|
|
|
|
{
|
|
|
|
hInstDS = LoadLibrary("dsound.dll");
|
|
|
|
|
|
|
|
if (hInstDS == NULL)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Couldn't load dsound.dll\n");
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if (!pDirectSoundCaptureCreate)
|
|
|
|
{
|
|
|
|
pDirectSoundCaptureCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCaptureCreate");
|
|
|
|
|
|
|
|
if (!pDirectSoundCreate)
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Couldn't get DS proc addr\n");
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// pDirectSoundCaptureEnumerate = (void *)GetProcAddress(hInstDS,"DirectSoundCaptureEnumerateA");
|
|
|
|
}
|
|
|
|
pDirectSoundCaptureCreate(NULL, &DSCapture, NULL);
|
|
|
|
|
|
|
|
if (FAILED(IDirectSoundCapture_CreateCaptureBuffer(DSCapture, &bufdesc, &DSCaptureBuffer, NULL)))
|
|
|
|
{
|
|
|
|
Con_SafePrintf ("Couldn't create a capture buffer\n");
|
|
|
|
IDirectSoundCapture_Release(DSCapture);
|
|
|
|
DSCapture=NULL;
|
|
|
|
return SIS_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
IDirectSoundCaptureBuffer_Start(DSCaptureBuffer, DSBPLAY_LOOPING);
|
|
|
|
|
|
|
|
lastreadpos = 0;
|
|
|
|
|
|
|
|
return SIS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SNDVC_Submit(qbyte *buffer, int samples, int freq, int width);
|
|
|
|
void S_UpdateCapture(void)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
LPBYTE lpbuf1 = NULL;
|
|
|
|
LPBYTE lpbuf2 = NULL;
|
|
|
|
DWORD dwsize1 = 0;
|
|
|
|
DWORD dwsize2 = 0;
|
|
|
|
|
|
|
|
DWORD capturePos;
|
|
|
|
DWORD readPos;
|
|
|
|
long filled;
|
|
|
|
static int update;
|
|
|
|
|
|
|
|
char *pBuffer;
|
|
|
|
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
if (!snd_capture.value)
|
|
|
|
{
|
|
|
|
if (DSCaptureBuffer)
|
|
|
|
{
|
|
|
|
IDirectSoundCaptureBuffer_Stop(DSCaptureBuffer);
|
|
|
|
IDirectSoundCaptureBuffer_Release(DSCaptureBuffer);
|
|
|
|
DSCaptureBuffer=NULL;
|
|
|
|
}
|
|
|
|
if (DSCapture)
|
|
|
|
{
|
|
|
|
IDirectSoundCapture_Release(DSCapture);
|
|
|
|
DSCapture=NULL;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (!DSCaptureBuffer)
|
|
|
|
{
|
|
|
|
SNDDMA_InitCapture();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query to see how much data is in buffer.
|
|
|
|
hr = IDirectSoundCaptureBuffer_GetCurrentPosition( DSCaptureBuffer, &capturePos, &readPos );
|
|
|
|
if( hr != DS_OK )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
filled = readPos - lastreadpos;
|
|
|
|
if( filled < 0 ) filled += bufferbytes; // unwrap offset
|
|
|
|
|
|
|
|
if (filled > 1400) //figure out how much we need to empty it by, and if that's enough to be worthwhile.
|
|
|
|
filled = 1400;
|
|
|
|
else if (filled < 1400)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((filled/inputwidth) & 1) //force even numbers of samples
|
|
|
|
filled -= inputwidth;
|
|
|
|
|
|
|
|
pBuffer = BZ_Malloc(filled*inputwidth);
|
|
|
|
|
|
|
|
|
|
|
|
// Lock free space in the DS
|
|
|
|
hr = IDirectSoundCaptureBuffer_Lock ( DSCaptureBuffer, lastreadpos, filled, (void **) &lpbuf1, &dwsize1,
|
|
|
|
(void **) &lpbuf2, &dwsize2, 0);
|
|
|
|
if (hr == DS_OK)
|
|
|
|
{
|
|
|
|
// Copy from DS to the buffer
|
|
|
|
memcpy( pBuffer, lpbuf1, dwsize1);
|
|
|
|
if(lpbuf2 != NULL)
|
|
|
|
{
|
|
|
|
memcpy( pBuffer+dwsize1, lpbuf2, dwsize2);
|
|
|
|
}
|
|
|
|
// Update our buffer offset and unlock sound buffer
|
|
|
|
lastreadpos = (lastreadpos + dwsize1 + dwsize2) % bufferbytes;
|
|
|
|
IDirectSoundCaptureBuffer_Unlock ( DSCaptureBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BZ_Free(pBuffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SNDVC_MicInput(pBuffer, filled, wfxFormat.nSamplesPerSec, inputwidth);
|
|
|
|
BZ_Free(pBuffer);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
void S_UpdateCapture(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|