SoundEmitter: Optimize source allocation strategy

Before, we would blindly rotate through the sources (like a
revolver through its chambers), which worked great if one
assumed all sounds to be relatively short and therefore oldest
use == most likely to be free, but breaks if there is one long
sound playing, which would be stopped and overtaken if we rotated
back to it even though there might be other free sources available.

Instead, keep an ascending priority list of sources with last
used == highest priorty that is iterated through for the first
free one, and only if none is found overtake the one with lowest
priority. This also ensures we're always able to play 'SE_SOURCES'
sounds at once independently of their length.

Fixes #37.
This commit is contained in:
Jonas Kulla 2014-06-15 06:07:11 +02:00
parent 282d547ad4
commit 6808b9a6dd
1 changed files with 33 additions and 10 deletions

View File

@ -143,6 +143,18 @@ private:
} }
}; };
/* Before: [a][b][c][d], After (index=1): [a][c][d][b] */
static void
arrayPushBack(size_t array[], size_t size, size_t index)
{
size_t v = array[index];
for (size_t t = index; t < size-1; ++t)
array[t] = array[t+1];
array[size-1] = v;
}
struct SoundEmitter struct SoundEmitter
{ {
typedef BoostHash<std::string, SoundBuffer*> BufferHash; typedef BoostHash<std::string, SoundBuffer*> BufferHash;
@ -155,17 +167,18 @@ struct SoundEmitter
AL::Source::ID alSrcs[SE_SOURCES]; AL::Source::ID alSrcs[SE_SOURCES];
SoundBuffer *atchBufs[SE_SOURCES]; SoundBuffer *atchBufs[SE_SOURCES];
/* Index of next source to be used */
int srcIndex; /* Indices of sources, sorted by priority (lowest first) */
size_t srcPrio[SE_SOURCES];
SoundEmitter() SoundEmitter()
: bufferBytes(0), : bufferBytes(0)
srcIndex(0)
{ {
for (int i = 0; i < SE_SOURCES; ++i) for (int i = 0; i < SE_SOURCES; ++i)
{ {
alSrcs[i] = AL::Source::gen(); alSrcs[i] = AL::Source::gen();
atchBufs[i] = 0; atchBufs[i] = 0;
srcPrio[i] = i;
} }
} }
@ -194,20 +207,30 @@ struct SoundEmitter
SoundBuffer *buffer = allocateBuffer(filename); SoundBuffer *buffer = allocateBuffer(filename);
int soundIndex = srcIndex++; /* Try to find first free source */
if (srcIndex > SE_SOURCES-1) size_t i;
srcIndex = 0; for (i = 0; i < SE_SOURCES; ++i)
if (AL::Source::getState(alSrcs[srcPrio[i]]) != AL_PLAYING)
break;
AL::Source::ID src = alSrcs[soundIndex]; /* If we didn't find any, overtake the one with lowest priority */
if (i == SE_SOURCES)
i = 0;
/* Push the used source to the back of the priority list */
size_t srcIndex = srcPrio[i];
arrayPushBack(srcPrio, SE_SOURCES, i);
AL::Source::ID src = alSrcs[srcIndex];
AL::Source::stop(src); AL::Source::stop(src);
AL::Source::detachBuffer(src); AL::Source::detachBuffer(src);
SoundBuffer *old = atchBufs[soundIndex]; SoundBuffer *old = atchBufs[srcIndex];
if (old) if (old)
SoundBuffer::deref(old); SoundBuffer::deref(old);
atchBufs[soundIndex] = SoundBuffer::ref(buffer); atchBufs[srcIndex] = SoundBuffer::ref(buffer);
AL::Source::attachBuffer(src, buffer->alBuffer); AL::Source::attachBuffer(src, buffer->alBuffer);