summaryrefslogtreecommitdiff
path: root/src/client/snd_openal.c
diff options
context:
space:
mode:
authorZack Middleton <zturtleman@gmail.com>2013-11-10 22:42:48 -0600
committerTim Angus <tim@ngus.net>2014-06-17 17:43:36 +0100
commitf7e122a3f11830fa4c5e049296b31969d2964280 (patch)
tree956dd3a1644dbf9c49a7369ae35c3f99e96a881e /src/client/snd_openal.c
parenta9f515331547b95d2c4813a57e89e8fbf66f161b (diff)
Make OpenAL buffer management work on OS X
Don't assume we have unlimited OpenAL buffers. Detach buffers from sources by setting AL_BUFFER to 0. Cannot delete buffers on OS X immediately after alSourceUnqueueBuffers. Free unprocessed stream and music buffers. Free unused sfx buffers when sound file exists and fails loading into OpenAL (though I haven't seen it fail).
Diffstat (limited to 'src/client/snd_openal.c')
-rw-r--r--src/client/snd_openal.c137
1 files changed, 105 insertions, 32 deletions
diff --git a/src/client/snd_openal.c b/src/client/snd_openal.c
index b7ce3f0f..fa18f2dc 100644
--- a/src/client/snd_openal.c
+++ b/src/client/snd_openal.c
@@ -317,6 +317,44 @@ static qboolean S_AL_BufferEvict( void )
/*
=================
+S_AL_GenBuffers
+=================
+*/
+static qboolean S_AL_GenBuffers(ALsizei numBuffers, ALuint *buffers, const char *name)
+{
+ ALenum error;
+
+ S_AL_ClearError( qfalse );
+ qalGenBuffers( numBuffers, buffers );
+ error = qalGetError();
+
+ // If we ran out of buffers, start evicting the least recently used sounds
+ while( error == AL_INVALID_VALUE )
+ {
+ if( !S_AL_BufferEvict( ) )
+ {
+ Com_Printf( S_COLOR_RED "ERROR: Out of audio buffers\n");
+ return qfalse;
+ }
+
+ // Try again
+ S_AL_ClearError( qfalse );
+ qalGenBuffers( numBuffers, buffers );
+ error = qalGetError();
+ }
+
+ if( error != AL_NO_ERROR )
+ {
+ Com_Printf( S_COLOR_RED "ERROR: Can't create a sound buffer for %s - %s\n",
+ name, S_AL_ErrorMsg(error));
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+/*
+=================
S_AL_BufferLoad
=================
*/
@@ -357,14 +395,10 @@ static void S_AL_BufferLoad(sfxHandle_t sfx, qboolean cache)
format = S_AL_Format(info.width, info.channels);
// Create a buffer
- S_AL_ClearError( qfalse );
- qalGenBuffers(1, &curSfx->buffer);
- if((error = qalGetError()) != AL_NO_ERROR)
+ if (!S_AL_GenBuffers(1, &curSfx->buffer, curSfx->filename))
{
S_AL_BufferUseDefault(sfx);
Hunk_FreeTempMemory(data);
- Com_Printf( S_COLOR_RED "ERROR: Can't create a sound buffer for %s - %s\n",
- curSfx->filename, S_AL_ErrorMsg(error));
return;
}
@@ -386,6 +420,7 @@ static void S_AL_BufferLoad(sfxHandle_t sfx, qboolean cache)
{
if( !S_AL_BufferEvict( ) )
{
+ qalDeleteBuffers(1, &curSfx->buffer);
S_AL_BufferUseDefault(sfx);
Hunk_FreeTempMemory(data);
Com_Printf( S_COLOR_RED "ERROR: Out of memory loading %s\n", curSfx->filename);
@@ -400,6 +435,7 @@ static void S_AL_BufferLoad(sfxHandle_t sfx, qboolean cache)
// Some other error condition
if(error != AL_NO_ERROR)
{
+ qalDeleteBuffers(1, &curSfx->buffer);
S_AL_BufferUseDefault(sfx);
Hunk_FreeTempMemory(data);
Com_Printf( S_COLOR_RED "ERROR: Can't fill sound buffer for %s - %s\n",
@@ -983,7 +1019,7 @@ static void S_AL_SrcKill(srcHandle_t src)
curSource->isPlaying = qfalse;
}
- // Remove the buffer
+ // Detach any buffers
qalSourcei(curSource->alSource, AL_BUFFER, 0);
curSource->sfx = 0;
@@ -1642,9 +1678,15 @@ ALuint S_AL_SrcGet(srcHandle_t src)
//===========================================================================
+// Q3A cinematics use up to 12 buffers at once
+#define MAX_STREAM_BUFFERS 20
+
static srcHandle_t streamSourceHandles[MAX_RAW_STREAMS];
static qboolean streamPlaying[MAX_RAW_STREAMS];
static ALuint streamSources[MAX_RAW_STREAMS];
+static ALuint streamBuffers[MAX_RAW_STREAMS][MAX_STREAM_BUFFERS];
+static int streamNumBuffers[MAX_RAW_STREAMS];
+static int streamBufIndex[MAX_RAW_STREAMS];
/*
=================
@@ -1702,6 +1744,9 @@ static void S_AL_AllocateStreamChannel(int stream, int entityNum)
streamSourceHandles[stream] = cursrc;
streamSources[stream] = alsrc;
+
+ streamNumBuffers[stream] = 0;
+ streamBufIndex[stream] = 0;
}
/*
@@ -1714,6 +1759,15 @@ static void S_AL_FreeStreamChannel( int stream )
if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
return;
+ // Detach any buffers
+ qalSourcei(streamSources[stream], AL_BUFFER, 0);
+
+ // Delete the buffers
+ if (streamNumBuffers[stream] > 0) {
+ qalDeleteBuffers(streamNumBuffers[stream], streamBuffers[stream]);
+ streamNumBuffers[stream] = 0;
+ }
+
// Release the output streamSource
S_AL_SrcUnlock(streamSourceHandles[stream]);
S_AL_SrcKill(streamSourceHandles[stream]);
@@ -1729,6 +1783,7 @@ S_AL_RawSamples
static
void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels, const byte *data, float volume, int entityNum)
{
+ int numBuffers;
ALuint buffer;
ALuint format;
@@ -1750,8 +1805,40 @@ void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels,
}
}
- // Create a buffer, and stuff the data into it
- qalGenBuffers(1, &buffer);
+ qalGetSourcei(streamSources[stream], AL_BUFFERS_QUEUED, &numBuffers);
+
+ if (numBuffers == MAX_STREAM_BUFFERS)
+ {
+ Com_DPrintf(S_COLOR_RED"WARNING: Steam dropping raw samples, reached MAX_STREAM_BUFFERS\n");
+ return;
+ }
+
+ // Allocate a new AL buffer if needed
+ if (numBuffers == streamNumBuffers[stream])
+ {
+ ALuint oldBuffers[MAX_STREAM_BUFFERS];
+ int i;
+
+ if (!S_AL_GenBuffers(1, &buffer, "stream"))
+ return;
+
+ Com_Memcpy(oldBuffers, &streamBuffers[stream], sizeof (oldBuffers));
+
+ // Reorder buffer array in order of oldest to newest
+ for ( i = 0; i < streamNumBuffers[stream]; ++i )
+ streamBuffers[stream][i] = oldBuffers[(streamBufIndex[stream] + i) % streamNumBuffers[stream]];
+
+ // Add the new buffer to end
+ streamBuffers[stream][streamNumBuffers[stream]] = buffer;
+ streamBufIndex[stream] = streamNumBuffers[stream];
+ streamNumBuffers[stream]++;
+ }
+
+ // Select next buffer in loop
+ buffer = streamBuffers[stream][ streamBufIndex[stream] ];
+ streamBufIndex[stream] = (streamBufIndex[stream] + 1) % streamNumBuffers[stream];
+
+ // Fill buffer
qalBufferData(buffer, format, (ALvoid *)data, (samples * width * channels), rate);
// Shove the data onto the streamSource
@@ -1781,13 +1868,12 @@ void S_AL_StreamUpdate( int stream )
if(streamSourceHandles[stream] == -1)
return;
- // Un-queue any buffers, and delete them
+ // Un-queue any buffers
qalGetSourcei( streamSources[stream], AL_BUFFERS_PROCESSED, &numBuffers );
while( numBuffers-- )
{
ALuint buffer;
qalSourceUnqueueBuffers(streamSources[stream], 1, &buffer);
- qalDeleteBuffers(1, &buffer);
}
// Start the streamSource playing if necessary
@@ -1818,8 +1904,6 @@ S_AL_StreamDie
static
void S_AL_StreamDie( int stream )
{
- int numBuffers;
-
if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
return;
@@ -1829,15 +1913,6 @@ void S_AL_StreamDie( int stream )
streamPlaying[stream] = qfalse;
qalSourceStop(streamSources[stream]);
- // Un-queue any buffers, and delete them
- qalGetSourcei( streamSources[stream], AL_BUFFERS_PROCESSED, &numBuffers );
- while( numBuffers-- )
- {
- ALuint buffer;
- qalSourceUnqueueBuffers(streamSources[stream], 1, &buffer);
- qalDeleteBuffers(1, &buffer);
- }
-
S_AL_FreeStreamChannel(stream);
}
@@ -1929,22 +2004,17 @@ S_AL_StopBackgroundTrack
static
void S_AL_StopBackgroundTrack( void )
{
- int numBuffers;
-
if(!musicPlaying)
return;
// Stop playing
qalSourceStop(musicSource);
- // Un-queue any buffers, and delete them
- qalGetSourcei( musicSource, AL_BUFFERS_PROCESSED, &numBuffers );
- while( numBuffers-- )
- {
- ALuint buffer;
- qalSourceUnqueueBuffers(musicSource, 1, &buffer);
- qalDeleteBuffers(1, &buffer);
- }
+ // Detach any buffers
+ qalSourcei(musicSource, AL_BUFFER, 0);
+
+ // Delete the buffers
+ qalDeleteBuffers(NUM_MUSIC_BUFFERS, musicBuffers);
// Free the musicSource
S_AL_MusicSourceFree();
@@ -2077,7 +2147,8 @@ void S_AL_StartBackgroundTrack( const char *intro, const char *loop )
}
// Generate the musicBuffers
- qalGenBuffers(NUM_MUSIC_BUFFERS, musicBuffers);
+ if (!S_AL_GenBuffers(NUM_MUSIC_BUFFERS, musicBuffers, "music"))
+ return;
// Queue the musicBuffers up
for(i = 0; i < NUM_MUSIC_BUFFERS; i++)
@@ -2438,6 +2509,8 @@ qboolean S_AL_Init( soundInterface_t *si )
streamSourceHandles[i] = -1;
streamPlaying[i] = qfalse;
streamSources[i] = 0;
+ streamNumBuffers[i] = 0;
+ streamBufIndex[i] = 0;
}
// New console variables