OpenAL buffer management; add a buffer cache to prevent loading the same sample in memory twice. Especially useful to save memory for multi-aircraft configurations and (later) for AI models.
This commit is contained in:
@@ -76,6 +76,7 @@ SGSampleGroup::~SGSampleGroup ()
|
||||
if ( sample->is_valid_source() && sample->is_playing() ) {
|
||||
sample->no_valid_source();
|
||||
_smgr->release_source( sample->get_source() );
|
||||
_smgr->release_buffer( sample );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,40 +98,8 @@ void SGSampleGroup::update( double dt ) {
|
||||
//
|
||||
// a request to start playing a sound has been filed.
|
||||
//
|
||||
ALboolean looping = sample->get_looping() ? AL_TRUE : AL_FALSE;
|
||||
|
||||
if ( !sample->is_valid_buffer() ) {
|
||||
// sample was not yet loaded or removed again
|
||||
|
||||
// TODO: Create a buffer cache that checks whether a file is already present
|
||||
// as an OpenAL buffer since buffers can be shared among sources.
|
||||
load_file(sample);
|
||||
if ( testForALError("load sample") ) {
|
||||
throw sg_exception("Failed to load sound sample.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// create an OpenAL buffer handle
|
||||
ALuint buffer;
|
||||
alGenBuffers(1, &buffer);
|
||||
if ( testForALError("generate buffer") ) {
|
||||
throw sg_exception("Failed to generate OpenAL buffer.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy data to the internal OpenAL buffer
|
||||
const ALvoid *data = sample->get_data();
|
||||
ALenum format = sample->get_format();
|
||||
ALsizei size = sample->get_size();
|
||||
ALsizei freq = sample->get_frequency();
|
||||
alBufferData( buffer, format, data, size, freq );
|
||||
sample->free_data();
|
||||
if ( testForALError("buffer add data") ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sample->set_buffer(buffer);
|
||||
}
|
||||
if ( _smgr->request_buffer(sample) == SGSoundMgr::NO_BUFFER )
|
||||
continue;
|
||||
|
||||
if ( _tied_to_listener && _smgr->has_changed() ) {
|
||||
sample->set_base_position( _smgr->get_position_vec() );
|
||||
@@ -139,6 +108,7 @@ void SGSampleGroup::update( double dt ) {
|
||||
}
|
||||
|
||||
// start playing the sample
|
||||
ALboolean looping = sample->get_looping() ? AL_TRUE : AL_FALSE;
|
||||
ALuint buffer = sample->get_buffer();
|
||||
ALuint source = _smgr->request_source();
|
||||
if (alIsSource(source) == AL_TRUE && alIsBuffer(buffer) == AL_TRUE)
|
||||
@@ -186,7 +156,6 @@ void SGSampleGroup::update( double dt ) {
|
||||
sample->no_valid_source();
|
||||
sample->stop();
|
||||
_smgr->release_source( source );
|
||||
|
||||
}
|
||||
}
|
||||
testForALError("update");
|
||||
@@ -216,7 +185,10 @@ bool SGSampleGroup::remove( const string &refname ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_samples.erase( sample_it );
|
||||
// remove the sources buffer
|
||||
_smgr->release_buffer( sample_it->second );
|
||||
_samples.erase( refname );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -397,23 +369,6 @@ void SGSampleGroup::update_sample_config( SGSoundSample *sample ) {
|
||||
}
|
||||
}
|
||||
|
||||
ALvoid
|
||||
SGSampleGroup::load_file(SGSoundSample *sample) {
|
||||
if (sample->is_file()) {
|
||||
unsigned int size;
|
||||
int freq, format;
|
||||
void *data;
|
||||
|
||||
string sample_name = sample->get_sample_name();
|
||||
_smgr->load(sample_name, &data, &format, &size, &freq);
|
||||
|
||||
sample->set_data( (unsigned char *)data );
|
||||
sample->set_frequency( freq );
|
||||
sample->set_format( format );
|
||||
sample->set_size( size );
|
||||
}
|
||||
}
|
||||
|
||||
void SGSampleGroup::set_volume( float vol )
|
||||
{
|
||||
_volume = vol;
|
||||
|
||||
@@ -151,11 +151,6 @@ public:
|
||||
*/
|
||||
void set_orientation( SGVec3f ori );
|
||||
|
||||
/**
|
||||
* load the data of the sound sample
|
||||
*/
|
||||
void load_file(SGSoundSample *sound);
|
||||
|
||||
inline void tie_to_listener() { _tied_to_listener = true; }
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
@@ -77,6 +78,7 @@ SGSoundMgr::SGSoundMgr() :
|
||||
// destructor
|
||||
|
||||
SGSoundMgr::~SGSoundMgr() {
|
||||
|
||||
stop();
|
||||
#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
|
||||
_alut_init--;
|
||||
@@ -151,6 +153,16 @@ void SGSoundMgr::stop() {
|
||||
if (_working) {
|
||||
_working = false;
|
||||
|
||||
// clear any OpenAL buffers before shutting down
|
||||
buffer_map_iterator buffers_current = _buffers.begin();
|
||||
buffer_map_iterator buffers_end = _buffers.end();
|
||||
for ( ; buffers_current != buffers_end; ++buffers_current ) {
|
||||
refUint ref = buffers_current->second;
|
||||
ALuint buffer = ref.id;
|
||||
alDeleteBuffers(1, &buffer);
|
||||
_buffers.erase( buffers_current );
|
||||
}
|
||||
|
||||
_context = alcGetCurrentContext();
|
||||
_device = alcGetContextsDevice(_context);
|
||||
alcMakeContextCurrent(NULL);
|
||||
@@ -196,7 +208,7 @@ void SGSoundMgr::unbind ()
|
||||
|
||||
void SGSoundMgr::update( double dt )
|
||||
{
|
||||
// nothing to do in the regular update,e verything is done on the following
|
||||
// nothing to do in the regular update, everything is done on the following
|
||||
// function
|
||||
}
|
||||
|
||||
@@ -325,7 +337,6 @@ unsigned int SGSoundMgr::request_source()
|
||||
if (_free_sources.size() > 0) {
|
||||
source = _free_sources.back();
|
||||
_free_sources.pop_back();
|
||||
|
||||
_sources_in_use.push_back(source);
|
||||
}
|
||||
|
||||
@@ -335,21 +346,93 @@ unsigned int SGSoundMgr::request_source()
|
||||
// Free up a source id for further use
|
||||
void SGSoundMgr::release_source( unsigned int source )
|
||||
{
|
||||
for (unsigned int i = 0; i<_sources_in_use.size(); i++) {
|
||||
if ( _sources_in_use[i] == source ) {
|
||||
ALint result;
|
||||
vector<ALuint>::iterator it;
|
||||
|
||||
alGetSourcei( source, AL_SOURCE_STATE, &result );
|
||||
if ( result == AL_PLAYING ) {
|
||||
alSourceStop( source );
|
||||
}
|
||||
testForALError("release source");
|
||||
it = std::find(_sources_in_use.begin(), _sources_in_use.end(), source);
|
||||
if ( it != _sources_in_use.end() ) {
|
||||
ALint result;
|
||||
|
||||
_free_sources.push_back(source);
|
||||
_sources_in_use.erase(_sources_in_use.begin()+i,
|
||||
_sources_in_use.begin()+i+1);
|
||||
break;
|
||||
alGetSourcei( source, AL_SOURCE_STATE, &result );
|
||||
if ( result == AL_PLAYING )
|
||||
alSourceStop( source );
|
||||
testForALError("release source");
|
||||
|
||||
_free_sources.push_back(source);
|
||||
_sources_in_use.erase(it, it+1);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
|
||||
{
|
||||
ALuint buffer = NO_BUFFER;
|
||||
|
||||
if ( !sample->is_valid_buffer() ) {
|
||||
// sample was not yet loaded or removed again
|
||||
string sample_name = sample->get_sample_name();
|
||||
|
||||
// see if the sample name is already cached
|
||||
buffer_map_iterator buffer_it = _buffers.find( sample_name );
|
||||
if ( buffer_it != _buffers.end() ) {
|
||||
buffer_it->second.refctr++;
|
||||
buffer = buffer_it->second.id;
|
||||
sample->set_buffer( buffer );
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// sample name was not found in the buffer cache.
|
||||
if ( sample->is_file() ) {
|
||||
unsigned int size;
|
||||
int freq, format;
|
||||
void *data;
|
||||
|
||||
load(sample_name, &data, &format, &size, &freq);
|
||||
sample->set_data( (unsigned char *)data );
|
||||
sample->set_frequency( freq );
|
||||
sample->set_format( format );
|
||||
sample->set_size( size );
|
||||
}
|
||||
|
||||
// create an OpenAL buffer handle
|
||||
alGenBuffers(1, &buffer);
|
||||
if ( !testForALError("generate buffer") ) {
|
||||
// Copy data to the internal OpenAL buffer
|
||||
|
||||
const ALvoid *data = sample->get_data();
|
||||
ALenum format = sample->get_format();
|
||||
ALsizei size = sample->get_size();
|
||||
ALsizei freq = sample->get_frequency();
|
||||
alBufferData( buffer, format, data, size, freq );
|
||||
sample->free_data();
|
||||
|
||||
if ( !testForALError("buffer add data") ) {
|
||||
sample->set_buffer(buffer);
|
||||
_buffers[sample_name] = refUint(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
buffer = sample->get_buffer();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void SGSoundMgr::release_buffer(SGSoundSample *sample)
|
||||
{
|
||||
string sample_name = sample->get_sample_name();
|
||||
|
||||
buffer_map_iterator buffer_it = _buffers.find( sample_name );
|
||||
if ( buffer_it == _buffers.end() ) {
|
||||
// buffer was not found
|
||||
return;
|
||||
}
|
||||
|
||||
sample->no_valid_buffer();
|
||||
buffer_it->second.refctr--;
|
||||
if (buffer_it->second.refctr == 0) {
|
||||
ALuint buffer = buffer_it->second.id;
|
||||
_buffers.erase( buffer_it );
|
||||
alDeleteBuffers(1, &buffer);
|
||||
testForALError("release buffer");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,14 +60,25 @@
|
||||
#include "sample_group.hxx"
|
||||
#include "sample_openal.hxx"
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
typedef map < string, SGSharedPtr<SGSampleGroup> > sample_group_map;
|
||||
struct refUint {
|
||||
unsigned int refctr;
|
||||
ALuint id;
|
||||
|
||||
refUint() { refctr = 0; id = (ALuint)-1; };
|
||||
refUint(ALuint i) { refctr = 1; id = i; };
|
||||
~refUint() {};
|
||||
};
|
||||
|
||||
typedef std::map < string, refUint > buffer_map;
|
||||
typedef buffer_map::iterator buffer_map_iterator;
|
||||
typedef buffer_map::const_iterator const_buffer_map_iterator;
|
||||
|
||||
typedef std::map < string, SGSharedPtr<SGSampleGroup> > sample_group_map;
|
||||
typedef sample_group_map::iterator sample_group_map_iterator;
|
||||
typedef sample_group_map::const_iterator const_sample_group_map_iterator;
|
||||
|
||||
|
||||
/**
|
||||
* Manage a collection of SGSampleGroup instances
|
||||
*/
|
||||
@@ -156,7 +167,7 @@ public:
|
||||
|
||||
/**
|
||||
* get a new OpenAL source id
|
||||
* returns NO_SOURCE is no source is available
|
||||
* returns NO_SOURCE if no source is available
|
||||
*/
|
||||
unsigned int request_source();
|
||||
|
||||
@@ -165,6 +176,18 @@ public:
|
||||
*/
|
||||
void release_source( unsigned int source );
|
||||
|
||||
/**
|
||||
* get a new OpenAL buffer id
|
||||
* returns NO_BUFFER if loading of the buffer failed.
|
||||
*/
|
||||
unsigned int request_buffer(SGSoundSample *sample);
|
||||
|
||||
/**
|
||||
* give back an OpenAL source id for further use.
|
||||
*/
|
||||
void release_buffer( SGSoundSample *sample );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* returns true if the position has changed
|
||||
@@ -197,6 +220,7 @@ private:
|
||||
ALfloat _listener_ori[6];
|
||||
|
||||
sample_group_map _sample_groups;
|
||||
buffer_map _buffers;
|
||||
|
||||
vector<ALuint> _free_sources;
|
||||
vector<ALuint> _sources_in_use;
|
||||
|
||||
Reference in New Issue
Block a user