Cache OpenAL buffers for easy reuse

This commit is contained in:
Chris Robinson 2012-03-19 10:33:06 -07:00
parent 91821ccd8c
commit 6c45d6668b
2 changed files with 87 additions and 45 deletions

View file

@ -313,35 +313,8 @@ public:
virtual void stop(); virtual void stop();
virtual bool isPlaying(); virtual bool isPlaying();
virtual void update(const float *pos); virtual void update(const float *pos);
static ALuint LoadBuffer(DecoderPtr decoder);
}; };
ALuint OpenAL_Sound::LoadBuffer(DecoderPtr decoder)
{
int srate;
ChannelConfig chans;
SampleType type;
ALenum format;
decoder->getInfo(&srate, &chans, &type);
format = getALFormat(chans, type);
std::vector<char> data(32768);
size_t got, total = 0;
while((got=decoder->read(&data[total], data.size()-total)) > 0)
{
total += got;
data.resize(total*2);
}
data.resize(total);
ALuint buf=0;
alGenBuffers(1, &buf);
alBufferData(buf, format, &data[0], total, srate);
return buf;
}
OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf) OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf)
: mOutput(output), mSource(src), mBuffer(buf) : mOutput(output), mSource(src), mBuffer(buf)
{ {
@ -352,8 +325,7 @@ OpenAL_Sound::~OpenAL_Sound()
alSourcei(mSource, AL_BUFFER, 0); alSourcei(mSource, AL_BUFFER, 0);
mOutput.mFreeSources.push_back(mSource); mOutput.mFreeSources.push_back(mSource);
alDeleteBuffers(1, &mBuffer); mOutput.bufferFinished(mBuffer);
alGetError();
} }
void OpenAL_Sound::stop() void OpenAL_Sound::stop()
@ -431,6 +403,15 @@ void OpenAL_Output::deinit()
alDeleteSources(mFreeSources.size(), mFreeSources.data()); alDeleteSources(mFreeSources.size(), mFreeSources.data());
mFreeSources.clear(); mFreeSources.clear();
} }
mBufferRefs.clear();
mUnusedBuffers.clear();
while(!mBufferCache.empty())
{
alDeleteBuffers(1, &mBufferCache.begin()->second);
mBufferCache.erase(mBufferCache.begin());
}
alcMakeContextCurrent(0); alcMakeContextCurrent(0);
if(mContext) if(mContext)
alcDestroyContext(mContext); alcDestroyContext(mContext);
@ -441,6 +422,63 @@ void OpenAL_Output::deinit()
} }
ALuint OpenAL_Output::getBuffer(const std::string &fname)
{
ALuint buf = 0;
NameMap::iterator iditer = mBufferCache.find(fname);
if(iditer != mBufferCache.end())
{
buf = iditer->second;
if(mBufferRefs[buf]++ == 0)
{
IDDq::iterator iter = std::find(mUnusedBuffers.begin(),
mUnusedBuffers.end(), buf);
if(iter != mUnusedBuffers.end())
mUnusedBuffers.erase(iter);
}
return buf;
}
throwALerror();
int srate;
ChannelConfig chans;
SampleType type;
ALenum format;
DecoderPtr decoder = mManager.getDecoder();
decoder->open(fname);
decoder->getInfo(&srate, &chans, &type);
format = getALFormat(chans, type);
std::vector<char> data(32768);
size_t got, total = 0;
while((got=decoder->read(&data[total], data.size()-total)) > 0)
{
total += got;
data.resize(total*2);
}
data.resize(total);
decoder->close();
alGenBuffers(1, &buf);
throwALerror();
alBufferData(buf, format, &data[0], total, srate);
mBufferCache[fname] = buf;
mBufferRefs[buf] = 1;
return buf;
}
void OpenAL_Output::bufferFinished(ALuint buf)
{
if(mBufferRefs.at(buf)-- == 1)
mUnusedBuffers.push_back(buf);
}
Sound* OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, bool loop) Sound* OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, bool loop)
{ {
throwALerror(); throwALerror();
@ -455,19 +493,14 @@ Sound* OpenAL_Output::playSound(const std::string &fname, float volume, float pi
try try
{ {
DecoderPtr decoder = mManager.getDecoder(); buf = getBuffer(fname);
decoder->open(fname);
buf = OpenAL_Sound::LoadBuffer(decoder);
throwALerror();
decoder->close();
sound.reset(new OpenAL_Sound(*this, src, buf)); sound.reset(new OpenAL_Sound(*this, src, buf));
} }
catch(std::exception &e) catch(std::exception &e)
{ {
mFreeSources.push_back(src); mFreeSources.push_back(src);
if(alIsBuffer(buf)) if(buf && alIsBuffer(buf))
alDeleteBuffers(1, &buf); bufferFinished(buf);
alGetError(); alGetError();
throw; throw;
} }
@ -509,19 +542,14 @@ Sound* OpenAL_Output::playSound3D(const std::string &fname, const float *pos, fl
try try
{ {
DecoderPtr decoder = mManager.getDecoder(); buf = getBuffer(fname);
decoder->open(fname);
buf = OpenAL_Sound::LoadBuffer(decoder);
throwALerror();
decoder->close();
sound.reset(new OpenAL_Sound(*this, src, buf)); sound.reset(new OpenAL_Sound(*this, src, buf));
} }
catch(std::exception &e) catch(std::exception &e)
{ {
mFreeSources.push_back(src); mFreeSources.push_back(src);
if(alIsBuffer(buf)) if(buf && alIsBuffer(buf))
alDeleteBuffers(1, &buf); bufferFinished(buf);
alGetError(); alGetError();
throw; throw;
} }

View file

@ -3,6 +3,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include <deque>
#include "alc.h" #include "alc.h"
#include "al.h" #include "al.h"
@ -22,6 +24,18 @@ namespace MWSound
typedef std::vector<ALuint> IDVec; typedef std::vector<ALuint> IDVec;
IDVec mFreeSources; IDVec mFreeSources;
typedef std::map<std::string,ALuint> NameMap;
NameMap mBufferCache;
typedef std::map<ALuint,ALuint> IDRefMap;
IDRefMap mBufferRefs;
typedef std::deque<ALuint> IDDq;
IDDq mUnusedBuffers;
ALuint getBuffer(const std::string &fname);
void bufferFinished(ALuint buffer);
virtual void init(const std::string &devname=""); virtual void init(const std::string &devname="");
virtual void deinit(); virtual void deinit();