mirror of
https://github.com/UberGames/ioef.git
synced 2025-01-31 13:00:46 +00:00
* Improved the robustity^Wrobustosity^Wrobustness of the OpenAL music and raw
stream systems * Implemented S_AL_BufferEvict for more graceful behaviour when OpenAL runs out of memory
This commit is contained in:
parent
0f8c177cf3
commit
5cc1fb1c05
1 changed files with 100 additions and 81 deletions
|
@ -100,13 +100,12 @@ static const char *S_AL_ErrorMsg(ALenum error)
|
||||||
|
|
||||||
typedef struct alSfx_s
|
typedef struct alSfx_s
|
||||||
{
|
{
|
||||||
char filename[MAX_QPATH];
|
char filename[MAX_QPATH];
|
||||||
ALuint buffer; // OpenAL buffer
|
ALuint buffer; // OpenAL buffer
|
||||||
qboolean isDefault; // Couldn't be loaded - use default FX
|
qboolean isDefault; // Couldn't be loaded - use default FX
|
||||||
qboolean inMemory; // Sound is stored in memory
|
qboolean inMemory; // Sound is stored in memory
|
||||||
qboolean isLocked; // Sound is locked (can not be unloaded)
|
qboolean isLocked; // Sound is locked (can not be unloaded)
|
||||||
int used; // Time last used
|
int lastUsedTime; // Time last used
|
||||||
struct alSfx_t *next; // Next entry in hash list
|
|
||||||
} alSfx_t;
|
} alSfx_t;
|
||||||
|
|
||||||
static qboolean alBuffersInitialised = qfalse;
|
static qboolean alBuffersInitialised = qfalse;
|
||||||
|
@ -197,15 +196,60 @@ static void S_AL_BufferUseDefault(sfxHandle_t sfx)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=================
|
=================
|
||||||
S_AL_BufferEvict
|
S_AL_BufferUnload
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
static void S_AL_BufferUnload(sfxHandle_t sfx)
|
||||||
|
{
|
||||||
|
ALenum error;
|
||||||
|
|
||||||
Doesn't work yet, so if OpenAL reports that you're out of memory, you'll just
|
if(knownSfx[sfx].filename[0] == '\0')
|
||||||
get "Catastrophic sound memory exhaustion". Whoops.
|
return;
|
||||||
|
|
||||||
|
if(!knownSfx[sfx].inMemory)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Delete it
|
||||||
|
qalDeleteBuffers(1, &knownSfx[sfx].buffer);
|
||||||
|
if((error = qalGetError()) != AL_NO_ERROR)
|
||||||
|
Com_Printf( S_COLOR_RED "ERROR: Can't delete sound buffer for %s\n",
|
||||||
|
knownSfx[sfx].filename);
|
||||||
|
|
||||||
|
knownSfx[sfx].inMemory = qfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
S_AL_BufferEvict
|
||||||
=================
|
=================
|
||||||
*/
|
*/
|
||||||
static qboolean S_AL_BufferEvict( void )
|
static qboolean S_AL_BufferEvict( void )
|
||||||
{
|
{
|
||||||
return qfalse;
|
int i, oldestBuffer = -1;
|
||||||
|
int oldestTime = Sys_Milliseconds( );
|
||||||
|
|
||||||
|
for( i = 0; i < MAX_SFX; i++ )
|
||||||
|
{
|
||||||
|
if( !knownSfx[ i ].filename[ 0 ] )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( !knownSfx[ i ].inMemory )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( knownSfx[ i ].lastUsedTime < oldestTime )
|
||||||
|
{
|
||||||
|
oldestTime = knownSfx[ i ].lastUsedTime;
|
||||||
|
oldestBuffer = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( oldestBuffer >= 0 )
|
||||||
|
{
|
||||||
|
S_AL_BufferUnload( oldestBuffer );
|
||||||
|
return qtrue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -270,8 +314,7 @@ static void S_AL_BufferLoad(sfxHandle_t sfx)
|
||||||
// If we ran out of memory, start evicting the least recently used sounds
|
// If we ran out of memory, start evicting the least recently used sounds
|
||||||
while(error == AL_OUT_OF_MEMORY)
|
while(error == AL_OUT_OF_MEMORY)
|
||||||
{
|
{
|
||||||
qboolean rv = S_AL_BufferEvict();
|
if( !S_AL_BufferEvict( ) )
|
||||||
if(!rv)
|
|
||||||
{
|
{
|
||||||
S_AL_BufferUseDefault(sfx);
|
S_AL_BufferUseDefault(sfx);
|
||||||
Z_Free(data);
|
Z_Free(data);
|
||||||
|
@ -314,7 +357,7 @@ void S_AL_BufferUse(sfxHandle_t sfx)
|
||||||
|
|
||||||
if((!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
|
if((!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
|
||||||
S_AL_BufferLoad(sfx);
|
S_AL_BufferLoad(sfx);
|
||||||
knownSfx[sfx].used = Com_Milliseconds();
|
knownSfx[sfx].lastUsedTime = Sys_Milliseconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -342,30 +385,6 @@ qboolean S_AL_BufferInit( void )
|
||||||
return qtrue;
|
return qtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
=================
|
|
||||||
S_AL_BufferUnload
|
|
||||||
=================
|
|
||||||
*/
|
|
||||||
static void S_AL_BufferUnload(sfxHandle_t sfx)
|
|
||||||
{
|
|
||||||
ALenum error;
|
|
||||||
|
|
||||||
if(knownSfx[sfx].filename[0] == '\0')
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!knownSfx[sfx].inMemory)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Delete it
|
|
||||||
qalDeleteBuffers(1, &knownSfx[sfx].buffer);
|
|
||||||
if((error = qalGetError()) != AL_NO_ERROR)
|
|
||||||
Com_Printf( S_COLOR_RED "ERROR: Can't delete sound buffer for %s\n",
|
|
||||||
knownSfx[sfx].filename);
|
|
||||||
|
|
||||||
knownSfx[sfx].inMemory = qfalse;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=================
|
=================
|
||||||
S_AL_BufferShutdown
|
S_AL_BufferShutdown
|
||||||
|
@ -405,7 +424,7 @@ sfxHandle_t S_AL_RegisterSound( const char *sample, qboolean compressed )
|
||||||
|
|
||||||
if( s_alPrecache->integer && (!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
|
if( s_alPrecache->integer && (!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
|
||||||
S_AL_BufferLoad(sfx);
|
S_AL_BufferLoad(sfx);
|
||||||
knownSfx[sfx].used = Com_Milliseconds();
|
knownSfx[sfx].lastUsedTime = Com_Milliseconds();
|
||||||
|
|
||||||
return sfx;
|
return sfx;
|
||||||
}
|
}
|
||||||
|
@ -432,7 +451,7 @@ typedef struct src_s
|
||||||
ALuint alSource; // OpenAL source object
|
ALuint alSource; // OpenAL source object
|
||||||
sfxHandle_t sfx; // Sound effect in use
|
sfxHandle_t sfx; // Sound effect in use
|
||||||
|
|
||||||
int lastUse; // Last time used
|
int lastUsedTime; // Last time used
|
||||||
alSrcPriority_t priority; // Priority
|
alSrcPriority_t priority; // Priority
|
||||||
int entity; // Owning entity (-1 if none)
|
int entity; // Owning entity (-1 if none)
|
||||||
int channel; // Associated channel (-1 if none)
|
int channel; // Associated channel (-1 if none)
|
||||||
|
@ -547,7 +566,7 @@ static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t prio
|
||||||
buffer = S_AL_BufferGet(sfx);
|
buffer = S_AL_BufferGet(sfx);
|
||||||
|
|
||||||
// Set up src struct
|
// Set up src struct
|
||||||
srcList[src].lastUse = Sys_Milliseconds();
|
srcList[src].lastUsedTime = Sys_Milliseconds();
|
||||||
srcList[src].sfx = sfx;
|
srcList[src].sfx = sfx;
|
||||||
srcList[src].priority = priority;
|
srcList[src].priority = priority;
|
||||||
srcList[src].entity = entity;
|
srcList[src].entity = entity;
|
||||||
|
@ -608,7 +627,7 @@ static void S_AL_SrcKill(srcHandle_t src)
|
||||||
qalSourcei(srcList[src].alSource, AL_BUFFER, 0);
|
qalSourcei(srcList[src].alSource, AL_BUFFER, 0);
|
||||||
|
|
||||||
srcList[src].sfx = 0;
|
srcList[src].sfx = 0;
|
||||||
srcList[src].lastUse = 0;
|
srcList[src].lastUsedTime = 0;
|
||||||
srcList[src].priority = 0;
|
srcList[src].priority = 0;
|
||||||
srcList[src].entity = -1;
|
srcList[src].entity = -1;
|
||||||
srcList[src].channel = -1;
|
srcList[src].channel = -1;
|
||||||
|
@ -645,10 +664,10 @@ srcHandle_t S_AL_SrcAlloc( alSrcPriority_t priority, int entnum, int channel )
|
||||||
{
|
{
|
||||||
// If it's older or has lower priority, flag it as weak
|
// If it's older or has lower priority, flag it as weak
|
||||||
if((srcList[i].priority < weakest_pri) ||
|
if((srcList[i].priority < weakest_pri) ||
|
||||||
(srcList[i].lastUse < weakest_time))
|
(srcList[i].lastUsedTime < weakest_time))
|
||||||
{
|
{
|
||||||
weakest_pri = srcList[i].priority;
|
weakest_pri = srcList[i].priority;
|
||||||
weakest_time = srcList[i].lastUse;
|
weakest_time = srcList[i].lastUsedTime;
|
||||||
weakest = i;
|
weakest = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1063,7 +1082,6 @@ void S_AL_RawSamples(int samples, int rate, int width, int channels, const byte
|
||||||
{
|
{
|
||||||
ALuint buffer;
|
ALuint buffer;
|
||||||
ALuint format;
|
ALuint format;
|
||||||
ALint state;
|
|
||||||
|
|
||||||
format = S_AL_Format( width, channels );
|
format = S_AL_Format( width, channels );
|
||||||
|
|
||||||
|
@ -1087,17 +1105,8 @@ void S_AL_RawSamples(int samples, int rate, int width, int channels, const byte
|
||||||
// Shove the data onto the streamSource
|
// Shove the data onto the streamSource
|
||||||
qalSourceQueueBuffers(streamSource, 1, &buffer);
|
qalSourceQueueBuffers(streamSource, 1, &buffer);
|
||||||
|
|
||||||
// Start the streamSource playing if necessary
|
|
||||||
qalGetSourcei(streamSource, AL_SOURCE_STATE, &state);
|
|
||||||
|
|
||||||
// Volume
|
// Volume
|
||||||
qalSourcef (streamSource, AL_GAIN, volume * s_volume->value * s_alGain->value);
|
qalSourcef (streamSource, AL_GAIN, volume * s_volume->value * s_alGain->value);
|
||||||
|
|
||||||
if(!streamPlaying)
|
|
||||||
{
|
|
||||||
qalSourcePlay(streamSource);
|
|
||||||
streamPlaying = qtrue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1108,31 +1117,38 @@ S_AL_StreamUpdate
|
||||||
static
|
static
|
||||||
void S_AL_StreamUpdate( void )
|
void S_AL_StreamUpdate( void )
|
||||||
{
|
{
|
||||||
int processed;
|
int numBuffers;
|
||||||
ALint state;
|
ALint state;
|
||||||
|
|
||||||
if(streamSourceHandle == -1)
|
if(streamSourceHandle == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Un-queue any buffers, and delete them
|
// Un-queue any buffers, and delete them
|
||||||
qalGetSourcei(streamSource, AL_BUFFERS_PROCESSED, &processed);
|
qalGetSourcei( streamSource, AL_BUFFERS_PROCESSED, &numBuffers );
|
||||||
if(processed)
|
while( numBuffers-- )
|
||||||
{
|
{
|
||||||
while(processed--)
|
ALuint buffer;
|
||||||
{
|
qalSourceUnqueueBuffers(streamSource, 1, &buffer);
|
||||||
ALuint buffer;
|
qalDeleteBuffers(1, &buffer);
|
||||||
qalSourceUnqueueBuffers(streamSource, 1, &buffer);
|
|
||||||
qalDeleteBuffers(1, &buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the streamSource playing if necessary
|
||||||
|
qalGetSourcei( streamSource, AL_BUFFERS_QUEUED, &numBuffers );
|
||||||
|
|
||||||
// If it's stopped, release the streamSource
|
// If it's stopped, release the streamSource
|
||||||
qalGetSourcei(streamSource, AL_SOURCE_STATE, &state);
|
qalGetSourcei(streamSource, AL_SOURCE_STATE, &state);
|
||||||
if(state == AL_STOPPED)
|
if(state == AL_STOPPED)
|
||||||
{
|
{
|
||||||
streamPlaying = qfalse;
|
streamPlaying = qfalse;
|
||||||
qalSourceStop(streamSource);
|
/*qalSourceStop(streamSource);*/
|
||||||
S_AL_FreeStreamChannel();
|
if( !numBuffers )
|
||||||
|
S_AL_FreeStreamChannel( );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !streamPlaying && numBuffers )
|
||||||
|
{
|
||||||
|
qalSourcePlay(streamSource);
|
||||||
|
streamPlaying = qtrue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1331,6 +1347,9 @@ void S_AL_StartBackgroundTrack( const char *intro, const char *loop )
|
||||||
S_AL_MusicProcess(musicBuffers[i]);
|
S_AL_MusicProcess(musicBuffers[i]);
|
||||||
qalSourceQueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers);
|
qalSourceQueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers);
|
||||||
|
|
||||||
|
// Set the initial gain property
|
||||||
|
qalSourcef(musicSource, AL_GAIN, s_alGain->value * s_musicVolume->value);
|
||||||
|
|
||||||
// Start playing
|
// Start playing
|
||||||
qalSourcePlay(musicSource);
|
qalSourcePlay(musicSource);
|
||||||
|
|
||||||
|
@ -1345,29 +1364,29 @@ S_AL_MusicUpdate
|
||||||
static
|
static
|
||||||
void S_AL_MusicUpdate( void )
|
void S_AL_MusicUpdate( void )
|
||||||
{
|
{
|
||||||
int processed;
|
int numBuffers;
|
||||||
ALint state;
|
ALint state;
|
||||||
|
|
||||||
if(!musicPlaying)
|
if(!musicPlaying)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
qalGetSourcei(musicSource, AL_BUFFERS_PROCESSED, &processed);
|
qalGetSourcei( musicSource, AL_BUFFERS_PROCESSED, &numBuffers );
|
||||||
if(processed)
|
while( numBuffers-- )
|
||||||
{
|
{
|
||||||
while(processed--)
|
ALuint b;
|
||||||
{
|
qalSourceUnqueueBuffers(musicSource, 1, &b);
|
||||||
ALuint b;
|
S_AL_MusicProcess(b);
|
||||||
qalSourceUnqueueBuffers(musicSource, 1, &b);
|
qalSourceQueueBuffers(musicSource, 1, &b);
|
||||||
S_AL_MusicProcess(b);
|
|
||||||
qalSourceQueueBuffers(musicSource, 1, &b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's not still playing, give it a kick
|
// Hitches can cause OpenAL to be starved of buffers when streaming.
|
||||||
qalGetSourcei(musicSource, AL_SOURCE_STATE, &state);
|
// If this happens, it will stop playback. This restarts the source if
|
||||||
if(state == AL_STOPPED)
|
// it is no longer playing, and if there are buffers available
|
||||||
|
qalGetSourcei( musicSource, AL_SOURCE_STATE, &state );
|
||||||
|
qalGetSourcei( musicSource, AL_BUFFERS_QUEUED, &numBuffers );
|
||||||
|
if( state == AL_STOPPED && numBuffers )
|
||||||
{
|
{
|
||||||
Com_DPrintf( "Restarted OpenAL music musicSource\n");
|
Com_DPrintf( S_COLOR_YELLOW "Restarted OpenAL music\n" );
|
||||||
qalSourcePlay(musicSource);
|
qalSourcePlay(musicSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue