* OpenAL support, from BlackAura aka Stuart Dalton <badcdev@gmail.com>
+ An abstract codec system, simplifying support for new formats + Changes versus BlackAura's patch: o Consolidated the OpenAL parts into one file o Changed the function naming scheme to more closely resemble Q3 o Changed the interface to fall back on the "base" sound system if loading OpenAL fails + This is enabled on Linux and MinGW for now, but should work on the other *nixs with appropriate additions to the Makefile + NOT enabled on OS X or MSVC Windows builds + Probably breaks the Windows build again * Retabulated sdl_snd.c and made the messages less verbose since there do not seem to be many having problems with SDL sound now
This commit is contained in:
parent
79ceef93cc
commit
84c4f21082
14 changed files with 3813 additions and 703 deletions
|
@ -56,233 +56,285 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
#include "../qcommon/q_shared.h"
|
||||
#include "../client/snd_local.h"
|
||||
|
||||
int snd_inited=0;
|
||||
qboolean snd_inited = qfalse;
|
||||
|
||||
cvar_t *sndbits;
|
||||
cvar_t *sndspeed;
|
||||
cvar_t *sndchannels;
|
||||
cvar_t *snddevice;
|
||||
cvar_t *sdldevsamps;
|
||||
cvar_t *sdlmixsamps;
|
||||
cvar_t *s_sdlBits;
|
||||
cvar_t *s_sdlSpeed;
|
||||
cvar_t *s_sdlChannels;
|
||||
cvar_t *s_sdlDevSamps;
|
||||
cvar_t *s_sdlMixSamps;
|
||||
|
||||
static qboolean use_custom_memset = qfalse;
|
||||
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
|
||||
|
||||
/*
|
||||
===============
|
||||
Snd_Memset
|
||||
|
||||
https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
|
||||
|
||||
<TTimo> some shitty mess with DMA buffers
|
||||
<TTimo> the mmap'ing permissions were write only
|
||||
<TTimo> and glibc optimized for mmx would do memcpy with a prefetch and a read
|
||||
<TTimo> causing segfaults
|
||||
<TTimo> some other systems would not let you mmap the DMA with read permissions
|
||||
<TTimo> so I think I ended up attempting opening with read/write, then try write only
|
||||
<TTimo> and use my own copy instead of the glibc crap
|
||||
===============
|
||||
*/
|
||||
void Snd_Memset (void* dest, const int val, const size_t count)
|
||||
{
|
||||
int *pDest;
|
||||
int i, iterate;
|
||||
int *pDest;
|
||||
int i, iterate;
|
||||
|
||||
if (!use_custom_memset)
|
||||
{
|
||||
Com_Memset(dest,val,count);
|
||||
return;
|
||||
}
|
||||
iterate = count / sizeof(int);
|
||||
pDest = (int*)dest;
|
||||
for(i=0; i<iterate; i++)
|
||||
{
|
||||
pDest[i] = val;
|
||||
}
|
||||
if (!use_custom_memset)
|
||||
{
|
||||
Com_Memset(dest,val,count);
|
||||
return;
|
||||
}
|
||||
iterate = count / sizeof(int);
|
||||
pDest = (int*)dest;
|
||||
for(i=0; i<iterate; i++)
|
||||
{
|
||||
pDest[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/* The audio callback. All the magic happens here. */
|
||||
static int dmapos = 0;
|
||||
static int dmasize = 0;
|
||||
|
||||
/*
|
||||
===============
|
||||
sdl_audio_callback
|
||||
===============
|
||||
*/
|
||||
static void sdl_audio_callback(void *userdata, Uint8 *stream, int len)
|
||||
{
|
||||
int pos = (dmapos * (dma.samplebits/8));
|
||||
if (pos >= dmasize)
|
||||
dmapos = pos = 0;
|
||||
int pos = (dmapos * (dma.samplebits/8));
|
||||
if (pos >= dmasize)
|
||||
dmapos = pos = 0;
|
||||
|
||||
if (!snd_inited) /* shouldn't happen, but just in case... */
|
||||
{
|
||||
memset(stream, '\0', len);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
int tobufend = dmasize - pos; /* bytes to buffer's end. */
|
||||
int len1 = len;
|
||||
int len2 = 0;
|
||||
if (!snd_inited) /* shouldn't happen, but just in case... */
|
||||
{
|
||||
memset(stream, '\0', len);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
int tobufend = dmasize - pos; /* bytes to buffer's end. */
|
||||
int len1 = len;
|
||||
int len2 = 0;
|
||||
|
||||
if (len1 > tobufend)
|
||||
{
|
||||
len1 = tobufend;
|
||||
len2 = len - len1;
|
||||
}
|
||||
memcpy(stream, dma.buffer + pos, len1);
|
||||
if (len2 <= 0)
|
||||
dmapos += (len1 / (dma.samplebits/8));
|
||||
else /* wraparound? */
|
||||
{
|
||||
memcpy(stream+len1, dma.buffer, len2);
|
||||
dmapos = (len2 / (dma.samplebits/8));
|
||||
}
|
||||
}
|
||||
|
||||
if (dmapos >= dmasize)
|
||||
dmapos = 0;
|
||||
}
|
||||
|
||||
static void print_audiospec(const char *str, const SDL_AudioSpec *spec)
|
||||
{
|
||||
Com_Printf("%s:\n", str);
|
||||
|
||||
// I'm sorry this is nasty.
|
||||
#define PRINT_AUDIO_FMT(x) \
|
||||
if (spec->format == x) Com_Printf("Format: %s\n", #x); else
|
||||
PRINT_AUDIO_FMT(AUDIO_U8)
|
||||
PRINT_AUDIO_FMT(AUDIO_S8)
|
||||
PRINT_AUDIO_FMT(AUDIO_U16LSB)
|
||||
PRINT_AUDIO_FMT(AUDIO_S16LSB)
|
||||
PRINT_AUDIO_FMT(AUDIO_U16MSB)
|
||||
PRINT_AUDIO_FMT(AUDIO_S16MSB)
|
||||
Com_Printf("Format: UNKNOWN\n");
|
||||
#undef PRINT_AUDIO_FMT
|
||||
|
||||
Com_Printf("Freq: %d\n", (int) spec->freq);
|
||||
Com_Printf("Samples: %d\n", (int) spec->samples);
|
||||
Com_Printf("Channels: %d\n", (int) spec->channels);
|
||||
Com_Printf("\n");
|
||||
}
|
||||
|
||||
qboolean SNDDMA_Init(void)
|
||||
{
|
||||
char drivername[128];
|
||||
SDL_AudioSpec desired;
|
||||
SDL_AudioSpec obtained;
|
||||
int tmp;
|
||||
|
||||
if (snd_inited)
|
||||
return 1;
|
||||
|
||||
Com_Printf("SDL Audio driver initializing...\n");
|
||||
|
||||
if (!snddevice) {
|
||||
sndbits = Cvar_Get("sndbits", "16", CVAR_ARCHIVE);
|
||||
sndspeed = Cvar_Get("sndspeed", "0", CVAR_ARCHIVE);
|
||||
sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE);
|
||||
snddevice = Cvar_Get("snddevice", "/dev/dsp", CVAR_ARCHIVE);
|
||||
sdldevsamps = Cvar_Get("sdldevsamps", "0", CVAR_ARCHIVE);
|
||||
sdlmixsamps = Cvar_Get("sdlmixsamps", "0", CVAR_ARCHIVE);
|
||||
if (len1 > tobufend)
|
||||
{
|
||||
len1 = tobufend;
|
||||
len2 = len - len1;
|
||||
}
|
||||
memcpy(stream, dma.buffer + pos, len1);
|
||||
if (len2 <= 0)
|
||||
dmapos += (len1 / (dma.samplebits/8));
|
||||
else /* wraparound? */
|
||||
{
|
||||
memcpy(stream+len1, dma.buffer, len2);
|
||||
dmapos = (len2 / (dma.samplebits/8));
|
||||
}
|
||||
}
|
||||
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO))
|
||||
{
|
||||
Com_Printf("Calling SDL_Init(SDL_INIT_AUDIO)...\n");
|
||||
if (SDL_Init(SDL_INIT_AUDIO) == -1)
|
||||
{
|
||||
Com_Printf("SDL_Init(SDL_INIT_AUDIO) failed: %s\n", SDL_GetError());
|
||||
return qfalse;
|
||||
}
|
||||
Com_Printf("SDL_Init(SDL_INIT_AUDIO) passed.\n");
|
||||
}
|
||||
|
||||
if (SDL_AudioDriverName(drivername, sizeof (drivername)) == NULL)
|
||||
strcpy(drivername, "(UNKNOWN)");
|
||||
Com_Printf("SDL audio driver is \"%s\"\n", drivername);
|
||||
|
||||
memset(&desired, '\0', sizeof (desired));
|
||||
memset(&obtained, '\0', sizeof (obtained));
|
||||
|
||||
tmp = ((int) sndbits->value);
|
||||
if ((tmp != 16) && (tmp != 8))
|
||||
tmp = 16;
|
||||
|
||||
desired.freq = (int) sndspeed->value;
|
||||
if(!desired.freq) desired.freq = 22050;
|
||||
desired.format = ((tmp == 16) ? AUDIO_S16SYS : AUDIO_U8);
|
||||
|
||||
// I dunno if this is the best idea, but I'll give it a try...
|
||||
// should probably check a cvar for this...
|
||||
if (sdldevsamps->value)
|
||||
desired.samples = sdldevsamps->value;
|
||||
else
|
||||
{
|
||||
// just pick a sane default.
|
||||
if (desired.freq <= 11025)
|
||||
desired.samples = 256;
|
||||
else if (desired.freq <= 22050)
|
||||
desired.samples = 512;
|
||||
else if (desired.freq <= 44100)
|
||||
desired.samples = 1024;
|
||||
else
|
||||
desired.samples = 2048; // (*shrug*)
|
||||
}
|
||||
|
||||
desired.channels = (int) sndchannels->value;
|
||||
desired.callback = sdl_audio_callback;
|
||||
|
||||
print_audiospec("Format we requested from SDL audio device", &desired);
|
||||
|
||||
if (SDL_OpenAudio(&desired, &obtained) == -1)
|
||||
{
|
||||
Com_Printf("SDL_OpenAudio() failed: %s\n", SDL_GetError());
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
return qfalse;
|
||||
} // if
|
||||
|
||||
print_audiospec("Format we actually got", &obtained);
|
||||
|
||||
// dma.samples needs to be big, or id's mixer will just refuse to
|
||||
// work at all; we need to keep it significantly bigger than the
|
||||
// amount of SDL callback samples, and just copy a little each time
|
||||
// the callback runs.
|
||||
// 32768 is what the OSS driver filled in here on my system. I don't
|
||||
// know if it's a good value overall, but at least we know it's
|
||||
// reasonable...this is why I let the user override.
|
||||
tmp = sdlmixsamps->value;
|
||||
if (!tmp)
|
||||
tmp = (obtained.samples * obtained.channels) * 10;
|
||||
|
||||
if (tmp & (tmp - 1)) // not a power of two? Seems to confuse something.
|
||||
{
|
||||
int val = 1;
|
||||
while (val < tmp)
|
||||
val <<= 1;
|
||||
|
||||
Com_Printf("WARNING: sdlmixsamps wasn't a power of two (%d),"
|
||||
" so we made it one (%d).\n", tmp, val);
|
||||
tmp = val;
|
||||
}
|
||||
|
||||
dmapos = 0;
|
||||
dma.samplebits = obtained.format & 0xFF; // first byte of format is bits.
|
||||
dma.channels = obtained.channels;
|
||||
dma.samples = tmp;
|
||||
dma.submission_chunk = 1;
|
||||
dma.speed = obtained.freq;
|
||||
dmasize = (dma.samples * (dma.samplebits/8));
|
||||
dma.buffer = calloc(1, dmasize);
|
||||
|
||||
Com_Printf("Starting SDL audio callback...\n");
|
||||
SDL_PauseAudio(0); // start callback.
|
||||
|
||||
Com_Printf("SDL audio initialized.\n");
|
||||
snd_inited = 1;
|
||||
return qtrue;
|
||||
if (dmapos >= dmasize)
|
||||
dmapos = 0;
|
||||
}
|
||||
|
||||
int SNDDMA_GetDMAPos(void)
|
||||
static struct
|
||||
{
|
||||
return dmapos;
|
||||
}
|
||||
Uint16 enumFormat;
|
||||
char *stringFormat;
|
||||
} formatToStringTable[ ] =
|
||||
{
|
||||
{ AUDIO_U8, "AUDIO_U8" },
|
||||
{ AUDIO_S8, "AUDIO_S8" },
|
||||
{ AUDIO_U16LSB, "AUDIO_U16LSB" },
|
||||
{ AUDIO_S16LSB, "AUDIO_S16LSB" },
|
||||
{ AUDIO_U16MSB, "AUDIO_U16MSB" },
|
||||
{ AUDIO_S16MSB, "AUDIO_S16MSB" }
|
||||
};
|
||||
|
||||
void SNDDMA_Shutdown(void)
|
||||
static int formatToStringTableSize =
|
||||
sizeof( formatToStringTable ) / sizeof( formatToStringTable[ 0 ] );
|
||||
|
||||
/*
|
||||
===============
|
||||
print_audiospec
|
||||
===============
|
||||
*/
|
||||
static void print_audiospec(const char *str, const SDL_AudioSpec *spec)
|
||||
{
|
||||
Com_Printf("Closing SDL audio device...\n");
|
||||
SDL_PauseAudio(1);
|
||||
SDL_CloseAudio();
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
free(dma.buffer);
|
||||
dma.buffer = NULL;
|
||||
dmapos = dmasize = 0;
|
||||
snd_inited = 0;
|
||||
Com_Printf("SDL audio device shut down.\n");
|
||||
int i;
|
||||
char *fmt = NULL;
|
||||
|
||||
Com_Printf("%s:\n", str);
|
||||
|
||||
for( i = 0; i < formatToStringTableSize; i++ ) {
|
||||
if( spec->format == formatToStringTable[ i ].enumFormat ) {
|
||||
fmt = formatToStringTable[ i ].stringFormat;
|
||||
}
|
||||
}
|
||||
|
||||
if( fmt ) {
|
||||
Com_Printf( " Format: %s\n", fmt );
|
||||
} else {
|
||||
Com_Printf( " Format: " S_COLOR_RED "UNKNOWN\n", fmt );
|
||||
}
|
||||
|
||||
Com_Printf( " Freq: %d\n", (int) spec->freq );
|
||||
Com_Printf( " Samples: %d\n", (int) spec->samples );
|
||||
Com_Printf( " Channels: %d\n", (int) spec->channels );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
===============
|
||||
SNDDMA_Init
|
||||
===============
|
||||
*/
|
||||
qboolean SNDDMA_Init(void)
|
||||
{
|
||||
char drivername[128];
|
||||
SDL_AudioSpec desired;
|
||||
SDL_AudioSpec obtained;
|
||||
int tmp;
|
||||
|
||||
if (snd_inited)
|
||||
return qtrue;
|
||||
|
||||
Com_Printf("Initializing SDL audio driver...\n");
|
||||
|
||||
if (!s_sdlBits) {
|
||||
s_sdlBits = Cvar_Get("s_sdlBits", "16", CVAR_ARCHIVE);
|
||||
s_sdlSpeed = Cvar_Get("s_sdlSpeed", "0", CVAR_ARCHIVE);
|
||||
s_sdlChannels = Cvar_Get("s_sdlChannels", "2", CVAR_ARCHIVE);
|
||||
s_sdlDevSamps = Cvar_Get("s_sdlDevSamps", "0", CVAR_ARCHIVE);
|
||||
s_sdlMixSamps = Cvar_Get("s_sdlMixSamps", "0", CVAR_ARCHIVE);
|
||||
}
|
||||
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO))
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_AUDIO) == -1)
|
||||
{
|
||||
Com_Printf("SDL_Init(SDL_INIT_AUDIO) failed: %s\n", SDL_GetError());
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_AudioDriverName(drivername, sizeof (drivername)) == NULL)
|
||||
strcpy(drivername, "(UNKNOWN)");
|
||||
Com_Printf("SDL audio driver is \"%s\".\n", drivername);
|
||||
|
||||
memset(&desired, '\0', sizeof (desired));
|
||||
memset(&obtained, '\0', sizeof (obtained));
|
||||
|
||||
tmp = ((int) s_sdlBits->value);
|
||||
if ((tmp != 16) && (tmp != 8))
|
||||
tmp = 16;
|
||||
|
||||
desired.freq = (int) s_sdlSpeed->value;
|
||||
if(!desired.freq) desired.freq = 22050;
|
||||
desired.format = ((tmp == 16) ? AUDIO_S16SYS : AUDIO_U8);
|
||||
|
||||
// I dunno if this is the best idea, but I'll give it a try...
|
||||
// should probably check a cvar for this...
|
||||
if (s_sdlDevSamps->value)
|
||||
desired.samples = s_sdlDevSamps->value;
|
||||
else
|
||||
{
|
||||
// just pick a sane default.
|
||||
if (desired.freq <= 11025)
|
||||
desired.samples = 256;
|
||||
else if (desired.freq <= 22050)
|
||||
desired.samples = 512;
|
||||
else if (desired.freq <= 44100)
|
||||
desired.samples = 1024;
|
||||
else
|
||||
desired.samples = 2048; // (*shrug*)
|
||||
}
|
||||
|
||||
desired.channels = (int) s_sdlChannels->value;
|
||||
desired.callback = sdl_audio_callback;
|
||||
|
||||
if (SDL_OpenAudio(&desired, &obtained) == -1)
|
||||
{
|
||||
Com_Printf("SDL_OpenAudio() failed: %s\n", SDL_GetError());
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
return qfalse;
|
||||
} // if
|
||||
|
||||
print_audiospec("SDL_AudioSpec", &obtained);
|
||||
|
||||
// dma.samples needs to be big, or id's mixer will just refuse to
|
||||
// work at all; we need to keep it significantly bigger than the
|
||||
// amount of SDL callback samples, and just copy a little each time
|
||||
// the callback runs.
|
||||
// 32768 is what the OSS driver filled in here on my system. I don't
|
||||
// know if it's a good value overall, but at least we know it's
|
||||
// reasonable...this is why I let the user override.
|
||||
tmp = s_sdlMixSamps->value;
|
||||
if (!tmp)
|
||||
tmp = (obtained.samples * obtained.channels) * 10;
|
||||
|
||||
if (tmp & (tmp - 1)) // not a power of two? Seems to confuse something.
|
||||
{
|
||||
int val = 1;
|
||||
while (val < tmp)
|
||||
val <<= 1;
|
||||
|
||||
tmp = val;
|
||||
}
|
||||
|
||||
dmapos = 0;
|
||||
dma.samplebits = obtained.format & 0xFF; // first byte of format is bits.
|
||||
dma.channels = obtained.channels;
|
||||
dma.samples = tmp;
|
||||
dma.submission_chunk = 1;
|
||||
dma.speed = obtained.freq;
|
||||
dmasize = (dma.samples * (dma.samplebits/8));
|
||||
dma.buffer = calloc(1, dmasize);
|
||||
|
||||
Com_Printf("Starting SDL audio callback...\n");
|
||||
SDL_PauseAudio(0); // start callback.
|
||||
|
||||
Com_Printf("SDL audio initialized.\n");
|
||||
snd_inited = qtrue;
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SNDDMA_GetDMAPos
|
||||
===============
|
||||
*/
|
||||
int SNDDMA_GetDMAPos(void)
|
||||
{
|
||||
return dmapos;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SNDDMA_Shutdown
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_Shutdown(void)
|
||||
{
|
||||
Com_Printf("Closing SDL audio device...\n");
|
||||
SDL_PauseAudio(1);
|
||||
SDL_CloseAudio();
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
free(dma.buffer);
|
||||
dma.buffer = NULL;
|
||||
dmapos = dmasize = 0;
|
||||
snd_inited = qfalse;
|
||||
Com_Printf("SDL audio device shut down.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SNDDMA_Submit
|
||||
|
||||
Send sound to device if buffer isn't really the dma buffer
|
||||
|
@ -290,15 +342,17 @@ Send sound to device if buffer isn't really the dma buffer
|
|||
*/
|
||||
void SNDDMA_Submit(void)
|
||||
{
|
||||
SDL_UnlockAudio();
|
||||
SDL_UnlockAudio();
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SNDDMA_BeginPainting
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_BeginPainting (void)
|
||||
{
|
||||
SDL_LockAudio();
|
||||
SDL_LockAudio();
|
||||
}
|
||||
|
||||
#endif // USE_SDL_SOUND
|
||||
|
||||
// end of linux_snd_sdl.c ...
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue