From 968f5d7693805d5df8c5e1a0a878ae0e57e1cb5a Mon Sep 17 00:00:00 2001 From: ehofman Date: Fri, 9 Oct 2009 09:00:53 +0000 Subject: [PATCH] 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. --- simgear/sound/sample_group.cxx | 61 +++------------- simgear/sound/sample_group.hxx | 5 -- simgear/sound/soundmgr_openal.cxx | 111 ++++++++++++++++++++++++++---- simgear/sound/soundmgr_openal.hxx | 32 +++++++-- 4 files changed, 133 insertions(+), 76 deletions(-) diff --git a/simgear/sound/sample_group.cxx b/simgear/sound/sample_group.cxx index 183a4894..dabf79bf 100644 --- a/simgear/sound/sample_group.cxx +++ b/simgear/sound/sample_group.cxx @@ -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; diff --git a/simgear/sound/sample_group.hxx b/simgear/sound/sample_group.hxx index e1ca92d1..b3170696 100644 --- a/simgear/sound/sample_group.hxx +++ b/simgear/sound/sample_group.hxx @@ -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; } diff --git a/simgear/sound/soundmgr_openal.cxx b/simgear/sound/soundmgr_openal.cxx index 98d7354b..4f49416a 100644 --- a/simgear/sound/soundmgr_openal.cxx +++ b/simgear/sound/soundmgr_openal.cxx @@ -34,6 +34,7 @@ #endif #include +#include #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::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; + + 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; + } - _free_sources.push_back(source); - _sources_in_use.erase(_sources_in_use.begin()+i, - _sources_in_use.begin()+i+1); - break; + // 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"); } } diff --git a/simgear/sound/soundmgr_openal.hxx b/simgear/sound/soundmgr_openal.hxx index 24e8f159..ef46df8f 100644 --- a/simgear/sound/soundmgr_openal.hxx +++ b/simgear/sound/soundmgr_openal.hxx @@ -60,14 +60,25 @@ #include "sample_group.hxx" #include "sample_openal.hxx" -using std::map; using std::string; -typedef map < string, SGSharedPtr > 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 > 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 _free_sources; vector _sources_in_use; -- 2.39.5