mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-30 07:31:13 +00:00
018aed2c19
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5022 fc73d0e0-1445-4013-8a0c-d673dee63da5
292 lines
8.5 KiB
C
292 lines
8.5 KiB
C
#include "quakedef.h"
|
|
#include "winquake.h"
|
|
|
|
#ifdef DYNAMIC_SDL
|
|
#define SDL_MAJOR_VERSION 2
|
|
//if we're not an sdl build, we probably want to link to sdl libraries dynamically or something.
|
|
#include <stdint.h>
|
|
#define SDL_AudioDeviceID uint32_t
|
|
#define SDL_INIT_AUDIO 0x00000010
|
|
#define SDL_INIT_NOPARACHUTE 0x00100000
|
|
#define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001
|
|
#define SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002
|
|
#define SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004
|
|
#define AUDIO_S16LSB 0x8010
|
|
#define AUDIO_S16MSB 0x9010
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
#define AUDIO_S16SYS AUDIO_S16MSB
|
|
#else
|
|
#define AUDIO_S16SYS AUDIO_S16LSB
|
|
#endif
|
|
|
|
typedef uint16_t SDL_AudioFormat;
|
|
typedef void VARGS (*SDL_AudioCallback)(void *userdata, uint8_t *stream, int len);
|
|
|
|
typedef struct SDL_AudioSpec
|
|
{
|
|
int freq;
|
|
SDL_AudioFormat format;
|
|
uint8_t channels;
|
|
uint8_t silence;
|
|
uint16_t samples;
|
|
uint16_t padding;
|
|
uint32_t size;
|
|
SDL_AudioCallback callback;
|
|
void *userdata;
|
|
} SDL_AudioSpec;
|
|
|
|
static int (*SDL_Init) (uint32_t flags);
|
|
static int (*SDL_InitSubSystem) (uint32_t flags);
|
|
static SDL_AudioDeviceID (*SDL_OpenAudioDevice) (const char *dev, int iscapture, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained, int allowed_changes);
|
|
static void (*SDL_PauseAudioDevice) (SDL_AudioDeviceID fd, int pausestate);
|
|
static void (*SDL_LockAudioDevice) (SDL_AudioDeviceID fd);
|
|
static void (*SDL_UnlockAudioDevice) (SDL_AudioDeviceID fd);
|
|
static int (*SDL_CloseAudioDevice) (SDL_AudioDeviceID fd);
|
|
static int (*SDL_GetNumAudioDevices) (int iscapture);
|
|
static const char *(*SDL_GetAudioDeviceName) (int index, int iscapture);
|
|
static const char *(*SDL_GetError) (void);
|
|
#else
|
|
#include <SDL.h>
|
|
#endif
|
|
|
|
#define SELFPAINT
|
|
|
|
//SDL calls a callback each time it needs to repaint the 'hardware' buffers
|
|
//This results in extra latency due it needing to buffer that much data.
|
|
//So we tell it a fairly pathetically sized buffer and try and get it to copy often
|
|
//hopefully this lowers sound latency, and has no suddenly starting sounds and stuff.
|
|
//It still has greater latency than direct access, of course.
|
|
//On the plus side, SDL calls the callback from another thread. this means we can set up some tiny buffer size, and if we're mixing inside the callback then you can actually get lower latency than waiting for an entire frame (yay rtlights)
|
|
|
|
static qboolean SSDL_InitAudio(void)
|
|
{
|
|
static qboolean inited = false;
|
|
#ifdef DYNAMIC_SDL
|
|
static dllfunction_t funcs[] =
|
|
{
|
|
{(void*)&SDL_Init, "SDL_Init"},
|
|
{(void*)&SDL_InitSubSystem, "SDL_InitSubSystem"},
|
|
{(void*)&SDL_OpenAudioDevice, "SDL_OpenAudioDevice"},
|
|
{(void*)&SDL_PauseAudioDevice, "SDL_PauseAudioDevice"},
|
|
{(void*)&SDL_LockAudioDevice, "SDL_LockAudioDevice"},
|
|
{(void*)&SDL_UnlockAudioDevice, "SDL_UnlockAudioDevice"},
|
|
{(void*)&SDL_CloseAudioDevice, "SDL_CloseAudioDevice"},
|
|
{(void*)&SDL_GetNumAudioDevices, "SDL_GetNumAudioDevices"},
|
|
{(void*)&SDL_GetAudioDeviceName, "SDL_GetAudioDeviceName"},
|
|
{(void*)&SDL_GetError, "SDL_GetError"},
|
|
{NULL, NULL}
|
|
};
|
|
static dllhandle_t *libsdl;
|
|
if (!libsdl)
|
|
{
|
|
libsdl = Sys_LoadLibrary("libSDL2-2.0.so.0", funcs);
|
|
if (!libsdl)
|
|
libsdl = Sys_LoadLibrary("libSDL2.so", funcs); //maybe they have a dev package installed that fixes this mess.
|
|
if (libsdl)
|
|
SDL_Init(SDL_INIT_NOPARACHUTE);
|
|
else
|
|
{
|
|
Con_Printf("Unable to load libSDL2 library\n");
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!inited)
|
|
if(SDL_InitSubSystem(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE))
|
|
{
|
|
Con_Printf("Couldn't initialize SDL audio subsystem (%s)\n", SDL_GetError());
|
|
return false;
|
|
}
|
|
inited = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static void SSDL_Shutdown(soundcardinfo_t *sc)
|
|
{
|
|
Con_DPrintf("Shutdown SDL sound\n");
|
|
|
|
#if SDL_MAJOR_VERSION >= 2
|
|
SDL_PauseAudioDevice(sc->audio_fd, 1);
|
|
SDL_CloseAudioDevice(sc->audio_fd);
|
|
#else
|
|
SDL_CloseAudio();
|
|
#endif
|
|
#ifndef SELFPAINT
|
|
if (sc->sn.buffer)
|
|
free(sc->sn.buffer);
|
|
#endif
|
|
sc->sn.buffer = NULL;
|
|
}
|
|
static unsigned int SSDL_GetDMAPos(soundcardinfo_t *sc)
|
|
{
|
|
sc->sn.samplepos = sc->snd_sent / (sc->sn.samplebits/8);
|
|
return sc->sn.samplepos;
|
|
}
|
|
|
|
//this function is called from inside SDL.
|
|
//transfer the 'dma' buffer into the buffer it requests.
|
|
static void VARGS SSDL_Paint(void *userdata, qbyte *stream, int len)
|
|
{
|
|
soundcardinfo_t *sc = userdata;
|
|
|
|
#ifdef SELFPAINT
|
|
sc->sn.buffer = stream;
|
|
sc->sn.samples = len / (sc->sn.samplebits/8);
|
|
sc->samplequeue = sc->sn.samples;
|
|
S_MixerThread(sc);
|
|
sc->snd_sent += len;
|
|
#else
|
|
int buffersize = sc->sn.samples*(sc->sn.samplebits/8);
|
|
|
|
if (len > buffersize)
|
|
{
|
|
len = buffersize; //whoa nellie!
|
|
}
|
|
|
|
if (len + sc->snd_sent%buffersize > buffersize)
|
|
{ //buffer will wrap, fill in the rest
|
|
memcpy(stream, (char*)sc->sn.buffer + (sc->snd_sent%buffersize), buffersize - (sc->snd_sent%buffersize));
|
|
stream += buffersize - sc->snd_sent%buffersize;
|
|
sc->snd_sent += buffersize - sc->snd_sent%buffersize;
|
|
len -= buffersize - (sc->snd_sent%buffersize);
|
|
if (len < 0)
|
|
return;
|
|
} //and finish from the start
|
|
memcpy(stream, (char*)sc->sn.buffer + (sc->snd_sent%buffersize), len);
|
|
sc->snd_sent += len;
|
|
#endif
|
|
}
|
|
|
|
static void *SSDL_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)
|
|
{
|
|
#if SDL_MAJOR_VERSION >= 2
|
|
SDL_LockAudioDevice(sc->audio_fd);
|
|
#else
|
|
SDL_LockAudio();
|
|
#endif
|
|
|
|
return sc->sn.buffer;
|
|
}
|
|
|
|
static void SSDL_UnlockBuffer(soundcardinfo_t *sc, void *buffer)
|
|
{
|
|
#if SDL_MAJOR_VERSION >= 2
|
|
SDL_UnlockAudioDevice(sc->audio_fd);
|
|
#else
|
|
SDL_UnlockAudio();
|
|
#endif
|
|
}
|
|
|
|
static void SSDL_Submit(soundcardinfo_t *sc, int start, int end)
|
|
{
|
|
//SDL will call SSDL_Paint to paint when it's time, and the sound buffer is always there...
|
|
}
|
|
|
|
static qboolean SDL_InitCard(soundcardinfo_t *sc, const char *devicename)
|
|
{
|
|
SDL_AudioSpec desired, obtained;
|
|
|
|
if(!SSDL_InitAudio())
|
|
{
|
|
Con_Printf("Couldn't initialize SDL audio subsystem\n");
|
|
return false;
|
|
}
|
|
|
|
memset(&desired, 0, sizeof(desired));
|
|
|
|
desired.freq = sc->sn.speed;
|
|
desired.channels = sc->sn.numchannels; //fixme!
|
|
desired.samples = 0x0200; //'Good values seem to range between 512 and 8192 inclusive, depending on the application and CPU speed.'
|
|
desired.format = AUDIO_S16SYS;
|
|
desired.callback = (void*)SSDL_Paint;
|
|
desired.userdata = sc;
|
|
memcpy(&obtained, &desired, sizeof(obtained));
|
|
|
|
#if SDL_MAJOR_VERSION >= 2
|
|
sc->audio_fd = SDL_OpenAudioDevice(devicename, false, &desired, &obtained, (sndcardinfo?0:SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) | SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
|
|
if (!sc->audio_fd)
|
|
{
|
|
Con_Printf("SDL_OpenAudioDevice(%s) failed: couldn't open sound device (%s).\n", devicename?devicename:"default", SDL_GetError());
|
|
return false;
|
|
}
|
|
if (devicename)
|
|
Con_Printf("Initing SDL audio device '%s'.\n", devicename);
|
|
else
|
|
Con_Printf("Initing default SDL audio device.\n");
|
|
#else
|
|
if (sndcardinfo)
|
|
return false; //SDL1 only supports opening one audio device at a time. the existing one might not be sdl, but I don't care.
|
|
if ( SDL_OpenAudio(&desired, &obtained) < 0 )
|
|
{
|
|
Con_Printf("SDL_OpenAudio failed: couldn't open sound device (%s).\n", SDL_GetError());
|
|
return false;
|
|
}
|
|
Con_Printf("Initing default SDL audio device.\n");
|
|
#endif
|
|
sc->sn.numchannels = obtained.channels;
|
|
sc->sn.speed = obtained.freq;
|
|
sc->sn.samplebits = obtained.format&0xff;
|
|
sc->sn.samples = 32768;//*sc->sn.numchannels; //doesn't really matter, so long as it's higher than obtained.samples
|
|
|
|
#ifdef SELFPAINT
|
|
sc->selfpainting = true;
|
|
#endif
|
|
|
|
Con_DPrintf("channels: %i\n", sc->sn.numchannels);
|
|
Con_DPrintf("Speed: %i\n", sc->sn.speed);
|
|
Con_DPrintf("Samplebits: %i\n", sc->sn.samplebits);
|
|
Con_DPrintf("SDLSamples: %i (low for latency)\n", obtained.samples);
|
|
Con_DPrintf("FakeSamples: %i\n", sc->sn.samples);
|
|
|
|
#ifndef SELFPAINT
|
|
sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebits/8);
|
|
#endif
|
|
Con_DPrintf("Got sound %i-%i\n", obtained.freq, obtained.format);
|
|
|
|
sc->Lock = SSDL_LockBuffer;
|
|
sc->Unlock = SSDL_UnlockBuffer;
|
|
sc->Submit = SSDL_Submit;
|
|
sc->Shutdown = SSDL_Shutdown;
|
|
sc->GetDMAPos = SSDL_GetDMAPos;
|
|
|
|
#if SDL_MAJOR_VERSION >= 2
|
|
SDL_PauseAudioDevice(sc->audio_fd, 0);
|
|
#else
|
|
SDL_PauseAudio(0);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#define SDRVNAME "SDL"
|
|
static qboolean QDECL SDL_Enumerate(void (QDECL *cb) (const char *drivername, const char *devicecode, const char *readablename))
|
|
{
|
|
#if SDL_MAJOR_VERSION >= 2
|
|
int max, i;
|
|
if(SSDL_InitAudio())
|
|
{
|
|
max = SDL_GetNumAudioDevices(false);
|
|
for (i = 0; i < max; i++)
|
|
{
|
|
const char *devname = SDL_GetAudioDeviceName(i, false);
|
|
if (devname)
|
|
cb(SDRVNAME, devname, va("SDL (%s)", devname));
|
|
}
|
|
}
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
sounddriver_t SDL_Output =
|
|
{
|
|
SDRVNAME,
|
|
SDL_InitCard,
|
|
SDL_Enumerate
|
|
};
|
|
|
|
|