0c8ad17f7c
Added sv_guidkey cvar, allowing cross-server guid key generation (although it lacks auth). Support .ico, because we can. preliminary support for sdl 2.0.6's vulkan stuff. will wait till its actually released before its properly used. Fix capturedemo. videomap should typically use premultiplied alpha, apparently. Updated sound drivers. No more old drivers. Better cvar registration. More drivers optionally support float output. Added certificate log for dtls connections. Rewrote font char cache, now supports full unicode char range, not just ucs-2. Attempt to support FreeType 2.5+ rgba fonts. XMPP now supports carbons, and shows avatars in conversations. Updated xmpp's scram auth to be more strict, including the plus variation (hopefully), to block evil tls proxies. ffmpeg plugin now uses the decoupled api for decoding too. Cef plugin updated to support fte-scheme post data properly, as well as request/response headers (like cross-origin). git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5148 fc73d0e0-1445-4013-8a0c-d673dee63da5
271 lines
8.5 KiB
C
271 lines
8.5 KiB
C
#include "quakedef.h"
|
|
|
|
//frankly, xaudio2 gives nothing over directsound, unless we're getting it to do all the mixing instead. which gets really messy and far too involved.
|
|
//I suppose it has a use with WINRT... although that doesn't apply to any actual users.
|
|
//(on xp, its actually implemented as a wrapper over directsound, so why even bother. on vista+ its implemented as a wrapper over wasapi)
|
|
|
|
//we're lazy and don't do any special threading, this makes it inferior to the directsound implementation - potentially, the callback feature could allow for slightly lower latencies.
|
|
//also no reverb (fixme: XAUDIO2FX_REVERB_PARAMETERS).
|
|
|
|
//dxsdk = 2.7 = win7+ (redistributable)
|
|
//w8sdk = 2.8 = win8+ (system component, not available on vista/win7)
|
|
//w10sdk = 2.9 = win10+ (system component, not available on vista/win7/win8/win8.1)
|
|
|
|
#if defined(AVAIL_XAUDIO2) && !defined(SERVERONLY)
|
|
#include "winquake.h"
|
|
#include <xaudio2.h>
|
|
|
|
#define SDRVNAME "XAudio2"
|
|
|
|
typedef struct
|
|
{
|
|
IXAudio2VoiceCallback cb; //must be first. yay for fake single inheritance.
|
|
|
|
IXAudio2 *ixa;
|
|
IXAudio2MasteringVoice *master;
|
|
IXAudio2SourceVoice *source;
|
|
|
|
//contiguous block of memory, because its easier.
|
|
qbyte *bufferstart;
|
|
unsigned int subbuffersize; //in samplepairs
|
|
unsigned int buffercount;
|
|
unsigned int bufferidx;
|
|
unsigned int bufferavail;
|
|
} xaud_t;
|
|
|
|
static void *XAUDIO_Lock(soundcardinfo_t *sc, unsigned int *startoffset)
|
|
{
|
|
qbyte *ret;
|
|
xaud_t *xa = sc->handle;
|
|
ret = xa->bufferstart;
|
|
|
|
// *startoffset = 0;
|
|
// ret += xa->subbuffersize * xa->bufferidx;
|
|
|
|
return ret;
|
|
}
|
|
static void XAUDIO_Unlock(soundcardinfo_t *sc, void *buffer)
|
|
{
|
|
}
|
|
static unsigned int XAUDIO_GetDMAPos(soundcardinfo_t *sc)
|
|
{
|
|
xaud_t *xa = sc->handle;
|
|
unsigned int s = (xa->bufferidx+xa->bufferavail) * xa->subbuffersize * sc->sn.numchannels;
|
|
return s;
|
|
}
|
|
static void XAUDIO_Submit(soundcardinfo_t *sc, int start, int end)
|
|
{
|
|
xaud_t *xa = sc->handle;
|
|
XAUDIO2_BUFFER buf;
|
|
|
|
//determine total buffer size
|
|
int buffersize = sc->sn.samples*sc->sn.samplebytes;
|
|
|
|
//determine time offsets in bytes
|
|
start *= sc->sn.numchannels*sc->sn.samplebytes;
|
|
end *= sc->sn.numchannels*sc->sn.samplebytes;
|
|
|
|
while (start < end)
|
|
{
|
|
if (!xa->bufferavail)
|
|
break; //o.O that's not meant to happen
|
|
memset(&buf, 0, sizeof(buf));
|
|
buf.AudioBytes = end - start;
|
|
if (buf.AudioBytes > xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebytes)
|
|
{
|
|
if (buf.AudioBytes < xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebytes)
|
|
{ //dma code should ensure that only multiples of 'samplequeue' are processed.
|
|
Con_Printf("XAudio2 underrun\n");
|
|
break;
|
|
}
|
|
buf.AudioBytes = xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebytes;
|
|
}
|
|
buf.pAudioData = xa->bufferstart + (start%buffersize);
|
|
if ((qbyte*)buf.pAudioData + buf.AudioBytes > xa->bufferstart + buffersize)
|
|
{ //this shouldn't ever happen either
|
|
Con_Printf("XAudio2 overrun\n");
|
|
break;
|
|
}
|
|
IXAudio2SourceVoice_SubmitSourceBuffer(xa->source, &buf, NULL);
|
|
xa->bufferidx += 1;
|
|
xa->bufferavail -= 1;
|
|
start += buf.AudioBytes;
|
|
}
|
|
}
|
|
|
|
static void XAUDIO_Shutdown(soundcardinfo_t *sc)
|
|
{
|
|
xaud_t *xa = sc->handle;
|
|
//releases are allowed to block, supposedly.
|
|
IXAudio2SourceVoice_DestroyVoice(xa->source);
|
|
IXAudio2MasteringVoice_DestroyVoice(xa->master);
|
|
IXAudio2_Release(xa->ixa);
|
|
BZ_Free(xa->bufferstart);
|
|
Z_Free(xa);
|
|
sc->handle = NULL;
|
|
}
|
|
|
|
void WINAPI XAUDIO_CB_OnVoiceProcessingPassStart (IXAudio2VoiceCallback *ths, UINT32 BytesRequired) {}
|
|
void WINAPI XAUDIO_CB_OnVoiceProcessingPassEnd (IXAudio2VoiceCallback *ths) {}
|
|
void WINAPI XAUDIO_CB_OnStreamEnd (IXAudio2VoiceCallback *ths) {}
|
|
void WINAPI XAUDIO_CB_OnBufferStart (IXAudio2VoiceCallback *ths, void* pBufferContext) {}
|
|
void WINAPI XAUDIO_CB_OnBufferEnd (IXAudio2VoiceCallback *ths, void* pBufferContext) {xaud_t *xa = (xaud_t*)ths; S_LockMixer(); xa->bufferavail+=1; S_UnlockMixer();}
|
|
void WINAPI XAUDIO_CB_OnLoopEnd (IXAudio2VoiceCallback *ths, void* pBufferContext) {}
|
|
void WINAPI XAUDIO_CB_OnVoiceError (IXAudio2VoiceCallback *ths, void* pBufferContext, HRESULT Error) {}
|
|
static IXAudio2VoiceCallbackVtbl cbvtbl =
|
|
{
|
|
XAUDIO_CB_OnVoiceProcessingPassStart,
|
|
XAUDIO_CB_OnVoiceProcessingPassEnd,
|
|
XAUDIO_CB_OnStreamEnd,
|
|
XAUDIO_CB_OnBufferStart,
|
|
XAUDIO_CB_OnBufferEnd,
|
|
XAUDIO_CB_OnLoopEnd,
|
|
XAUDIO_CB_OnVoiceError
|
|
};
|
|
|
|
static qboolean QDECL XAUDIO_InitCard(soundcardinfo_t *sc, const char *cardname)
|
|
{
|
|
#ifdef WINRT
|
|
char *dev = NULL;
|
|
#else
|
|
int dev = 0;
|
|
#endif
|
|
xaud_t *xa = Z_Malloc(sizeof(*xa));
|
|
WAVEFORMATEXTENSIBLE wfmt;
|
|
const static GUID QKSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003,0x0000,0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
|
|
|
xa->cb.lpVtbl = &cbvtbl;
|
|
|
|
sc->sn.numchannels = 2;
|
|
|
|
memset(&wfmt, 0, sizeof(wfmt));
|
|
wfmt.Format.wFormatTag = WAVE_FORMAT_PCM;
|
|
wfmt.Format.nChannels = sc->sn.numchannels;
|
|
wfmt.Format.nSamplesPerSec = sc->sn.speed;
|
|
wfmt.Format.wBitsPerSample = sc->sn.samplebytes*8;
|
|
wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8);
|
|
wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;
|
|
wfmt.Format.cbSize = 0;
|
|
if (wfmt.Format.wBitsPerSample == 32)
|
|
{
|
|
wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
wfmt.Format.cbSize = 22;
|
|
memcpy(&wfmt.SubFormat, &QKSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(wfmt.SubFormat));
|
|
}
|
|
|
|
sc->inactive_sound = true;
|
|
xa->buffercount = xa->bufferavail = 3; //submit this many straight up
|
|
xa->subbuffersize = 256; //number of sampleblocks per submission
|
|
sc->samplequeue = -1; //-1 means we're streaming, XAUDIO_GetDMAPos returns exactly as much as we want to paint to.
|
|
|
|
sc->sn.samples = xa->buffercount * xa->subbuffersize * sc->sn.numchannels;
|
|
|
|
xa->bufferstart = BZ_Malloc(sc->sn.samples * sc->sn.samplebytes);
|
|
|
|
if (xa->bufferstart)
|
|
{
|
|
if (SUCCEEDED(XAudio2Create(&xa->ixa, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
|
{
|
|
#ifdef WINRT
|
|
if (SUCCEEDED(IXAudio2_CreateMasteringVoice(xa->ixa, &xa->master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, dev, NULL, AudioCategory_GameEffects)))
|
|
#else
|
|
if (cardname && *cardname)
|
|
{
|
|
UINT32 devs = 0;
|
|
XAUDIO2_DEVICE_DETAILS details;
|
|
char id[MAX_QPATH];
|
|
if (FAILED(IXAudio2_GetDeviceCount(xa->ixa, &devs)))
|
|
devs = 0;
|
|
|
|
for (dev = 0; dev < devs; dev++)
|
|
{
|
|
if (SUCCEEDED(IXAudio2_GetDeviceDetails(xa->ixa, dev, &details)))
|
|
{
|
|
narrowen(id, sizeof(id), details.DeviceID);
|
|
|
|
if (!strcmp(id, cardname))
|
|
break;
|
|
}
|
|
}
|
|
if (dev == devs)
|
|
dev = ~0; //something invalid.
|
|
}
|
|
|
|
/*
|
|
//FIXME: correct the details to match the hardware
|
|
*/
|
|
|
|
|
|
if (dev != ~0 && SUCCEEDED(IXAudio2_CreateMasteringVoice(xa->ixa, &xa->master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, dev, NULL)))
|
|
#endif
|
|
{
|
|
//egads
|
|
XAUDIO2_VOICE_SENDS vs;
|
|
XAUDIO2_SEND_DESCRIPTOR sd[1];
|
|
vs.SendCount = 1;
|
|
vs.pSends = sd;
|
|
sd[0].Flags = 0;
|
|
sd[0].pOutputVoice = (IXAudio2Voice*)xa->master;
|
|
|
|
if (SUCCEEDED(IXAudio2_CreateSourceVoice(xa->ixa, &xa->source, &wfmt.Format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &xa->cb, &vs, NULL)))
|
|
{
|
|
sc->handle = xa;
|
|
sc->GetDMAPos = XAUDIO_GetDMAPos;
|
|
sc->Lock = XAUDIO_Lock;
|
|
sc->Unlock = XAUDIO_Unlock;
|
|
sc->Submit = XAUDIO_Submit;
|
|
sc->Shutdown = XAUDIO_Shutdown;
|
|
|
|
IXAudio2SourceVoice_Start(xa->source, 0, XAUDIO2_COMMIT_NOW);
|
|
return true;
|
|
}
|
|
IXAudio2MasteringVoice_DestroyVoice(xa->master);
|
|
}
|
|
IXAudio2_Release(xa->ixa);
|
|
}
|
|
BZ_Free(xa->bufferstart);
|
|
}
|
|
Z_Free(xa);
|
|
return false;
|
|
}
|
|
|
|
qboolean QDECL XAUDIO_Enumerate(void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))
|
|
{
|
|
IXAudio2 *ixa;
|
|
#ifndef WINRT
|
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
//winrt provides no enumeration mechanism.
|
|
if (SUCCEEDED(XAudio2Create(&ixa, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
|
{
|
|
UINT32 devs = 0, i;
|
|
XAUDIO2_DEVICE_DETAILS details;
|
|
char id[MAX_QPATH], name[MAX_QPATH];
|
|
if (FAILED(IXAudio2_GetDeviceCount(ixa, &devs)))
|
|
devs = 0;
|
|
|
|
strcpy(name, "XA2:");
|
|
|
|
for (i = 0; i < devs; i++)
|
|
{
|
|
if (SUCCEEDED(IXAudio2_GetDeviceDetails(ixa, i, &details)))
|
|
{
|
|
narrowen(id, sizeof(id), details.DeviceID);
|
|
narrowen(name+4, sizeof(name)-4, details.DisplayName);
|
|
|
|
callback(SDRVNAME, id, name);
|
|
}
|
|
}
|
|
IXAudio2_Release(ixa);
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
sounddriver_t XAUDIO2_Output =
|
|
{
|
|
SDRVNAME,
|
|
XAUDIO_InitCard,
|
|
XAUDIO_Enumerate
|
|
};
|
|
#endif
|